From 12970c2da6ad0e29219be3ef198739816157696b Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 6 May 2026 04:24:48 +0200 Subject: [PATCH 1/7] PPCAsm: Fix string parsing when string contains escaped double quote Also fix write position not being advanced after a string --- src/Cemu/PPCAssembler/ppcAssembler.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Cemu/PPCAssembler/ppcAssembler.cpp b/src/Cemu/PPCAssembler/ppcAssembler.cpp index 7b89a068..ab1c20c9 100644 --- a/src/Cemu/PPCAssembler/ppcAssembler.cpp +++ b/src/Cemu/PPCAssembler/ppcAssembler.cpp @@ -2360,6 +2360,7 @@ bool _ppcAssembler_emitDataDirective(PPCAssemblerContext& internalInfo, ASM_DATA // write string bytes + null-termination character internalInfo.ctx->outputData.insert(internalInfo.ctx->outputData.end(), stringData.data(), stringData.data() + stringData.size()); internalInfo.ctx->outputData.emplace_back(0); + writeIndex = internalInfo.ctx->outputData.size(); continue; } // numeric constants @@ -2492,6 +2493,16 @@ bool ppcAssembler_assembleSingleInstruction(char const* text, PPCAssemblerInOut* internalInfo.listOperandStr.clear(); bool isInString = false; + auto isEscaped = [](const char* operandStartPtr, const char* ptr) + { + size_t backslashCount = 0; + while (ptr > operandStartPtr && ptr[-1] == '\\') + { + backslashCount++; + ptr--; + } + return (backslashCount & 1) != 0; + }; while (currentPtr < endPtr) { @@ -2500,7 +2511,7 @@ bool ppcAssembler_assembleSingleInstruction(char const* text, PPCAssemblerInOut* // find end of operand while (currentPtr < endPtr) { - if (*currentPtr == '"') + if (*currentPtr == '"' && !isEscaped(startPtr, currentPtr)) isInString=!isInString; if (*currentPtr == ',' && !isInString) @@ -3587,6 +3598,9 @@ void ppcAsmTestDisassembler() _testAsmArray({ 0x7f }, ".byte 0x7f"); _testAsmArray({ 0x74, 0x65, 0x73, 0x74, 0x00 }, ".byte \"test\""); _testAsmArray({ 0x41, 0x42, 0x43, 0x00, 0x74, 0x65, 0x73, 0x74, 0x00 }, ".byte \"ABC\", \"test\""); + _testAsmArray({ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x22, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00, 0x00 }, R"(.byte "hello\"world", 0)"); + _testAsmArray({ 0x61, 0x5c, 0x00, 0x00 }, R"(.byte "a\\", 0)"); + _testAsmArray({ 0x61, 0x5c, 0x22, 0x62, 0x00, 0x00 }, R"(.byte "a\\\"b", 0)"); } From e8bee64b5e9c63024921caca76bd1d45b2a574d2 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Wed, 6 May 2026 05:49:06 +0200 Subject: [PATCH 2/7] Latte: Fix rare corruption in buffer cache --- src/Cafe/HW/Latte/Core/LatteBufferCache.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp b/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp index de18df33..a561a20c 100644 --- a/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp +++ b/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp @@ -776,9 +776,8 @@ private: { TreeNode* treeNodeParent = (treeNode->parentNodeIndex != INVALID_NODE_INDEX) ? &m_treeNodes[treeNode->parentNodeIndex] : nullptr; ReleaseTreeNode(treeNode, true); - if (treeNodeParent && treeNodeParent->usedCount > 0) - PropagateMinValue(treeNodeParent); - // note - this will never shrink the tree, since there is at least one left or right neighbor + if (treeNodeParent) + CollapseNode(treeNodeParent, depth - 1); return; } // todo - redistribute values From 0d832c48b178e3f185798ad53ed7221a254f0e6b Mon Sep 17 00:00:00 2001 From: shinra-electric <50119606+shinra-electric@users.noreply.github.com> Date: Thu, 7 May 2026 12:44:07 +0100 Subject: [PATCH 3/7] CI: Disable fail-fast for macOS builds (#1901) --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 66329d0a..c1afde6c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -207,6 +207,7 @@ jobs: build-macos: runs-on: macos-14 strategy: + fail-fast: false matrix: arch: [x86_64, arm64] steps: From f3fecba313f146d5cf822491f0b3786ad606fceb Mon Sep 17 00:00:00 2001 From: bslhq Date: Fri, 8 May 2026 23:11:19 +0800 Subject: [PATCH 4/7] Migrate from SDL2 to SDL3 3.4.2 (#1847) --- CMakeLists.txt | 3 +- dependencies/vcpkg | 2 +- src/CMakeLists.txt | 2 +- src/gui/wxgui/CMakeLists.txt | 2 +- src/gui/wxgui/CemuApp.cpp | 35 ++ src/gui/wxgui/CemuApp.h | 6 + src/input/CMakeLists.txt | 4 - src/input/ControllerFactory.cpp | 2 +- src/input/InputManager.cpp | 21 +- src/input/InputManager.h | 2 + src/input/api/DSU/DSUControllerProvider.cpp | 78 ++-- src/input/api/DSU/DSUControllerProvider.h | 8 +- src/input/api/SDL/SDLController.cpp | 117 +++-- src/input/api/SDL/SDLController.h | 24 +- src/input/api/SDL/SDLControllerProvider.cpp | 400 ++++++++++-------- src/input/api/SDL/SDLControllerProvider.h | 43 +- .../api/Wiimote/WiimoteControllerProvider.cpp | 31 +- .../api/Wiimote/WiimoteControllerProvider.h | 2 + .../api/Wiimote/hidapi/HidapiWiimote.cpp | 20 +- src/input/api/Wiimote/hidapi/HidapiWiimote.h | 6 +- src/main.cpp | 3 +- src/util/ScreenSaver/ScreenSaver.h | 90 ++-- src/util/helpers/ConcurrentQueue.h | 4 +- vcpkg.json | 18 +- 24 files changed, 538 insertions(+), 385 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d3c28c3..fa662b94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,7 +148,7 @@ option(ENABLE_CUBEB "Enabled cubeb backend" ON) option(ENABLE_WXWIDGETS "Build with wxWidgets UI (Currently required)" ON) find_package(Threads REQUIRED) -find_package(SDL2 REQUIRED) +find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3) find_package(CURL REQUIRED) find_package(pugixml REQUIRED) find_package(RapidJSON REQUIRED) @@ -211,7 +211,6 @@ if (ENABLE_DISCORD_RPC) endif() if (ENABLE_HIDAPI) - find_package(hidapi REQUIRED) set(SUPPORTS_WIIMOTE ON) add_compile_definitions(HAS_HIDAPI) endif () diff --git a/dependencies/vcpkg b/dependencies/vcpkg index af752f21..c3867e71 160000 --- a/dependencies/vcpkg +++ b/dependencies/vcpkg @@ -1 +1 @@ -Subproject commit af752f21c9d79ba3df9cb0250ce2233933f58486 +Subproject commit c3867e714dd3a51c272826eea77267876517ed99 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b60a5e21..213da956 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -207,7 +207,7 @@ target_link_libraries(CemuBin PRIVATE CemuGui CemuInput CemuUtil - SDL2::SDL2 + SDL3::SDL3 ) if(UNIX AND NOT APPLE) diff --git a/src/gui/wxgui/CMakeLists.txt b/src/gui/wxgui/CMakeLists.txt index 9a2e880d..c2fc235b 100644 --- a/src/gui/wxgui/CMakeLists.txt +++ b/src/gui/wxgui/CMakeLists.txt @@ -147,7 +147,7 @@ target_link_libraries(CemuWxGui PRIVATE libzip::zip ZArchive::zarchive CemuComponents - SDL2::SDL2 + SDL3::SDL3 pugixml::pugixml CemuCafe PUBLIC diff --git a/src/gui/wxgui/CemuApp.cpp b/src/gui/wxgui/CemuApp.cpp index 7b381bc0..58e8c391 100644 --- a/src/gui/wxgui/CemuApp.cpp +++ b/src/gui/wxgui/CemuApp.cpp @@ -9,6 +9,7 @@ #include "config/LaunchSettings.h" #include "wxgui/GettingStartedDialog.h" #include "input/InputManager.h" +#include "input/api/SDL/SDLControllerProvider.h" #include "wxgui/helpers/wxHelpers.h" #include "Cemu/ncrypto/ncrypto.h" #include "wxgui/input/HotkeySettings.h" @@ -26,6 +27,7 @@ #include #include #include +#include #include "wxHelper.h" #include "Cafe/TitleList/TitleList.h" @@ -332,8 +334,18 @@ bool CemuApp::OnInit() #ifdef CEMU_DEBUG_ASSERT UnitTests(); #endif + +#if BOOST_OS_MACOS + SDLControllerProvider::InitSDL(); +#endif CemuCommonInit(); +#if BOOST_OS_MACOS + m_sdlEventPumpTimer = new wxTimer(this); + Bind(wxEVT_TIMER, &CemuApp::OnSDLEventPumpTimer, this); + m_sdlEventPumpTimer->Start(5, wxTIMER_CONTINUOUS); +#endif + wxInitAllImageHandlers(); // fill colour db @@ -389,8 +401,22 @@ bool CemuApp::OnInit() int CemuApp::OnExit() { +#if BOOST_OS_MACOS + if (m_sdlEventPumpTimer) + { + m_sdlEventPumpTimer->Stop(); + Unbind(wxEVT_TIMER, &CemuApp::OnSDLEventPumpTimer, this); + delete m_sdlEventPumpTimer; + m_sdlEventPumpTimer = nullptr; + } +#endif + wxApp::OnExit(); wxTheClipboard->Flush(); + InputManager::instance().Shutdown(); +#if BOOST_OS_MACOS + SDLControllerProvider::ShutdownSDL(); +#endif #if BOOST_OS_WINDOWS ExitProcess(0); #else @@ -398,6 +424,15 @@ int CemuApp::OnExit() #endif } +#if BOOST_OS_MACOS +void CemuApp::OnSDLEventPumpTimer(wxTimerEvent& event) +{ + // this callback is only used on macOS where SDL event functions need to be called on the main thread + // on other platforms SDLControllerProvider creates a separate thread for SDL event polling + SDLControllerProvider::PumpSDLEvents(); +} +#endif + #if BOOST_OS_WINDOWS void DumpThreadStackTrace(); #endif diff --git a/src/gui/wxgui/CemuApp.h b/src/gui/wxgui/CemuApp.h index b73d627d..7735ef74 100644 --- a/src/gui/wxgui/CemuApp.h +++ b/src/gui/wxgui/CemuApp.h @@ -3,6 +3,8 @@ #include class MainWindow; +class wxTimer; +class wxTimerEvent; class CemuApp : public wxApp { @@ -30,6 +32,10 @@ private: static std::vector GetAvailableTranslationLanguages(wxTranslations* translationsMgr); MainWindow* m_mainFrame = nullptr; +#if BOOST_OS_MACOS + void OnSDLEventPumpTimer(wxTimerEvent& event); + wxTimer* m_sdlEventPumpTimer = nullptr; +#endif wxLocale m_locale; std::vector m_availableTranslations; diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 4e5a990f..0b40a965 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -99,10 +99,6 @@ target_link_libraries(CemuInput PRIVATE CemuGui ) -if (ENABLE_HIDAPI) - target_link_libraries(CemuInput PRIVATE hidapi::hidapi) -endif() - if (ENABLE_BLUEZ) target_link_libraries(CemuInput PRIVATE bluez::bluez) endif () diff --git a/src/input/ControllerFactory.cpp b/src/input/ControllerFactory.cpp index 1284c6b1..968fb8f3 100644 --- a/src/input/ControllerFactory.cpp +++ b/src/input/ControllerFactory.cpp @@ -68,7 +68,7 @@ ControllerPtr ControllerFactory::CreateController(InputAPI::Type api, std::strin throw std::invalid_argument(fmt::format("invalid sdl uuid format: {}", uuid)); const auto guid_index = ConvertString(uuid.substr(0, index)); - const auto guid = SDL_JoystickGetGUIDFromString(std::string{uuid.substr(index + 1)}.c_str()); + const auto guid = SDL_StringToGUID(std::string{uuid.substr(index + 1)}.c_str()); if (display_name.empty()) return std::make_shared(guid, guid_index); diff --git a/src/input/InputManager.cpp b/src/input/InputManager.cpp index 5b228414..af99b261 100644 --- a/src/input/InputManager.cpp +++ b/src/input/InputManager.cpp @@ -53,8 +53,7 @@ InputManager::InputManager() InputManager::~InputManager() { - m_update_thread_shutdown.store(true); - m_update_thread.join(); + // destructors will not invoked forever, so we manually release resources in Shutdown(). } void InputManager::load() noexcept @@ -952,3 +951,21 @@ void InputManager::update_thread() std::this_thread::yield(); } } + +void InputManager::Shutdown() +{ + m_update_thread_shutdown = true; + + if (m_update_thread.joinable()) + { + m_update_thread.join(); + } + + for (auto& pad : m_vpad) pad.reset(); + for (auto& pad : m_wpad) pad.reset(); + + for (auto& providers : m_api_available) + { + providers.clear(); + } +} diff --git a/src/input/InputManager.h b/src/input/InputManager.h index 715d8f2e..7957fbf7 100644 --- a/src/input/InputManager.h +++ b/src/input/InputManager.h @@ -46,6 +46,8 @@ public: bool is_gameprofile_set(size_t player_index) const; + void Shutdown(); + EmulatedControllerPtr set_controller(EmulatedControllerPtr controller); EmulatedControllerPtr set_controller(size_t player_index, EmulatedController::Type type); EmulatedControllerPtr set_controller(size_t player_index, EmulatedController::Type type, const std::shared_ptr& controller); diff --git a/src/input/api/DSU/DSUControllerProvider.cpp b/src/input/api/DSU/DSUControllerProvider.cpp index fa00277c..a94aaae0 100644 --- a/src/input/api/DSU/DSUControllerProvider.cpp +++ b/src/input/api/DSU/DSUControllerProvider.cpp @@ -42,8 +42,24 @@ DSUControllerProvider::~DSUControllerProvider() if (m_running) { m_running = false; - m_writer_thread.join(); + // wake up the reader thread by sending a packet to self + if (m_socketWakeupEndpoint.port() != 0) + { + boost::asio::io_context io_context; + boost::asio::ip::udp::socket socket(io_context); + boost::system::error_code ec; + socket.open(m_socketWakeupEndpoint.protocol(), ec); + if (!ec) + { + std::array data{}; + socket.send_to(boost::asio::buffer(data), m_socketWakeupEndpoint, 0, ec); + } + else + cemuLog_log(LogType::Force, "DSUControllerProvider wakeup failed"); + } m_reader_thread.join(); + m_writerJobs.push(nullptr); // wake up writer thread by pushing an empty message + m_writer_thread.join(); } } @@ -84,14 +100,8 @@ bool DSUControllerProvider::connect() m_socket.close(); m_socket.open(ip::udp::v4()); - - // set timeout for our threads to give a chance to exit -#if BOOST_OS_WINDOWS - m_socket.set_option(boost::asio::detail::socket_option::integer{200}); -#elif BOOST_OS_LINUX || BOOST_OS_MACOS - timeval timeout{.tv_usec = 200 * 1000}; - setsockopt(m_socket.native_handle(), SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeval)); -#endif + m_socket.bind(ip::udp::endpoint(ip::udp::v4(), 0)); + m_socketWakeupEndpoint = ip::udp::endpoint(boost::asio::ip::address_v4::loopback(), m_socket.local_endpoint().port()); // reset data m_state = {}; m_prev_state = {}; @@ -189,19 +199,13 @@ uint32_t DSUControllerProvider::get_packet_index(uint8_t index) const void DSUControllerProvider::request_version() { auto msg = std::make_unique(m_uid); - - std::scoped_lock lock(m_writer_mutex); - m_writer_jobs.push(std::move(msg)); - m_writer_cond.notify_one(); + m_writerJobs.push(std::move(msg)); } void DSUControllerProvider::request_pad_info() { auto msg = std::make_unique(m_uid, 4, std::array{0, 1, 2, 3}); - - std::scoped_lock lock(m_writer_mutex); - m_writer_jobs.push(std::move(msg)); - m_writer_cond.notify_one(); + m_writerJobs.push(std::move(msg)); } void DSUControllerProvider::request_pad_info(uint8_t index) @@ -210,19 +214,13 @@ void DSUControllerProvider::request_pad_info(uint8_t index) return; auto msg = std::make_unique(m_uid, 1, std::array{index}); - - std::scoped_lock lock(m_writer_mutex); - m_writer_jobs.push(std::move(msg)); - m_writer_cond.notify_one(); + m_writerJobs.push(std::move(msg)); } void DSUControllerProvider::request_pad_data() { auto msg = std::make_unique(m_uid); - - std::scoped_lock lock(m_writer_mutex); - m_writer_jobs.push(std::move(msg)); - m_writer_cond.notify_one(); + m_writerJobs.push(std::move(msg)); } void DSUControllerProvider::request_pad_data(uint8_t index) @@ -231,10 +229,7 @@ void DSUControllerProvider::request_pad_data(uint8_t index) return; auto msg = std::make_unique(m_uid, index); - - std::scoped_lock lock(m_writer_mutex); - m_writer_jobs.push(std::move(msg)); - m_writer_cond.notify_one(); + m_writerJobs.push(std::move(msg)); } MotionSample DSUControllerProvider::get_motion_sample(uint8_t index) const @@ -259,8 +254,11 @@ void DSUControllerProvider::reader_thread() boost::asio::ip::udp::endpoint sender_endpoint; boost::system::error_code ec{}; const size_t len = m_socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint, 0, ec); + if (!m_running.load(std::memory_order_relaxed)) + break; if (ec) { + #ifdef DEBUG_DSU_CLIENT printf(" DSUControllerProvider::ReaderThread: exception %s\n", ec.what()); #endif @@ -336,6 +334,7 @@ void DSUControllerProvider::reader_thread() } index = info->GetIndex(); + cemu_assert(index < kMaxClients); #ifdef DEBUG_DSU_CLIENT printf(" DSUControllerProvider::ReaderThread: received PortInfo for index %d\n", index); #endif @@ -359,6 +358,7 @@ void DSUControllerProvider::reader_thread() } index = rsp->GetIndex(); + cemu_assert(index < kMaxClients); #ifdef DEBUG_DSU_CLIENT printf(" DSUControllerProvider::ReaderThread: received DataResponse for index %d\n", index); #endif @@ -384,20 +384,10 @@ void DSUControllerProvider::writer_thread() SetThreadName("DSU-writer"); while (m_running.load(std::memory_order_relaxed)) { - std::unique_lock lock(m_writer_mutex); - while (m_writer_jobs.empty()) - { - if (m_writer_cond.wait_for(lock, std::chrono::milliseconds(250)) == std::cv_status::timeout) - { - if (!m_running.load(std::memory_order_relaxed)) - return; - } - } - - const auto msg = std::move(m_writer_jobs.front()); - m_writer_jobs.pop(); - lock.unlock(); - + std::unique_ptr msg = m_writerJobs.pop(); + if (!m_running.load(std::memory_order_relaxed)) + return; + cemu_assert_debug(msg.get()); #ifdef DEBUG_DSU_CLIENT printf(" DSUControllerProvider::WriterThread: sending message: 0x%x (len: 0x%x)\n", (int)msg->GetMessageType(), msg->GetSize()); #endif @@ -457,4 +447,4 @@ DSUControllerProvider::ControllerState& DSUControllerProvider::ControllerState:: this->operator=(static_cast(data_response)); data = data_response.GetData(); return *this; -} +} \ No newline at end of file diff --git a/src/input/api/DSU/DSUControllerProvider.h b/src/input/api/DSU/DSUControllerProvider.h index 692da619..a0a933da 100644 --- a/src/input/api/DSU/DSUControllerProvider.h +++ b/src/input/api/DSU/DSUControllerProvider.h @@ -4,6 +4,7 @@ #include "input/api/DSU/DSUMessages.h" #include "input/api/ControllerProvider.h" +#include "util/helpers/ConcurrentQueue.h" #include @@ -87,7 +88,6 @@ public: void request_pad_data(); void request_pad_data(uint8_t index); - private: uint16 m_server_version = 0; @@ -98,20 +98,18 @@ private: void writer_thread(); void integrate_motion(uint8_t index, const DataResponse& data_response); - std::mutex m_writer_mutex; - std::condition_variable m_writer_cond; - uint32 m_uid; boost::asio::io_context m_io_service; boost::asio::ip::udp::endpoint m_receiver_endpoint; boost::asio::ip::udp::socket m_socket; + boost::asio::ip::udp::endpoint m_socketWakeupEndpoint; std::array m_state{}; std::array m_prev_state{}; mutable std::array m_mutex; mutable std::array m_wait_cond; - std::queue> m_writer_jobs; + ConcurrentQueue> m_writerJobs; std::array m_motion_handler; std::array m_last_motion_timestamp{}; diff --git a/src/input/api/SDL/SDLController.cpp b/src/input/api/SDL/SDLController.cpp index 57971ecc..4d181369 100644 --- a/src/input/api/SDL/SDLController.cpp +++ b/src/input/api/SDL/SDLController.cpp @@ -2,27 +2,30 @@ #include "input/api/SDL/SDLControllerProvider.h" -SDLController::SDLController(const SDL_JoystickGUID& guid, size_t guid_index) +SDLController::SDLController(const SDL_GUID& guid, size_t guid_index) : base_type(fmt::format("{}_", guid_index), fmt::format("Controller {}", guid_index + 1)), m_guid_index(guid_index), m_guid(guid) { char tmp[64]; - SDL_JoystickGetGUIDString(m_guid, tmp, std::size(tmp)); + SDL_GUIDToString(m_guid, tmp, std::size(tmp)); m_uuid += tmp; } -SDLController::SDLController(const SDL_JoystickGUID& guid, size_t guid_index, std::string_view display_name) +SDLController::SDLController(const SDL_GUID& guid, size_t guid_index, std::string_view display_name) : base_type(fmt::format("{}_", guid_index), display_name), m_guid_index(guid_index), m_guid(guid) { char tmp[64]; - SDL_JoystickGetGUIDString(m_guid, tmp, std::size(tmp)); + SDL_GUIDToString(m_guid, tmp, std::size(tmp)); m_uuid += tmp; } SDLController::~SDLController() { if (m_controller) - SDL_GameControllerClose(m_controller); + { + SDL_CloseGamepad(m_controller); + m_controller = nullptr; + } } bool SDLController::is_connected() @@ -33,8 +36,9 @@ bool SDLController::is_connected() return false; } - if (!SDL_GameControllerGetAttached(m_controller)) + if (!SDL_GamepadConnected(m_controller)) { + SDL_CloseGamepad(m_controller); m_controller = nullptr; return false; } @@ -45,49 +49,39 @@ bool SDLController::is_connected() bool SDLController::connect() { if (is_connected()) - { return true; - } m_has_rumble = false; - const auto index = m_provider->get_index(m_guid_index, m_guid); - std::scoped_lock lock(m_controller_mutex); - m_diid = SDL_JoystickGetDeviceInstanceID(index); - if (m_diid == -1) + + int gamepad_count = 0; + + SDL_JoystickID *gamepad_ids = SDL_GetGamepads(&gamepad_count); + + if (!gamepad_ids || index < 0 || index >= gamepad_count) return false; - m_controller = SDL_GameControllerOpen(index); + m_diid = gamepad_ids[index]; + SDL_free(gamepad_ids); + + m_controller = SDL_OpenGamepad(m_diid); + if (!m_controller) return false; - if (const char* name = SDL_GameControllerName(m_controller)) + if (const char* name = SDL_GetGamepadName(m_controller)) m_display_name = name; - for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) - { - m_buttons[i] = SDL_GameControllerHasButton(m_controller, (SDL_GameControllerButton)i); - } - - for (int i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i) - { - m_axis[i] = SDL_GameControllerHasAxis(m_controller, (SDL_GameControllerAxis)i); - } - - if (SDL_GameControllerHasSensor(m_controller, SDL_SENSOR_ACCEL)) - { - m_has_accel = true; - SDL_GameControllerSetSensorEnabled(m_controller, SDL_SENSOR_ACCEL, SDL_TRUE); - } - - if (SDL_GameControllerHasSensor(m_controller, SDL_SENSOR_GYRO)) - { - m_has_gyro = true; - SDL_GameControllerSetSensorEnabled(m_controller, SDL_SENSOR_GYRO, SDL_TRUE); - } - - m_has_rumble = SDL_GameControllerRumble(m_controller, 0, 0, 0) == 0; + for (size_t i = 0; i < SDL_GAMEPAD_BUTTON_COUNT; ++i) + m_buttons[i] = SDL_GamepadHasButton(m_controller, (SDL_GamepadButton)i); + for (size_t i = 0; i < SDL_GAMEPAD_AXIS_COUNT; ++i) + m_axis[i] = SDL_GamepadHasAxis(m_controller, (SDL_GamepadAxis)i); + if (SDL_GamepadHasSensor(m_controller, SDL_SENSOR_ACCEL)) + m_has_accel = SDL_SetGamepadSensorEnabled(m_controller, SDL_SENSOR_ACCEL, true); + if (SDL_GamepadHasSensor(m_controller, SDL_SENSOR_GYRO)) + m_has_gyro = SDL_SetGamepadSensorEnabled(m_controller, SDL_SENSOR_GYRO, true); + m_has_rumble = SDL_RumbleGamepad(m_controller, 0, 0, 0); return true; } @@ -96,12 +90,9 @@ void SDLController::start_rumble() std::scoped_lock lock(m_controller_mutex); if (is_connected() && !m_has_rumble) return; - if (m_settings.rumble <= 0) return; - - SDL_GameControllerRumble(m_controller, (Uint16)(m_settings.rumble * 0xFFFF), (Uint16)(m_settings.rumble * 0xFFFF), - 5 * 1000); + SDL_RumbleGamepad(m_controller, (Uint16)(m_settings.rumble * 0xFFFF), (Uint16)(m_settings.rumble * 0xFFFF), 5 * 1000); } void SDLController::stop_rumble() @@ -109,59 +100,47 @@ void SDLController::stop_rumble() std::scoped_lock lock(m_controller_mutex); if (is_connected() && !m_has_rumble) return; - - SDL_GameControllerRumble(m_controller, 0, 0, 0); + SDL_RumbleGamepad(m_controller, 0, 0, 0); } MotionSample SDLController::get_motion_sample() { if (is_connected() && has_motion()) - { return m_provider->motion_sample(m_diid); - } - return {}; } std::string SDLController::get_button_name(uint64 button) const { - if (const char* name = SDL_GameControllerGetStringForButton((SDL_GameControllerButton)button)) + if (const char* name = SDL_GetGamepadStringForButton((SDL_GamepadButton)button)) return name; - return base_type::get_button_name(button); } ControllerState SDLController::raw_state() { ControllerState result{}; - std::scoped_lock lock(m_controller_mutex); if (!is_connected()) return result; - - for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) + for (size_t i = 0; i < SDL_GAMEPAD_BUTTON_COUNT; ++i) { - if (m_buttons[i] && SDL_GameControllerGetButton(m_controller, (SDL_GameControllerButton)i)) + if (m_buttons[i] && SDL_GetGamepadButton(m_controller, (SDL_GamepadButton)i)) result.buttons.SetButtonState(i, true); } - if (m_axis[SDL_CONTROLLER_AXIS_LEFTX]) - result.axis.x = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_LEFTX) / 32767.0f; - - if (m_axis[SDL_CONTROLLER_AXIS_LEFTY]) - result.axis.y = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_LEFTY) / 32767.0f; - - if (m_axis[SDL_CONTROLLER_AXIS_RIGHTX]) - result.rotation.x = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_RIGHTX) / 32767.0f; - - if (m_axis[SDL_CONTROLLER_AXIS_RIGHTY]) - result.rotation.y = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_RIGHTY) / 32767.0f; - - if (m_axis[SDL_CONTROLLER_AXIS_TRIGGERLEFT]) - result.trigger.x = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) / 32767.0f; - - if (m_axis[SDL_CONTROLLER_AXIS_TRIGGERRIGHT]) - result.trigger.y = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) / 32767.0f; + if (m_axis[SDL_GAMEPAD_AXIS_LEFTX]) + result.axis.x = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_LEFTX) / 32767.0f; + if (m_axis[SDL_GAMEPAD_AXIS_LEFTY]) + result.axis.y = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_LEFTY) / 32767.0f; + if (m_axis[SDL_GAMEPAD_AXIS_RIGHTX]) + result.rotation.x = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_RIGHTX) / 32767.0f; + if (m_axis[SDL_GAMEPAD_AXIS_RIGHTY]) + result.rotation.y = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_RIGHTY) / 32767.0f; + if (m_axis[SDL_GAMEPAD_AXIS_LEFT_TRIGGER]) + result.trigger.x = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) / 32767.0f; + if (m_axis[SDL_GAMEPAD_AXIS_RIGHT_TRIGGER]) + result.trigger.y = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) / 32767.0f; return result; } diff --git a/src/input/api/SDL/SDLController.h b/src/input/api/SDL/SDLController.h index 0a0aa183..1a4dd8dc 100644 --- a/src/input/api/SDL/SDLController.h +++ b/src/input/api/SDL/SDLController.h @@ -3,13 +3,13 @@ #include "input/api/Controller.h" #include "input/api/SDL/SDLControllerProvider.h" -#include +#include class SDLController : public Controller { public: - SDLController(const SDL_JoystickGUID& guid, size_t guid_index); - SDLController(const SDL_JoystickGUID& guid, size_t guid_index, std::string_view display_name); + SDLController(const SDL_GUID& guid, size_t guid_index); + SDLController(const SDL_GUID& guid, size_t guid_index, std::string_view display_name); ~SDLController() override; @@ -32,29 +32,29 @@ public: MotionSample get_motion_sample() override; std::string get_button_name(uint64 button) const override; - const SDL_JoystickGUID& get_guid() const { return m_guid; } + const SDL_GUID& get_guid() const { return m_guid; } - constexpr static SDL_JoystickGUID kLeftJoyCon{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00,0x68 ,0x00 }; - constexpr static SDL_JoystickGUID kRightJoyCon{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00 }; - constexpr static SDL_JoystickGUID kSwitchProController{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x09, 0x20, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00 }; + constexpr static SDL_GUID kLeftJoyCon{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00,0x68 ,0x00 }; + constexpr static SDL_GUID kRightJoyCon{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00 }; + constexpr static SDL_GUID kSwitchProController{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x09, 0x20, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00 }; protected: ControllerState raw_state() override; private: - inline static SDL_JoystickGUID kEmptyGUID{}; + inline static SDL_GUID kEmptyGUID{}; size_t m_guid_index; - SDL_JoystickGUID m_guid; + SDL_GUID m_guid; std::recursive_mutex m_controller_mutex; - SDL_GameController* m_controller = nullptr; + SDL_Gamepad* m_controller = nullptr; SDL_JoystickID m_diid = -1; bool m_has_gyro = false; bool m_has_accel = false; bool m_has_rumble = false; - std::array m_buttons{}; - std::array m_axis{}; + std::array m_buttons{}; + std::array m_axis{}; }; diff --git a/src/input/api/SDL/SDLControllerProvider.cpp b/src/input/api/SDL/SDLControllerProvider.cpp index 9b21b306..3aa146a4 100644 --- a/src/input/api/SDL/SDLControllerProvider.cpp +++ b/src/input/api/SDL/SDLControllerProvider.cpp @@ -3,12 +3,12 @@ #include "input/api/SDL/SDLController.h" #include "util/helpers/TempState.h" -#include +#include #include struct SDL_JoystickGUIDHash { - std::size_t operator()(const SDL_JoystickGUID& guid) const + std::size_t operator()(const SDL_GUID& guid) const { return boost::hash_value(guid.data); } @@ -16,229 +16,285 @@ struct SDL_JoystickGUIDHash SDLControllerProvider::SDLControllerProvider() { - SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); - - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1"); - - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); - - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, "1"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STADIA, "1"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1"); - SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_LUNA, "1"); - - if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC | SDL_INIT_EVENTS) < 0) - throw std::runtime_error(fmt::format("couldn't initialize SDL: {}", SDL_GetError())); - - - if (SDL_GameControllerEventState(SDL_ENABLE) < 0) { - cemuLog_log(LogType::Force, "Couldn't enable SDL gamecontroller event polling: {}", SDL_GetError()); +#if !BOOST_OS_MACOS + std::scoped_lock _l(s_mutex); + if (s_initCount.fetch_add(1) == 0) + { + s_running = true; + s_thread = std::thread(&SDLControllerProvider::event_thread, this); } - - m_running = true; - m_thread = std::thread(&SDLControllerProvider::event_thread, this); +#endif } SDLControllerProvider::~SDLControllerProvider() { - if (m_running) +#if !BOOST_OS_MACOS + bool shutdownSDL = false; { - m_running = false; - // wake the thread with a quit event if it's currently waiting for events - SDL_Event evt; - evt.type = SDL_QUIT; - SDL_PushEvent(&evt); - // wait until thread exited - m_thread.join(); + std::scoped_lock _l(s_mutex); + if (s_initCount.fetch_sub(1) == 1) + { + cemu_assert_debug(s_running); + s_running = false; + shutdownSDL = true; + } } - SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC | SDL_INIT_EVENTS); + if (shutdownSDL) + { + // wake the thread with a quit event if it's currently waiting for events + SDL_Event evt; + SDL_zero(evt); + evt.type = SDL_EVENT_QUIT; + SDL_PushEvent(&evt); + if (s_thread.joinable()) + { + s_thread.join(); + } + } +#endif } std::vector> SDLControllerProvider::get_controllers() { std::vector> result; - std::unordered_map guid_counter; + std::unordered_map guid_counter; TempState lock(SDL_LockJoysticks, SDL_UnlockJoysticks); - for (int i = 0; i < SDL_NumJoysticks(); ++i) + int gamepad_count = 0; + SDL_JoystickID *gamepad_ids = SDL_GetGamepads(&gamepad_count); + if (gamepad_ids) { - if (SDL_JoystickGetDeviceType(i) == SDL_JOYSTICK_TYPE_GAMECONTROLLER) + for (size_t i = 0; i < gamepad_count; ++i) { - const auto guid = SDL_JoystickGetDeviceGUID(i); - + const auto guid = SDL_GetGamepadGUIDForID(gamepad_ids[i]); const auto it = guid_counter.try_emplace(guid, 0); - - if (auto* controller = SDL_GameControllerOpen(i)) - { - const char* name = SDL_GameControllerName(controller); - + if (const char* name = SDL_GetGamepadNameForID(gamepad_ids[i])) result.emplace_back(std::make_shared(guid, it.first->second, name)); - SDL_GameControllerClose(controller); - } else result.emplace_back(std::make_shared(guid, it.first->second)); - ++it.first->second; } + SDL_free(gamepad_ids); } - return result; } -int SDLControllerProvider::get_index(size_t guid_index, const SDL_JoystickGUID& guid) const +int SDLControllerProvider::get_index(size_t guid_index, const SDL_GUID& guid) const { size_t index = 0; - + int gamepad_count = 0; TempState lock(SDL_LockJoysticks, SDL_UnlockJoysticks); - for (int i = 0; i < SDL_NumJoysticks(); ++i) + SDL_JoystickID *gamepad_ids = SDL_GetGamepads(&gamepad_count); + if (gamepad_ids) { - if (SDL_JoystickGetDeviceType(i) == SDL_JOYSTICK_TYPE_GAMECONTROLLER) + for (size_t i = 0; i < gamepad_count; ++i) { - if(guid == SDL_JoystickGetDeviceGUID(i)) + if (guid == SDL_GetGamepadGUIDForID(gamepad_ids[i])) { if (index == guid_index) { + SDL_free(gamepad_ids); return i; } - ++index; } - } + SDL_free(gamepad_ids); } - return -1; } -MotionSample SDLControllerProvider::motion_sample(int diid) +MotionSample SDLControllerProvider::motion_sample(SDL_JoystickID diid) { - std::scoped_lock lock(m_motion_data_mtx[diid]); - return m_motion_data[diid]; + std::shared_lock lock(s_mutex); + auto it = s_motion_states.find(diid); + return (it != s_motion_states.end()) ? it->second.data : MotionSample{}; +} + +void SDLControllerProvider::InitSDL() +{ + SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH2, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STADIA, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1"); + SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_LUNA, "1"); + + if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC)) + { + throw std::runtime_error(fmt::format("couldn't initialize SDL: {}", SDL_GetError())); + } + + SDL_SetGamepadEventsEnabled(true); + if (!SDL_GamepadEventsEnabled()) + { + cemuLog_log(LogType::Force, "Couldn't enable SDL gamecontroller event polling: {}", SDL_GetError()); + } +} + +void SDLControllerProvider::ShutdownSDL() +{ + SDL_QuitSubSystem(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC); +} + +#if BOOST_OS_MACOS +void SDLControllerProvider::PumpSDLEvents() +{ + SDL_Event event; + while (SDL_PollEvent(&event)) + HandleSDLEvent(event); +} +#endif + +void SDLControllerProvider::HandleSDLEvent(SDL_Event& event) +{ + switch (event.type) + { + case SDL_EVENT_QUIT: + { + std::scoped_lock _l(s_mutex); + s_running = false; + break; + } + case SDL_EVENT_GAMEPAD_AXIS_MOTION: /**< Game controller axis motion */ + { + break; + } + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: /**< Game controller button pressed */ + { + break; + } + case SDL_EVENT_GAMEPAD_BUTTON_UP: /**< Game controller button released */ + { + break; + } + case SDL_EVENT_GAMEPAD_ADDED: /**< A new Game controller has been inserted into the system */ + { + std::scoped_lock _l(s_mutex); + InputManager::instance().on_device_changed(); + break; + } + case SDL_EVENT_GAMEPAD_REMOVED: /**< An opened Game controller has been removed */ + { + std::scoped_lock _l(s_mutex); + InputManager::instance().on_device_changed(); + s_motion_states.erase(event.gdevice.which); + break; + } + case SDL_EVENT_GAMEPAD_REMAPPED: /**< The controller mapping was updated */ + { + break; + } + case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: /**< Game controller touchpad was touched */ + { + break; + } + case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: /**< Game controller touchpad finger was moved */ + { + break; + } + case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: /**< Game controller touchpad finger was lifted */ + { + break; + } + case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: /**< Game controller sensor was updated */ + { + SDL_JoystickID id = event.gsensor.which; + uint64_t ts = event.gsensor.timestamp; + std::scoped_lock _l(s_mutex); + auto& state = s_motion_states[id]; + auto& tracking = state.tracking; + + if (event.gsensor.sensor == SDL_SENSOR_ACCEL) + { + const auto dif = ts - tracking.lastTimestampAccel; + if (dif <= 0) + { + break; + } + + if (dif >= 10000000000) + { + tracking.hasAcc = false; + tracking.hasGyro = false; + tracking.lastTimestampAccel = ts; + break; + } + + tracking.lastTimestampAccel = ts; + tracking.acc[0] = -event.gsensor.data[0] / 9.81f; + tracking.acc[1] = -event.gsensor.data[1] / 9.81f; + tracking.acc[2] = -event.gsensor.data[2] / 9.81f; + tracking.hasAcc = true; + } + if (event.gsensor.sensor == SDL_SENSOR_GYRO) + { + const auto dif = ts - tracking.lastTimestampGyro; + if (dif <= 0) + { + break; + } + + if (dif >= 10000000000) + { + tracking.hasAcc = false; + tracking.hasGyro = false; + tracking.lastTimestampGyro = ts; + break; + } + + tracking.lastTimestampGyro = ts; + tracking.gyro[0] = event.gsensor.data[0]; + tracking.gyro[1] = -event.gsensor.data[1]; + tracking.gyro[2] = -event.gsensor.data[2]; + tracking.hasGyro = true; + } + if (tracking.hasAcc && tracking.hasGyro) + { + auto ts = std::max(tracking.lastTimestampGyro, tracking.lastTimestampAccel); + + if (ts > tracking.lastTimestampIntegrate) + { + const auto tsDif = ts - tracking.lastTimestampIntegrate; + tracking.lastTimestampIntegrate = ts; + float tsDifD = (float)tsDif / 1000000000.0f; + + if (tsDifD >= 1.0f) + { + tsDifD = 1.0f; + } + + state.handler.processMotionSample(tsDifD, tracking.gyro.x, tracking.gyro.y, tracking.gyro.z, tracking.acc.x, -tracking.acc.y, -tracking.acc.z); + state.data = state.handler.getMotionSample(); + } + + tracking.hasAcc = false; + tracking.hasGyro = false; + } + break; + } + } } void SDLControllerProvider::event_thread() { +#if BOOST_OS_MACOS + cemu_assert(false); +#endif SetThreadName("SDL_events"); - while (m_running.load(std::memory_order_relaxed)) + InitSDL(); + while (s_running.load(std::memory_order_relaxed)) { SDL_Event event{}; SDL_WaitEvent(&event); - - switch (event.type) - { - case SDL_QUIT: - m_running = false; - return; - - case SDL_CONTROLLERAXISMOTION: /**< Game controller axis motion */ - { - break; - } - case SDL_CONTROLLERBUTTONDOWN: /**< Game controller button pressed */ - { - break; - } - case SDL_CONTROLLERBUTTONUP: /**< Game controller button released */ - { - break; - } - case SDL_CONTROLLERDEVICEADDED: /**< A new Game controller has been inserted into the system */ - { - InputManager::instance().on_device_changed(); - break; - } - case SDL_CONTROLLERDEVICEREMOVED: /**< An opened Game controller has been removed */ - { - InputManager::instance().on_device_changed(); - break; - } - case SDL_CONTROLLERDEVICEREMAPPED: /**< The controller mapping was updated */ - { - break; - } - case SDL_CONTROLLERTOUCHPADDOWN: /**< Game controller touchpad was touched */ - { - break; - } - case SDL_CONTROLLERTOUCHPADMOTION: /**< Game controller touchpad finger was moved */ - { - break; - } - case SDL_CONTROLLERTOUCHPADUP: /**< Game controller touchpad finger was lifted */ - { - break; - } - case SDL_CONTROLLERSENSORUPDATE: /**< Game controller sensor was updated */ - { - const auto index = event.csensor.which; - const auto ts = event.csensor.timestamp; - auto& motionTracking = m_motion_tracking[index]; - - if (event.csensor.sensor == SDL_SENSOR_ACCEL) - { - const auto dif = ts - motionTracking.lastTimestampAccel; - if (dif <= 0) - break; - - if (dif >= 10000) - { - motionTracking.hasAcc = false; - motionTracking.hasGyro = false; - motionTracking.lastTimestampAccel = ts; - break; - } - - motionTracking.lastTimestampAccel = ts; - motionTracking.acc[0] = -event.csensor.data[0] / 9.81f; - motionTracking.acc[1] = -event.csensor.data[1] / 9.81f; - motionTracking.acc[2] = -event.csensor.data[2] / 9.81f; - motionTracking.hasAcc = true; - } - if (event.csensor.sensor == SDL_SENSOR_GYRO) - { - const auto dif = ts - motionTracking.lastTimestampGyro; - if (dif <= 0) - break; - - if (dif >= 10000) - { - motionTracking.hasAcc = false; - motionTracking.hasGyro = false; - motionTracking.lastTimestampGyro = ts; - break; - } - motionTracking.lastTimestampGyro = ts; - motionTracking.gyro[0] = event.csensor.data[0]; - motionTracking.gyro[1] = -event.csensor.data[1]; - motionTracking.gyro[2] = -event.csensor.data[2]; - motionTracking.hasGyro = true; - } - if (motionTracking.hasAcc && motionTracking.hasGyro) - { - auto ts = std::max(motionTracking.lastTimestampGyro, motionTracking.lastTimestampAccel); - if (ts > motionTracking.lastTimestampIntegrate) - { - const auto tsDif = ts - motionTracking.lastTimestampIntegrate; - motionTracking.lastTimestampIntegrate = ts; - float tsDifD = (float)tsDif / 1000.0f; - if (tsDifD >= 1.0f) - tsDifD = 1.0f; - m_motion_handler[index].processMotionSample(tsDifD, motionTracking.gyro.x, motionTracking.gyro.y, motionTracking.gyro.z, motionTracking.acc.x, -motionTracking.acc.y, -motionTracking.acc.z); - - std::scoped_lock lock(m_motion_data_mtx[index]); - m_motion_data[index] = m_motion_handler[index].getMotionSample(); - } - motionTracking.hasAcc = false; - motionTracking.hasGyro = false; - } - break; - } - } + HandleSDLEvent(event); } + ShutdownSDL(); } diff --git a/src/input/api/SDL/SDLControllerProvider.h b/src/input/api/SDL/SDLControllerProvider.h index b736ba75..53864234 100644 --- a/src/input/api/SDL/SDLControllerProvider.h +++ b/src/input/api/SDL/SDLControllerProvider.h @@ -1,5 +1,5 @@ #pragma once -#include +#include #include "input/motion/MotionHandler.h" #include "input/api/ControllerProvider.h" @@ -7,9 +7,9 @@ #define HAS_SDL 1 #endif -static bool operator==(const SDL_JoystickGUID& g1, const SDL_JoystickGUID& g2) +static bool operator==(const SDL_GUID& g1, const SDL_GUID& g2) { - return memcmp(&g1, &g2, sizeof(SDL_JoystickGUID)) == 0; + return memcmp(&g1, &g2, sizeof(SDL_GUID)) == 0; } class SDLControllerProvider : public ControllerProviderBase @@ -24,19 +24,30 @@ public: std::vector> get_controllers() override; - int get_index(size_t guid_index, const SDL_JoystickGUID& guid) const; + int get_index(size_t guid_index, const SDL_GUID& guid) const; - MotionSample motion_sample(int diid); + MotionSample motion_sample(SDL_JoystickID diid); + + // exposed for manual event handling on macOS +#if BOOST_OS_MACOS + static void InitSDL(); + static void ShutdownSDL(); + static void PumpSDLEvents(); +#endif private: void event_thread(); - - std::atomic_bool m_running = false; - std::thread m_thread; + static void HandleSDLEvent(union SDL_Event& event); +#if !BOOST_OS_MACOS + static void InitSDL(); + static void ShutdownSDL(); +#endif - std::array m_motion_handler{}; - std::array m_motion_data{}; - std::array m_motion_data_mtx{}; + // there is only one SDL instance, for this reason all of our state can be static + inline static std::atomic_int s_initCount{0}; + inline static std::shared_mutex s_mutex; + inline static std::atomic_bool s_running = false; + inline static std::thread s_thread; struct MotionInfoTracking { @@ -49,6 +60,14 @@ private: glm::vec3 acc{}; }; - std::array m_motion_tracking{}; + struct MotionState + { + WiiUMotionHandler handler; + MotionSample data; + MotionInfoTracking tracking; + MotionState() = default; + }; + + inline static std::unordered_map s_motion_states{}; }; diff --git a/src/input/api/Wiimote/WiimoteControllerProvider.cpp b/src/input/api/Wiimote/WiimoteControllerProvider.cpp index b55c9cd4..76b8f63b 100644 --- a/src/input/api/Wiimote/WiimoteControllerProvider.cpp +++ b/src/input/api/Wiimote/WiimoteControllerProvider.cpp @@ -25,9 +25,31 @@ WiimoteControllerProvider::~WiimoteControllerProvider() if (m_running) { m_running = false; - m_writer_thread.join(); - m_reader_thread.join(); - m_connectionThread.join(); + + { + std::scoped_lock lock(m_writer_mutex); + m_writer_cond.notify_all(); + } + + { + std::scoped_lock lock(m_connectionMutex); + m_connectionCond.notify_all(); + } + + if (m_writer_thread.joinable()) + { + m_writer_thread.join(); + } + + if (m_reader_thread.joinable()) + { + m_reader_thread.join(); + } + + if (m_connectionThread.joinable()) + { + m_connectionThread.join(); + } } } @@ -169,7 +191,8 @@ void WiimoteControllerProvider::connectionThread() m_connectedDevices.clear(); std::ranges::move(devices, std::back_inserter(m_connectedDevices)); } - std::this_thread::sleep_for(std::chrono::seconds(2)); + std::unique_lock lock(m_connectionMutex); + m_connectionCond.wait_for(lock, std::chrono::seconds(2)); } } diff --git a/src/input/api/Wiimote/WiimoteControllerProvider.h b/src/input/api/Wiimote/WiimoteControllerProvider.h index 90f28d5c..4e813023 100644 --- a/src/input/api/Wiimote/WiimoteControllerProvider.h +++ b/src/input/api/Wiimote/WiimoteControllerProvider.h @@ -80,6 +80,8 @@ private: std::shared_mutex m_device_mutex; std::thread m_connectionThread; + std::mutex m_connectionMutex; + std::condition_variable m_connectionCond; std::vector m_connectedDevices; std::mutex m_connectedDeviceMutex; struct Wiimote diff --git a/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp b/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp index 5780909f..02e12cee 100644 --- a/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp +++ b/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp @@ -7,18 +7,18 @@ static constexpr uint16 WIIMOTE_MP_PRODUCT_ID = 0x0330; static constexpr uint16 WIIMOTE_MAX_INPUT_REPORT_LENGTH = 22; static constexpr auto PRO_CONTROLLER_NAME = L"Nintendo RVL-CNT-01-UC"; -HidapiWiimote::HidapiWiimote(hid_device* dev, std::string_view path) +HidapiWiimote::HidapiWiimote(SDL_hid_device* dev, std::string_view path) : m_handle(dev), m_path(path) { } bool HidapiWiimote::write_data(const std::vector &data) { - return hid_write(m_handle, data.data(), data.size()) >= 0; + return SDL_hid_write(m_handle, data.data(), data.size()) >= 0; } std::optional> HidapiWiimote::read_data() { std::array read_data{}; - const auto result = hid_read(m_handle, read_data.data(), WIIMOTE_MAX_INPUT_REPORT_LENGTH); + const auto result = SDL_hid_read(m_handle, read_data.data(), WIIMOTE_MAX_INPUT_REPORT_LENGTH); if (result < 0) return {}; return {{read_data.cbegin(), read_data.cbegin() + result}}; @@ -26,24 +26,24 @@ std::optional> HidapiWiimote::read_data() { std::vector HidapiWiimote::get_devices() { std::vector wiimote_devices; - hid_init(); - const auto device_enumeration = hid_enumerate(WIIMOTE_VENDOR_ID, 0x0); + SDL_hid_init(); + const auto device_enumeration = SDL_hid_enumerate(WIIMOTE_VENDOR_ID, 0x0); for (auto it = device_enumeration; it != nullptr; it = it->next){ if (it->product_id != WIIMOTE_PRODUCT_ID && it->product_id != WIIMOTE_MP_PRODUCT_ID) continue; if (std::wcscmp(it->product_string, PRO_CONTROLLER_NAME) == 0) continue; - auto dev = hid_open_path(it->path); + auto dev = SDL_hid_open_path(it->path); if (!dev){ - cemuLog_logDebug(LogType::Force, "Unable to open Wiimote device at {}: {}", it->path, boost::nowide::narrow(hid_error(nullptr))); + cemuLog_logDebug(LogType::Force, "Unable to open Wiimote device at {}: {}", it->path, SDL_GetError()); } else { - hid_set_nonblocking(dev, true); + SDL_hid_set_nonblocking(dev, true); wiimote_devices.push_back(std::make_shared(dev, it->path)); } } - hid_free_enumeration(device_enumeration); + SDL_hid_free_enumeration(device_enumeration); return wiimote_devices; } @@ -55,5 +55,5 @@ bool HidapiWiimote::operator==(const WiimoteDevice& rhs) const { } HidapiWiimote::~HidapiWiimote() { - hid_close(m_handle); + SDL_hid_close(m_handle); } diff --git a/src/input/api/Wiimote/hidapi/HidapiWiimote.h b/src/input/api/Wiimote/hidapi/HidapiWiimote.h index 952a36f0..ffb83541 100644 --- a/src/input/api/Wiimote/hidapi/HidapiWiimote.h +++ b/src/input/api/Wiimote/hidapi/HidapiWiimote.h @@ -1,11 +1,11 @@ #pragma once #include -#include +#include class HidapiWiimote : public WiimoteDevice { public: - HidapiWiimote(hid_device* dev, std::string_view path); + HidapiWiimote(SDL_hid_device* dev, std::string_view path); ~HidapiWiimote() override; bool write_data(const std::vector &data) override; @@ -15,7 +15,7 @@ public: static std::vector get_devices(); private: - hid_device* m_handle; + SDL_hid_device* m_handle; const std::string m_path; }; diff --git a/src/main.cpp b/src/main.cpp index adce2680..05c3246b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,7 +31,8 @@ #endif #define SDL_MAIN_HANDLED -#include +#include +#include #if BOOST_OS_LINUX #define _putenv(__s) putenv((char*)(__s)) diff --git a/src/util/ScreenSaver/ScreenSaver.h b/src/util/ScreenSaver/ScreenSaver.h index 45291122..cb543c7e 100644 --- a/src/util/ScreenSaver/ScreenSaver.h +++ b/src/util/ScreenSaver/ScreenSaver.h @@ -1,40 +1,62 @@ #include "Cemu/Logging/CemuLogging.h" -#include +#include class ScreenSaver { public: - static void SetInhibit(bool inhibit) - { - // temporary workaround because feature crashes on macOS -#if BOOST_OS_MACOS - return; -#endif - // Initialize video subsystem if necessary - if (SDL_WasInit(SDL_INIT_VIDEO) == 0) - { - int initErr = SDL_InitSubSystem(SDL_INIT_VIDEO); - if (initErr) - { - cemuLog_log(LogType::Force, "Could not disable screen saver (SDL video subsystem initialization error)"); - } - } - // Toggle SDL's screen saver inhibition - if (inhibit) - { - SDL_DisableScreenSaver(); - if (SDL_IsScreenSaverEnabled() == SDL_TRUE) - { - cemuLog_log(LogType::Force, "Could not verify if screen saver was disabled (`SDL_IsScreenSaverEnabled()` returned SDL_TRUE)"); - } - } - else - { - SDL_EnableScreenSaver(); - if (SDL_IsScreenSaverEnabled() == SDL_FALSE) - { - cemuLog_log(LogType::Force, "Could not verify if screen saver was re-enabled (`SDL_IsScreenSaverEnabled()` returned SDL_FALSE)"); - } - } - }; + static void SetInhibit(bool inhibit) + { + bool* inhibitArg = new bool(inhibit); + + if (!SDL_RunOnMainThread(SetInhibitCallback, inhibitArg, false)) + { + delete inhibitArg; + cemuLog_log(LogType::Force, "Failed to schedule screen saver logic on main thread: {}", SDL_GetError()); + } + } + +private: + static void SDLCALL SetInhibitCallback(void* userdata) + { + if (!userdata) + { + return; + } + + auto* inhibitArg = static_cast(userdata); + bool inhibit = *inhibitArg; + delete inhibitArg; + + if (SDL_WasInit(SDL_INIT_VIDEO) == 0) + { + if (!SDL_InitSubSystem(SDL_INIT_VIDEO)) + { + cemuLog_log(LogType::Force, "Could not disable screen saver (SDL video subsystem initialization error: {})", SDL_GetError()); + return; + } + } + + if (inhibit) + { + if (!SDL_DisableScreenSaver()) + { + cemuLog_log(LogType::Force, "Could not disable screen saver: {}", SDL_GetError()); + } + else if (SDL_ScreenSaverEnabled()) + { + cemuLog_log(LogType::Force, "Could not verify if screen saver was disabled (`SDL_IsScreenSaverEnabled()` returned true)"); + } + } + else + { + if (!SDL_EnableScreenSaver()) + { + cemuLog_log(LogType::Force, "Could not enable screen saver: {}", SDL_GetError()); + } + else if (!SDL_ScreenSaverEnabled()) + { + cemuLog_log(LogType::Force, "Could not verify if screen saver was re-enabled (`SDL_IsScreenSaverEnabled()` returned false)"); + } + } + } }; diff --git a/src/util/helpers/ConcurrentQueue.h b/src/util/helpers/ConcurrentQueue.h index 2e7347b7..7cd370ee 100644 --- a/src/util/helpers/ConcurrentQueue.h +++ b/src/util/helpers/ConcurrentQueue.h @@ -62,7 +62,7 @@ public: m_condVar.wait(mlock); } - auto val = m_queue.front(); + auto val = std::move(m_queue.front()); m_queue.pop(); return val; } @@ -75,7 +75,7 @@ public: m_condVar.wait(mlock); } - item = m_queue.front(); + item = std::move(m_queue.front()); m_queue.pop(); } diff --git a/vcpkg.json b/vcpkg.json index 634b7e5a..576c7b27 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -11,7 +11,10 @@ "default-features": false }, "rapidjson", - "sdl2", + { + "name": "sdl3", + "default-features": false + }, "boost-tokenizer", "boost-container", "boost-program-options", @@ -29,7 +32,6 @@ "name": "fmt", "version>=": "12.1.0" }, - "hidapi", "libpng", "glm", { @@ -37,7 +39,13 @@ "default-features": false }, "zstd", - "wxwidgets", + { + "name": "wxwidgets", + "default-features": false, + "features": [ + "debug-support" + ] + }, "openssl", { "name": "curl", @@ -67,8 +75,8 @@ "version": "15.1.0" }, { - "name": "sdl2", - "version": "2.32.10" + "name": "sdl3", + "version": "3.4.2" }, { "name": "wxwidgets", From 0fc7403543dd337255693abf835519a04e6db44b Mon Sep 17 00:00:00 2001 From: Emma Date: Sat, 9 May 2026 13:03:25 +0000 Subject: [PATCH 5/7] build+Latte: Add working ENABLE_OPENGL and ENABLE_VULKAN build options (#1887) --- BUILD.md | 2 +- CMakeLists.txt | 9 +- src/CMakeLists.txt | 89 ++++++----- src/Cafe/CMakeLists.txt | 114 ++++++++------ src/Cafe/CafeSystem.cpp | 2 +- src/Cafe/GameProfile/GameProfile.cpp | 8 +- src/Cafe/GameProfile/GameProfile.h | 4 +- src/Cafe/HW/Latte/Core/FetchShader.cpp | 21 ++- src/Cafe/HW/Latte/Core/FetchShader.h | 2 +- src/Cafe/HW/Latte/Core/LatteBufferData.cpp | 4 +- .../HW/Latte/Core/LatteDefaultShaders.cpp | 87 ----------- src/Cafe/HW/Latte/Core/LatteDefaultShaders.h | 13 -- src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp | 3 + src/Cafe/HW/Latte/Core/LatteRingBuffer.h | 1 + src/Cafe/HW/Latte/Core/LatteShader.cpp | 113 +++++++++----- src/Cafe/HW/Latte/Core/LatteShaderCache.cpp | 143 +++++++++++++----- src/Cafe/HW/Latte/Core/LatteSurfaceCopy.cpp | 1 - src/Cafe/HW/Latte/Core/LatteTexture.cpp | 3 + src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp | 5 + .../HW/Latte/Core/LatteTextureReadback.cpp | 5 - src/Cafe/HW/Latte/Core/LatteTiming.cpp | 4 + .../LatteDecompiler.cpp | 18 ++- .../LatteDecompilerAnalyzer.cpp | 10 +- .../LatteDecompilerInternal.h | 2 +- .../HW/Latte/Renderer/Metal/MetalRenderer.cpp | 2 +- .../OpenGL}/LatteShaderGL.cpp | 0 .../Renderer/OpenGL/OpenGLRendererCore.cpp | 73 +-------- .../Renderer/OpenGL/OpenGLSurfaceCopy.cpp | 89 ++++++++++- .../Latte/Renderer/OpenGL/OpenGLSurfaceCopy.h | 14 ++ src/Cafe/HW/Latte/Renderer/RendererCore.cpp | 132 ++++++++++++++++ src/Cafe/HW/Latte/Renderer/RendererCore.h | 11 ++ .../HW/Latte/Renderer/RendererOuputShader.cpp | 63 +++++--- .../Vulkan/VulkanPipelineCompiler.cpp | 53 +------ .../Renderer/Vulkan/VulkanRendererCore.cpp | 1 + src/config/ActiveSettings.cpp | 30 +++- src/config/CemuConfig.cpp | 10 +- src/config/CemuConfig.h | 15 +- src/gui/wxgui/CMakeLists.txt | 18 ++- src/gui/wxgui/CemuApp.cpp | 4 + src/gui/wxgui/GameProfileWindow.cpp | 8 +- src/gui/wxgui/GameProfileWindow.h | 2 +- src/gui/wxgui/GeneralSettings2.cpp | 99 ++++++++---- src/gui/wxgui/GeneralSettings2.h | 6 +- src/gui/wxgui/MainWindow.cpp | 45 +++--- src/gui/wxgui/PadViewFrame.cpp | 21 ++- src/gui/wxgui/wxWindowSystem.cpp | 4 +- src/imgui/CMakeLists.txt | 18 ++- src/imgui/imgui_extension.cpp | 6 +- src/main.cpp | 10 +- 49 files changed, 858 insertions(+), 539 deletions(-) delete mode 100644 src/Cafe/HW/Latte/Core/LatteDefaultShaders.cpp delete mode 100644 src/Cafe/HW/Latte/Core/LatteDefaultShaders.h rename src/Cafe/HW/Latte/{Core => Renderer/OpenGL}/LatteShaderGL.cpp (100%) create mode 100644 src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.h create mode 100644 src/Cafe/HW/Latte/Renderer/RendererCore.cpp create mode 100644 src/Cafe/HW/Latte/Renderer/RendererCore.h diff --git a/BUILD.md b/BUILD.md index 458ffba1..ff99caa3 100644 --- a/BUILD.md +++ b/BUILD.md @@ -244,7 +244,7 @@ Example usage: `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DENABLE_SDL=ON - | CEMU_CXX_FLAGS | | Flags passed straight to the compiler, e.g. `-march=native`, `-Wall`, `/W3` | "" | | | ENABLE_CUBEB | | Enable cubeb audio backend | ON | | | ENABLE_DISCORD_RPC | | Enable Discord Rich presence support | ON | | -| ENABLE_OPENGL | | Enable OpenGL graphics backend | ON | Currently required | +| ENABLE_OPENGL | | Enable OpenGL graphics backend | ON | | | ENABLE_HIDAPI | | Enable HIDAPI (used for Wiimote controller API) | ON | | | ENABLE_SDL | | Enable SDLController controller API | ON | Currently required | | ENABLE_VCPKG | | Use VCPKG package manager to obtain dependencies | ON | | diff --git a/CMakeLists.txt b/CMakeLists.txt index fa662b94..cdc77156 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,11 +110,13 @@ endif() if (APPLE) set(ENABLE_METAL_DEFAULT ON) + set(ENABLE_OPENGL_DEFAULT OFF) else() set(ENABLE_METAL_DEFAULT OFF) + set(ENABLE_OPENGL_DEFAULT ON) endif() -option(ENABLE_OPENGL "Enables the OpenGL backend" ON) +option(ENABLE_OPENGL "Enables the OpenGL backend" ${ENABLE_OPENGL_DEFAULT}) option(ENABLE_VULKAN "Enables the Vulkan backend" ON) option(ENABLE_METAL "Enables the Metal backend" ${ENABLE_METAL_DEFAULT}) option(ENABLE_DISCORD_RPC "Enables the Discord Rich Presence feature" ON) @@ -194,16 +196,19 @@ endif() if (ENABLE_VULKAN) include_directories("dependencies/Vulkan-Headers/include") + add_compile_definitions(ENABLE_VULKAN) + add_compile_definitions(VK_NO_PROTOTYPES) endif() if (ENABLE_OPENGL) find_package(OpenGL REQUIRED) + add_compile_definitions(ENABLE_OPENGL) endif() if (ENABLE_METAL) include_directories(${CMAKE_SOURCE_DIR}/dependencies/metal-cpp) - add_definitions(-DENABLE_METAL=1) + add_compile_definitions(ENABLE_METAL) endif() if (ENABLE_DISCORD_RPC) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 213da956..863bdd07 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,18 +13,24 @@ if(MSVC) add_compile_definitions(WIN32_LEAN_AND_MEAN CURL_STATICLIB) elseif(UNIX) if(APPLE) - add_compile_definitions( - _XOPEN_SOURCE - VK_USE_PLATFORM_MACOS_MVK - VK_USE_PLATFORM_METAL_EXT - ) + if (ENABLE_VULKAN) + add_compile_definitions( + _XOPEN_SOURCE + VK_USE_PLATFORM_MACOS_MVK + VK_USE_PLATFORM_METAL_EXT + ) + else() + add_compile_definitions(_XOPEN_SOURCE) + endif() else() - add_compile_definitions( - VK_USE_PLATFORM_XLIB_KHR # legacy. Do we need to support XLIB surfaces? - VK_USE_PLATFORM_XCB_KHR - ) - if (ENABLE_WAYLAND) - add_compile_definitions(VK_USE_PLATFORM_WAYLAND_KHR) + if (ENABLE_VULKAN) + add_compile_definitions( + VK_USE_PLATFORM_XLIB_KHR # legacy. Do we need to support XLIB surfaces? + VK_USE_PLATFORM_XCB_KHR + ) + if (ENABLE_WAYLAND) + add_compile_definitions(VK_USE_PLATFORM_WAYLAND_KHR) + endif() endif() endif() # warnings @@ -35,8 +41,6 @@ elseif(UNIX) add_compile_options(-Wno-multichar -Wno-invalid-offsetof -Wno-switch -Wno-ignored-attributes -Wno-deprecated-enum-enum-conversion) endif() -add_compile_definitions(VK_NO_PROTOTYPES) - set(CMAKE_INCLUDE_CURRENT_DIR ON) # on msvc we can reuse the PCH from CemuCommon, on other platforms it's not straight forward to reuse the PCH so we opt out of it @@ -119,6 +123,16 @@ if (MACOS_BUNDLE) COMMAND ${CMAKE_COMMAND} ARGS -E copy_directory "${CMAKE_SOURCE_DIR}/bin/${folder}" "$/Contents/SharedSupport/${folder}") endforeach(folder) + if (ENABLE_VULKAN) + if (EXISTS "/usr/local/lib/libMoltenVK.dylib") + set(MOLTENVK_PATH "/usr/local/lib/libMoltenVK.dylib") + elseif (EXISTS "/opt/homebrew/lib/libMoltenVK.dylib") + set(MOLTENVK_PATH "/opt/homebrew/lib/libMoltenVK.dylib") + else() + message(FATAL_ERROR "failed to find libMoltenVK.dylib") + endif () + endif() + if (ENABLE_LIBUSB) if(CMAKE_BUILD_TYPE STREQUAL "Debug") set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/debug/lib/libusb-1.0.0.dylib") @@ -127,23 +141,20 @@ if (MACOS_BUNDLE) endif() endif() - if (EXISTS "/usr/local/lib/libMoltenVK.dylib") - set(MOLTENVK_PATH "/usr/local/lib/libMoltenVK.dylib") - elseif (EXISTS "/opt/homebrew/lib/libMoltenVK.dylib") - set(MOLTENVK_PATH "/opt/homebrew/lib/libMoltenVK.dylib") - else() - message(FATAL_ERROR "failed to find libMoltenVK.dylib") - endif () set(UPDATE_SH_PATH "${CMAKE_SOURCE_DIR}/src/resource/update.sh") - set(APP_BUNDLE_DIR "$") set(FRAMEWORKS_DIR "${APP_BUNDLE_DIR}/Contents/Frameworks") set(RESOURCES_DIR "${APP_BUNDLE_DIR}/Contents/Resources") + if (ENABLE_VULKAN) + add_custom_command(TARGET CemuBin POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${MOLTENVK_PATH}" + "${FRAMEWORKS_DIR}/libMoltenVK.dylib" + ) + endif() + add_custom_command(TARGET CemuBin POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - "${MOLTENVK_PATH}" - "${FRAMEWORKS_DIR}/libMoltenVK.dylib" COMMAND ${CMAKE_COMMAND} -E copy "${UPDATE_SH_PATH}" "${RESOURCES_DIR}/update.sh" @@ -174,20 +185,22 @@ if (MACOS_BUNDLE) endif() else() if(APPLE) - find_library(MOLTENVK_LIBRARY - NAMES MoltenVK moltenvk libMoltenVK.dylib - PATHS /usr/local/lib /opt/homebrew/lib - ) - if(MOLTENVK_LIBRARY) - message(STATUS "Found MoltenVK: ${MOLTENVK_LIBRARY}") - target_link_libraries(CemuBin PRIVATE ${MOLTENVK_LIBRARY}) - else() - message(WARNING "libMoltenVK.dylib not found") - endif() - set_target_properties(CemuBin PROPERTIES - BUILD_WITH_INSTALL_RPATH TRUE - INSTALL_RPATH "/usr/local/lib;/opt/homebrew/lib" - ) + if (ENABLE_VULKAN) + find_library(MOLTENVK_LIBRARY + NAMES MoltenVK moltenvk libMoltenVK.dylib + PATHS /usr/local/lib /opt/homebrew/lib + ) + if(MOLTENVK_LIBRARY) + message(STATUS "Found MoltenVK: ${MOLTENVK_LIBRARY}") + target_link_libraries(CemuBin PRIVATE ${MOLTENVK_LIBRARY}) + else() + message(WARNING "libMoltenVK.dylib not found") + endif() + set_target_properties(CemuBin PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + INSTALL_RPATH "/usr/local/lib;/opt/homebrew/lib" + ) + endif() endif() endif() diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index 16202b8f..b509e722 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -106,8 +106,6 @@ add_library(CemuCafe HW/Latte/Core/LatteCachedFBO.h HW/Latte/Core/LatteCommandProcessor.cpp HW/Latte/Core/LatteConst.h - HW/Latte/Core/LatteDefaultShaders.cpp - HW/Latte/Core/LatteDefaultShaders.h HW/Latte/Core/LatteDraw.h HW/Latte/Core/LatteGSCopyShaderParser.cpp HW/Latte/Core/Latte.h @@ -127,7 +125,6 @@ add_library(CemuCafe HW/Latte/Core/LatteShaderCache.cpp HW/Latte/Core/LatteShaderCache.h HW/Latte/Core/LatteShader.cpp - HW/Latte/Core/LatteShaderGL.cpp HW/Latte/Core/LatteShader.h HW/Latte/Core/LatteSoftware.cpp HW/Latte/Core/LatteSoftware.h @@ -163,58 +160,14 @@ add_library(CemuCafe HW/Latte/LegacyShaderDecompiler/LatteDecompilerInstructions.h HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h HW/Latte/LegacyShaderDecompiler/LatteDecompilerRegisterDataTypeTracker.cpp - HW/Latte/Renderer/OpenGL/CachedFBOGL.h - HW/Latte/Renderer/OpenGL/LatteTextureGL.cpp - HW/Latte/Renderer/OpenGL/LatteTextureGL.h - HW/Latte/Renderer/OpenGL/LatteTextureViewGL.cpp - HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h - HW/Latte/Renderer/OpenGL/OpenGLQuery.cpp - HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp - HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp - HW/Latte/Renderer/OpenGL/OpenGLRenderer.h - HW/Latte/Renderer/OpenGL/OpenGLRendererStreamout.cpp - HW/Latte/Renderer/OpenGL/OpenGLRendererUniformData.cpp - HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.cpp - HW/Latte/Renderer/OpenGL/OpenGLTextureReadback.h - HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp - HW/Latte/Renderer/OpenGL/RendererShaderGL.h - HW/Latte/Renderer/OpenGL/TextureReadbackGL.cpp HW/Latte/Renderer/Renderer.cpp HW/Latte/Renderer/Renderer.h + HW/Latte/Renderer/RendererCore.cpp + HW/Latte/Renderer/RendererCore.h HW/Latte/Renderer/RendererOuputShader.cpp HW/Latte/Renderer/RendererOuputShader.h HW/Latte/Renderer/RendererShader.cpp HW/Latte/Renderer/RendererShader.h - HW/Latte/Renderer/Vulkan/CachedFBOVk.cpp - HW/Latte/Renderer/Vulkan/CachedFBOVk.h - HW/Latte/Renderer/Vulkan/CocoaSurface.h - HW/Latte/Renderer/Vulkan/LatteTextureViewVk.cpp - HW/Latte/Renderer/Vulkan/LatteTextureViewVk.h - HW/Latte/Renderer/Vulkan/LatteTextureVk.cpp - HW/Latte/Renderer/Vulkan/LatteTextureVk.h - HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp - HW/Latte/Renderer/Vulkan/RendererShaderVk.h - HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp - HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h - HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp - HW/Latte/Renderer/Vulkan/VKRBase.h - HW/Latte/Renderer/Vulkan/VKRMemoryManager.cpp - HW/Latte/Renderer/Vulkan/VKRMemoryManager.h - HW/Latte/Renderer/Vulkan/VKRPipelineInfo.cpp - HW/Latte/Renderer/Vulkan/VsyncDriver.cpp - HW/Latte/Renderer/Vulkan/VsyncDriver.h - HW/Latte/Renderer/Vulkan/VulkanAPI.cpp - HW/Latte/Renderer/Vulkan/VulkanAPI.h - HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp - HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.h - HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp - HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.h - HW/Latte/Renderer/Vulkan/VulkanQuery.cpp - HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp - HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp - HW/Latte/Renderer/Vulkan/VulkanRenderer.h - HW/Latte/Renderer/Vulkan/VulkanSurfaceCopy.cpp - HW/Latte/Renderer/Vulkan/VulkanTextureReadback.h HW/Latte/ShaderInfo/ShaderDescription.cpp HW/Latte/ShaderInfo/ShaderInfo.h HW/Latte/ShaderInfo/ShaderInstanceInfo.cpp @@ -538,12 +491,73 @@ add_library(CemuCafe TitleList/TitleList.h ) +if (ENABLE_OPENGL) + target_sources(CemuCafe PRIVATE + HW/Latte/Renderer/OpenGL/CachedFBOGL.h + HW/Latte/Renderer/OpenGL/LatteShaderGL.cpp + HW/Latte/Renderer/OpenGL/LatteTextureGL.cpp + HW/Latte/Renderer/OpenGL/LatteTextureGL.h + HW/Latte/Renderer/OpenGL/LatteTextureViewGL.cpp + HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h + HW/Latte/Renderer/OpenGL/OpenGLQuery.cpp + HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp + HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp + HW/Latte/Renderer/OpenGL/OpenGLRenderer.h + HW/Latte/Renderer/OpenGL/OpenGLRendererStreamout.cpp + HW/Latte/Renderer/OpenGL/OpenGLRendererUniformData.cpp + HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.cpp + HW/Latte/Renderer/OpenGL/OpenGLTextureReadback.h + HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp + HW/Latte/Renderer/OpenGL/RendererShaderGL.h + HW/Latte/Renderer/OpenGL/TextureReadbackGL.cpp + ) +endif() + +if (ENABLE_VULKAN) + target_sources(CemuCafe PRIVATE + HW/Latte/Renderer/Vulkan/CachedFBOVk.cpp + HW/Latte/Renderer/Vulkan/CachedFBOVk.h + HW/Latte/Renderer/Vulkan/CocoaSurface.h + HW/Latte/Renderer/Vulkan/LatteTextureViewVk.cpp + HW/Latte/Renderer/Vulkan/LatteTextureViewVk.h + HW/Latte/Renderer/Vulkan/LatteTextureVk.cpp + HW/Latte/Renderer/Vulkan/LatteTextureVk.h + HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp + HW/Latte/Renderer/Vulkan/RendererShaderVk.h + HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp + HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h + HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp + HW/Latte/Renderer/Vulkan/VKRBase.h + HW/Latte/Renderer/Vulkan/VKRMemoryManager.cpp + HW/Latte/Renderer/Vulkan/VKRMemoryManager.h + HW/Latte/Renderer/Vulkan/VKRPipelineInfo.cpp + HW/Latte/Renderer/Vulkan/VsyncDriver.cpp + HW/Latte/Renderer/Vulkan/VsyncDriver.h + HW/Latte/Renderer/Vulkan/VulkanAPI.cpp + HW/Latte/Renderer/Vulkan/VulkanAPI.h + HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp + HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.h + HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp + HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.h + HW/Latte/Renderer/Vulkan/VulkanQuery.cpp + HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp + HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp + HW/Latte/Renderer/Vulkan/VulkanRenderer.h + HW/Latte/Renderer/Vulkan/VulkanSurfaceCopy.cpp + HW/Latte/Renderer/Vulkan/VulkanTextureReadback.h + ) +endif() + if(APPLE) target_sources(CemuCafe PRIVATE - HW/Latte/Renderer/Vulkan/CocoaSurface.mm HW/Latte/Renderer/MetalView.mm HW/Latte/Renderer/MetalView.h ) + if (ENABLE_VULKAN) + target_sources(CemuCafe PRIVATE + HW/Latte/Renderer/Vulkan/CocoaSurface.mm + ) + endif() endif() if(ENABLE_METAL) diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index f016773c..fc0d1ae6 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -251,7 +251,7 @@ void InfoLog_PrintActiveSettings() if (!GetConfig().vk_accurate_barriers.GetValue()) cemuLog_log(LogType::Force, "Accurate barriers are disabled!"); } -#if ENABLE_METAL +#ifdef ENABLE_METAL else if (ActiveSettings::GetGraphicsAPI() == GraphicAPI::kMetal) { cemuLog_log(LogType::Force, "Async compile: {}", GetConfig().async_compile.GetValue() ? "true" : "false"); diff --git a/src/Cafe/GameProfile/GameProfile.cpp b/src/Cafe/GameProfile/GameProfile.cpp index 9ee0a696..5c15d7c3 100644 --- a/src/Cafe/GameProfile/GameProfile.cpp +++ b/src/Cafe/GameProfile/GameProfile.cpp @@ -226,7 +226,7 @@ bool GameProfile::Load(uint64_t title_id) m_graphics_api = (GraphicAPI)graphicsApi.value; gameProfile_loadEnumOption(iniParser, "accurateShaderMul", m_accurateShaderMul); -#if ENABLE_METAL +#ifdef ENABLE_METAL gameProfile_loadBooleanOption2(iniParser, "shaderFastMath", m_shaderFastMath); gameProfile_loadEnumOption(iniParser, "metalBufferCacheMode2", m_metalBufferCacheMode); gameProfile_loadEnumOption(iniParser, "positionInvariance2", m_positionInvariance); @@ -311,7 +311,7 @@ void GameProfile::Save(uint64_t title_id) fs->writeLine("[Graphics]"); WRITE_ENTRY(accurateShaderMul); -#if ENABLE_METAL +#ifdef ENABLE_METAL WRITE_ENTRY(shaderFastMath); WRITE_ENTRY_NUMBERED(metalBufferCacheMode, 2); WRITE_ENTRY_NUMBERED(positionInvariance, 2); @@ -346,7 +346,7 @@ void GameProfile::ResetOptional() // graphic settings m_accurateShaderMul = AccurateShaderMulOption::True; -#if ENABLE_METAL +#ifdef ENABLE_METAL m_shaderFastMath = true; m_metalBufferCacheMode = MetalBufferCacheMode::Auto; m_positionInvariance = PositionInvariance::Auto; @@ -371,7 +371,7 @@ void GameProfile::Reset() // graphic settings m_accurateShaderMul = AccurateShaderMulOption::True; -#if ENABLE_METAL +#ifdef ENABLE_METAL m_shaderFastMath = true; m_metalBufferCacheMode = MetalBufferCacheMode::Auto; m_positionInvariance = PositionInvariance::Auto; diff --git a/src/Cafe/GameProfile/GameProfile.h b/src/Cafe/GameProfile/GameProfile.h index 5155beac..06c2a13b 100644 --- a/src/Cafe/GameProfile/GameProfile.h +++ b/src/Cafe/GameProfile/GameProfile.h @@ -29,7 +29,7 @@ public: [[nodiscard]] const std::optional& GetGraphicsAPI() const { return m_graphics_api; } [[nodiscard]] const AccurateShaderMulOption& GetAccurateShaderMul() const { return m_accurateShaderMul; } -#if ENABLE_METAL +#ifdef ENABLE_METAL [[nodiscard]] bool GetShaderFastMath() const { return m_shaderFastMath; } [[nodiscard]] MetalBufferCacheMode GetBufferCacheMode() const { return m_metalBufferCacheMode; } [[nodiscard]] PositionInvariance GetPositionInvariance() const { return m_positionInvariance; } @@ -57,7 +57,7 @@ private: // graphic settings std::optional m_graphics_api{}; AccurateShaderMulOption m_accurateShaderMul = AccurateShaderMulOption::True; -#if ENABLE_METAL +#ifdef ENABLE_METAL bool m_shaderFastMath = true; MetalBufferCacheMode m_metalBufferCacheMode = MetalBufferCacheMode::Auto; PositionInvariance m_positionInvariance = PositionInvariance::Auto; diff --git a/src/Cafe/HW/Latte/Core/FetchShader.cpp b/src/Cafe/HW/Latte/Core/FetchShader.cpp index 7d724e13..4ea633ba 100644 --- a/src/Cafe/HW/Latte/Core/FetchShader.cpp +++ b/src/Cafe/HW/Latte/Core/FetchShader.cpp @@ -11,7 +11,7 @@ #include "HW/Latte/Renderer/Renderer.h" #include "util/containers/LookupTableL3.h" #include "util/helpers/fspinlock.h" -#if ENABLE_METAL +#ifdef ENABLE_METAL #include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h" #endif #include /* SHA1_DIGEST_LENGTH */ @@ -108,21 +108,28 @@ void LatteShader_calculateFSKey(LatteFetchShader* fetchShader) key = std::rotl(key, 8); key += (uint64)attrib->semanticId; key = std::rotl(key, 8); - if (g_renderer->GetType() == RendererAPI::Metal) + switch(g_renderer->GetType()) + { +#ifdef ENABLE_METAL + case RendererAPI::Metal: { key += (uint64)attrib->offset; key = std::rotl(key, 7); + break; } - else +#endif + default: { key += (uint64)(attrib->offset & 3); key = std::rotl(key, 2); + break; + } } } } // todo - also hash invalid buffer groups? -#if ENABLE_METAL +#ifdef ENABLE_METAL if (g_renderer->GetType() == RendererAPI::Metal) { for (sint32 g = 0; g < fetchShader->bufferGroups.size(); g++) @@ -171,7 +178,7 @@ void LatteFetchShader::CalculateFetchShaderVkHash() this->vkPipelineHashFragment = h; } -#if ENABLE_METAL +#ifdef ENABLE_METAL void LatteFetchShader::CheckIfVerticesNeedManualFetchMtl(uint32* contextRegister) { for (sint32 g = 0; g < bufferGroups.size(); g++) @@ -376,7 +383,7 @@ LatteFetchShader* LatteShaderRecompiler_createFetchShader(LatteFetchShader::Cach // these only make sense when vertex shader does not call FS? LatteShader_calculateFSKey(newFetchShader); newFetchShader->CalculateFetchShaderVkHash(); -#if ENABLE_METAL +#ifdef ENABLE_METAL newFetchShader->CheckIfVerticesNeedManualFetchMtl(contextRegister); #endif return newFetchShader; @@ -438,7 +445,7 @@ LatteFetchShader* LatteShaderRecompiler_createFetchShader(LatteFetchShader::Cach } LatteShader_calculateFSKey(newFetchShader); newFetchShader->CalculateFetchShaderVkHash(); -#if ENABLE_METAL +#ifdef ENABLE_METAL newFetchShader->CheckIfVerticesNeedManualFetchMtl(contextRegister); #endif diff --git a/src/Cafe/HW/Latte/Core/FetchShader.h b/src/Cafe/HW/Latte/Core/FetchShader.h index 54cf6ada..db72987b 100644 --- a/src/Cafe/HW/Latte/Core/FetchShader.h +++ b/src/Cafe/HW/Latte/Core/FetchShader.h @@ -55,7 +55,7 @@ struct LatteFetchShader void CalculateFetchShaderVkHash(); -#if ENABLE_METAL +#ifdef ENABLE_METAL void CheckIfVerticesNeedManualFetchMtl(uint32* contextRegister); #endif diff --git a/src/Cafe/HW/Latte/Core/LatteBufferData.cpp b/src/Cafe/HW/Latte/Core/LatteBufferData.cpp index 7620e6a7..33d148bb 100644 --- a/src/Cafe/HW/Latte/Core/LatteBufferData.cpp +++ b/src/Cafe/HW/Latte/Core/LatteBufferData.cpp @@ -9,7 +9,9 @@ #include "Cafe/GameProfile/GameProfile.h" #include "Cafe/HW/Latte/Core/LatteBufferCache.h" +#ifdef ENABLE_VULKAN #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h" +#endif template void rectGenerate4thVertex(uint32be* output, uint32be* input0, uint32be* input1, uint32be* input2) @@ -196,7 +198,7 @@ bool LatteBufferCache_Sync(uint32 minIndex, uint32 maxIndex, uint32 baseInstance fixedBufferSize += 128; -#if BOOST_OS_MACOS +#if BOOST_OS_MACOS && defined(ENABLE_VULKAN) if(bufferStride % 4 != 0) { if (g_renderer->GetType() == RendererAPI::Vulkan) diff --git a/src/Cafe/HW/Latte/Core/LatteDefaultShaders.cpp b/src/Cafe/HW/Latte/Core/LatteDefaultShaders.cpp deleted file mode 100644 index 7c590dbb..00000000 --- a/src/Cafe/HW/Latte/Core/LatteDefaultShaders.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "Cafe/HW/Latte/Core/Latte.h" -#include "Cafe/HW/Latte/Core/LatteDraw.h" -#include "Cafe/HW/Latte/Core/LatteShader.h" -#include "Cafe/HW/Latte/Core/LatteDefaultShaders.h" -#include "util/helpers/StringBuf.h" - -LatteDefaultShader_t* _copyShader_depthToColor; -LatteDefaultShader_t* _copyShader_colorToDepth; - -void LatteDefaultShader_pixelCopyShader_generateVSBody(StringBuf* vs) -{ - vs->add("#version 420\r\n"); - vs->add("out vec2 passUV;\r\n"); - vs->add("uniform vec4 uf_vertexOffsets[4];\r\n"); - vs->add("\r\n"); - vs->add("void main(){\r\n"); - vs->add("int vID = gl_VertexID;\r\n"); - vs->add("passUV = uf_vertexOffsets[vID].zw;\r\n"); - vs->add("gl_Position = vec4(uf_vertexOffsets[vID].xy, 0.0, 1.0);\r\n"); - vs->add("}\r\n"); -} - -GLuint gxShaderDepr_compileRaw(StringBuf* strSourceVS, StringBuf* strSourceFS); -GLuint gxShaderDepr_compileRaw(const std::string& vertex_source, const std::string& fragment_source); - -LatteDefaultShader_t* LatteDefaultShader_getPixelCopyShader_depthToColor() -{ - if (_copyShader_depthToColor != 0) - return _copyShader_depthToColor; - catchOpenGLError(); - LatteDefaultShader_t* defaultShader = (LatteDefaultShader_t*)malloc(sizeof(LatteDefaultShader_t)); - memset(defaultShader, 0, sizeof(LatteDefaultShader_t)); - - StringBuf fCStr_vertexShader(1024 * 16); - LatteDefaultShader_pixelCopyShader_generateVSBody(&fCStr_vertexShader); - - StringBuf fCStr_defaultFragShader(1024 * 16); - fCStr_defaultFragShader.add("#version 420\r\n"); - fCStr_defaultFragShader.add("in vec2 passUV;\r\n"); - fCStr_defaultFragShader.add("uniform sampler2D textureSrc;\r\n"); - fCStr_defaultFragShader.add("layout(location = 0) out vec4 colorOut0;\r\n"); - fCStr_defaultFragShader.add("\r\n"); - fCStr_defaultFragShader.add("void main(){\r\n"); - fCStr_defaultFragShader.add("colorOut0 = vec4(texture(textureSrc, passUV).r,0.0,0.0,1.0);\r\n"); - fCStr_defaultFragShader.add("}\r\n"); - - defaultShader->glProgamId = gxShaderDepr_compileRaw(&fCStr_vertexShader, &fCStr_defaultFragShader); - catchOpenGLError(); - - defaultShader->copyShaderUniforms.uniformLoc_textureSrc = glGetUniformLocation(defaultShader->glProgamId, "textureSrc"); - defaultShader->copyShaderUniforms.uniformLoc_vertexOffsets = glGetUniformLocation(defaultShader->glProgamId, "uf_vertexOffsets"); - - _copyShader_depthToColor = defaultShader; - catchOpenGLError(); - return defaultShader; -} - -LatteDefaultShader_t* LatteDefaultShader_getPixelCopyShader_colorToDepth() -{ - if (_copyShader_colorToDepth != 0) - return _copyShader_colorToDepth; - catchOpenGLError(); - LatteDefaultShader_t* defaultShader = (LatteDefaultShader_t*)malloc(sizeof(LatteDefaultShader_t)); - memset(defaultShader, 0, sizeof(LatteDefaultShader_t)); - - StringBuf fCStr_vertexShader(1024 * 16); - LatteDefaultShader_pixelCopyShader_generateVSBody(&fCStr_vertexShader); - - StringBuf fCStr_defaultFragShader(1024 * 16); - fCStr_defaultFragShader.add("#version 420\r\n"); - fCStr_defaultFragShader.add("in vec2 passUV;\r\n"); - fCStr_defaultFragShader.add("uniform sampler2D textureSrc;\r\n"); - fCStr_defaultFragShader.add("layout(location = 0) out vec4 colorOut0;\r\n"); - fCStr_defaultFragShader.add("\r\n"); - fCStr_defaultFragShader.add("void main(){\r\n"); - fCStr_defaultFragShader.add("gl_FragDepth = texture(textureSrc, passUV).r;\r\n"); - fCStr_defaultFragShader.add("}\r\n"); - - - defaultShader->glProgamId = gxShaderDepr_compileRaw(&fCStr_vertexShader, &fCStr_defaultFragShader); - defaultShader->copyShaderUniforms.uniformLoc_textureSrc = glGetUniformLocation(defaultShader->glProgamId, "textureSrc"); - defaultShader->copyShaderUniforms.uniformLoc_vertexOffsets = glGetUniformLocation(defaultShader->glProgamId, "uf_vertexOffsets"); - - _copyShader_colorToDepth = defaultShader; - catchOpenGLError(); - return defaultShader; -} \ No newline at end of file diff --git a/src/Cafe/HW/Latte/Core/LatteDefaultShaders.h b/src/Cafe/HW/Latte/Core/LatteDefaultShaders.h deleted file mode 100644 index 3776a3d2..00000000 --- a/src/Cafe/HW/Latte/Core/LatteDefaultShaders.h +++ /dev/null @@ -1,13 +0,0 @@ - -typedef struct -{ - GLuint glProgamId; - struct - { - GLuint uniformLoc_textureSrc; - GLuint uniformLoc_vertexOffsets; - }copyShaderUniforms; -}LatteDefaultShader_t; - -LatteDefaultShader_t* LatteDefaultShader_getPixelCopyShader_depthToColor(); -LatteDefaultShader_t* LatteDefaultShader_getPixelCopyShader_colorToDepth(); \ No newline at end of file diff --git a/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp b/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp index 1bdbc4bb..2edcf4ea 100644 --- a/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp +++ b/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp @@ -10,6 +10,7 @@ #include "Cafe/HW/Latte/Renderer/Renderer.h" #include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h" #include "Cafe/GraphicPack/GraphicPack2.h" +#include "HW/Latte/Renderer/RendererCore.h" #include "config/ActiveSettings.h" #include "WindowSystem.h" #include "Cafe/OS/libs/erreula/erreula.h" @@ -694,7 +695,9 @@ void LatteRenderTarget_itHLESwapScanBuffer() performanceMonitor.gpuTime_frameTime.beginMeasuring(); LatteTC_CleanupUnusedTextures(); +#ifdef ENABLE_OPENGL LatteDraw_cleanupAfterFrame(); +#endif LatteQuery_CancelActiveGPU7Queries(); LatteBufferCache_notifySwapTVScanBuffer(); LattePerformanceMonitor_frameBegin(); diff --git a/src/Cafe/HW/Latte/Core/LatteRingBuffer.h b/src/Cafe/HW/Latte/Core/LatteRingBuffer.h index ce28c483..325836cc 100644 --- a/src/Cafe/HW/Latte/Core/LatteRingBuffer.h +++ b/src/Cafe/HW/Latte/Core/LatteRingBuffer.h @@ -1,3 +1,4 @@ +#pragma once typedef struct { diff --git a/src/Cafe/HW/Latte/Core/LatteShader.cpp b/src/Cafe/HW/Latte/Core/LatteShader.cpp index 91de6310..6c4a07d4 100644 --- a/src/Cafe/HW/Latte/Core/LatteShader.cpp +++ b/src/Cafe/HW/Latte/Core/LatteShader.cpp @@ -6,16 +6,16 @@ #include "Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h" #include "Cafe/HW/Latte/Core/FetchShader.h" #include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h" +#ifdef ENABLE_VULKAN #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h" -#include "Cafe/OS/libs/gx2/GX2.h" // todo - remove dependency +#endif #include "Cafe/GraphicPack/GraphicPack2.h" #include "HW/Latte/Core/Latte.h" #include "HW/Latte/Renderer/Renderer.h" -#include "util/helpers/StringParser.h" #include "config/ActiveSettings.h" #include "Cafe/GameProfile/GameProfile.h" #include "util/containers/flat_hash_map.hpp" -#if ENABLE_METAL +#ifdef ENABLE_METAL #include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h" #endif #include @@ -376,7 +376,9 @@ void LatteShader_FinishCompilation(LatteDecompilerShader* shader) } shader->shader->WaitForCompiled(); +#ifdef ENABLE_OPENGL LatteShader_prepareSeparableUniforms(shader); +#endif LatteShader_CleanupAfterCompile(shader); } @@ -525,7 +527,7 @@ void LatteSHRC_UpdateVSBaseHash(uint8* vertexShaderPtr, uint32 vertexShaderSize, if (LatteGPUState.contextNew.PA_CL_CLIP_CNTL.get_DX_CLIP_SPACE_DEF()) vsHash += 0x1537; -#if ENABLE_METAL +#ifdef ENABLE_METAL if (g_renderer->GetType() == RendererAPI::Metal) { bool isRectVertexShader = (primitiveType == Latte::LATTE_VGT_PRIMITIVE_TYPE::E_PRIMITIVE_TYPE::RECTS); @@ -550,7 +552,7 @@ void LatteSHRC_UpdateVSBaseHash(uint8* vertexShaderPtr, uint32 vertexShaderSize, vsHash += 51ULL; // Vertex fetch - if (_activeFetchShader->mtlFetchVertexManually) + if (_activeFetchShader->mtlFetchVertexManually) vsHash += 349ULL; } } @@ -650,7 +652,7 @@ uint64 LatteSHRC_CalcPSAuxHash(LatteDecompilerShader* pixelShader, uint32* conte auxHash += (uint64)dim; } -#if ENABLE_METAL +#ifdef ENABLE_METAL if (g_renderer->GetType() == RendererAPI::Metal) { // Textures as render targets @@ -683,56 +685,75 @@ uint64 LatteSHRC_CalcPSAuxHash(LatteDecompilerShader* pixelShader, uint32* conte return auxHash; } +static void InitUniformLayoutFromDecompiler( + LatteDecompilerShader* shader, + const LatteDecompilerOutput_t& decompilerOutput +) +{ + if (g_renderer->GetType() == RendererAPI::OpenGL) + { + // hack - for OpenGL these are retrieved in _prepareSeparableUniforms() + shader->uniform.count_uniformRegister = decompilerOutput.uniformOffsetsGL.count_uniformRegister; + return; + } + const auto& offsets = decompilerOutput.uniformOffsetsVK; + + shader->uniform.loc_remapped = offsets.offset_remapped; + shader->uniform.loc_uniformRegister = offsets.offset_uniformRegister; + shader->uniform.count_uniformRegister = offsets.count_uniformRegister; + shader->uniform.loc_windowSpaceToClipSpaceTransform = offsets.offset_windowSpaceToClipSpaceTransform; + shader->uniform.loc_alphaTestRef = offsets.offset_alphaTestRef; + shader->uniform.loc_pointSize = offsets.offset_pointSize; + shader->uniform.loc_fragCoordScale = offsets.offset_fragCoordScale; + + // Texture scale uniforms + shader->uniform.list_ufTexRescale.clear(); + for (sint32 t = 0; t < LATTE_NUM_MAX_TEX_UNITS; t++) + { + if (offsets.offset_texScale[t] >= 0) + { + LatteUniformTextureScaleEntry_t entry{}; + entry.texUnit = t; + entry.uniformLocation = offsets.offset_texScale[t]; + shader->uniform.list_ufTexRescale.push_back(entry); + } + } + + shader->uniform.loc_verticesPerInstance = offsets.offset_verticesPerInstance; + + // Streamout buffers + for (sint32 t = 0; t < LATTE_NUM_STREAMOUT_BUFFER; t++) + { + shader->uniform.loc_streamoutBufferBase[t] = offsets.offset_streamoutBufferBase[t]; + } + + shader->uniform.uniformRangeSize = offsets.offset_endOfBlock; +} + LatteDecompilerShader* LatteShader_CreateShaderFromDecompilerOutput(LatteDecompilerOutput_t& decompilerOutput, uint64 baseHash, bool calculateAuxHash, uint64 optionalAuxHash, uint32* contextRegister) { LatteDecompilerShader* shader = decompilerOutput.shader; shader->baseHash = baseHash; // copy resource mapping - // HACK - if (g_renderer->GetType() == RendererAPI::OpenGL) + switch (g_renderer->GetType()) + { + case RendererAPI::OpenGL: shader->resourceMapping = decompilerOutput.resourceMappingGL; - else if (g_renderer->GetType() == RendererAPI::Vulkan) + break; + case RendererAPI::Vulkan: shader->resourceMapping = decompilerOutput.resourceMappingVK; -#if ENABLE_METAL - else + break; + case RendererAPI::Metal: shader->resourceMapping = decompilerOutput.resourceMappingMTL; -#endif + break; + } // copy texture info shader->textureUnitMask2 = decompilerOutput.textureUnitMask; // copy streamout info shader->streamoutBufferWriteMask = decompilerOutput.streamoutBufferWriteMask; shader->hasStreamoutBufferWrite = decompilerOutput.streamoutBufferWriteMask.any(); // copy uniform offsets - // for OpenGL these are retrieved in _prepareSeparableUniforms() - // HACK - if (g_renderer->GetType() == RendererAPI::Vulkan || g_renderer->GetType() == RendererAPI::Metal) - { - shader->uniform.loc_remapped = decompilerOutput.uniformOffsetsVK.offset_remapped; - shader->uniform.loc_uniformRegister = decompilerOutput.uniformOffsetsVK.offset_uniformRegister; - shader->uniform.count_uniformRegister = decompilerOutput.uniformOffsetsVK.count_uniformRegister; - shader->uniform.loc_windowSpaceToClipSpaceTransform = decompilerOutput.uniformOffsetsVK.offset_windowSpaceToClipSpaceTransform; - shader->uniform.loc_alphaTestRef = decompilerOutput.uniformOffsetsVK.offset_alphaTestRef; - shader->uniform.loc_pointSize = decompilerOutput.uniformOffsetsVK.offset_pointSize; - shader->uniform.loc_fragCoordScale = decompilerOutput.uniformOffsetsVK.offset_fragCoordScale; - for (sint32 t = 0; t < LATTE_NUM_MAX_TEX_UNITS; t++) - { - if (decompilerOutput.uniformOffsetsVK.offset_texScale[t] >= 0) - { - LatteUniformTextureScaleEntry_t entry = { 0 }; - entry.texUnit = t; - entry.uniformLocation = decompilerOutput.uniformOffsetsVK.offset_texScale[t]; - shader->uniform.list_ufTexRescale.push_back(entry); - } - } - shader->uniform.loc_verticesPerInstance = decompilerOutput.uniformOffsetsVK.offset_verticesPerInstance; - for (sint32 t = 0; t < LATTE_NUM_STREAMOUT_BUFFER; t++) - shader->uniform.loc_streamoutBufferBase[t] = decompilerOutput.uniformOffsetsVK.offset_streamoutBufferBase[t]; - shader->uniform.uniformRangeSize = decompilerOutput.uniformOffsetsVK.offset_endOfBlock; - } - else - { - shader->uniform.count_uniformRegister = decompilerOutput.uniformOffsetsGL.count_uniformRegister; - } + InitUniformLayoutFromDecompiler(shader, decompilerOutput); // calculate aux hash if (calculateAuxHash) { @@ -766,10 +787,12 @@ void LatteShader_GetDecompilerOptions(LatteDecompilerOptions& options, LatteCons options.usesGeometryShader = geometryShaderEnabled; options.spirvInstrinsics.hasRoundingModeRTEFloat32 = false; options.useTFViaSSBO = g_renderer->UseTFViaSSBO(); +#ifdef ENABLE_VULKAN if (g_renderer->GetType() == RendererAPI::Vulkan) { options.spirvInstrinsics.hasRoundingModeRTEFloat32 = VulkanRenderer::GetInstance()->HasSPRIVRoundingModeRTE32(); } +#endif options.strictMul = g_current_game_profile->GetAccurateShaderMul() != AccurateShaderMulOption::False; } @@ -845,12 +868,14 @@ LatteDecompilerShader* LatteShader_CompileSeparableVertexShader(uint64 baseHash, LatteShader_CreateRendererShader(vertexShader, false); performanceMonitor.numCompiledVS++; +#ifdef ENABLE_OPENGL if (g_renderer->GetType() == RendererAPI::OpenGL) { if (vertexShader->shader) vertexShader->shader->PreponeCompilation(true); LatteShader_FinishCompilation(vertexShader); } +#endif LatteSHRC_RegisterShader(vertexShader, vertexShader->baseHash, vertexShader->auxHash); return vertexShader; @@ -874,12 +899,14 @@ LatteDecompilerShader* LatteShader_CompileSeparableGeometryShader(uint64 baseHas LatteShader_CreateRendererShader(geometryShader, false); performanceMonitor.numCompiledGS++; +#ifdef ENABLE_OPENGL if (g_renderer->GetType() == RendererAPI::OpenGL) { if (geometryShader->shader) geometryShader->shader->PreponeCompilation(true); LatteShader_FinishCompilation(geometryShader); } +#endif LatteSHRC_RegisterShader(geometryShader, geometryShader->baseHash, geometryShader->auxHash); return geometryShader; @@ -903,12 +930,14 @@ LatteDecompilerShader* LatteShader_CompileSeparablePixelShader(uint64 baseHash, LatteShaderCache_writeSeparablePixelShader(_shaderBaseHash_ps, psAuxHash, pixelShaderPtr, pixelShaderSize, LatteGPUState.contextRegister, usesGeometryShader); } +#ifdef ENABLE_OPENGL if (g_renderer->GetType() == RendererAPI::OpenGL) { if (pixelShader->shader) pixelShader->shader->PreponeCompilation(true); LatteShader_FinishCompilation(pixelShader); } +#endif LatteSHRC_RegisterShader(pixelShader, _shaderBaseHash_ps, psAuxHash); return pixelShader; diff --git a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp index 14a1f9b0..ab2898b7 100644 --- a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp +++ b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp @@ -9,13 +9,17 @@ #include "WindowSystem.h" #include "Cafe/HW/Latte/Renderer/Renderer.h" +#ifdef ENABLE_OPENGL #include "Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.h" +#endif +#ifdef ENABLE_VULKAN #include "Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h" -#if ENABLE_METAL +#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.h" +#endif +#ifdef ENABLE_METAL #include "Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h" #include "Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h" #endif -#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.h" #include #include "imgui/imgui_extension.h" @@ -273,14 +277,24 @@ static BootSoundPlayer g_bootSndPlayer; void LatteShaderCache_finish() { - if (g_renderer->GetType() == RendererAPI::Vulkan) + switch (g_renderer->GetType()) + { +#ifdef ENABLE_VULKAN + case RendererAPI::Vulkan: RendererShaderVk::ShaderCacheLoading_end(); - else if (g_renderer->GetType() == RendererAPI::OpenGL) - RendererShaderGL::ShaderCacheLoading_end(); -#if ENABLE_METAL - else if (g_renderer->GetType() == RendererAPI::Metal) - RendererShaderMtl::ShaderCacheLoading_end(); + return; #endif +#ifdef ENABLE_OPENGL + case RendererAPI::OpenGL: + RendererShaderGL::ShaderCacheLoading_end(); + return; +#endif +#ifdef ENABLE_METAL + case RendererAPI::Metal: + RendererShaderMtl::ShaderCacheLoading_end(); + return; +#endif + } } uint32 LatteShaderCache_getShaderCacheExtraVersion(uint64 titleId) @@ -359,21 +373,36 @@ void LatteShaderCache_Load() fs::create_directories(ActiveSettings::GetCachePath("shaderCache/transferable"), ec); fs::create_directories(ActiveSettings::GetCachePath("shaderCache/precompiled"), ec); // initialize renderer specific caches - if (g_renderer->GetType() == RendererAPI::Vulkan) + switch(g_renderer->GetType()) + { +#ifdef ENABLE_VULKAN + case RendererAPI::Vulkan: RendererShaderVk::ShaderCacheLoading_begin(cacheTitleId); - else if (g_renderer->GetType() == RendererAPI::OpenGL) - RendererShaderGL::ShaderCacheLoading_begin(cacheTitleId); -#if ENABLE_METAL - else if (g_renderer->GetType() == RendererAPI::Metal) - RendererShaderMtl::ShaderCacheLoading_begin(cacheTitleId); + break; #endif +#ifdef ENABLE_OPENGL + case RendererAPI::OpenGL: + RendererShaderGL::ShaderCacheLoading_begin(cacheTitleId); + break; +#endif +#ifdef ENABLE_METAL + case RendererAPI::Metal: + RendererShaderMtl::ShaderCacheLoading_begin(cacheTitleId); + break; +#endif + } // get cache file name fs::path pathGeneric; - if (g_renderer->GetType() == RendererAPI::Metal) + switch(g_renderer->GetType()) + { + case RendererAPI::Metal: pathGeneric = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}_mtlshaders.bin", cacheTitleId); - else + break; + default: pathGeneric = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}_shaders.bin", cacheTitleId); + break; + } // calculate extraVersion for transferable and precompiled shader cache uint32 transferableExtraVersion = SHADER_CACHE_GENERIC_EXTRA_VERSION; @@ -471,8 +500,10 @@ void LatteShaderCache_Load() #endif LatteShaderCache_finish(); // if Vulkan or Metal then also load pipeline cache +#if defined(ENABLE_VULKAN) || defined(ENABLE_METAL) if (g_renderer->GetType() == RendererAPI::Vulkan || g_renderer->GetType() == RendererAPI::Metal) LatteShaderCache_LoadPipelineCache(cacheTitleId); +#endif g_renderer->BeginFrame(true); @@ -634,31 +665,52 @@ void LatteShaderCache_ShowProgress(const std::function & loadUpdateF void LatteShaderCache_LoadPipelineCache(uint64 cacheTitleId) { - if (g_renderer->GetType() == RendererAPI::Vulkan) + switch(g_renderer->GetType()) + { + #ifdef ENABLE_VULKAN + case RendererAPI::Vulkan: g_shaderCacheLoaderState.pipelineFileCount = VulkanPipelineStableCache::GetInstance().BeginLoading(cacheTitleId); -#if ENABLE_METAL - else if (g_renderer->GetType() == RendererAPI::Metal) + break; + #endif +#ifdef ENABLE_METAL + case RendererAPI::Metal: g_shaderCacheLoaderState.pipelineFileCount = MetalPipelineCache::GetInstance().BeginLoading(cacheTitleId); + break; #endif + } + g_shaderCacheLoaderState.loadedPipelines = 0; LatteShaderCache_ShowProgress(LatteShaderCache_updatePipelineLoadingProgress, true); - if (g_renderer->GetType() == RendererAPI::Vulkan) + + switch(g_renderer->GetType()) + { +#ifdef ENABLE_VULKAN + case RendererAPI::Vulkan: VulkanPipelineStableCache::GetInstance().EndLoading(); -#if ENABLE_METAL - else if (g_renderer->GetType() == RendererAPI::Metal) - MetalPipelineCache::GetInstance().EndLoading(); + break; #endif +#ifdef ENABLE_METAL + case RendererAPI::Metal: + MetalPipelineCache::GetInstance().EndLoading(); + break; +#endif + } } bool LatteShaderCache_updatePipelineLoadingProgress() { uint32 pipelinesMissingShaders = 0; - if (g_renderer->GetType() == RendererAPI::Vulkan) + switch(g_renderer->GetType()) + { +#ifdef ENABLE_VULKAN + case RendererAPI::Vulkan: return VulkanPipelineStableCache::GetInstance().UpdateLoading(g_shaderCacheLoaderState.loadedPipelines, pipelinesMissingShaders); -#if ENABLE_METAL - else if (g_renderer->GetType() == RendererAPI::Metal) +#endif +#ifdef ENABLE_METAL + case RendererAPI::Metal: return MetalPipelineCache::GetInstance().UpdateLoading(g_shaderCacheLoaderState.loadedPipelines, pipelinesMissingShaders); #endif + } return false; } @@ -918,20 +970,37 @@ void LatteShaderCache_Close() delete s_shaderCacheGeneric; s_shaderCacheGeneric = nullptr; } - if (g_renderer->GetType() == RendererAPI::Vulkan) + switch(g_renderer->GetType()) + { +#ifdef ENABLE_VULKAN + case RendererAPI::Vulkan: RendererShaderVk::ShaderCacheLoading_Close(); - else if (g_renderer->GetType() == RendererAPI::OpenGL) - RendererShaderGL::ShaderCacheLoading_Close(); -#if ENABLE_METAL - else if (g_renderer->GetType() == RendererAPI::Metal) - RendererShaderMtl::ShaderCacheLoading_Close(); + break; #endif +#ifdef ENABLE_OPENGL + case RendererAPI::OpenGL: + RendererShaderGL::ShaderCacheLoading_Close(); + break; +#endif +#ifdef ENABLE_METAL + case RendererAPI::Metal: + RendererShaderMtl::ShaderCacheLoading_Close(); + break; +#endif + } // if Vulkan or Metal then also close pipeline cache - if (g_renderer->GetType() == RendererAPI::Vulkan) - VulkanPipelineStableCache::GetInstance().Close(); -#if ENABLE_METAL - else if (g_renderer->GetType() == RendererAPI::Metal) - MetalPipelineCache::GetInstance().Close(); + switch(g_renderer->GetType()) + { +#ifdef ENABLE_VULKAN + case RendererAPI::Vulkan: + VulkanPipelineStableCache::GetInstance().Close(); + break; #endif +#ifdef ENABLE_METAL + case RendererAPI::Metal: + MetalPipelineCache::GetInstance().Close(); + break; +#endif + } } diff --git a/src/Cafe/HW/Latte/Core/LatteSurfaceCopy.cpp b/src/Cafe/HW/Latte/Core/LatteSurfaceCopy.cpp index fe52547f..7e516172 100644 --- a/src/Cafe/HW/Latte/Core/LatteSurfaceCopy.cpp +++ b/src/Cafe/HW/Latte/Core/LatteSurfaceCopy.cpp @@ -1,7 +1,6 @@ #include "Cafe/HW/Latte/Core/Latte.h" #include "Cafe/HW/Latte/Core/LatteDraw.h" #include "Cafe/HW/Latte/Core/LatteShader.h" -#include "Cafe/HW/Latte/Core/LatteDefaultShaders.h" #include "Cafe/HW/Latte/Core/LatteTexture.h" #include "Cafe/HW/Latte/Core/LatteSurfaceCopy.h" diff --git a/src/Cafe/HW/Latte/Core/LatteTexture.cpp b/src/Cafe/HW/Latte/Core/LatteTexture.cpp index c2d4f05b..43f64b2c 100644 --- a/src/Cafe/HW/Latte/Core/LatteTexture.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTexture.cpp @@ -567,6 +567,8 @@ bool __LatteTexture_IsBlockedFormatRelation(LatteTexture* texture1, LatteTexture if (texture1->format == Latte::E_GX2SURFFMT::D32_FLOAT && Latte::GetHWFormat(texture2->format) == Latte::E_HWSURFFMT::HWFMT_8_8_8_8) return true; } + +#ifdef ENABLE_VULKAN // Vulkan has stricter rules if (g_renderer->GetType() == RendererAPI::Vulkan) { @@ -574,6 +576,7 @@ bool __LatteTexture_IsBlockedFormatRelation(LatteTexture* texture1, LatteTexture if (texture1->format == Latte::E_GX2SURFFMT::D32_FLOAT && Latte::GetHWFormat(texture2->format) == Latte::E_HWSURFFMT::HWFMT_8_24) return true; } +#endif return false; } diff --git a/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp b/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp index 4c11e57c..7b8a0b7b 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTextureLegacy.cpp @@ -4,9 +4,11 @@ #include "Cafe/HW/Latte/Renderer/Renderer.h" +#ifdef ENABLE_OPENGL #include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h" #include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.h" #include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h" +#endif struct TexScaleXY { @@ -192,6 +194,7 @@ void LatteTexture_updateTexturesForStage(LatteDecompilerShader* shaderContext, u LatteGPUState.repeatTextureInitialization = true; } +#ifdef ENABLE_OPENGL if (g_renderer->GetType() == RendererAPI::OpenGL) { // on OpenGL, texture views and sampler parameters are tied together (we are avoiding sampler objects due to driver bugs) @@ -214,6 +217,8 @@ void LatteTexture_updateTexturesForStage(LatteDecompilerShader* shaderContext, u textureView->lastTextureBindIndex = LatteGPUState.textureBindCounter; rendererGL->renderstate_updateTextureSettingsGL(shaderContext, textureView, textureIndex + glBackendBaseTexUnit, word4, textureIndex, isDepthSampler); } +#endif + g_renderer->texture_setLatteTexture(textureView, textureIndex + glBackendBaseTexUnit); // update if data changed bool swizzleChanged = false; diff --git a/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp b/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp index 8df5dcea..a6690a3f 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp @@ -1,12 +1,7 @@ #include "Cafe/HW/Latte/Core/Latte.h" -#include "Cafe/HW/Latte/Core/LatteDraw.h" #include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h" - -#include "Common/GLInclude/GLInclude.h" - #include "Cafe/HW/Latte/Renderer/Renderer.h" #include "Cafe/HW/Latte/Core/LatteTexture.h" -#include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h" #define LOG_READBACK_TIME diff --git a/src/Cafe/HW/Latte/Core/LatteTiming.cpp b/src/Cafe/HW/Latte/Core/LatteTiming.cpp index 115b60d9..60f4c52d 100644 --- a/src/Cafe/HW/Latte/Core/LatteTiming.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTiming.cpp @@ -1,6 +1,8 @@ #include "Cafe/HW/Latte/Core/Latte.h" #include "Cafe/OS/libs/gx2/GX2_Event.h" +#ifdef ENABLE_VULKAN #include "Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.h" +#endif #include "util/highresolutiontimer/HighResolutionTimer.h" #include "config/CemuConfig.h" #include "Cafe/CafeSystem.h" @@ -55,8 +57,10 @@ void LatteTiming_EnableHostDrivenVSync() { if (s_usingHostDrivenVSync) return; + #ifdef ENABLE_VULKAN VsyncDriver_startThread(LatteTiming_NotifyHostVSync); s_usingHostDrivenVSync = true; + #endif } bool LatteTiming_IsUsingHostDrivenVSync() diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.cpp index 13188743..6a975a4e 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.cpp @@ -10,7 +10,9 @@ #include "Cafe/HW/Latte/Core/FetchShader.h" #include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h" #include "Cafe/HW/Latte/Renderer/Renderer.h" +#ifdef ENABLE_VULKAN #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h" +#endif #include "util/helpers/helpers.h" // parse instruction and if valid append it to instructionList @@ -1069,12 +1071,18 @@ void _LatteDecompiler_Process(LatteDecompilerShaderContext* shaderContext, uint8 // emit code if (shaderContext->shader->hasError == false) { - if (g_renderer->GetType() == RendererAPI::OpenGL || g_renderer->GetType() == RendererAPI::Vulkan) - LatteDecompiler_emitGLSLShader(shaderContext, shaderContext->shader); -#if ENABLE_METAL - else - LatteDecompiler_emitMSLShader(shaderContext, shaderContext->shader); + if (g_renderer->GetType() == RendererAPI::OpenGL || g_renderer->GetType() == RendererAPI::Vulkan) + { +#if defined(ENABLE_OPENGL) || defined(ENABLE_VULKAN) + LatteDecompiler_emitGLSLShader(shaderContext, shaderContext->shader); #endif + } + if (g_renderer->GetType() == RendererAPI::Metal) + { +#ifdef ENABLE_METAL + LatteDecompiler_emitMSLShader(shaderContext, shaderContext->shader); +#endif + } } LatteDecompiler_cleanup(shaderContext); // fast access diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp index 18cba4b4..e8d5d490 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp @@ -10,7 +10,7 @@ #include "Cafe/HW/Latte/Renderer/Renderer.h" #include "Common/MemPtr.h" #include "HW/Latte/ISA/LatteReg.h" -#if ENABLE_METAL +#ifdef ENABLE_METAL #include "HW/Latte/Renderer/Metal/MetalCommon.h" #endif @@ -403,11 +403,9 @@ void LatteDecompiler_analyzeExport(LatteDecompilerShaderContext* shaderContext, } else if (cfInstruction->exportType == 0 && cfInstruction->exportArrayBase == 61) { -#if ENABLE_METAL // Only check for depth buffer mask on Metal, as its not in the PS hash on other backends if (g_renderer->GetType() != RendererAPI::Metal || LatteMRT::GetActiveDepthBufferMask(*shaderContext->contextRegistersNew)) shader->depthMask = true; -#endif } else debugBreakpoint(); @@ -512,7 +510,7 @@ namespace LatteDecompiler } } -#if ENABLE_METAL +#ifdef ENABLE_METAL void _initTextureBindingPointsMTL(LatteDecompilerShaderContext* decompilerContext) { // for Vulkan we use consecutive indices @@ -563,7 +561,7 @@ namespace LatteDecompiler { decompilerContext->hasUniformVarBlock = true; // uf_verticesPerInstance and uf_streamoutBufferBase* } -#if ENABLE_METAL +#ifdef ENABLE_METAL if (g_renderer->GetType() == RendererAPI::Metal) { bool usesGeometryShader = UseGeometryShader(*decompilerContext->contextRegistersNew, decompilerContext->options->usesGeometryShader); @@ -1113,7 +1111,7 @@ void LatteDecompiler_analyze(LatteDecompilerShaderContext* shaderContext, LatteD shaderContext->output->resourceMappingVK.setIndex = 2; LatteDecompiler::_initTextureBindingPointsGL(shaderContext); LatteDecompiler::_initTextureBindingPointsVK(shaderContext); -#if ENABLE_METAL +#ifdef ENABLE_METAL LatteDecompiler::_initTextureBindingPointsMTL(shaderContext); #endif LatteDecompiler::_initUniformBindingPoints(shaderContext); diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h index 4c6b158a..0b19bcae 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h @@ -266,7 +266,7 @@ struct LatteDecompilerShaderContext void LatteDecompiler_analyze(LatteDecompilerShaderContext* shaderContext, LatteDecompilerShader* shader); void LatteDecompiler_analyzeDataTypes(LatteDecompilerShaderContext* shaderContext); void LatteDecompiler_emitGLSLShader(LatteDecompilerShaderContext* shaderContext, LatteDecompilerShader* shader); -#if ENABLE_METAL +#ifdef ENABLE_METAL void LatteDecompiler_emitMSLShader(LatteDecompilerShaderContext* shaderContext, LatteDecompilerShader* shader); #endif diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp index 7ceaf74d..8c2da361 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp @@ -33,7 +33,7 @@ extern bool hasValidFramebufferAttached; float supportBufferData[512 * 4]; -// Defined in the OpenGL renderer +// Defined in the Common renderer void LatteDraw_handleSpecialState8_clearAsDepth(); std::vector MetalRenderer::GetDevices() diff --git a/src/Cafe/HW/Latte/Core/LatteShaderGL.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/LatteShaderGL.cpp similarity index 100% rename from src/Cafe/HW/Latte/Core/LatteShaderGL.cpp rename to src/Cafe/HW/Latte/Renderer/OpenGL/LatteShaderGL.cpp diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp index 571961f4..c9437353 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp @@ -17,6 +17,7 @@ #include "Cafe/OS/libs/gx2/GX2.h" #include "Cafe/GameProfile/GameProfile.h" +#include "HW/Latte/Renderer/RendererCore.h" #include "config/ActiveSettings.h" @@ -52,7 +53,7 @@ struct uint32 maxIndex; uint32 minIndex; uint8* indexData; - // buffer + // buffer GLuint glIndexCacheBuffer; VirtualBufferHeap_t* indexBufferVirtualHeap; uint8* mappedIndexBuffer; @@ -371,6 +372,8 @@ void _decodeAndUploadIndexData(indexDataCacheEntry2_t* cacheEntry) void LatteDraw_cleanupAfterFrame() { + if (g_renderer->GetType() != RendererAPI::OpenGL) + return; // drop everything from cache that is older than 30 frames uint32 frameCounter = LatteGPUState.frameCounter; while (indexDataCacheFirst) @@ -524,70 +527,6 @@ void LatteDrawGL_prepareIndicesWithGPUCache(MPTR indexDataMPTR, _INDEX_TYPE inde indexState.indexData = (uint8*)(size_t)cacheEntry->heapEntry->startOffset; } -void LatteDraw_handleSpecialState8_clearAsDepth() -{ - if (LatteGPUState.contextNew.GetSpecialStateValues()[0] == 0) - cemuLog_logDebug(LogType::Force, "Special state 8 requires special state 0 but it is not set?"); - // get depth buffer information - uint32 regDepthBuffer = LatteGPUState.contextRegister[mmDB_HTILE_DATA_BASE]; - uint32 regDepthSize = LatteGPUState.contextRegister[mmDB_DEPTH_SIZE]; - uint32 regDepthBufferInfo = LatteGPUState.contextRegister[mmDB_DEPTH_INFO]; - // get format and tileMode from info reg - uint32 depthBufferTileMode = (regDepthBufferInfo >> 15) & 0xF; - - MPTR depthBufferPhysMem = regDepthBuffer << 8; - uint32 depthBufferPitch = (((regDepthSize >> 0) & 0x3FF) + 1); - uint32 depthBufferHeight = ((((regDepthSize >> 10) & 0xFFFFF) + 1) / depthBufferPitch); - depthBufferPitch <<= 3; - depthBufferHeight <<= 3; - uint32 depthBufferWidth = depthBufferPitch; - - sint32 sliceIndex = 0; // todo - sint32 mipIndex = 0; - - // clear all color buffers that match the format of the depth buffer - sint32 searchIndex = 0; - bool targetFound = false; - while (true) - { - LatteTextureView* view = LatteTC_LookupTextureByData(depthBufferPhysMem, depthBufferWidth, depthBufferHeight, depthBufferPitch, 0, 1, sliceIndex, 1, &searchIndex); - if (!view) - { - // should we clear in RAM instead? - break; - } - sint32 effectiveClearWidth = view->baseTexture->width; - sint32 effectiveClearHeight = view->baseTexture->height; - LatteTexture_scaleToEffectiveSize(view->baseTexture, &effectiveClearWidth, &effectiveClearHeight, 0); - - // hacky way to get clear color - float* regClearColor = (float*)(LatteGPUState.contextRegister + 0xC000 + 0); // REG_BASE_ALU_CONST - - uint8 clearColor[4] = { 0 }; - clearColor[0] = (uint8)(regClearColor[0] * 255.0f); - clearColor[1] = (uint8)(regClearColor[1] * 255.0f); - clearColor[2] = (uint8)(regClearColor[2] * 255.0f); - clearColor[3] = (uint8)(regClearColor[3] * 255.0f); - - // todo - use fragment shader software emulation (evoke for one pixel) to determine clear color - // todo - dont clear entire slice, use effectiveClearWidth, effectiveClearHeight - - if (g_renderer->GetType() == RendererAPI::OpenGL) - { - //cemu_assert_debug(false); // implement g_renderer->texture_clearColorSlice properly for OpenGL renderer - if (glClearTexSubImage) - glClearTexSubImage(((LatteTextureViewGL*)view)->glTexId, mipIndex, 0, 0, 0, effectiveClearWidth, effectiveClearHeight, 1, GL_RGBA, GL_UNSIGNED_BYTE, clearColor); - } - else - { - if (view->baseTexture->isDepth) - g_renderer->texture_clearDepthSlice(view->baseTexture, sliceIndex + view->firstSlice, mipIndex + view->firstMip, true, view->baseTexture->hasStencil, 0.0f, 0); - else - g_renderer->texture_clearColorSlice(view->baseTexture, sliceIndex + view->firstSlice, mipIndex + view->firstMip, clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - } - } -} - void LatteDrawGL_doDraw(_INDEX_TYPE indexType, uint32 baseVertex, uint32 baseInstance, uint32 instanceCount, uint32 count) { if (indexType == _INDEX_TYPE::U16_BE) @@ -755,10 +694,6 @@ void OpenGLRenderer::_setupVertexAttributes() } } -void rectsEmulationGS_outputSingleVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 vIdx); -void rectsEmulationGS_outputGeneratedVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, const char* variant); -void rectsEmulationGS_outputVerticesCode(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 p0, sint32 p1, sint32 p2, sint32 p3, const char* variant, const LatteContextRegister& latteRegister); - std::map g_mapGLRectEmulationGS; RendererShaderGL* rectsEmulationGS_generateShaderGL(LatteDecompilerShader* vertexShader) diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.cpp index d578b842..de1e1222 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.cpp @@ -1,6 +1,7 @@ #include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h" #include "Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.h" #include "Cafe/HW/Latte/Renderer/OpenGL/CachedFBOGL.h" +#include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.h" #include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.h" #include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h" @@ -8,7 +9,7 @@ #include "Cafe/HW/Latte/Core/LatteShader.h" #include "Cafe/HW/Latte/Core/LatteDraw.h" -#include "Cafe/HW/Latte/Core/LatteDefaultShaders.h" +#include "util/helpers/StringBuf.h" void LatteDraw_resetAttributePointerCache(); @@ -66,9 +67,9 @@ void OpenGLRenderer::surfaceCopy_copySurfaceWithFormatConversion(LatteTexture* s if (destinationTexture->isDepth) renderstate_setAlwaysWriteDepth(); // bind format specific copy shader - LatteDefaultShader_t* copyShader = LatteDefaultShader_getPixelCopyShader_depthToColor(); + LatteGLDefaultShader_t* copyShader = LatteGLDefaultShader_getPixelCopyShader_depthToColor(); if (destinationTexture->isDepth) - copyShader = LatteDefaultShader_getPixelCopyShader_colorToDepth(); + copyShader = LatteGLDefaultShader_getPixelCopyShader_colorToDepth(); glUseProgram(copyShader->glProgamId); catchOpenGLError(); // setup uniforms @@ -113,4 +114,86 @@ void OpenGLRenderer::surfaceCopy_copySurfaceWithFormatConversion(LatteTexture* s LatteGPUState.repeatTextureInitialization = true; glUseProgram(0); +} + +LatteGLDefaultShader_t* _copyShader_depthToColor; +LatteGLDefaultShader_t* _copyShader_colorToDepth; + +void LatteGLDefaultShader_pixelCopyShader_generateVSBody(StringBuf* vs) +{ + vs->add("#version 420\r\n"); + vs->add("out vec2 passUV;\r\n"); + vs->add("uniform vec4 uf_vertexOffsets[4];\r\n"); + vs->add("\r\n"); + vs->add("void main(){\r\n"); + vs->add("int vID = gl_VertexID;\r\n"); + vs->add("passUV = uf_vertexOffsets[vID].zw;\r\n"); + vs->add("gl_Position = vec4(uf_vertexOffsets[vID].xy, 0.0, 1.0);\r\n"); + vs->add("}\r\n"); +} + +GLuint gxShaderDepr_compileRaw(StringBuf* strSourceVS, StringBuf* strSourceFS); +GLuint gxShaderDepr_compileRaw(const std::string& vertex_source, const std::string& fragment_source); + +LatteGLDefaultShader_t* LatteGLDefaultShader_getPixelCopyShader_depthToColor() +{ + if (_copyShader_depthToColor != 0) + return _copyShader_depthToColor; + catchOpenGLError(); + LatteGLDefaultShader_t* defaultShader = (LatteGLDefaultShader_t*)malloc(sizeof(LatteGLDefaultShader_t)); + memset(defaultShader, 0, sizeof(LatteGLDefaultShader_t)); + + StringBuf fCStr_vertexShader(1024 * 16); + LatteGLDefaultShader_pixelCopyShader_generateVSBody(&fCStr_vertexShader); + + StringBuf fCStr_defaultFragShader(1024 * 16); + fCStr_defaultFragShader.add("#version 420\r\n"); + fCStr_defaultFragShader.add("in vec2 passUV;\r\n"); + fCStr_defaultFragShader.add("uniform sampler2D textureSrc;\r\n"); + fCStr_defaultFragShader.add("layout(location = 0) out vec4 colorOut0;\r\n"); + fCStr_defaultFragShader.add("\r\n"); + fCStr_defaultFragShader.add("void main(){\r\n"); + fCStr_defaultFragShader.add("colorOut0 = vec4(texture(textureSrc, passUV).r,0.0,0.0,1.0);\r\n"); + fCStr_defaultFragShader.add("}\r\n"); + + defaultShader->glProgamId = gxShaderDepr_compileRaw(&fCStr_vertexShader, &fCStr_defaultFragShader); + catchOpenGLError(); + + defaultShader->copyShaderUniforms.uniformLoc_textureSrc = glGetUniformLocation(defaultShader->glProgamId, "textureSrc"); + defaultShader->copyShaderUniforms.uniformLoc_vertexOffsets = glGetUniformLocation(defaultShader->glProgamId, "uf_vertexOffsets"); + + _copyShader_depthToColor = defaultShader; + catchOpenGLError(); + return defaultShader; +} + +LatteGLDefaultShader_t* LatteGLDefaultShader_getPixelCopyShader_colorToDepth() +{ + if (_copyShader_colorToDepth != 0) + return _copyShader_colorToDepth; + catchOpenGLError(); + LatteGLDefaultShader_t* defaultShader = (LatteGLDefaultShader_t*)malloc(sizeof(LatteGLDefaultShader_t)); + memset(defaultShader, 0, sizeof(LatteGLDefaultShader_t)); + + StringBuf fCStr_vertexShader(1024 * 16); + LatteGLDefaultShader_pixelCopyShader_generateVSBody(&fCStr_vertexShader); + + StringBuf fCStr_defaultFragShader(1024 * 16); + fCStr_defaultFragShader.add("#version 420\r\n"); + fCStr_defaultFragShader.add("in vec2 passUV;\r\n"); + fCStr_defaultFragShader.add("uniform sampler2D textureSrc;\r\n"); + fCStr_defaultFragShader.add("layout(location = 0) out vec4 colorOut0;\r\n"); + fCStr_defaultFragShader.add("\r\n"); + fCStr_defaultFragShader.add("void main(){\r\n"); + fCStr_defaultFragShader.add("gl_FragDepth = texture(textureSrc, passUV).r;\r\n"); + fCStr_defaultFragShader.add("}\r\n"); + + + defaultShader->glProgamId = gxShaderDepr_compileRaw(&fCStr_vertexShader, &fCStr_defaultFragShader); + defaultShader->copyShaderUniforms.uniformLoc_textureSrc = glGetUniformLocation(defaultShader->glProgamId, "textureSrc"); + defaultShader->copyShaderUniforms.uniformLoc_vertexOffsets = glGetUniformLocation(defaultShader->glProgamId, "uf_vertexOffsets"); + + _copyShader_colorToDepth = defaultShader; + catchOpenGLError(); + return defaultShader; } \ No newline at end of file diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.h b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.h new file mode 100644 index 00000000..3c6e4c25 --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.h @@ -0,0 +1,14 @@ +#pragma once + +typedef struct +{ + GLuint glProgamId; + struct + { + GLuint uniformLoc_textureSrc; + GLuint uniformLoc_vertexOffsets; + }copyShaderUniforms; +}LatteGLDefaultShader_t; + +LatteGLDefaultShader_t* LatteGLDefaultShader_getPixelCopyShader_depthToColor(); +LatteGLDefaultShader_t* LatteGLDefaultShader_getPixelCopyShader_colorToDepth(); \ No newline at end of file diff --git a/src/Cafe/HW/Latte/Renderer/RendererCore.cpp b/src/Cafe/HW/Latte/Renderer/RendererCore.cpp new file mode 100644 index 00000000..96d7b7c2 --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/RendererCore.cpp @@ -0,0 +1,132 @@ +#include "RendererCore.h" +#include "Cafe/HW/Latte/Renderer/Renderer.h" +#include "Cafe/HW/Latte/ISA/RegDefines.h" +#include "HW/Latte/Core/LatteShader.h" +#include "config/CemuConfig.h" + +#ifdef ENABLE_OPENGL +#include "Common/GLInclude/GLInclude.h" +#include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h" +#endif + +void LatteDraw_handleSpecialState8_clearAsDepth() +{ + if (LatteGPUState.contextNew.GetSpecialStateValues()[0] == 0) + cemuLog_logDebug(LogType::Force, "Special state 8 requires special state 0 but it is not set?"); + // get depth buffer information + uint32 regDepthBuffer = LatteGPUState.contextRegister[mmDB_HTILE_DATA_BASE]; + uint32 regDepthSize = LatteGPUState.contextRegister[mmDB_DEPTH_SIZE]; + uint32 regDepthBufferInfo = LatteGPUState.contextRegister[mmDB_DEPTH_INFO]; + // get format and tileMode from info reg + uint32 depthBufferTileMode = (regDepthBufferInfo >> 15) & 0xF; + + MPTR depthBufferPhysMem = regDepthBuffer << 8; + uint32 depthBufferPitch = (((regDepthSize >> 0) & 0x3FF) + 1); + uint32 depthBufferHeight = ((((regDepthSize >> 10) & 0xFFFFF) + 1) / depthBufferPitch); + depthBufferPitch <<= 3; + depthBufferHeight <<= 3; + uint32 depthBufferWidth = depthBufferPitch; + + sint32 sliceIndex = 0; // todo + sint32 mipIndex = 0; + + // clear all color buffers that match the format of the depth buffer + sint32 searchIndex = 0; + bool targetFound = false; + while (true) + { + LatteTextureView* view = LatteTC_LookupTextureByData(depthBufferPhysMem, depthBufferWidth, depthBufferHeight, depthBufferPitch, 0, 1, sliceIndex, 1, &searchIndex); + if (!view) + { + // should we clear in RAM instead? + break; + } + sint32 effectiveClearWidth = view->baseTexture->width; + sint32 effectiveClearHeight = view->baseTexture->height; + LatteTexture_scaleToEffectiveSize(view->baseTexture, &effectiveClearWidth, &effectiveClearHeight, 0); + + // hacky way to get clear color + float* regClearColor = (float*)(LatteGPUState.contextRegister + 0xC000 + 0); // REG_BASE_ALU_CONST + + uint8 clearColor[4] = { 0 }; + clearColor[0] = (uint8)(regClearColor[0] * 255.0f); + clearColor[1] = (uint8)(regClearColor[1] * 255.0f); + clearColor[2] = (uint8)(regClearColor[2] * 255.0f); + clearColor[3] = (uint8)(regClearColor[3] * 255.0f); + + // todo - use fragment shader software emulation (evoke for one pixel) to determine clear color + // todo - dont clear entire slice, use effectiveClearWidth, effectiveClearHeight + + switch (g_renderer->GetType()) + { +#ifdef ENABLE_OPENGL + case RendererAPI::OpenGL: + { + //cemu_assert_debug(false); // implement g_renderer->texture_clearColorSlice properly for OpenGL renderer + if (glClearTexSubImage) + glClearTexSubImage(((LatteTextureViewGL*)view)->glTexId, mipIndex, 0, 0, 0, effectiveClearWidth, effectiveClearHeight, 1, GL_RGBA, GL_UNSIGNED_BYTE, clearColor); + break; + } +#endif + default: + { + if (view->baseTexture->isDepth) + g_renderer->texture_clearDepthSlice(view->baseTexture, sliceIndex + view->firstSlice, mipIndex + view->firstMip, true, view->baseTexture->hasStencil, 0.0f, 0); + else + g_renderer->texture_clearColorSlice(view->baseTexture, sliceIndex + view->firstSlice, mipIndex + view->firstMip, clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + } + } + } +} + +/* rects emulation */ + +void rectsEmulationGS_outputSingleVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 vIdx, const LatteContextRegister& latteRegister) +{ + auto parameterMask = vertexShader->outputParameterMask; + for (uint32 i = 0; i < 32; i++) + { + if ((parameterMask & (1 << i)) == 0) + continue; + sint32 vsSemanticId = psInputTable->getVertexShaderOutParamSemanticId(latteRegister.GetRawView(), i); + if (vsSemanticId < 0) + continue; + // make sure PS has matching input + if (!psInputTable->hasPSImportForSemanticId(vsSemanticId)) + continue; + gsSrc.append(fmt::format("passParameterSem{}Out = passParameterSem{}In[{}];\r\n", vsSemanticId, vsSemanticId, vIdx)); + } + gsSrc.append(fmt::format("gl_Position = gl_in[{}].gl_Position;\r\n", vIdx)); + gsSrc.append("EmitVertex();\r\n"); +} + +void rectsEmulationGS_outputGeneratedVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, const char* variant, const LatteContextRegister& latteRegister) +{ + auto parameterMask = vertexShader->outputParameterMask; + for (uint32 i = 0; i < 32; i++) + { + if ((parameterMask & (1 << i)) == 0) + continue; + sint32 vsSemanticId = psInputTable->getVertexShaderOutParamSemanticId(latteRegister.GetRawView(), i); + if (vsSemanticId < 0) + continue; + // make sure PS has matching input + if (!psInputTable->hasPSImportForSemanticId(vsSemanticId)) + continue; + gsSrc.append(fmt::format("passParameterSem{}Out = gen4thVertex{}(passParameterSem{}In[0], passParameterSem{}In[1], passParameterSem{}In[2]);\r\n", vsSemanticId, variant, vsSemanticId, vsSemanticId, vsSemanticId)); + } + gsSrc.append(fmt::format("gl_Position = gen4thVertex{}(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_in[2].gl_Position);\r\n", variant)); + gsSrc.append("EmitVertex();\r\n"); +} + +void rectsEmulationGS_outputVerticesCode(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 p0, sint32 p1, sint32 p2, sint32 p3, const char* variant, const LatteContextRegister& latteRegister) +{ + sint32 pList[4] = { p0, p1, p2, p3 }; + for (sint32 i = 0; i < 4; i++) + { + if (pList[i] == 3) + rectsEmulationGS_outputGeneratedVertex(gsSrc, vertexShader, psInputTable, variant, latteRegister); + else + rectsEmulationGS_outputSingleVertex(gsSrc, vertexShader, psInputTable, pList[i], latteRegister); + } +} diff --git a/src/Cafe/HW/Latte/Renderer/RendererCore.h b/src/Cafe/HW/Latte/Renderer/RendererCore.h new file mode 100644 index 00000000..aaffec56 --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/RendererCore.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Cafe/HW/Latte/Core/LatteRingBuffer.h" +#include "Cafe/HW/Latte/Core/Latte.h" + +void LatteDraw_handleSpecialState8_clearAsDepth(); + +class LatteShaderPSInputTable; +void rectsEmulationGS_outputSingleVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 vIdx); +void rectsEmulationGS_outputGeneratedVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, const char* variant); +void rectsEmulationGS_outputVerticesCode(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 p0, sint32 p1, sint32 p2, sint32 p3, const char* variant, const LatteContextRegister& latteRegister); diff --git a/src/Cafe/HW/Latte/Renderer/RendererOuputShader.cpp b/src/Cafe/HW/Latte/Renderer/RendererOuputShader.cpp index a4f538a8..a43b7eed 100644 --- a/src/Cafe/HW/Latte/Renderer/RendererOuputShader.cpp +++ b/src/Cafe/HW/Latte/Renderer/RendererOuputShader.cpp @@ -1,5 +1,6 @@ #include "Cafe/HW/Latte/Renderer/RendererOuputShader.h" -#include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h" +#include "Cafe/HW/Latte/Renderer/Renderer.h" +#include "Cafe/HW/Latte/Core/Latte.h" #include "config/ActiveSettings.h" const std::string RendererOutputShader::s_copy_shader_source = @@ -246,10 +247,17 @@ fragment float4 main0(VertexOut in [[stage_in]], texture2d textureSrc [[t RendererOutputShader::RendererOutputShader(const std::string& vertex_source, const std::string& fragment_source) { std::string finalFragmentSrc; - if (g_renderer->GetType() == RendererAPI::Metal) - finalFragmentSrc = fragment_source; - else - finalFragmentSrc = PrependFragmentPreamble(fragment_source); + switch(g_renderer->GetType()) + { +#ifdef ENABLE_METAL + case RendererAPI::Metal: + finalFragmentSrc = fragment_source; + break; +#endif + default: + finalFragmentSrc = PrependFragmentPreamble(fragment_source); + break; + } m_vertex_shader.reset(g_renderer->shader_create(RendererShader::ShaderType::kVertex, 0, 0, vertex_source, false, false)); m_fragment_shader.reset(g_renderer->shader_create(RendererShader::ShaderType::kFragment, 0, 0, finalFragmentSrc, false, false)); @@ -484,7 +492,10 @@ void main() } void RendererOutputShader::InitializeStatic() { - if (g_renderer->GetType() == RendererAPI::Metal) + switch(g_renderer->GetType()) + { +#ifdef ENABLE_METAL + case RendererAPI::Metal: { std::string vertex_source = GetMetalVertexSource(false); std::string vertex_source_ud = GetMetalVertexSource(true); @@ -497,21 +508,17 @@ void RendererOutputShader::InitializeStatic() s_hermit_shader = new RendererOutputShader(vertex_source, s_hermite_shader_source_mtl); s_hermit_shader_ud = new RendererOutputShader(vertex_source_ud, s_hermite_shader_source_mtl); + break; } - else +#endif +#ifdef ENABLE_OPENGL + case RendererAPI::OpenGL: { std::string vertex_source, vertex_source_ud; // vertex shader - if (g_renderer->GetType() == RendererAPI::OpenGL) - { - vertex_source = GetOpenGlVertexSource(false); - vertex_source_ud = GetOpenGlVertexSource(true); - } - else if (g_renderer->GetType() == RendererAPI::Vulkan) - { - vertex_source = GetVulkanVertexSource(false); - vertex_source_ud = GetVulkanVertexSource(true); - } + vertex_source = GetOpenGlVertexSource(false); + vertex_source_ud = GetOpenGlVertexSource(true); + s_copy_shader = new RendererOutputShader(vertex_source, s_copy_shader_source); s_copy_shader_ud = new RendererOutputShader(vertex_source_ud, s_copy_shader_source); @@ -520,7 +527,29 @@ void RendererOutputShader::InitializeStatic() s_hermit_shader = new RendererOutputShader(vertex_source, s_hermite_shader_source); s_hermit_shader_ud = new RendererOutputShader(vertex_source_ud, s_hermite_shader_source); + break; } +#endif +#ifdef ENABLE_VULKAN + case RendererAPI::Vulkan: + { + std::string vertex_source, vertex_source_ud; + // vertex shader + vertex_source = GetVulkanVertexSource(false); + vertex_source_ud = GetVulkanVertexSource(true); + + s_copy_shader = new RendererOutputShader(vertex_source, s_copy_shader_source); + s_copy_shader_ud = new RendererOutputShader(vertex_source_ud, s_copy_shader_source); + + s_bicubic_shader = new RendererOutputShader(vertex_source, s_bicubic_shader_source); + s_bicubic_shader_ud = new RendererOutputShader(vertex_source_ud, s_bicubic_shader_source); + + s_hermit_shader = new RendererOutputShader(vertex_source, s_hermite_shader_source); + s_hermit_shader_ud = new RendererOutputShader(vertex_source_ud, s_hermite_shader_source); + break; + } +#endif + } } void RendererOutputShader::ShutdownStatic() diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp index 55b2330e..ca166e69 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp @@ -9,58 +9,7 @@ #include "util/helpers/helpers.h" #include "util/helpers/Serializer.h" #include "Cafe/HW/Latte/Common/RegisterSerializer.h" - -/* rects emulation */ - -void rectsEmulationGS_outputSingleVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 vIdx, const LatteContextRegister& latteRegister) -{ - auto parameterMask = vertexShader->outputParameterMask; - for (uint32 i = 0; i < 32; i++) - { - if ((parameterMask & (1 << i)) == 0) - continue; - sint32 vsSemanticId = psInputTable->getVertexShaderOutParamSemanticId(latteRegister.GetRawView(), i); - if (vsSemanticId < 0) - continue; - // make sure PS has matching input - if (!psInputTable->hasPSImportForSemanticId(vsSemanticId)) - continue; - gsSrc.append(fmt::format("passParameterSem{}Out = passParameterSem{}In[{}];\r\n", vsSemanticId, vsSemanticId, vIdx)); - } - gsSrc.append(fmt::format("gl_Position = gl_in[{}].gl_Position;\r\n", vIdx)); - gsSrc.append("EmitVertex();\r\n"); -} - -void rectsEmulationGS_outputGeneratedVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, const char* variant, const LatteContextRegister& latteRegister) -{ - auto parameterMask = vertexShader->outputParameterMask; - for (uint32 i = 0; i < 32; i++) - { - if ((parameterMask & (1 << i)) == 0) - continue; - sint32 vsSemanticId = psInputTable->getVertexShaderOutParamSemanticId(latteRegister.GetRawView(), i); - if (vsSemanticId < 0) - continue; - // make sure PS has matching input - if (!psInputTable->hasPSImportForSemanticId(vsSemanticId)) - continue; - gsSrc.append(fmt::format("passParameterSem{}Out = gen4thVertex{}(passParameterSem{}In[0], passParameterSem{}In[1], passParameterSem{}In[2]);\r\n", vsSemanticId, variant, vsSemanticId, vsSemanticId, vsSemanticId)); - } - gsSrc.append(fmt::format("gl_Position = gen4thVertex{}(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_in[2].gl_Position);\r\n", variant)); - gsSrc.append("EmitVertex();\r\n"); -} - -void rectsEmulationGS_outputVerticesCode(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 p0, sint32 p1, sint32 p2, sint32 p3, const char* variant, const LatteContextRegister& latteRegister) -{ - sint32 pList[4] = { p0, p1, p2, p3 }; - for (sint32 i = 0; i < 4; i++) - { - if (pList[i] == 3) - rectsEmulationGS_outputGeneratedVertex(gsSrc, vertexShader, psInputTable, variant, latteRegister); - else - rectsEmulationGS_outputSingleVertex(gsSrc, vertexShader, psInputTable, pList[i], latteRegister); - } -} +#include "HW/Latte/Renderer/RendererCore.h" RendererShaderVk* rectsEmulationGS_generate(LatteDecompilerShader* vertexShader, const LatteContextRegister& latteRegister) { diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp index a6814186..d0933e35 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp @@ -1221,6 +1221,7 @@ void VulkanRenderer::draw_endRenderPass() m_state.activeRenderpassFBO = nullptr; } +// Defined in the Common renderer void LatteDraw_handleSpecialState8_clearAsDepth(); // transfer depth buffer data to color buffer diff --git a/src/config/ActiveSettings.cpp b/src/config/ActiveSettings.cpp index 862ac6c3..08def5a3 100644 --- a/src/config/ActiveSettings.cpp +++ b/src/config/ActiveSettings.cpp @@ -1,7 +1,9 @@ #include "Cafe/GameProfile/GameProfile.h" #include "Cafe/IOSU/legacy/iosu_crypto.h" #include "Cafe/HW/Latte/Core/Latte.h" +#ifdef ENABLE_VULKAN #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" +#endif #include "Cafe/CafeSystem.h" #include "Cemu/Logging/CemuLogging.h" #include "config/ActiveSettings.h" @@ -106,12 +108,28 @@ bool ActiveSettings::WaitForGX2DrawDoneEnabled() GraphicAPI ActiveSettings::GetGraphicsAPI() { - GraphicAPI api = g_current_game_profile->GetGraphicsAPI().value_or(GetConfig().graphic_api); - // check if vulkan even available - if (api == kVulkan && !g_vulkan_available) - api = kOpenGL; - - return api; + const GraphicAPI api = g_current_game_profile->GetGraphicsAPI().value_or(GetConfig().graphic_api); + std::optional fallbackAPI; +#ifdef ENABLE_VULKAN + if (g_vulkan_available) + { + if (api == kVulkan) + return api; + fallbackAPI = kVulkan; + } +#endif +#ifdef ENABLE_METAL + if (api == kMetal) + return api; + fallbackAPI = fallbackAPI.value_or(kMetal); +#endif +#ifdef ENABLE_OPENGL + if (api == kOpenGL) + return api; + fallbackAPI = fallbackAPI.value_or(kOpenGL); +#endif + cemu_assert(fallbackAPI.has_value()); + return *fallbackAPI; } float ActiveSettings::GetTVGamma() diff --git a/src/config/CemuConfig.cpp b/src/config/CemuConfig.cpp index 097587f8..139c290c 100644 --- a/src/config/CemuConfig.cpp +++ b/src/config/CemuConfig.cpp @@ -123,7 +123,7 @@ XMLConfigParser CemuConfig::Load(XMLConfigParser& parser) // graphics auto graphic = parser.get("Graphic"); - graphic_api = graphic.get("api", kOpenGL); + graphic_api = graphic.get("api", kDefaultGraphicsAPI); graphic.get("device", legacy_graphic_device_uuid); if (graphic.get("vkDevice").valid()) graphic.get("vkDevice", vk_graphic_device_uuid); @@ -144,7 +144,7 @@ XMLConfigParser CemuConfig::Load(XMLConfigParser& parser) fullscreen_scaling = graphic.get("FullscreenScaling", kKeepAspectRatio); async_compile = graphic.get("AsyncCompile", async_compile); vk_accurate_barriers = graphic.get("vkAccurateBarriers", true); // this used to be "VulkanAccurateBarriers" but because we changed the default to true in 1.27.1 the option name had to be changed -#if ENABLE_METAL +#ifdef ENABLE_METAL force_mesh_shaders = graphic.get("ForceMeshShaders", false); #endif @@ -273,7 +273,7 @@ XMLConfigParser CemuConfig::Load(XMLConfigParser& parser) crash_dump = debug.get("CrashDumpUnix", crash_dump); #endif gdb_port = debug.get("GDBPort", 1337); -#if ENABLE_METAL +#ifdef ENABLE_METAL gpu_capture_dir = debug.get("GPUCaptureDir", ""); framebuffer_fetch = debug.get("FramebufferFetch", true); #endif @@ -368,7 +368,7 @@ XMLConfigParser CemuConfig::Save(XMLConfigParser& parser) graphic.set("OverrideGammaValue", overrideGammaValue); graphic.set("UserDisplayGamma", userDisplayGamma); graphic.set("GX2DrawdoneSync", gx2drawdone_sync); -#if ENABLE_METAL +#ifdef ENABLE_METAL graphic.set("ForceMeshShaders", force_mesh_shaders); #endif //graphic.set("PrecompiledShaders", precompiled_shaders.GetValue()); @@ -437,7 +437,7 @@ XMLConfigParser CemuConfig::Save(XMLConfigParser& parser) debug.set("CrashDumpUnix", crash_dump.GetValue()); #endif debug.set("GDBPort", gdb_port); -#if ENABLE_METAL +#ifdef ENABLE_METAL debug.set("GPUCaptureDir", gpu_capture_dir); debug.set("FramebufferFetch", framebuffer_fetch); #endif diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index 6778ceb5..48c396f2 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -70,8 +70,17 @@ enum GraphicAPI kOpenGL = 0, kVulkan, kMetal, + COUNT }; +#if defined(ENABLE_VULKAN) +constexpr GraphicAPI kDefaultGraphicsAPI = kVulkan; +#elif defined(ENABLE_METAL) +constexpr GraphicAPI kDefaultGraphicsAPI = kMetal; +#elif defined(ENABLE_OPENGL) +constexpr GraphicAPI kDefaultGraphicsAPI = kOpenGL; +#endif + enum AudioChannels { kMono = 0, @@ -429,7 +438,7 @@ struct CemuConfig ConfigValueBounds console_language{ CafeConsoleLanguage::EN }; // graphics - ConfigValue graphic_api{ kVulkan }; + ConfigValue graphic_api{ kDefaultGraphicsAPI }; std::array legacy_graphic_device_uuid{}; // placeholder option for backwards compatibility with settings from 2.6 and before (renamed to "vkDevice") std::array vk_graphic_device_uuid; uint64 mtl_graphic_device_uuid{ 0 }; @@ -437,7 +446,7 @@ struct CemuConfig ConfigValue gx2drawdone_sync { true }; ConfigValue render_upside_down{ false }; ConfigValue async_compile{ true }; -#if ENABLE_METAL +#ifdef ENABLE_METAL ConfigValue force_mesh_shaders{ false }; #endif @@ -503,7 +512,7 @@ struct CemuConfig // debug ConfigValueBounds crash_dump{ CrashDump::Disabled }; ConfigValue gdb_port{ 1337 }; -#if ENABLE_METAL +#ifdef ENABLE_METAL ConfigValue gpu_capture_dir{ "" }; ConfigValue framebuffer_fetch{ true }; #endif diff --git a/src/gui/wxgui/CMakeLists.txt b/src/gui/wxgui/CMakeLists.txt index c2fc235b..b3cc5dbc 100644 --- a/src/gui/wxgui/CMakeLists.txt +++ b/src/gui/wxgui/CMakeLists.txt @@ -1,9 +1,5 @@ add_library(CemuWxGui STATIC canvas/IRenderCanvas.h - canvas/OpenGLCanvas.cpp - canvas/OpenGLCanvas.h - canvas/VulkanCanvas.cpp - canvas/VulkanCanvas.h CemuApp.cpp CemuApp.h CemuUpdateWindow.cpp @@ -118,6 +114,20 @@ add_library(CemuWxGui STATIC wxHelper.h ) +if (ENABLE_OPENGL) + target_sources(CemuWxGui PRIVATE + canvas/OpenGLCanvas.cpp + canvas/OpenGLCanvas.h + ) +endif() + +if (ENABLE_VULKAN) + target_sources(CemuWxGui PRIVATE + canvas/VulkanCanvas.cpp + canvas/VulkanCanvas.h + ) +endif() + if (ENABLE_METAL) target_sources(CemuWxGui PRIVATE canvas/MetalCanvas.cpp diff --git a/src/gui/wxgui/CemuApp.cpp b/src/gui/wxgui/CemuApp.cpp index 58e8c391..9a636989 100644 --- a/src/gui/wxgui/CemuApp.cpp +++ b/src/gui/wxgui/CemuApp.cpp @@ -3,7 +3,9 @@ #include "wxgui/MainWindow.h" #include "wxgui/wxgui.h" #include "config/CemuConfig.h" +#ifdef ENABLE_VULKAN #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" +#endif #include "Cafe/HW/Latte/Core/LatteOverlay.h" #include "config/ActiveSettings.h" #include "config/LaunchSettings.h" @@ -361,7 +363,9 @@ bool CemuApp::OnInit() __fastfail(0); } #endif + #ifdef ENABLE_VULKAN InitializeGlobalVulkan(); + #endif Bind(wxEVT_ACTIVATE_APP, &CemuApp::ActivateApp, this); diff --git a/src/gui/wxgui/GameProfileWindow.cpp b/src/gui/wxgui/GameProfileWindow.cpp index 37fac6bc..5962133e 100644 --- a/src/gui/wxgui/GameProfileWindow.cpp +++ b/src/gui/wxgui/GameProfileWindow.cpp @@ -113,7 +113,7 @@ GameProfileWindow::GameProfileWindow(wxWindow* parent, uint64_t title_id) first_row->Add(new wxStaticText(panel, wxID_ANY, _("Graphics API")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); wxString gapi_values[] = { "", "OpenGL", "Vulkan", -#if ENABLE_METAL +#ifdef ENABLE_METAL "Metal" #endif }; @@ -127,7 +127,7 @@ GameProfileWindow::GameProfileWindow(wxWindow* parent, uint64_t title_id) m_shader_mul_accuracy->SetToolTip(_("EXPERT OPTION\nControls the accuracy of floating point multiplication in shaders.\n\nRecommended: true")); first_row->Add(m_shader_mul_accuracy, 0, wxALL, 5); -#if ENABLE_METAL +#ifdef ENABLE_METAL first_row->Add(new wxStaticText(panel, wxID_ANY, _("Shader fast math")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); wxString math_values[] = { _("false"), _("true") }; @@ -296,7 +296,7 @@ void GameProfileWindow::ApplyProfile() else m_graphic_api->SetSelection(1 + m_game_profile.m_graphics_api.value()); // "", OpenGL, Vulkan, Metal m_shader_mul_accuracy->SetSelection((int)m_game_profile.m_accurateShaderMul); -#if ENABLE_METAL +#ifdef ENABLE_METAL m_shader_fast_math->SetSelection((int)m_game_profile.m_shaderFastMath); m_metal_buffer_cache_mode->SetSelection((int)m_game_profile.m_metalBufferCacheMode); m_position_invariance->SetSelection((int)m_game_profile.m_positionInvariance); @@ -362,7 +362,7 @@ void GameProfileWindow::SaveProfile() m_game_profile.m_accurateShaderMul = (AccurateShaderMulOption)m_shader_mul_accuracy->GetSelection(); if (m_game_profile.m_accurateShaderMul != AccurateShaderMulOption::False && m_game_profile.m_accurateShaderMul != AccurateShaderMulOption::True) m_game_profile.m_accurateShaderMul = AccurateShaderMulOption::True; // force a legal value -#if ENABLE_METAL +#ifdef ENABLE_METAL m_game_profile.m_shaderFastMath = (bool)m_shader_fast_math->GetSelection(); m_game_profile.m_metalBufferCacheMode = (MetalBufferCacheMode)m_metal_buffer_cache_mode->GetSelection(); m_game_profile.m_positionInvariance = (PositionInvariance)m_position_invariance->GetSelection(); diff --git a/src/gui/wxgui/GameProfileWindow.h b/src/gui/wxgui/GameProfileWindow.h index 3cb381d9..fe6150dc 100644 --- a/src/gui/wxgui/GameProfileWindow.h +++ b/src/gui/wxgui/GameProfileWindow.h @@ -40,7 +40,7 @@ private: wxChoice* m_graphic_api; wxChoice* m_shader_mul_accuracy; -#if ENABLE_METAL +#ifdef ENABLE_METAL wxChoice* m_shader_fast_math; wxChoice* m_metal_buffer_cache_mode; wxChoice* m_position_invariance; diff --git a/src/gui/wxgui/GeneralSettings2.cpp b/src/gui/wxgui/GeneralSettings2.cpp index 003bd130..f1435fba 100644 --- a/src/gui/wxgui/GeneralSettings2.cpp +++ b/src/gui/wxgui/GeneralSettings2.cpp @@ -27,9 +27,11 @@ #include "audio/IAudioInputAPI.h" +#ifdef ENABLE_VULKAN #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h" -#if ENABLE_METAL +#endif +#ifdef ENABLE_METAL #include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h" #endif #include "Cafe/Account/Account.h" @@ -87,6 +89,7 @@ private: IAudioInputAPI::DeviceDescriptionPtr m_description; }; +#ifdef ENABLE_VULKAN class wxVulkanUUID : public wxClientData { public: @@ -97,8 +100,9 @@ public: private: VulkanRenderer::DeviceInfo m_device_info; }; +#endif -#if ENABLE_METAL +#ifdef ENABLE_METAL class wxMetalUUID : public wxClientData { public: @@ -352,15 +356,25 @@ wxPanel* GeneralSettings2::AddGraphicsPage(wxNotebook* notebook) row->Add(new wxStaticText(box, wxID_ANY, _("Graphics API")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - sint32 api_size = 1; - wxString choices[3] = { "OpenGL" }; + sint32 api_size = 0; + wxString choices[size_t(GraphicAPI::COUNT)]; + +#ifdef ENABLE_OPENGL + choices[api_size++] = "OpenGL"; + m_api_map.push_back(GraphicAPI::kOpenGL); +#endif +#ifdef ENABLE_VULKAN if (g_vulkan_available) { choices[api_size++] = "Vulkan"; + m_api_map.push_back(GraphicAPI::kVulkan); } -#if ENABLE_METAL - choices[api_size++] = "Metal"; #endif +#ifdef ENABLE_METAL + choices[api_size++] = "Metal"; + m_api_map.push_back(GraphicAPI::kMetal); +#endif + wxASSERT(api_size > 0); m_graphic_api = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, api_size, choices); m_graphic_api->SetSelection(0); @@ -400,7 +414,7 @@ wxPanel* GeneralSettings2::AddGraphicsPage(wxNotebook* notebook) m_gx2drawdone_sync->SetToolTip(_("If synchronization is requested by the game, the emulated CPU will wait for the GPU to finish all operations.\nThis is more accurate behavior, but may cause lower performance")); graphic_misc_row->Add(m_gx2drawdone_sync, 0, wxALL, 5); -#if ENABLE_METAL +#ifdef ENABLE_METAL m_force_mesh_shaders = new wxCheckBox(box, wxID_ANY, _("Force mesh shaders")); m_force_mesh_shaders->SetToolTip(_("Force mesh shaders on all GPUs that support them. Mesh shaders are disabled by default on Intel GPUs due to potential stability issues.\nMetal only")); graphic_misc_row->Add(m_force_mesh_shaders, 0, wxALL, 5); @@ -1028,7 +1042,7 @@ wxPanel* GeneralSettings2::AddDebugPage(wxNotebook* notebook) debug_panel_sizer->Add(debug_row, 0, wxALL | wxEXPAND, 5); } -#if ENABLE_METAL +#ifdef ENABLE_METAL { auto* debug_row = new wxFlexGridSizer(0, 2, 0, 0); debug_row->SetFlexibleDirection(wxBOTH); @@ -1216,9 +1230,10 @@ void GeneralSettings2::StoreConfig() } // graphics - config.graphic_api = (GraphicAPI)m_graphic_api->GetSelection(); + config.graphic_api = m_api_map[m_graphic_api->GetSelection()]; selection = m_graphic_device->GetSelection(); +#ifdef ENABLE_VULKAN if (config.graphic_api == GraphicAPI::kVulkan) { if (selection != wxNOT_FOUND) @@ -1232,25 +1247,26 @@ void GeneralSettings2::StoreConfig() else config.vk_graphic_device_uuid = {}; } -#if ENABLE_METAL - else if (config.graphic_api == GraphicAPI::kMetal) +#endif +#ifdef ENABLE_METAL + if (config.graphic_api == GraphicAPI::kMetal) { - if (selection != wxNOT_FOUND) - { - const auto* info = (wxMetalUUID*)m_graphic_device->GetClientObject(selection); - if (info) - config.mtl_graphic_device_uuid = info->GetDeviceInfo().uuid; - else - config.mtl_graphic_device_uuid = {}; - } - else - config.mtl_graphic_device_uuid = {}; + if (selection != wxNOT_FOUND) + { + const auto* info = (wxMetalUUID*)m_graphic_device->GetClientObject(selection); + if (info) + config.mtl_graphic_device_uuid = info->GetDeviceInfo().uuid; + else + config.mtl_graphic_device_uuid = {}; + } + else + config.mtl_graphic_device_uuid = {}; } #endif config.gx2drawdone_sync = m_gx2drawdone_sync->IsChecked(); -#if ENABLE_METAL +#ifdef ENABLE_METAL config.force_mesh_shaders = m_force_mesh_shaders->IsChecked(); #endif config.async_compile = m_async_compile->IsChecked(); @@ -1281,7 +1297,7 @@ void GeneralSettings2::StoreConfig() // debug config.crash_dump = (CrashDump)m_crash_dump->GetSelection(); config.gdb_port = m_gdb_port->GetValue(); -#if ENABLE_METAL +#ifdef ENABLE_METAL config.gpu_capture_dir = m_gpu_capture_dir->GetValue().utf8_string(); config.framebuffer_fetch = m_framebuffer_fetch->IsChecked(); #endif @@ -1721,7 +1737,12 @@ void GeneralSettings2::HandleGraphicsApiSelection() selection = GetConfig().vsync; m_vsync->Clear(); - if (m_graphic_api->GetSelection() == 0) + + auto api = m_api_map[m_graphic_api->GetSelection()]; + switch (api) + { +#ifdef ENABLE_OPENGL + case GraphicAPI::kOpenGL: { // OpenGL m_vsync->AppendString(_("Off")); @@ -1736,16 +1757,19 @@ void GeneralSettings2::HandleGraphicsApiSelection() m_gx2drawdone_sync->Enable(); m_async_compile->Disable(); -#if ENABLE_METAL +#ifdef ENABLE_METAL m_force_mesh_shaders->Disable(); #endif + break; } - else if (m_graphic_api->GetSelection() == 1) +#endif +#ifdef ENABLE_VULKAN + case GraphicAPI::kVulkan: { // Vulkan m_gx2drawdone_sync->Disable(); m_async_compile->Enable(); -#if ENABLE_METAL +#ifdef ENABLE_METAL m_force_mesh_shaders->Disable(); #endif @@ -1779,9 +1803,11 @@ void GeneralSettings2::HandleGraphicsApiSelection() } } } + break; } -#if ENABLE_METAL - else + #endif +#ifdef ENABLE_METAL + case GraphicAPI::kMetal: { // Metal m_gx2drawdone_sync->Disable(); @@ -1815,8 +1841,10 @@ void GeneralSettings2::HandleGraphicsApiSelection() } } } + break; } #endif + } } void GeneralSettings2::ApplyConfig() @@ -1870,7 +1898,14 @@ void GeneralSettings2::ApplyConfig() } // graphics - m_graphic_api->SetSelection(config.graphic_api); + for (int i = 0; i < (int)m_api_map.size(); ++i) + { + if (m_api_map[i] == config.graphic_api) + { + m_graphic_api->SetSelection(i); + break; + } + } m_vsync->SetSelection(config.vsync); m_overrideGamma->SetValue(config.overrideAppGammaPreference); m_overrideGammaValue->SetValue(config.overrideGammaValue); @@ -1883,7 +1918,7 @@ void GeneralSettings2::ApplyConfig() } m_async_compile->SetValue(config.async_compile); m_gx2drawdone_sync->SetValue(config.gx2drawdone_sync); -#if ENABLE_METAL +#ifdef ENABLE_METAL m_force_mesh_shaders->SetValue(config.force_mesh_shaders); #endif m_upscale_filter->SetSelection(config.upscale_filter); @@ -2022,7 +2057,7 @@ void GeneralSettings2::ApplyConfig() // debug m_crash_dump->SetSelection((int)config.crash_dump.GetValue()); m_gdb_port->SetValue(config.gdb_port.GetValue()); -#if ENABLE_METAL +#ifdef ENABLE_METAL m_gpu_capture_dir->SetValue(wxString::FromUTF8(config.gpu_capture_dir.GetValue())); m_framebuffer_fetch->SetValue(config.framebuffer_fetch); #endif diff --git a/src/gui/wxgui/GeneralSettings2.h b/src/gui/wxgui/GeneralSettings2.h index 634f0cfa..2f7e3b68 100644 --- a/src/gui/wxgui/GeneralSettings2.h +++ b/src/gui/wxgui/GeneralSettings2.h @@ -1,4 +1,5 @@ #pragma once +#include "config/CemuConfig.h" #include #include #include @@ -36,6 +37,7 @@ private: bool m_game_launched; bool m_has_account_change = false; // keep track of dirty state of accounts + std::vector m_api_map; // map from dropdown index to GraphicsAPISetting, used in HandleGraphicsApiSelection wxPanel* AddGeneralPage(wxNotebook* notebook); @@ -71,7 +73,7 @@ private: wxCheckBox* m_userDisplayisSRGB; wxCheckBox *m_async_compile, *m_gx2drawdone_sync; -#if ENABLE_METAL +#ifdef ENABLE_METAL wxCheckBox *m_force_mesh_shaders; #endif wxRadioBox* m_upscale_filter, *m_downscale_filter, *m_fullscreen_scaling; @@ -99,7 +101,7 @@ private: // Debug wxChoice* m_crash_dump; wxSpinCtrl* m_gdb_port; -#if ENABLE_METAL +#ifdef ENABLE_METAL wxTextCtrl* m_gpu_capture_dir; wxCheckBox* m_framebuffer_fetch; #endif diff --git a/src/gui/wxgui/MainWindow.cpp b/src/gui/wxgui/MainWindow.cpp index d2deef7b..cac5db85 100644 --- a/src/gui/wxgui/MainWindow.cpp +++ b/src/gui/wxgui/MainWindow.cpp @@ -3,18 +3,21 @@ #include "wxCemuConfig.h" #include "wxgui/wxgui.h" #include "wxgui/MainWindow.h" - -#include - #include "wxgui/GameUpdateWindow.h" #include "wxgui/PadViewFrame.h" #include "wxgui/windows/TextureRelationViewer/TextureRelationWindow.h" #include "wxgui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h" #include "AudioDebuggerWindow.h" +#ifdef ENABLE_OPENGL #include "wxgui/canvas/OpenGLCanvas.h" +#endif +#ifdef ENABLE_VULKAN #include "wxgui/canvas/VulkanCanvas.h" -#if ENABLE_METAL +#include "Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.h" +#endif +#ifdef ENABLE_METAL #include "wxgui/canvas/MetalCanvas.h" +#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h" #endif #include "Cafe/OS/libs/nfc/nfc.h" #include "Cafe/OS/libs/swkbd/swkbd.h" @@ -42,7 +45,6 @@ #include "wxgui/DownloadGraphicPacksWindow.h" #include "wxgui/GettingStartedDialog.h" #include "wxgui/helpers/wxHelpers.h" -#include "Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.h" #include "wxgui/input/InputSettings2.h" #include "wxgui/input/HotkeySettings.h" #include "input/InputManager.h" @@ -66,10 +68,6 @@ #include "gamemode_client.h" #endif -#if ENABLE_METAL -#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h" -#endif - #include "Cafe/TitleList/TitleInfo.h" #include "Cafe/TitleList/TitleList.h" #include "wxHelper.h" @@ -1043,7 +1041,7 @@ void MainWindow::OnDebugSetting(wxCommandEvent& event) if(!GetConfig().vk_accurate_barriers) wxMessageBox(_("Warning: Disabling the accurate barriers option will lead to flickering graphics but may improve performance. It is highly recommended to leave it turned on."), _("Accurate barriers are off"), wxOK); } -#if ENABLE_METAL +#ifdef ENABLE_METAL else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_GPU_CAPTURE) { cemu_assert_debug(g_renderer->GetType() == RendererAPI::Metal); @@ -1607,14 +1605,21 @@ void MainWindow::CreateCanvas() this->GetSizer()->Add(m_game_panel, 1, wxEXPAND); // create canvas - if (ActiveSettings::GetGraphicsAPI() == kVulkan) - m_render_canvas = new VulkanCanvas(m_game_panel, wxSize(1280, 720), true); - else if (ActiveSettings::GetGraphicsAPI() == kOpenGL) + #ifdef ENABLE_OPENGL + if (ActiveSettings::GetGraphicsAPI() == kOpenGL) m_render_canvas = GLCanvas_Create(m_game_panel, wxSize(1280, 720), true); -#if ENABLE_METAL - else - m_render_canvas = new MetalCanvas(m_game_panel, wxSize(1280, 720), true); -#endif + #endif + #ifdef ENABLE_VULKAN + if (ActiveSettings::GetGraphicsAPI() == kVulkan) + m_render_canvas = new VulkanCanvas(m_game_panel, wxSize(1280, 720), true); + #endif + #ifdef ENABLE_METAL + if (ActiveSettings::GetGraphicsAPI() == kMetal) + m_render_canvas = new MetalCanvas(m_game_panel, wxSize(1280, 720), true); + #endif + if (!m_render_canvas) + cemu_assert(false && "Failed to create canvas or invalid graphics API selected"); + cemu_assert(m_render_canvas != nullptr); // mouse events m_render_canvas->Bind(wxEVT_MOTION, &MainWindow::OnMouseMove, this); @@ -1676,7 +1681,9 @@ void MainWindow::OnSizeEvent(wxSizeEvent& event) event.Skip(); + #ifdef ENABLE_VULKAN VsyncDriver_notifyWindowPosChanged(); + #endif } void MainWindow::OnDPIChangedEvent(wxDPIChangedEvent& event) @@ -1697,7 +1704,9 @@ void MainWindow::OnMove(wxMoveEvent& event) if (m_debugger_window && m_debugger_window->IsShown()) m_debugger_window->OnParentMove(GetPosition(), GetSize()); + #ifdef ENABLE_VULKAN VsyncDriver_notifyWindowPosChanged(); + #endif } void MainWindow::OnDebuggerClose(wxCloseEvent& event) @@ -2351,7 +2360,7 @@ void MainWindow::RecreateMenu() auto accurateBarriers = debugMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_VK_ACCURATE_BARRIERS, _("&Accurate barriers (Vulkan)")); accurateBarriers->Check(GetConfig().vk_accurate_barriers); -#if ENABLE_METAL +#ifdef ENABLE_METAL auto gpuCapture = debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_GPU_CAPTURE, _("&GPU capture (Metal)")); gpuCapture->Enable(m_game_launched && g_renderer->GetType() == RendererAPI::Metal); #endif diff --git a/src/gui/wxgui/PadViewFrame.cpp b/src/gui/wxgui/PadViewFrame.cpp index 2079f1eb..ca213849 100644 --- a/src/gui/wxgui/PadViewFrame.cpp +++ b/src/gui/wxgui/PadViewFrame.cpp @@ -6,9 +6,13 @@ #include "config/ActiveSettings.h" #include "Cafe/OS/libs/swkbd/swkbd.h" +#ifdef ENABLE_OPENGL #include "wxgui/canvas/OpenGLCanvas.h" +#endif +#ifdef ENABLE_VULKAN #include "wxgui/canvas/VulkanCanvas.h" -#if ENABLE_METAL +#endif +#ifdef ENABLE_METAL #include "wxgui/canvas/MetalCanvas.h" #endif #include "config/CemuConfig.h" @@ -75,16 +79,21 @@ void PadViewFrame::InitializeRenderCanvas() { auto sizer = new wxBoxSizer(wxVERTICAL); { + #ifdef ENABLE_VULKAN if (ActiveSettings::GetGraphicsAPI() == kVulkan) m_render_canvas = new VulkanCanvas(this, wxSize(854, 480), false); - else if (ActiveSettings::GetGraphicsAPI() == kOpenGL) + #endif + #ifdef ENABLE_OPENGL + if (ActiveSettings::GetGraphicsAPI() == kOpenGL) m_render_canvas = GLCanvas_Create(this, wxSize(854, 480), false); -#if ENABLE_METAL - else - m_render_canvas = new MetalCanvas(this, wxSize(854, 480), false); -#endif + #endif + #ifdef ENABLE_METAL + if (ActiveSettings::GetGraphicsAPI() == kMetal) + m_render_canvas = new MetalCanvas(this, wxSize(854, 480), false); + #endif sizer->Add(m_render_canvas, 1, wxEXPAND, 0, nullptr); } + cemu_assert(m_render_canvas != nullptr); SetSizer(sizer); Layout(); diff --git a/src/gui/wxgui/wxWindowSystem.cpp b/src/gui/wxgui/wxWindowSystem.cpp index 4b199741..bb730db8 100644 --- a/src/gui/wxgui/wxWindowSystem.cpp +++ b/src/gui/wxgui/wxWindowSystem.cpp @@ -101,12 +101,10 @@ void WindowSystem::UpdateWindowTitles(bool isIdle, bool isLoading, double fps) case RendererAPI::Vulkan: renderer = "[Vulkan]"; break; -#if ENABLE_METAL case RendererAPI::Metal: renderer = "[Metal]"; break; -#endif - default:; + default: break; } } diff --git a/src/imgui/CMakeLists.txt b/src/imgui/CMakeLists.txt index e2d669c5..b5e91075 100644 --- a/src/imgui/CMakeLists.txt +++ b/src/imgui/CMakeLists.txt @@ -1,12 +1,22 @@ add_library(imguiImpl - imgui_impl_opengl3.cpp - imgui_impl_opengl3.h - imgui_impl_vulkan.cpp - imgui_impl_vulkan.h imgui_extension.cpp imgui_extension.h ) +if (ENABLE_OPENGL) + target_sources(imguiImpl PRIVATE + imgui_impl_opengl3.cpp + imgui_impl_opengl3.h + ) +endif() + +if (ENABLE_VULKAN) + target_sources(imguiImpl PRIVATE + imgui_impl_vulkan.cpp + imgui_impl_vulkan.h + ) +endif() + if (ENABLE_METAL) target_sources(imguiImpl PRIVATE imgui_impl_metal.mm diff --git a/src/imgui/imgui_extension.cpp b/src/imgui/imgui_extension.cpp index dadd2031..eb0e31ef 100644 --- a/src/imgui/imgui_extension.cpp +++ b/src/imgui/imgui_extension.cpp @@ -2,9 +2,13 @@ #include "WindowSystem.h" #include "Cafe/HW/Latte/Renderer/Renderer.h" #include "resource/IconsFontAwesome5.h" -#include "imgui_impl_opengl3.h" #include "resource/resource.h" +#ifdef ENABLE_OPENGL +#include "imgui_impl_opengl3.h" +#endif +#ifdef ENABLE_VULKAN #include "imgui_impl_vulkan.h" +#endif #include "input/InputManager.h" // diff --git a/src/main.cpp b/src/main.cpp index 05c3246b..da367a6a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,7 +19,6 @@ #include "util/helpers/helpers.h" #include "config/ActiveSettings.h" -#include "Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.h" #include "Cafe/IOSU/legacy/iosu_crypto.h" #include "Cafe/OS/libs/vpad/vpad.h" @@ -68,7 +67,8 @@ void _putenvSafe(const char* c) void reconfigureGLDrivers() { - // reconfigure GL drivers to store +#ifdef ENABLE_OPENGL + // reconfigure GL drivers to store const fs::path nvCacheDir = ActiveSettings::GetCachePath("shaderCache/driver/nvidia/"); std::error_code err; @@ -84,13 +84,15 @@ void reconfigureGLDrivers() _putenvSafe(nvCacheDirEnvOption.c_str()); #endif _putenvSafe("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1"); - +#endif } void reconfigureVkDrivers() { +#ifdef ENABLE_VULKAN _putenvSafe("DISABLE_LAYER_AMD_SWITCHABLE_GRAPHICS_1=1"); _putenvSafe("DISABLE_VK_LAYER_VALVE_steam_fossilize_1=1"); +#endif } void WindowsInitCwd() @@ -259,7 +261,7 @@ int main(int argc, char* argv[]) int BreathOfTheWildChildProcessMain(); int main(int argc, char *argv[]) { -#if BOOST_OS_LINUX +#if BOOST_OS_LINUX && defined(ENABLE_VULKAN) if (getenv("CEMU_DETECT_RADV") != nullptr) return BreathOfTheWildChildProcessMain(); #endif From 6ab2f501ffd9512cb425871cbb82a59bebece970 Mon Sep 17 00:00:00 2001 From: "Hr. Vedel" <38987957+SirHrVedel@users.noreply.github.com> Date: Sat, 9 May 2026 15:23:45 +0200 Subject: [PATCH 6/7] coreinit: Stub MCP_DemoGetRemainder to 99 (#1902) --- src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp b/src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp index 95f3c6db..afbe0bfa 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_MCP.cpp @@ -421,6 +421,15 @@ namespace coreinit return 0; } + uint32 MCP_DemoLaunchGetRemainder(uint32 mcpHandle, uint64 titleId, uint32be* outRemainder) + { + // stub to always return 99 uses remaining for game demos. + // used by drmapp.rpl to determine remaining uses for game demos + if (outRemainder) + *outRemainder = 99; + return 0; + } + void InitializeMCP() { osLib_addFunction("coreinit", "MCP_Open", coreinitExport_MCP_Open); @@ -450,6 +459,7 @@ namespace coreinit cafeExportRegister("coreinit", MCP_GetEcoSettings, LogType::Placeholder); cafeExportRegister("coreinit", MCP_GetTitleId, LogType::Placeholder); + cafeExportRegister("coreinit", MCP_DemoLaunchGetRemainder, LogType::Placeholder); } } From 8e3e961b8e197a9f84cba1f216ce9728f02389d8 Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Sat, 9 May 2026 16:27:26 +0300 Subject: [PATCH 7/7] build+input: Make SDL optional (#1895) --- BUILD.md | 2 +- CMakeLists.txt | 6 ++- src/CMakeLists.txt | 5 ++- src/gui/wxgui/CMakeLists.txt | 5 ++- src/input/CMakeLists.txt | 14 +++++-- src/input/ControllerFactory.cpp | 9 +++-- src/input/InputManager.cpp | 2 +- src/input/InputManager.h | 18 ++++----- src/input/api/Controller.cpp | 40 ++++++++----------- src/input/api/Controller.h | 4 +- src/input/api/SDL/SDLControllerProvider.h | 4 -- .../api/Wiimote/hidapi/HidapiWiimote.cpp | 1 + src/input/api/Wiimote/hidapi/HidapiWiimote.h | 5 +-- src/input/emulated/ClassicController.cpp | 4 ++ src/input/emulated/ProController.cpp | 4 ++ src/input/emulated/VPADController.cpp | 4 ++ src/main.cpp | 6 +++ src/util/ScreenSaver/ScreenSaver.h | 14 ++++++- 18 files changed, 89 insertions(+), 58 deletions(-) diff --git a/BUILD.md b/BUILD.md index ff99caa3..6e1a2be9 100644 --- a/BUILD.md +++ b/BUILD.md @@ -246,7 +246,7 @@ Example usage: `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DENABLE_SDL=ON - | ENABLE_DISCORD_RPC | | Enable Discord Rich presence support | ON | | | ENABLE_OPENGL | | Enable OpenGL graphics backend | ON | | | ENABLE_HIDAPI | | Enable HIDAPI (used for Wiimote controller API) | ON | | -| ENABLE_SDL | | Enable SDLController controller API | ON | Currently required | +| ENABLE_SDL | | Enable SDLController controller API | ON | | | ENABLE_VCPKG | | Use VCPKG package manager to obtain dependencies | ON | | | ENABLE_VULKAN | | Enable the Vulkan graphics backend | ON | | | ENABLE_WXWIDGETS | | Enable wxWidgets UI | ON | Currently required | diff --git a/CMakeLists.txt b/CMakeLists.txt index cdc77156..69a5b0b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,7 +150,6 @@ option(ENABLE_CUBEB "Enabled cubeb backend" ON) option(ENABLE_WXWIDGETS "Build with wxWidgets UI (Currently required)" ON) find_package(Threads REQUIRED) -find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3) find_package(CURL REQUIRED) find_package(pugixml REQUIRED) find_package(RapidJSON REQUIRED) @@ -164,6 +163,11 @@ find_package(glm REQUIRED) find_package(fmt 9 REQUIRED) find_package(PNG REQUIRED) +if(ENABLE_SDL) + find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3) + add_compile_definitions(HAS_SDL) +endif() + # glslang versions older than 11.11.0 define targets without a namespace if (NOT TARGET glslang::SPIRV AND TARGET SPIRV) add_library(glslang::SPIRV ALIAS SPIRV) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 863bdd07..977f48b7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -220,9 +220,12 @@ target_link_libraries(CemuBin PRIVATE CemuGui CemuInput CemuUtil - SDL3::SDL3 ) +if(ENABLE_SDL) + target_link_libraries(CemuBin PRIVATE SDL3::SDL3) +endif() + if(UNIX AND NOT APPLE) # due to nasm output some linkers will make stack executable # cemu does not require this so we explicity disable it diff --git a/src/gui/wxgui/CMakeLists.txt b/src/gui/wxgui/CMakeLists.txt index b3cc5dbc..079a78cd 100644 --- a/src/gui/wxgui/CMakeLists.txt +++ b/src/gui/wxgui/CMakeLists.txt @@ -157,7 +157,6 @@ target_link_libraries(CemuWxGui PRIVATE libzip::zip ZArchive::zarchive CemuComponents - SDL3::SDL3 pugixml::pugixml CemuCafe PUBLIC @@ -176,6 +175,10 @@ if(ENABLE_CUBEB) target_link_libraries(CemuWxGui PRIVATE cubeb::cubeb) endif() +if(ENABLE_SDL) + target_link_libraries(CemuWxGui PRIVATE SDL3::SDL3) +endif() + if(UNIX AND NOT APPLE) if(ENABLE_FERAL_GAMEMODE) target_link_libraries(CemuWxGui PRIVATE gamemode) diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 0b40a965..4978ef0a 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -15,10 +15,6 @@ add_library(CemuInput api/DSU/DSUControllerProvider.h api/DSU/DSUMessages.h api/DSU/DSUMessages.cpp - api/SDL/SDLController.cpp - api/SDL/SDLControllerProvider.cpp - api/SDL/SDLController.h - api/SDL/SDLControllerProvider.h api/Keyboard/KeyboardControllerProvider.h api/Keyboard/KeyboardControllerProvider.cpp api/Keyboard/KeyboardController.cpp @@ -92,6 +88,16 @@ if (SUPPORTS_WIIMOTE) endif () +if(ENABLE_SDL) + target_sources(CemuInput PRIVATE + api/SDL/SDLController.cpp + api/SDL/SDLControllerProvider.cpp + api/SDL/SDLController.h + api/SDL/SDLControllerProvider.h + ) + target_link_libraries(CemuInput PRIVATE SDL3::SDL3) +endif() + target_include_directories(CemuInput PUBLIC "../") target_link_libraries(CemuInput PRIVATE diff --git a/src/input/ControllerFactory.cpp b/src/input/ControllerFactory.cpp index 968fb8f3..3827e1d4 100644 --- a/src/input/ControllerFactory.cpp +++ b/src/input/ControllerFactory.cpp @@ -5,7 +5,6 @@ #include "input/emulated/ClassicController.h" #include "input/emulated/WiimoteController.h" -#include "input/api/SDL/SDLController.h" #include "input/api/Keyboard/KeyboardController.h" #include "input/api/DSU/DSUController.h" #include "input/api/GameCube/GameCubeController.h" @@ -19,6 +18,10 @@ #include "input/api/Wiimote/NativeWiimoteController.h" #endif +#ifdef HAS_SDL +#include "input/api/SDL/SDLController.h" +#endif + ControllerPtr ControllerFactory::CreateController(InputAPI::Type api, std::string_view uuid, std::string_view display_name) { @@ -59,7 +62,7 @@ ControllerPtr ControllerFactory::CreateController(InputAPI::Type api, std::strin return std::make_shared(index); } #endif -#if HAS_SDL +#ifdef HAS_SDL case InputAPI::SDLController: { // diid_guid @@ -137,7 +140,7 @@ ControllerProviderPtr ControllerFactory::CreateControllerProvider(InputAPI::Type case InputAPI::Keyboard: return std::make_shared(); #endif -#if HAS_SDL +#ifdef HAS_SDL case InputAPI::SDLController: return std::make_shared(); #endif diff --git a/src/input/InputManager.cpp b/src/input/InputManager.cpp index af99b261..071b43dc 100644 --- a/src/input/InputManager.cpp +++ b/src/input/InputManager.cpp @@ -28,7 +28,7 @@ InputManager::InputManager() #if HAS_KEYBOARD create_provider(); #endif -#if HAS_SDL +#ifdef HAS_SDL create_provider(); #endif #if HAS_XINPUT diff --git a/src/input/InputManager.h b/src/input/InputManager.h index 7957fbf7..b510474b 100644 --- a/src/input/InputManager.h +++ b/src/input/InputManager.h @@ -9,9 +9,10 @@ #include "input/api/Wiimote/WiimoteControllerProvider.h" #endif -#include "util/helpers/Singleton.h" - +#ifdef HAS_SDL #include "input/api/SDL/SDLControllerProvider.h" +#endif + #include "input/api/Keyboard/KeyboardControllerProvider.h" #include "input/api/DSU/DSUControllerProvider.h" #include "input/api/GameCube/GameCubeControllerProvider.h" @@ -19,8 +20,7 @@ #include "input/emulated/VPADController.h" #include "input/emulated/WPADController.h" -#include -#include +#include "util/helpers/Singleton.h" class InputManager : public Singleton { @@ -108,16 +108,14 @@ private: std::array m_is_gameprofile_set{}; - template - void create_provider() // lambda templates only work in c++20 -> define locally in ctor + template TProvider> + void create_provider() { - static_assert(std::is_base_of_v); try { auto controller = std::make_shared(); - m_api_available[controller->api()] = std::vector{ controller }; - } - catch (const std::exception& ex) + m_api_available[controller->api()] = std::vector{controller}; + } catch (const std::exception& ex) { cemuLog_log(LogType::Force, ex.what()); } diff --git a/src/input/api/Controller.cpp b/src/input/api/Controller.cpp index a6b0f5e7..a2280e9a 100644 --- a/src/input/api/Controller.cpp +++ b/src/input/api/Controller.cpp @@ -7,6 +7,19 @@ ControllerBase::ControllerBase(std::string_view uuid, std::string_view display_n { } +inline void apply_axis_button(ControllerButtonState& buttons, const glm::vec2& axis, int flag) +{ + if (axis.x < -ControllerState::kAxisThreshold) + buttons.SetButtonState(flag + (kAxisXN - kAxisXP), true); + else if (axis.x > ControllerState::kAxisThreshold) + buttons.SetButtonState(flag, true); + + if (axis.y < -ControllerState::kAxisThreshold) + buttons.SetButtonState(flag + 1 + (kAxisXN - kAxisXP), true); + else if (axis.y > ControllerState::kAxisThreshold) + buttons.SetButtonState(flag + 1, true); +} + const ControllerState& ControllerBase::update_state() { if (!m_is_calibrated) @@ -21,26 +34,9 @@ const ControllerState& ControllerBase::update_state() apply_axis_setting(result.rotation, m_default_state.rotation, m_settings.rotation); apply_axis_setting(result.trigger, m_default_state.trigger, m_settings.trigger); -#define APPLY_AXIS_BUTTON(_axis_, _flag_) \ - if (result._axis_.x < -ControllerState::kAxisThreshold) \ - result.buttons.SetButtonState((_flag_) + (kAxisXN - kAxisXP), true); \ - else if (result._axis_.x > ControllerState::kAxisThreshold) \ - result.buttons.SetButtonState((_flag_), true); \ - if (result._axis_.y < -ControllerState::kAxisThreshold) \ - result.buttons.SetButtonState((_flag_) + 1 + (kAxisXN - kAxisXP), true); \ - else if (result._axis_.y > ControllerState::kAxisThreshold) \ - result.buttons.SetButtonState((_flag_) + 1, true); - - if (result.axis.x < -ControllerState::kAxisThreshold) - result.buttons.SetButtonState((kAxisXP) + (kAxisXN - kAxisXP), true); - else if (result.axis.x > ControllerState::kAxisThreshold) - result.buttons.SetButtonState((kAxisXP), true); - if (result.axis.y < -ControllerState::kAxisThreshold) - result.buttons.SetButtonState((kAxisXP) + 1 + (kAxisXN - kAxisXP), true); - else if (result.axis.y > ControllerState::kAxisThreshold) - result.buttons.SetButtonState((kAxisXP) + 1, true); - APPLY_AXIS_BUTTON(rotation, kRotationXP); - APPLY_AXIS_BUTTON(trigger, kTriggerXP); + apply_axis_button(result.buttons, result.axis, kAxisXP); + apply_axis_button(result.buttons, result.rotation, kRotationXP); + apply_axis_button(result.buttons, result.trigger, kTriggerXP); /* // positive values @@ -64,9 +60,6 @@ const ControllerState& ControllerBase::update_state() kTriggerYN, */ - -#undef APPLY_AXIS_BUTTON - WindowSystem::CaptureInput(result, m_last_state); m_last_state = std::move(result); @@ -200,7 +193,6 @@ std::string ControllerBase::get_button_name(uint64 button) const case kTriggerYN: return "y-Trigger-"; } - return fmt::format("Button {}", (uint64)button); } diff --git a/src/input/api/Controller.h b/src/input/api/Controller.h index e2475191..feae19a5 100644 --- a/src/input/api/Controller.h +++ b/src/input/api/Controller.h @@ -168,14 +168,13 @@ protected: Settings m_settings{}; }; -template +template TProvider> class Controller : public ControllerBase { public: Controller(std::string_view uuid, std::string_view display_name) : ControllerBase(uuid, display_name) { - static_assert(std::is_base_of_v); m_provider = std::dynamic_pointer_cast(InputManager::instance().get_api_provider(TProvider::kAPIType)); cemu_assert_debug(m_provider != nullptr); } @@ -183,7 +182,6 @@ public: Controller(std::string_view uuid, std::string_view display_name, const ControllerProviderSettings& settings) : ControllerBase(uuid, display_name) { - static_assert(std::is_base_of_v); m_provider = std::dynamic_pointer_cast(InputManager::instance().get_api_provider(TProvider::kAPIType, settings)); cemu_assert_debug(m_provider != nullptr); } diff --git a/src/input/api/SDL/SDLControllerProvider.h b/src/input/api/SDL/SDLControllerProvider.h index 53864234..e67eb067 100644 --- a/src/input/api/SDL/SDLControllerProvider.h +++ b/src/input/api/SDL/SDLControllerProvider.h @@ -3,10 +3,6 @@ #include "input/motion/MotionHandler.h" #include "input/api/ControllerProvider.h" -#ifndef HAS_SDL -#define HAS_SDL 1 -#endif - static bool operator==(const SDL_GUID& g1, const SDL_GUID& g2) { return memcmp(&g1, &g2, sizeof(SDL_GUID)) == 0; diff --git a/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp b/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp index 02e12cee..629425c4 100644 --- a/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp +++ b/src/input/api/Wiimote/hidapi/HidapiWiimote.cpp @@ -1,4 +1,5 @@ #include "HidapiWiimote.h" +#include #include static constexpr uint16 WIIMOTE_VENDOR_ID = 0x057e; diff --git a/src/input/api/Wiimote/hidapi/HidapiWiimote.h b/src/input/api/Wiimote/hidapi/HidapiWiimote.h index ffb83541..482aab8a 100644 --- a/src/input/api/Wiimote/hidapi/HidapiWiimote.h +++ b/src/input/api/Wiimote/hidapi/HidapiWiimote.h @@ -1,11 +1,10 @@ #pragma once #include -#include class HidapiWiimote : public WiimoteDevice { public: - HidapiWiimote(SDL_hid_device* dev, std::string_view path); + HidapiWiimote(struct SDL_hid_device* dev, std::string_view path); ~HidapiWiimote() override; bool write_data(const std::vector &data) override; @@ -15,7 +14,7 @@ public: static std::vector get_devices(); private: - SDL_hid_device* m_handle; + struct SDL_hid_device* m_handle; const std::string m_path; }; diff --git a/src/input/emulated/ClassicController.cpp b/src/input/emulated/ClassicController.cpp index 3386aefe..8328a10d 100644 --- a/src/input/emulated/ClassicController.cpp +++ b/src/input/emulated/ClassicController.cpp @@ -1,7 +1,9 @@ #include "input/emulated/ClassicController.h" #include "input/api/Controller.h" +#ifdef HAS_SDL #include "input/api/SDL/SDLController.h" +#endif ClassicController::ClassicController(size_t player_index) : WPADController(player_index, kDataFormat_CLASSIC) @@ -130,6 +132,7 @@ bool ClassicController::set_default_mapping(const std::shared_ptr> mapping; switch (controller->api()) { +#ifdef HAS_SDL case InputAPI::SDLController: { const auto sdl_controller = std::static_pointer_cast(controller); if (sdl_controller->get_guid() == SDLController::kLeftJoyCon) @@ -206,6 +209,7 @@ bool ClassicController::set_default_mapping(const std::shared_ptr& c std::vector> mapping; switch (controller->api()) { +#ifdef HAS_SDL case InputAPI::SDLController: { const auto sdl_controller = std::static_pointer_cast(controller); if (sdl_controller->get_guid() == SDLController::kLeftJoyCon) @@ -219,6 +222,7 @@ bool ProController::set_default_mapping(const std::shared_ptr& c } break; } +#endif case InputAPI::XInput: { mapping = diff --git a/src/input/emulated/VPADController.cpp b/src/input/emulated/VPADController.cpp index 6e54e401..f7e9447a 100644 --- a/src/input/emulated/VPADController.cpp +++ b/src/input/emulated/VPADController.cpp @@ -1,6 +1,8 @@ #include "input/emulated/VPADController.h" #include "input/api/Controller.h" +#ifdef HAS_SDL #include "input/api/SDL/SDLController.h" +#endif #include "WindowSystem.h" #include "input/InputManager.h" #include "Cafe/HW/Latte/Core/Latte.h" @@ -510,6 +512,7 @@ bool VPADController::set_default_mapping(const std::shared_ptr& std::vector> mapping; switch (controller->api()) { +#ifdef HAS_SDL case InputAPI::SDLController: { const auto sdl_controller = std::static_pointer_cast(controller); if (sdl_controller->get_guid() == SDLController::kLeftJoyCon) @@ -633,6 +636,7 @@ bool VPADController::set_default_mapping(const std::shared_ptr& } break; } +#endif case InputAPI::XInput: { mapping = diff --git a/src/main.cpp b/src/main.cpp index da367a6a..9b9e04c2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -29,9 +29,11 @@ #pragma comment(lib,"Dbghelp.lib") #endif +#ifdef HAS_SDL #define SDL_MAIN_HANDLED #include #include +#endif #if BOOST_OS_LINUX #define _putenv(__s) putenv((char*)(__s)) @@ -237,7 +239,9 @@ int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int { if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE))) cemuLog_log(LogType::Force, "CoInitializeEx() failed"); +#ifdef HAS_SDL SDL_SetMainReady(); +#endif if (!LaunchSettings::HandleCommandline(lpCmdLine)) return 0; WindowSystem::Create(); @@ -249,7 +253,9 @@ int main(int argc, char* argv[]) { if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE))) cemuLog_log(LogType::Force, "CoInitializeEx() failed"); +#ifdef HAS_SDL SDL_SetMainReady(); +#endif if (!LaunchSettings::HandleCommandline(argc, argv)) return 0; WindowSystem::Create(); diff --git a/src/util/ScreenSaver/ScreenSaver.h b/src/util/ScreenSaver/ScreenSaver.h index cb543c7e..8d718afb 100644 --- a/src/util/ScreenSaver/ScreenSaver.h +++ b/src/util/ScreenSaver/ScreenSaver.h @@ -1,9 +1,13 @@ #include "Cemu/Logging/CemuLogging.h" + +#ifdef HAS_SDL #include +#endif class ScreenSaver { -public: +#ifdef HAS_SDL + public: static void SetInhibit(bool inhibit) { bool* inhibitArg = new bool(inhibit); @@ -15,7 +19,7 @@ public: } } -private: + private: static void SDLCALL SetInhibitCallback(void* userdata) { if (!userdata) @@ -59,4 +63,10 @@ private: } } } +#else + public: + static void SetInhibit(bool /*inhibit*/) + { + } +#endif };