diff options
Diffstat (limited to '')
51 files changed, 1161 insertions, 233 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6c99dd5e2..9aea4af87 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,9 @@ if (MSVC) # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors. add_definitions(-DWIN32_LEAN_AND_MEAN) + # Ensure that projects build with Unicode support. + add_definitions(-DUNICODE -D_UNICODE) + # /W3 - Level 3 warnings # /MP - Multi-threaded compilation # /Zi - Output debugging information diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c59107102..2ace866ee 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -88,6 +88,10 @@ add_library(core STATIC file_sys/vfs_vector.h file_sys/xts_archive.cpp file_sys/xts_archive.h + frontend/applets/error.cpp + frontend/applets/error.h + frontend/applets/general_frontend.cpp + frontend/applets/general_frontend.h frontend/applets/profile_select.cpp frontend/applets/profile_select.h frontend/applets/software_keyboard.cpp @@ -177,12 +181,14 @@ add_library(core STATIC hle/service/am/applet_oe.h hle/service/am/applets/applets.cpp hle/service/am/applets/applets.h + hle/service/am/applets/error.cpp + hle/service/am/applets/error.h + hle/service/am/applets/general_backend.cpp + hle/service/am/applets/general_backend.h hle/service/am/applets/profile_select.cpp hle/service/am/applets/profile_select.h hle/service/am/applets/software_keyboard.cpp hle/service/am/applets/software_keyboard.h - hle/service/am/applets/stub_applet.cpp - hle/service/am/applets/stub_applet.h hle/service/am/applets/web_browser.cpp hle/service/am/applets/web_browser.h hle/service/am/idle.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index 175a5f2ea..7106151bd 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -18,13 +18,18 @@ #include "core/file_sys/registered_cache.h" #include "core/file_sys/vfs_concat.h" #include "core/file_sys/vfs_real.h" +#include "core/frontend/applets/error.h" +#include "core/frontend/applets/general_frontend.h" +#include "core/frontend/applets/profile_select.h" +#include "core/frontend/applets/software_keyboard.h" +#include "core/frontend/applets/web_browser.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" -#include "core/hle/service/am/applets/software_keyboard.h" +#include "core/hle/service/am/applets/applets.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" #include "core/loader/loader.h" @@ -110,12 +115,7 @@ struct System::Impl { content_provider = std::make_unique<FileSys::ContentProviderUnion>(); /// Create default implementations of applets if one is not provided. - if (profile_selector == nullptr) - profile_selector = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>(); - if (software_keyboard == nullptr) - software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>(); - if (web_browser == nullptr) - web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); + applet_manager.SetDefaultAppletsIfMissing(); telemetry_session = std::make_unique<Core::TelemetrySession>(); service_manager = std::make_shared<Service::SM::ServiceManager>(); @@ -223,9 +223,7 @@ struct System::Impl { app_loader.reset(); // Clear all applets - profile_selector.reset(); - software_keyboard.reset(); - web_browser.reset(); + applet_manager.ClearAll(); LOG_DEBUG(Core, "Shutdown OK"); } @@ -264,9 +262,7 @@ struct System::Impl { std::unique_ptr<FileSys::CheatEngine> cheat_engine; /// Frontend applets - std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector; - std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; - std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser; + Service::AM::Applets::AppletManager applet_manager; /// Service manager std::shared_ptr<Service::SM::ServiceManager> service_manager; @@ -476,20 +472,20 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const { return impl->virtual_filesystem; } -void System::SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet) { - impl->profile_selector = std::move(applet); +void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) { + impl->applet_manager.SetAppletFrontendSet(std::move(set)); } -const Frontend::ProfileSelectApplet& System::GetProfileSelector() const { - return *impl->profile_selector; +void System::SetDefaultAppletFrontendSet() { + impl->applet_manager.SetDefaultAppletFrontendSet(); } -void System::SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet) { - impl->software_keyboard = std::move(applet); +Service::AM::Applets::AppletManager& System::GetAppletManager() { + return impl->applet_manager; } -const Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const { - return *impl->software_keyboard; +const Service::AM::Applets::AppletManager& System::GetAppletManager() const { + return impl->applet_manager; } void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) { @@ -513,18 +509,6 @@ void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) { impl->content_provider->ClearSlot(slot); } -void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) { - impl->web_browser = std::move(applet); -} - -Frontend::WebBrowserApplet& System::GetWebBrowser() { - return *impl->web_browser; -} - -const Frontend::WebBrowserApplet& System::GetWebBrowser() const { - return *impl->web_browser; -} - System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { return impl->Init(*this, emu_window); } diff --git a/src/core/core.h b/src/core/core.h index 82b2e087e..a9a756a4c 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -14,9 +14,6 @@ namespace Core::Frontend { class EmuWindow; -class ProfileSelectApplet; -class SoftwareKeyboardApplet; -class WebBrowserApplet; } // namespace Core::Frontend namespace FileSys { @@ -38,9 +35,18 @@ class AppLoader; enum class ResultStatus : u16; } // namespace Loader -namespace Service::SM { +namespace Service { + +namespace AM::Applets { +struct AppletFrontendSet; +class AppletManager; +} // namespace AM::Applets + +namespace SM { class ServiceManager; -} // namespace Service::SM +} // namespace SM + +} // namespace Service namespace Tegra { class DebugContext; @@ -260,18 +266,13 @@ public: void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id, VAddr code_region_start, VAddr code_region_end); - void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet); - - const Frontend::ProfileSelectApplet& GetProfileSelector() const; - - void SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet); + void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set); - const Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const; + void SetDefaultAppletFrontendSet(); - void SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet); + Service::AM::Applets::AppletManager& GetAppletManager(); - Frontend::WebBrowserApplet& GetWebBrowser(); - const Frontend::WebBrowserApplet& GetWebBrowser() const; + const Service::AM::Applets::AppletManager& GetAppletManager() const; void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider); diff --git a/src/core/frontend/applets/error.cpp b/src/core/frontend/applets/error.cpp new file mode 100644 index 000000000..4002a9211 --- /dev/null +++ b/src/core/frontend/applets/error.cpp @@ -0,0 +1,34 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/frontend/applets/error.h" + +namespace Core::Frontend { + +ErrorApplet::~ErrorApplet() = default; + +void DefaultErrorApplet::ShowError(ResultCode error, std::function<void()> finished) const { + LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})", + static_cast<u32>(error.module.Value()), error.description.Value(), error.raw); +} + +void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, + std::function<void()> finished) const { + LOG_CRITICAL( + Service_Fatal, + "Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}", + static_cast<u32>(error.module.Value()), error.description.Value(), error.raw, time.count()); +} + +void DefaultErrorApplet::ShowCustomErrorText(ResultCode error, std::string main_text, + std::string detail_text, + std::function<void()> finished) const { + LOG_CRITICAL(Service_Fatal, + "Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})", + static_cast<u32>(error.module.Value()), error.description.Value(), error.raw); + LOG_CRITICAL(Service_Fatal, " Main Text: {}", main_text); + LOG_CRITICAL(Service_Fatal, " Detail Text: {}", detail_text); +} + +} // namespace Core::Frontend diff --git a/src/core/frontend/applets/error.h b/src/core/frontend/applets/error.h new file mode 100644 index 000000000..699df940d --- /dev/null +++ b/src/core/frontend/applets/error.h @@ -0,0 +1,37 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <chrono> +#include <functional> + +#include "core/hle/result.h" + +namespace Core::Frontend { + +class ErrorApplet { +public: + virtual ~ErrorApplet(); + + virtual void ShowError(ResultCode error, std::function<void()> finished) const = 0; + + virtual void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, + std::function<void()> finished) const = 0; + + virtual void ShowCustomErrorText(ResultCode error, std::string dialog_text, + std::string fullscreen_text, + std::function<void()> finished) const = 0; +}; + +class DefaultErrorApplet final : public ErrorApplet { +public: + void ShowError(ResultCode error, std::function<void()> finished) const override; + void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, + std::function<void()> finished) const override; + void ShowCustomErrorText(ResultCode error, std::string main_text, std::string detail_text, + std::function<void()> finished) const override; +}; + +} // namespace Core::Frontend diff --git a/src/core/frontend/applets/general_frontend.cpp b/src/core/frontend/applets/general_frontend.cpp new file mode 100644 index 000000000..b974f2289 --- /dev/null +++ b/src/core/frontend/applets/general_frontend.cpp @@ -0,0 +1,27 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/frontend/applets/general_frontend.h" + +namespace Core::Frontend { + +PhotoViewerApplet::~PhotoViewerApplet() = default; + +DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() {} + +void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id, + std::function<void()> finished) const { + LOG_INFO(Service_AM, + "Application requested frontend to display stored photos for title_id={:016X}", + title_id); + finished(); +} + +void DefaultPhotoViewerApplet::ShowAllPhotos(std::function<void()> finished) const { + LOG_INFO(Service_AM, "Application requested frontend to display all stored photos."); + finished(); +} + +} // namespace Core::Frontend diff --git a/src/core/frontend/applets/general_frontend.h b/src/core/frontend/applets/general_frontend.h new file mode 100644 index 000000000..d4506c999 --- /dev/null +++ b/src/core/frontend/applets/general_frontend.h @@ -0,0 +1,28 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <functional> +#include "common/common_types.h" + +namespace Core::Frontend { + +class PhotoViewerApplet { +public: + virtual ~PhotoViewerApplet(); + + virtual void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const = 0; + virtual void ShowAllPhotos(std::function<void()> finished) const = 0; +}; + +class DefaultPhotoViewerApplet final : public PhotoViewerApplet { +public: + ~DefaultPhotoViewerApplet() override; + + void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const override; + void ShowAllPhotos(std::function<void()> finished) const override; +}; + +} // namespace Core::Frontend diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 1aa4ce1ac..26a665bfd 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -22,7 +22,6 @@ #include "core/hle/service/am/applets/applets.h" #include "core/hle/service/am/applets/profile_select.h" #include "core/hle/service/am/applets/software_keyboard.h" -#include "core/hle/service/am/applets/stub_applet.h" #include "core/hle/service/am/applets/web_browser.h" #include "core/hle/service/am/idle.h" #include "core/hle/service/am/omm.h" @@ -42,12 +41,6 @@ constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2}; constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3}; constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; -enum class AppletId : u32 { - ProfileSelect = 0x10, - SoftwareKeyboard = 0x11, - LibAppletOff = 0x17, -}; - constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; struct LaunchParameters { @@ -886,30 +879,16 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple ILibraryAppletCreator::~ILibraryAppletCreator() = default; -static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) { - switch (id) { - case AppletId::ProfileSelect: - return std::make_shared<Applets::ProfileSelect>(); - case AppletId::SoftwareKeyboard: - return std::make_shared<Applets::SoftwareKeyboard>(); - case AppletId::LibAppletOff: - return std::make_shared<Applets::WebBrowser>(); - default: - LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!", - static_cast<u32>(id)); - return std::make_shared<Applets::StubApplet>(); - } -} - void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto applet_id = rp.PopRaw<AppletId>(); + const auto applet_id = rp.PopRaw<Applets::AppletId>(); const auto applet_mode = rp.PopRaw<u32>(); LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", static_cast<u32>(applet_id), applet_mode); - const auto applet = GetAppletFromId(applet_id); + const auto& applet_manager{Core::System::GetInstance().GetAppletManager()}; + const auto applet = applet_manager.GetApplet(applet_id); if (applet == nullptr) { LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id)); diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index a6064c63f..7f70b10df 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -5,11 +5,21 @@ #include <cstring> #include "common/assert.h" #include "core/core.h" +#include "core/frontend/applets/error.h" +#include "core/frontend/applets/general_frontend.h" +#include "core/frontend/applets/profile_select.h" +#include "core/frontend/applets/software_keyboard.h" +#include "core/frontend/applets/web_browser.h" #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/server_session.h" #include "core/hle/kernel/writable_event.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applets.h" +#include "core/hle/service/am/applets/error.h" +#include "core/hle/service/am/applets/general_backend.h" +#include "core/hle/service/am/applets/profile_select.h" +#include "core/hle/service/am/applets/software_keyboard.h" +#include "core/hle/service/am/applets/web_browser.h" namespace Service::AM::Applets { @@ -111,4 +121,76 @@ void Applet::Initialize() { initialized = true; } +AppletManager::AppletManager() = default; + +AppletManager::~AppletManager() = default; + +void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { + if (set.error != nullptr) + frontend.error = std::move(set.error); + if (set.photo_viewer != nullptr) + frontend.photo_viewer = std::move(set.photo_viewer); + if (set.profile_select != nullptr) + frontend.profile_select = std::move(set.profile_select); + if (set.software_keyboard != nullptr) + frontend.software_keyboard = std::move(set.software_keyboard); + if (set.web_browser != nullptr) + frontend.web_browser = std::move(set.web_browser); +} + +void AppletManager::SetDefaultAppletFrontendSet() { + frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); + frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>(); + frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>(); + frontend.software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>(); + frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); +} + +void AppletManager::SetDefaultAppletsIfMissing() { + if (frontend.error == nullptr) { + frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); + } + + if (frontend.photo_viewer == nullptr) { + frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>(); + } + + if (frontend.profile_select == nullptr) { + frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>(); + } + + if (frontend.software_keyboard == nullptr) { + frontend.software_keyboard = + std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>(); + } + + if (frontend.web_browser == nullptr) { + frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); + } +} + +void AppletManager::ClearAll() { + frontend = {}; +} + +std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const { + switch (id) { + case AppletId::Error: + return std::make_shared<Error>(*frontend.error); + case AppletId::ProfileSelect: + return std::make_shared<ProfileSelect>(*frontend.profile_select); + case AppletId::SoftwareKeyboard: + return std::make_shared<SoftwareKeyboard>(*frontend.software_keyboard); + case AppletId::PhotoViewer: + return std::make_shared<PhotoViewer>(*frontend.photo_viewer); + case AppletId::LibAppletOff: + return std::make_shared<WebBrowser>(*frontend.web_browser); + default: + UNIMPLEMENTED_MSG( + "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.", + static_cast<u8>(id)); + return std::make_shared<StubApplet>(); + } +} + } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 37424c379..7f932672c 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -12,12 +12,43 @@ union ResultCode; +namespace Core::Frontend { +class ErrorApplet; +class PhotoViewerApplet; +class ProfileSelectApplet; +class SoftwareKeyboardApplet; +class WebBrowserApplet; +} // namespace Core::Frontend + namespace Service::AM { class IStorage; namespace Applets { +enum class AppletId : u32 { + OverlayDisplay = 0x02, + QLaunch = 0x03, + Starter = 0x04, + Auth = 0x0A, + Cabinet = 0x0B, + Controller = 0x0C, + DataErase = 0x0D, + Error = 0x0E, + NetConnect = 0x0F, + ProfileSelect = 0x10, + SoftwareKeyboard = 0x11, + MiiEdit = 0x12, + LibAppletWeb = 0x13, + LibAppletShop = 0x14, + PhotoViewer = 0x15, + Settings = 0x16, + LibAppletOff = 0x17, + LibAppletWhitelisted = 0x18, + LibAppletAuth = 0x19, + MyPage = 0x1A, +}; + class AppletDataBroker final { public: AppletDataBroker(); @@ -105,5 +136,29 @@ protected: bool initialized = false; }; +struct AppletFrontendSet { + std::unique_ptr<Core::Frontend::ErrorApplet> error; + std::unique_ptr<Core::Frontend::PhotoViewerApplet> photo_viewer; + std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_select; + std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; + std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser; +}; + +class AppletManager { +public: + AppletManager(); + ~AppletManager(); + + void SetAppletFrontendSet(AppletFrontendSet set); + void SetDefaultAppletFrontendSet(); + void SetDefaultAppletsIfMissing(); + void ClearAll(); + + std::shared_ptr<Applet> GetApplet(AppletId id) const; + +private: + AppletFrontendSet frontend; +}; + } // namespace Applets } // namespace Service::AM diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp new file mode 100644 index 000000000..04774bedc --- /dev/null +++ b/src/core/hle/service/am/applets/error.cpp @@ -0,0 +1,182 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <array> +#include <cstring> +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/error.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/error.h" + +namespace Service::AM::Applets { + +#pragma pack(push, 4) +struct ShowError { + u8 mode; + bool jump; + INSERT_PADDING_BYTES(4); + bool use_64bit_error_code; + INSERT_PADDING_BYTES(1); + u64 error_code_64; + u32 error_code_32; +}; +static_assert(sizeof(ShowError) == 0x14, "ShowError has incorrect size."); +#pragma pack(pop) + +struct ShowErrorRecord { + u8 mode; + bool jump; + INSERT_PADDING_BYTES(6); + u64 error_code_64; + u64 posix_time; +}; +static_assert(sizeof(ShowErrorRecord) == 0x18, "ShowErrorRecord has incorrect size."); + +struct SystemErrorArg { + u8 mode; + bool jump; + INSERT_PADDING_BYTES(6); + u64 error_code_64; + std::array<char, 8> language_code; + std::array<char, 0x800> main_text; + std::array<char, 0x800> detail_text; +}; +static_assert(sizeof(SystemErrorArg) == 0x1018, "SystemErrorArg has incorrect size."); + +struct ApplicationErrorArg { + u8 mode; + bool jump; + INSERT_PADDING_BYTES(6); + u32 error_code; + std::array<char, 8> language_code; + std::array<char, 0x800> main_text; + std::array<char, 0x800> detail_text; +}; +static_assert(sizeof(ApplicationErrorArg) == 0x1014, "ApplicationErrorArg has incorrect size."); + +union Error::ErrorArguments { + ShowError error; + ShowErrorRecord error_record; + SystemErrorArg system_error; + ApplicationErrorArg application_error; +}; + +namespace { +template <typename T> +void CopyArgumentData(const std::vector<u8>& data, T& variable) { + ASSERT(data.size() >= sizeof(T)); + std::memcpy(&variable, data.data(), sizeof(T)); +} + +ResultCode Decode64BitError(u64 error) { + const auto description = (error >> 32) & 0x1FFF; + auto module = error & 0x3FF; + if (module >= 2000) + module -= 2000; + module &= 0x1FF; + return {static_cast<ErrorModule>(module), static_cast<u32>(description)}; +} + +} // Anonymous namespace + +Error::Error(const Core::Frontend::ErrorApplet& frontend) : frontend(frontend) {} + +Error::~Error() = default; + +void Error::Initialize() { + Applet::Initialize(); + args = std::make_unique<ErrorArguments>(); + complete = false; + + const auto storage = broker.PopNormalDataToApplet(); + ASSERT(storage != nullptr); + const auto data = storage->GetData(); + + ASSERT(!data.empty()); + std::memcpy(&mode, data.data(), sizeof(ErrorAppletMode)); + + switch (mode) { + case ErrorAppletMode::ShowError: + CopyArgumentData(data, args->error); + if (args->error.use_64bit_error_code) { + error_code = Decode64BitError(args->error.error_code_64); + } else { + error_code = ResultCode(args->error.error_code_32); + } + break; + case ErrorAppletMode::ShowSystemError: + CopyArgumentData(data, args->system_error); + error_code = ResultCode(Decode64BitError(args->system_error.error_code_64)); + break; + case ErrorAppletMode::ShowApplicationError: + CopyArgumentData(data, args->application_error); + error_code = ResultCode(args->application_error.error_code); + break; + case ErrorAppletMode::ShowErrorRecord: + CopyArgumentData(data, args->error_record); + error_code = Decode64BitError(args->error_record.error_code_64); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", static_cast<u8>(mode)); + } +} + +bool Error::TransactionComplete() const { + return complete; +} + +ResultCode Error::GetStatus() const { + return RESULT_SUCCESS; +} + +void Error::ExecuteInteractive() { + UNREACHABLE_MSG("Unexpected interactive applet data!"); +} + +void Error::Execute() { + if (complete) { + return; + } + + const auto callback = [this] { DisplayCompleted(); }; + + switch (mode) { + case ErrorAppletMode::ShowError: + frontend.ShowError(error_code, callback); + break; + case ErrorAppletMode::ShowSystemError: + case ErrorAppletMode::ShowApplicationError: { + const auto system = mode == ErrorAppletMode::ShowSystemError; + const auto& main_text = + system ? args->system_error.main_text : args->application_error.main_text; + const auto& detail_text = + system ? args->system_error.detail_text : args->application_error.detail_text; + + frontend.ShowCustomErrorText( + error_code, + Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()), + Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()), + callback); + break; + } + case ErrorAppletMode::ShowErrorRecord: + frontend.ShowErrorWithTimestamp( + error_code, std::chrono::seconds{args->error_record.posix_time}, callback); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", static_cast<u8>(mode)); + DisplayCompleted(); + } +} + +void Error::DisplayCompleted() { + complete = true; + broker.PushNormalDataFromApplet(IStorage{{}}); + broker.SignalStateChanged(); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/error.h b/src/core/hle/service/am/applets/error.h new file mode 100644 index 000000000..a3590d181 --- /dev/null +++ b/src/core/hle/service/am/applets/error.h @@ -0,0 +1,47 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/result.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Service::AM::Applets { + +enum class ErrorAppletMode : u8 { + ShowError = 0, + ShowSystemError = 1, + ShowApplicationError = 2, + ShowEula = 3, + ShowErrorPctl = 4, + ShowErrorRecord = 5, + ShowUpdateEula = 8, +}; + +class Error final : public Applet { +public: + explicit Error(const Core::Frontend::ErrorApplet& frontend); + ~Error() override; + + void Initialize() override; + + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void DisplayCompleted(); + +private: + union ErrorArguments; + + const Core::Frontend::ErrorApplet& frontend; + ResultCode error_code = RESULT_SUCCESS; + ErrorAppletMode mode = ErrorAppletMode::ShowError; + std::unique_ptr<ErrorArguments> args; + + bool complete = false; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/stub_applet.cpp b/src/core/hle/service/am/applets/general_backend.cpp index ed166b87d..c591b9ac2 100644 --- a/src/core/hle/service/am/applets/stub_applet.cpp +++ b/src/core/hle/service/am/applets/general_backend.cpp @@ -4,11 +4,15 @@ #include <string> +#include "common/assert.h" #include "common/hex_util.h" #include "common/logging/log.h" +#include "core/core.h" +#include "core/frontend/applets/general_frontend.h" +#include "core/hle/kernel/process.h" #include "core/hle/result.h" #include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/stub_applet.h" +#include "core/hle/service/am/applets/general_backend.h" namespace Service::AM::Applets { @@ -30,6 +34,55 @@ static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) { } } +PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {} + +PhotoViewer::~PhotoViewer() = default; + +void PhotoViewer::Initialize() { + Applet::Initialize(); + complete = false; + + const auto storage = broker.PopNormalDataToApplet(); + ASSERT(storage != nullptr); + const auto data = storage->GetData(); + ASSERT(!data.empty()); + mode = static_cast<PhotoViewerAppletMode>(data[0]); +} + +bool PhotoViewer::TransactionComplete() const { + return complete; +} + +ResultCode PhotoViewer::GetStatus() const { + return RESULT_SUCCESS; +} + +void PhotoViewer::ExecuteInteractive() { + UNREACHABLE_MSG("Unexpected interactive applet data."); +} + +void PhotoViewer::Execute() { + if (complete) + return; + + const auto callback = [this] { ViewFinished(); }; + switch (mode) { + case PhotoViewerAppletMode::CurrentApp: + frontend.ShowPhotosForApplication(Core::CurrentProcess()->GetTitleID(), callback); + break; + case PhotoViewerAppletMode::AllApps: + frontend.ShowAllPhotos(callback); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", static_cast<u8>(mode)); + } +} + +void PhotoViewer::ViewFinished() { + broker.PushNormalDataFromApplet(IStorage{{}}); + broker.SignalStateChanged(); +} + StubApplet::StubApplet() = default; StubApplet::~StubApplet() = default; @@ -67,4 +120,5 @@ void StubApplet::Execute() { broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)}); broker.SignalStateChanged(); } + } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h new file mode 100644 index 000000000..2dd255d7c --- /dev/null +++ b/src/core/hle/service/am/applets/general_backend.h @@ -0,0 +1,48 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/am/applets/applets.h" + +namespace Service::AM::Applets { + +enum class PhotoViewerAppletMode : u8 { + CurrentApp = 0, + AllApps = 1, +}; + +class PhotoViewer final : public Applet { +public: + explicit PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend); + ~PhotoViewer() override; + + void Initialize() override; + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void ViewFinished(); + +private: + const Core::Frontend::PhotoViewerApplet& frontend; + bool complete = false; + PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp; +}; + +class StubApplet final : public Applet { +public: + StubApplet(); + ~StubApplet() override; + + void Initialize() override; + + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp index 14e2a1fee..d113bd2eb 100644 --- a/src/core/hle/service/am/applets/profile_select.cpp +++ b/src/core/hle/service/am/applets/profile_select.cpp @@ -15,7 +15,9 @@ namespace Service::AM::Applets { constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; -ProfileSelect::ProfileSelect() = default; +ProfileSelect::ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend) + : frontend(frontend) {} + ProfileSelect::~ProfileSelect() = default; void ProfileSelect::Initialize() { @@ -51,8 +53,6 @@ void ProfileSelect::Execute() { return; } - const auto& frontend{Core::System::GetInstance().GetProfileSelector()}; - frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); }); } diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h index 787485f22..a2ac6cf50 100644 --- a/src/core/hle/service/am/applets/profile_select.h +++ b/src/core/hle/service/am/applets/profile_select.h @@ -28,7 +28,7 @@ static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has inco class ProfileSelect final : public Applet { public: - ProfileSelect(); + explicit ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend); ~ProfileSelect() override; void Initialize() override; @@ -41,6 +41,8 @@ public: void SelectionComplete(std::optional<Account::UUID> uuid); private: + const Core::Frontend::ProfileSelectApplet& frontend; + UserSelectionConfig config; bool complete = false; ResultCode status = RESULT_SUCCESS; diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 8c5bd6059..e197990f7 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -39,7 +39,8 @@ static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( return params; } -SoftwareKeyboard::SoftwareKeyboard() = default; +SoftwareKeyboard::SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend) + : frontend(frontend) {} SoftwareKeyboard::~SoftwareKeyboard() = default; @@ -90,8 +91,6 @@ void SoftwareKeyboard::ExecuteInteractive() { if (status == INTERACTIVE_STATUS_OK) { complete = true; } else { - const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()}; - std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; std::memcpy(string.data(), data.data() + 4, string.size() * 2); frontend.SendTextCheckDialog( @@ -106,8 +105,6 @@ void SoftwareKeyboard::Execute() { return; } - const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()}; - const auto parameters = ConvertToFrontendParameters(config, initial_text); frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); }, diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index b93a30d28..0fbc43e51 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -55,7 +55,7 @@ static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect siz class SoftwareKeyboard final : public Applet { public: - SoftwareKeyboard(); + explicit SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend); ~SoftwareKeyboard() override; void Initialize() override; @@ -68,6 +68,8 @@ public: void WriteText(std::optional<std::u16string> text); private: + const Core::Frontend::SoftwareKeyboardApplet& frontend; + KeyboardConfig config; std::u16string initial_text; bool complete = false; diff --git a/src/core/hle/service/am/applets/stub_applet.h b/src/core/hle/service/am/applets/stub_applet.h deleted file mode 100644 index 7d8dc968d..000000000 --- a/src/core/hle/service/am/applets/stub_applet.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/hle/service/am/applets/applets.h" - -namespace Service::AM::Applets { - -class StubApplet final : public Applet { -public: - StubApplet(); - ~StubApplet() override; - - void Initialize() override; - - bool TransactionComplete() const override; - ResultCode GetStatus() const override; - void ExecuteInteractive() override; - void Execute() override; -}; - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp index 7e17df98a..7878f5136 100644 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ b/src/core/hle/service/am/applets/web_browser.cpp @@ -95,7 +95,7 @@ static FileSys::VirtualFile GetManualRomFS() { return nullptr; } -WebBrowser::WebBrowser() = default; +WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend) : frontend(frontend) {} WebBrowser::~WebBrowser() = default; @@ -152,8 +152,6 @@ void WebBrowser::Execute() { return; } - auto& frontend{Core::System::GetInstance().GetWebBrowser()}; - frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); } diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h index b9e228fac..7e0f34c7d 100644 --- a/src/core/hle/service/am/applets/web_browser.h +++ b/src/core/hle/service/am/applets/web_browser.h @@ -12,7 +12,7 @@ namespace Service::AM::Applets { class WebBrowser final : public Applet { public: - WebBrowser(); + WebBrowser(Core::Frontend::WebBrowserApplet& frontend); ~WebBrowser() override; void Initialize() override; @@ -32,6 +32,8 @@ public: void Finalize(); private: + Core::Frontend::WebBrowserApplet& frontend; + bool complete = false; bool unpacked = false; ResultCode status = RESULT_SUCCESS; diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp index b6b71f966..f43e512e9 100644 --- a/src/core/hle/service/audio/audctl.cpp +++ b/src/core/hle/service/audio/audctl.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/service/audio/audctl.h" namespace Service::Audio { @@ -11,8 +13,8 @@ AudCtl::AudCtl() : ServiceFramework{"audctl"} { static const FunctionInfo functions[] = { {0, nullptr, "GetTargetVolume"}, {1, nullptr, "SetTargetVolume"}, - {2, nullptr, "GetTargetVolumeMin"}, - {3, nullptr, "GetTargetVolumeMax"}, + {2, &AudCtl::GetTargetVolumeMin, "GetTargetVolumeMin"}, + {3, &AudCtl::GetTargetVolumeMax, "GetTargetVolumeMax"}, {4, nullptr, "IsTargetMute"}, {5, nullptr, "SetTargetMute"}, {6, nullptr, "IsTargetConnected"}, @@ -44,4 +46,28 @@ AudCtl::AudCtl() : ServiceFramework{"audctl"} { AudCtl::~AudCtl() = default; +void AudCtl::GetTargetVolumeMin(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Audio, "called."); + + // This service function is currently hardcoded on the + // actual console to this value (as of 6.0.0). + constexpr s32 target_min_volume = 0; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(target_min_volume); +} + +void AudCtl::GetTargetVolumeMax(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Audio, "called."); + + // This service function is currently hardcoded on the + // actual console to this value (as of 6.0.0). + constexpr s32 target_max_volume = 15; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(target_max_volume); +} + } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audctl.h b/src/core/hle/service/audio/audctl.h index 9d2d9e83b..c7fafc02e 100644 --- a/src/core/hle/service/audio/audctl.h +++ b/src/core/hle/service/audio/audctl.h @@ -12,6 +12,10 @@ class AudCtl final : public ServiceFramework<AudCtl> { public: explicit AudCtl(); ~AudCtl() override; + +private: + void GetTargetVolumeMin(Kernel::HLERequestContext& ctx); + void GetTargetVolumeMax(Kernel::HLERequestContext& ctx); }; } // namespace Service::Audio diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 1e31a2900..6821f275d 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -36,6 +36,8 @@ add_library(video_core STATIC renderer_base.h renderer_opengl/gl_buffer_cache.cpp renderer_opengl/gl_buffer_cache.h + renderer_opengl/gl_device.cpp + renderer_opengl/gl_device.h renderer_opengl/gl_global_cache.cpp renderer_opengl/gl_global_cache.h renderer_opengl/gl_primitive_assembler.cpp diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 046d047cb..6674d9405 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp @@ -57,8 +57,8 @@ bool DmaPusher::Step() { // Push buffer non-empty, read a word command_headers.resize(command_list_header.size); - gpu.MemoryManager().ReadBlock(dma_get, command_headers.data(), - command_list_header.size * sizeof(u32)); + gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), + command_list_header.size * sizeof(u32)); for (const CommandHeader& command_header : command_headers) { diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index cd51a31d7..7387886a3 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp @@ -10,6 +10,7 @@ #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_base.h" +#include "video_core/textures/decoders.h" namespace Tegra::Engines { @@ -27,30 +28,46 @@ void KeplerMemory::CallMethod(const GPU::MethodCall& method_call) { switch (method_call.method) { case KEPLERMEMORY_REG_INDEX(exec): { - state.write_offset = 0; + ProcessExec(); break; } case KEPLERMEMORY_REG_INDEX(data): { - ProcessData(method_call.argument); + ProcessData(method_call.argument, method_call.IsLastCall()); break; } } } -void KeplerMemory::ProcessData(u32 data) { - ASSERT_MSG(regs.exec.linear, "Non-linear uploads are not supported"); - ASSERT(regs.dest.x == 0 && regs.dest.y == 0 && regs.dest.z == 0); - - // We have to invalidate the destination region to evict any outdated surfaces from the cache. - // We do this before actually writing the new data because the destination address might - // contain a dirty surface that will have to be written back to memory. - const GPUVAddr address{regs.dest.Address() + state.write_offset * sizeof(u32)}; - rasterizer.InvalidateRegion(ToCacheAddr(memory_manager.GetPointer(address)), sizeof(u32)); - memory_manager.Write<u32>(address, data); - - system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); +void KeplerMemory::ProcessExec() { + state.write_offset = 0; + state.copy_size = regs.line_length_in * regs.line_count; + state.inner_buffer.resize(state.copy_size); +} - state.write_offset++; +void KeplerMemory::ProcessData(u32 data, bool is_last_call) { + const u32 sub_copy_size = std::min(4U, state.copy_size - state.write_offset); + std::memcpy(&state.inner_buffer[state.write_offset], ®s.data, sub_copy_size); + state.write_offset += sub_copy_size; + if (is_last_call) { + const GPUVAddr address{regs.dest.Address()}; + if (regs.exec.linear != 0) { + memory_manager.WriteBlock(address, state.inner_buffer.data(), state.copy_size); + } else { + UNIMPLEMENTED_IF(regs.dest.z != 0); + UNIMPLEMENTED_IF(regs.dest.depth != 1); + UNIMPLEMENTED_IF(regs.dest.BlockWidth() != 1); + UNIMPLEMENTED_IF(regs.dest.BlockDepth() != 1); + const std::size_t dst_size = Tegra::Texture::CalculateSize( + true, 1, regs.dest.width, regs.dest.height, 1, regs.dest.BlockHeight(), 1); + std::vector<u8> tmp_buffer(dst_size); + memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size); + Tegra::Texture::SwizzleKepler(regs.dest.width, regs.dest.height, regs.dest.x, + regs.dest.y, regs.dest.BlockHeight(), state.copy_size, + state.inner_buffer.data(), tmp_buffer.data()); + memory_manager.WriteBlock(address, tmp_buffer.data(), dst_size); + } + system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); + } } } // namespace Tegra::Engines diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h index 78b6c3e45..5f892ddad 100644 --- a/src/video_core/engines/kepler_memory.h +++ b/src/video_core/engines/kepler_memory.h @@ -6,6 +6,7 @@ #include <array> #include <cstddef> +#include <vector> #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" @@ -51,7 +52,11 @@ public: u32 address_high; u32 address_low; u32 pitch; - u32 block_dimensions; + union { + BitField<0, 4, u32> block_width; + BitField<4, 4, u32> block_height; + BitField<8, 4, u32> block_depth; + }; u32 width; u32 height; u32 depth; @@ -63,6 +68,18 @@ public: return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low); } + + u32 BlockWidth() const { + return 1U << block_width.Value(); + } + + u32 BlockHeight() const { + return 1U << block_height.Value(); + } + + u32 BlockDepth() const { + return 1U << block_depth.Value(); + } } dest; struct { @@ -81,6 +98,8 @@ public: struct { u32 write_offset = 0; + u32 copy_size = 0; + std::vector<u8> inner_buffer; } state{}; private: @@ -88,7 +107,8 @@ private: VideoCore::RasterizerInterface& rasterizer; MemoryManager& memory_manager; - void ProcessData(u32 data); + void ProcessExec(); + void ProcessData(u32 data, bool is_last_call); }; #define ASSERT_REG_POSITION(field_name, position) \ diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index b198793bc..9780417f2 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -418,7 +418,7 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { const GPUVAddr tic_address_gpu{regs.tic.TICAddress() + tic_index * sizeof(Texture::TICEntry)}; Texture::TICEntry tic_entry; - memory_manager.ReadBlock(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry)); + memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry)); ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear || tic_entry.header_version == Texture::TICHeaderVersion::Pitch, @@ -439,7 +439,7 @@ Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const { const GPUVAddr tsc_address_gpu{regs.tsc.TSCAddress() + tsc_index * sizeof(Texture::TSCEntry)}; Texture::TSCEntry tsc_entry; - memory_manager.ReadBlock(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry)); + memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry)); return tsc_entry; } diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 0f4e820aa..6c98c6701 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -199,7 +199,15 @@ const u8* MemoryManager::GetPointer(GPUVAddr addr) const { return {}; } -void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const { +bool MemoryManager::IsBlockContinous(const GPUVAddr start, const std::size_t size) { + const GPUVAddr end = start + size; + const auto host_ptr_start = reinterpret_cast<std::uintptr_t>(GetPointer(start)); + const auto host_ptr_end = reinterpret_cast<std::uintptr_t>(GetPointer(end)); + const std::size_t range = static_cast<std::size_t>(host_ptr_end - host_ptr_start); + return range == size; +} + +void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const { std::size_t remaining_size{size}; std::size_t page_index{src_addr >> page_bits}; std::size_t page_offset{src_addr & page_mask}; @@ -226,7 +234,30 @@ void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t } } -void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) { +void MemoryManager::ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, + const std::size_t size) const { + std::size_t remaining_size{size}; + std::size_t page_index{src_addr >> page_bits}; + std::size_t page_offset{src_addr & page_mask}; + + while (remaining_size > 0) { + const std::size_t copy_amount{ + std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; + const u8* page_pointer = page_table.pointers[page_index]; + if (page_pointer) { + const u8* src_ptr{page_pointer + page_offset}; + std::memcpy(dest_buffer, src_ptr, copy_amount); + } else { + std::memset(dest_buffer, 0, copy_amount); + } + page_index++; + page_offset = 0; + dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; + remaining_size -= copy_amount; + } +} + +void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size) { std::size_t remaining_size{size}; std::size_t page_index{dest_addr >> page_bits}; std::size_t page_offset{dest_addr & page_mask}; @@ -253,7 +284,28 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std:: } } -void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size) { +void MemoryManager::WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, + const std::size_t size) { + std::size_t remaining_size{size}; + std::size_t page_index{dest_addr >> page_bits}; + std::size_t page_offset{dest_addr & page_mask}; + + while (remaining_size > 0) { + const std::size_t copy_amount{ + std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; + u8* page_pointer = page_table.pointers[page_index]; + if (page_pointer) { + u8* dest_ptr{page_pointer + page_offset}; + std::memcpy(dest_ptr, src_buffer, copy_amount); + } + page_index++; + page_offset = 0; + src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; + remaining_size -= copy_amount; + } +} + +void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) { std::size_t remaining_size{size}; std::size_t page_index{src_addr >> page_bits}; std::size_t page_offset{src_addr & page_mask}; @@ -281,6 +333,12 @@ void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t } } +void MemoryManager::CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) { + std::vector<u8> tmp_buffer(size); + ReadBlockUnsafe(src_addr, tmp_buffer.data(), size); + WriteBlockUnsafe(dest_addr, tmp_buffer.data(), size); +} + void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, VAddr backing_addr) { LOG_DEBUG(HW_GPU, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * page_size, diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 647cbf93a..e4f0c4bd6 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -65,9 +65,32 @@ public: u8* GetPointer(GPUVAddr addr); const u8* GetPointer(GPUVAddr addr) const; - void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const; - void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); - void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); + // Returns true if the block is continous in host memory, false otherwise + bool IsBlockContinous(const GPUVAddr start, const std::size_t size); + + /** + * ReadBlock and WriteBlock are full read and write operations over virtual + * GPU Memory. It's important to use these when GPU memory may not be continous + * in the Host Memory counterpart. Note: This functions cause Host GPU Memory + * Flushes and Invalidations, respectively to each operation. + */ + void ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const; + void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size); + void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size); + + /** + * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and + * WriteBlock respectively. In this versions, no flushing or invalidation is actually + * done and their performance is similar to a memcpy. This functions can be used + * on either of this 2 scenarios instead of their safe counterpart: + * - Memory which is sure to never be represented in the Host GPU. + * - Memory Managed by a Cache Manager. Example: Texture Flushing should use + * WriteBlockUnsafe instead of WriteBlock since it shouldn't invalidate the texture + * being flushed. + */ + void ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const; + void WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size); + void CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size); private: using VMAMap = std::map<GPUVAddr, VirtualMemoryArea>; diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp new file mode 100644 index 000000000..b6d9e0ddb --- /dev/null +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -0,0 +1,45 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstddef> +#include <glad/glad.h> + +#include "common/logging/log.h" +#include "video_core/renderer_opengl/gl_device.h" + +namespace OpenGL { + +namespace { +template <typename T> +T GetInteger(GLenum pname) { + GLint temporary; + glGetIntegerv(pname, &temporary); + return static_cast<T>(temporary); +} +} // Anonymous namespace + +Device::Device() { + uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); + has_variable_aoffi = TestVariableAoffi(); +} + +bool Device::TestVariableAoffi() { + const GLchar* AOFFI_TEST = R"(#version 430 core +uniform sampler2D tex; +uniform ivec2 variable_offset; +void main() { + gl_Position = textureOffset(tex, vec2(0), variable_offset); +} +)"; + const GLuint shader{glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &AOFFI_TEST)}; + GLint link_status{}; + glGetProgramiv(shader, GL_LINK_STATUS, &link_status); + glDeleteProgram(shader); + + const bool supported{link_status == GL_TRUE}; + LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", supported); + return supported; +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h new file mode 100644 index 000000000..78ff5ee58 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_device.h @@ -0,0 +1,30 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <cstddef> + +namespace OpenGL { + +class Device { +public: + Device(); + + std::size_t GetUniformBufferAlignment() const { + return uniform_buffer_alignment; + } + + bool HasVariableAoffi() const { + return has_variable_aoffi; + } + +private: + static bool TestVariableAoffi(); + + std::size_t uniform_buffer_alignment{}; + bool has_variable_aoffi{}; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 6034dc489..9a088a503 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -99,7 +99,7 @@ struct FramebufferCacheKey { }; RasterizerOpenGL::RasterizerOpenGL(Core::System& system, ScreenInfo& info) - : res_cache{*this}, shader_cache{*this, system}, global_cache{*this}, system{system}, + : res_cache{*this}, shader_cache{*this, system, device}, global_cache{*this}, system{system}, screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) { OpenGLState::ApplyDefaultState(); @@ -107,8 +107,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, ScreenInfo& info) state.draw.shader_program = 0; state.Apply(); - glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); - LOG_DEBUG(Render_OpenGL, "Sync fixed function OpenGL state here"); CheckExtensions(); } @@ -315,8 +313,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { GLShader::MaxwellUniformData ubo{}; ubo.SetFromRegs(gpu, stage); - const GLintptr offset = buffer_cache.UploadHostMemory( - &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); + const GLintptr offset = + buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment()); // Bind the emulation info buffer bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), offset, @@ -700,23 +698,24 @@ void RasterizerOpenGL::DrawArrays() { // Add space for index buffer (keeping in mind non-core primitives) switch (regs.draw.topology) { case Maxwell::PrimitiveTopology::Quads: - buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + + buffer_size = Common::AlignUp(buffer_size, 4) + primitive_assembler.CalculateQuadSize(regs.vertex_buffer.count); break; default: if (is_indexed) { - buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + CalculateIndexBufferSize(); + buffer_size = Common::AlignUp(buffer_size, 4) + CalculateIndexBufferSize(); } break; } // Uniform space for the 5 shader stages - buffer_size = - Common::AlignUp<std::size_t>(buffer_size, 4) + - (sizeof(GLShader::MaxwellUniformData) + uniform_buffer_alignment) * Maxwell::MaxShaderStage; + buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + + (sizeof(GLShader::MaxwellUniformData) + device.GetUniformBufferAlignment()) * + Maxwell::MaxShaderStage; // Add space for at least 18 constant buffers - buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment); + buffer_size += + Maxwell::MaxConstBuffers * (MaxConstbufferSize + device.GetUniformBufferAlignment()); const bool invalidate = buffer_cache.Map(buffer_size); if (invalidate) { @@ -848,8 +847,8 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader size = Common::AlignUp(size, sizeof(GLvec4)); ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big"); - const GLintptr const_buffer_offset = buffer_cache.UploadMemory( - buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment)); + const GLintptr const_buffer_offset = + buffer_cache.UploadMemory(buffer.address, size, device.GetUniformBufferAlignment()); bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), const_buffer_offset, size); } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index a0e056142..71b9c5ead 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -21,6 +21,7 @@ #include "video_core/rasterizer_cache.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_opengl/gl_buffer_cache.h" +#include "video_core/renderer_opengl/gl_device.h" #include "video_core/renderer_opengl/gl_global_cache.h" #include "video_core/renderer_opengl/gl_primitive_assembler.h" #include "video_core/renderer_opengl/gl_rasterizer_cache.h" @@ -172,6 +173,7 @@ private: /// but are needed for correct emulation void CheckExtensions(); + const Device device; OpenGLState state; RasterizerCacheOpenGL res_cache; @@ -180,7 +182,6 @@ private: SamplerCacheOpenGL sampler_cache; Core::System& system; - ScreenInfo& screen_info; std::unique_ptr<GLShader::ProgramManager> shader_program_manager; @@ -196,7 +197,6 @@ private: static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; OGLBufferCache buffer_cache; PrimitiveAssembler primitive_assembler{buffer_cache}; - GLint uniform_buffer_alignment; BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER}; BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER}; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 7a68b8738..5a25f5b37 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -640,13 +640,16 @@ void CachedSurface::LoadGLBuffer() { SwizzleFunc(MortonSwizzleMode::MortonToLinear, params, gl_buffer[i], i); } else { const u32 bpp = params.GetFormatBpp() / 8; - const u32 copy_size = params.width * bpp; + const u32 copy_size = (params.width * bpp + GetDefaultBlockWidth(params.pixel_format) - 1) / + GetDefaultBlockWidth(params.pixel_format); if (params.pitch == copy_size) { std::memcpy(gl_buffer[0].data(), params.host_ptr, params.size_in_bytes_gl); } else { + const u32 height = (params.height + GetDefaultBlockHeight(params.pixel_format) - 1) / + GetDefaultBlockHeight(params.pixel_format); const u8* start{params.host_ptr}; u8* write_to = gl_buffer[0].data(); - for (u32 h = params.height; h > 0; h--) { + for (u32 h = height; h > 0; h--) { std::memcpy(write_to, start, copy_size); start += params.pitch; write_to += copy_size; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 99f67494c..2a81b1169 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -38,13 +38,15 @@ GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) { } /// Gets the shader program code from memory for the specified address -ProgramCode GetShaderCode(const u8* host_ptr) { +ProgramCode GetShaderCode(Tegra::MemoryManager& memory_manager, const GPUVAddr gpu_addr, + const u8* host_ptr) { ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH); ASSERT_OR_EXECUTE(host_ptr != nullptr, { std::fill(program_code.begin(), program_code.end(), 0); return program_code; }); - std::memcpy(program_code.data(), host_ptr, program_code.size() * sizeof(u64)); + memory_manager.ReadBlockUnsafe(gpu_addr, program_code.data(), + program_code.size() * sizeof(u64)); return program_code; } @@ -134,8 +136,8 @@ u64 GetUniqueIdentifier(Maxwell::ShaderProgram program_type, const ProgramCode& } /// Creates an unspecialized program from code streams -GLShader::ProgramResult CreateProgram(Maxwell::ShaderProgram program_type, ProgramCode program_code, - ProgramCode program_code_b) { +GLShader::ProgramResult CreateProgram(const Device& device, Maxwell::ShaderProgram program_type, + ProgramCode program_code, ProgramCode program_code_b) { GLShader::ShaderSetup setup(program_code); if (program_type == Maxwell::ShaderProgram::VertexA) { // VertexB is always enabled, so when VertexA is enabled, we have two vertex shaders. @@ -149,11 +151,11 @@ GLShader::ProgramResult CreateProgram(Maxwell::ShaderProgram program_type, Progr switch (program_type) { case Maxwell::ShaderProgram::VertexA: case Maxwell::ShaderProgram::VertexB: - return GLShader::GenerateVertexShader(setup); + return GLShader::GenerateVertexShader(device, setup); case Maxwell::ShaderProgram::Geometry: - return GLShader::GenerateGeometryShader(setup); + return GLShader::GenerateGeometryShader(device, setup); case Maxwell::ShaderProgram::Fragment: - return GLShader::GenerateFragmentShader(setup); + return GLShader::GenerateFragmentShader(device, setup); default: LOG_CRITICAL(HW_GPU, "Unimplemented program_type={}", static_cast<u32>(program_type)); UNREACHABLE(); @@ -212,22 +214,20 @@ std::set<GLenum> GetSupportedFormats() { return supported_formats; } -} // namespace +} // Anonymous namespace -CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier, +CachedShader::CachedShader(const Device& device, VAddr cpu_addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache, const PrecompiledPrograms& precompiled_programs, ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr) : RasterizerCacheObject{host_ptr}, host_ptr{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier}, program_type{program_type}, disk_cache{disk_cache}, precompiled_programs{precompiled_programs} { - - const std::size_t code_size = CalculateProgramSize(program_code); - const std::size_t code_size_b = - program_code_b.empty() ? 0 : CalculateProgramSize(program_code_b); - - GLShader::ProgramResult program_result = - CreateProgram(program_type, program_code, program_code_b); + const std::size_t code_size{CalculateProgramSize(program_code)}; + const std::size_t code_size_b{program_code_b.empty() ? 0 + : CalculateProgramSize(program_code_b)}; + GLShader::ProgramResult program_result{ + CreateProgram(device, program_type, program_code, program_code_b)}; if (program_result.first.empty()) { // TODO(Rodrigo): Unimplemented shader stages hit here, avoid using these for now return; @@ -251,7 +251,6 @@ CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier, : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier}, program_type{program_type}, disk_cache{disk_cache}, precompiled_programs{ precompiled_programs} { - code = std::move(result.first); entries = result.second; shader_length = entries.shader_length; @@ -344,8 +343,9 @@ ShaderDiskCacheUsage CachedShader::GetUsage(GLenum primitive_mode, return {unique_identifier, base_bindings, primitive_mode}; } -ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system) - : RasterizerCache{rasterizer}, disk_cache{system} {} +ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, + const Device& device) + : RasterizerCache{rasterizer}, disk_cache{system}, device{device} {} void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback) { @@ -439,17 +439,18 @@ std::unordered_map<u64, UnspecializedShader> ShaderCacheOpenGL::GenerateUnspecia const std::unordered_map<u64, ShaderDiskCacheDecompiled>& decompiled) { std::unordered_map<u64, UnspecializedShader> unspecialized; - if (callback) + if (callback) { callback(VideoCore::LoadCallbackStage::Decompile, 0, raws.size()); + } for (std::size_t i = 0; i < raws.size(); ++i) { - if (stop_loading) + if (stop_loading) { return {}; - + } const auto& raw{raws[i]}; - const u64 unique_identifier = raw.GetUniqueIdentifier(); - const u64 calculated_hash = - GetUniqueIdentifier(raw.GetProgramType(), raw.GetProgramCode(), raw.GetProgramCodeB()); + const u64 unique_identifier{raw.GetUniqueIdentifier()}; + const u64 calculated_hash{ + GetUniqueIdentifier(raw.GetProgramType(), raw.GetProgramCode(), raw.GetProgramCodeB())}; if (unique_identifier != calculated_hash) { LOG_ERROR( Render_OpenGL, @@ -466,8 +467,8 @@ std::unordered_map<u64, UnspecializedShader> ShaderCacheOpenGL::GenerateUnspecia result = {stored_decompiled.code, stored_decompiled.entries}; } else { // Otherwise decompile the shader at boot and save the result to the decompiled file - result = - CreateProgram(raw.GetProgramType(), raw.GetProgramCode(), raw.GetProgramCodeB()); + result = CreateProgram(device, raw.GetProgramType(), raw.GetProgramCode(), + raw.GetProgramCodeB()); disk_cache.SaveDecompiled(unique_identifier, result.first, result.second); } @@ -477,8 +478,9 @@ std::unordered_map<u64, UnspecializedShader> ShaderCacheOpenGL::GenerateUnspecia {raw.GetUniqueIdentifier(), {std::move(result.first), std::move(result.second), raw.GetProgramType()}}); - if (callback) + if (callback) { callback(VideoCore::LoadCallbackStage::Decompile, i, raws.size()); + } } return unspecialized; } @@ -497,11 +499,12 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { if (!shader) { // No shader found - create a new one - ProgramCode program_code{GetShaderCode(host_ptr)}; + ProgramCode program_code{GetShaderCode(memory_manager, program_addr, host_ptr)}; ProgramCode program_code_b; if (program == Maxwell::ShaderProgram::VertexA) { - program_code_b = GetShaderCode( - memory_manager.GetPointer(GetShaderAddress(Maxwell::ShaderProgram::VertexB))); + const GPUVAddr program_addr_b{GetShaderAddress(Maxwell::ShaderProgram::VertexB)}; + program_code_b = GetShaderCode(memory_manager, program_addr_b, + memory_manager.GetPointer(program_addr_b)); } const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b); const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)}; @@ -512,7 +515,7 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { precompiled_programs, found->second, host_ptr); } else { shader = std::make_shared<CachedShader>( - cpu_addr, unique_identifier, program, disk_cache, precompiled_programs, + device, cpu_addr, unique_identifier, program, disk_cache, precompiled_programs, std::move(program_code), std::move(program_code_b), host_ptr); } Register(shader); diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 0cf8e0b3d..a332087f8 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -27,6 +27,7 @@ class System; namespace OpenGL { class CachedShader; +class Device; class RasterizerOpenGL; struct UnspecializedShader; @@ -38,7 +39,7 @@ using PrecompiledShaders = std::unordered_map<u64, GLShader::ProgramResult>; class CachedShader final : public RasterizerCacheObject { public: - explicit CachedShader(VAddr cpu_addr, u64 unique_identifier, + explicit CachedShader(const Device& device, VAddr cpu_addr, u64 unique_identifier, Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache, const PrecompiledPrograms& precompiled_programs, ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr); @@ -112,7 +113,8 @@ private: class ShaderCacheOpenGL final : public RasterizerCache<Shader> { public: - explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system); + explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, + const Device& device); /// Loads disk cache for the current game void LoadDiskCache(const std::atomic_bool& stop_loading, @@ -130,6 +132,8 @@ private: CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump, const std::set<GLenum>& supported_formats); + const Device& device; + std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; ShaderDiskCacheOpenGL disk_cache; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index cd462621d..ef1a1995f 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -15,6 +15,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "video_core/engines/maxwell_3d.h" +#include "video_core/renderer_opengl/gl_device.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_decompiler.h" #include "video_core/shader/shader_ir.h" @@ -135,8 +136,9 @@ bool IsPrecise(Node node) { class GLSLDecompiler final { public: - explicit GLSLDecompiler(const ShaderIR& ir, ShaderStage stage, std::string suffix) - : ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {} + explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderStage stage, + std::string suffix) + : device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {} void Decompile() { DeclareVertex(); @@ -802,8 +804,12 @@ private: // Inline the string as an immediate integer in GLSL (AOFFI arguments are required // to be constant by the standard). expr += std::to_string(static_cast<s32>(immediate->GetValue())); - } else { + } else if (device.HasVariableAoffi()) { + // Avoid using variable AOFFI on unsupported devices. expr += "ftoi(" + Visit(operand) + ')'; + } else { + // Insert 0 on devices not supporting variable AOFFI. + expr += '0'; } if (index + 1 < aoffi.size()) { expr += ", "; @@ -1645,6 +1651,7 @@ private: return name + '_' + std::to_string(index) + '_' + suffix; } + const Device& device; const ShaderIR& ir; const ShaderStage stage; const std::string suffix; @@ -1676,8 +1683,9 @@ std::string GetCommonDeclarations() { "}\n"; } -ProgramResult Decompile(const ShaderIR& ir, Maxwell::ShaderStage stage, const std::string& suffix) { - GLSLDecompiler decompiler(ir, stage, suffix); +ProgramResult Decompile(const Device& device, const ShaderIR& ir, Maxwell::ShaderStage stage, + const std::string& suffix) { + GLSLDecompiler decompiler(device, ir, stage, suffix); decompiler.Decompile(); return {decompiler.GetResult(), decompiler.GetShaderEntries()}; } diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h index 74032d237..c1569e737 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.h +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h @@ -12,6 +12,10 @@ #include "video_core/engines/maxwell_3d.h" #include "video_core/shader/shader_ir.h" +namespace OpenGL { +class Device; +} + namespace VideoCommon::Shader { class ShaderIR; } @@ -77,7 +81,7 @@ struct ShaderEntries { std::string GetCommonDeclarations(); -ProgramResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage, - const std::string& suffix); +ProgramResult Decompile(const Device& device, const VideoCommon::Shader::ShaderIR& ir, + Maxwell::ShaderStage stage, const std::string& suffix); } // namespace OpenGL::GLShader diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 8763d9c71..6abf948f8 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -16,7 +16,7 @@ using VideoCommon::Shader::ShaderIR; static constexpr u32 PROGRAM_OFFSET{10}; -ProgramResult GenerateVertexShader(const ShaderSetup& setup) { +ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup) { const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; @@ -34,14 +34,15 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config { )"; ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); - ProgramResult program = Decompile(program_ir, Maxwell3D::Regs::ShaderStage::Vertex, "vertex"); + ProgramResult program = + Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Vertex, "vertex"); out += program.first; if (setup.IsDualProgram()) { ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET); ProgramResult program_b = - Decompile(program_ir_b, Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b"); + Decompile(device, program_ir_b, Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b"); out += program_b.first; } @@ -57,6 +58,9 @@ void main() { } out += R"( + + // Set Position Y direction + position.y *= utof(config_pack[2]); // Check if the flip stage is VertexB // Config pack's second value is flip_stage if (config_pack[1] == 1) { @@ -75,7 +79,7 @@ void main() { return {out, program.second}; } -ProgramResult GenerateGeometryShader(const ShaderSetup& setup) { +ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& setup) { const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; @@ -95,7 +99,7 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config { )"; ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); ProgramResult program = - Decompile(program_ir, Maxwell3D::Regs::ShaderStage::Geometry, "geometry"); + Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Geometry, "geometry"); out += program.first; out += R"( @@ -106,7 +110,7 @@ void main() { return {out, program.second}; } -ProgramResult GenerateFragmentShader(const ShaderSetup& setup) { +ProgramResult GenerateFragmentShader(const Device& device, const ShaderSetup& setup) { const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; @@ -158,7 +162,7 @@ bool AlphaFunc(in float value) { )"; ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); ProgramResult program = - Decompile(program_ir, Maxwell3D::Regs::ShaderStage::Fragment, "fragment"); + Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Fragment, "fragment"); out += program.first; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index fad346b48..0536c8a03 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -10,6 +10,10 @@ #include "video_core/renderer_opengl/gl_shader_decompiler.h" #include "video_core/shader/shader_ir.h" +namespace OpenGL { +class Device; +} + namespace OpenGL::GLShader { using VideoCommon::Shader::ProgramCode; @@ -39,22 +43,13 @@ private: bool has_program_b{}; }; -/** - * Generates the GLSL vertex shader program source code for the given VS program - * @returns String of the shader source code - */ -ProgramResult GenerateVertexShader(const ShaderSetup& setup); - -/** - * Generates the GLSL geometry shader program source code for the given GS program - * @returns String of the shader source code - */ -ProgramResult GenerateGeometryShader(const ShaderSetup& setup); - -/** - * Generates the GLSL fragment shader program source code for the given FS program - * @returns String of the shader source code - */ -ProgramResult GenerateFragmentShader(const ShaderSetup& setup); +/// Generates the GLSL vertex shader program source code for the given VS program +ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup); + +/// Generates the GLSL geometry shader program source code for the given GS program +ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& setup); + +/// Generates the GLSL fragment shader program source code for the given FS program +ProgramResult GenerateFragmentShader(const Device& device, const ShaderSetup& setup); } // namespace OpenGL::GLShader diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 52d569a1b..7425fbe5d 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -471,8 +471,9 @@ void OpenGLState::ApplyTextures() const { const auto& texture_unit = texture_units[i]; auto& cur_state_texture_unit = cur_state.texture_units[i]; textures[i] = texture_unit.texture; - if (cur_state_texture_unit.texture == textures[i]) + if (cur_state_texture_unit.texture == textures[i]) { continue; + } cur_state_texture_unit.texture = textures[i]; if (!has_delta) { first = i; @@ -493,10 +494,11 @@ void OpenGLState::ApplySamplers() const { std::array<GLuint, Maxwell::NumTextureSamplers> samplers; for (std::size_t i = 0; i < std::size(samplers); ++i) { - if (cur_state.texture_units[i].sampler == texture_units[i].sampler) + samplers[i] = texture_units[i].sampler; + if (cur_state.texture_units[i].sampler == texture_units[i].sampler) { continue; + } cur_state.texture_units[i].sampler = texture_units[i].sampler; - samplers[i] = texture_units[i].sampler; if (!has_delta) { first = i; has_delta = true; diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 995d0e068..217805386 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -288,6 +288,29 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 } } +void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y, + const u32 block_height, const std::size_t copy_size, const u8* source_data, + u8* swizzle_data) { + const u32 image_width_in_gobs{(width + gob_size_x - 1) / gob_size_x}; + std::size_t count = 0; + for (std::size_t y = dst_y; y < height && count < copy_size; ++y) { + const std::size_t gob_address_y = + (y / (gob_size_y * block_height)) * gob_size * block_height * image_width_in_gobs + + ((y % (gob_size_y * block_height)) / gob_size_y) * gob_size; + const auto& table = legacy_swizzle_table[y % gob_size_y]; + for (std::size_t x = dst_x; x < width && count < copy_size; ++x) { + const std::size_t gob_address = + gob_address_y + (x / gob_size_x) * gob_size * block_height; + const std::size_t swizzled_offset = gob_address + table[x % gob_size_x]; + const u8* source_line = source_data + count; + u8* dest_addr = swizzle_data + swizzled_offset; + count++; + + std::memcpy(dest_addr, source_line, 1); + } + } +} + std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, u32 height) { std::vector<u8> rgba_data; diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index e078fa274..e072d8401 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h @@ -51,4 +51,8 @@ void UnswizzleSubrect(u32 subrect_width, u32 subrect_height, u32 dest_pitch, u32 u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height, u32 offset_x, u32 offset_y); +void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y, + const u32 block_height, const std::size_t copy_size, const u8* source_data, + u8* swizzle_data); + } // namespace Tegra::Texture diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 31b65c04c..5138bd9a3 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -7,6 +7,8 @@ add_executable(yuzu Info.plist about_dialog.cpp about_dialog.h + applets/error.cpp + applets/error.h applets/profile_select.cpp applets/profile_select.h applets/software_keyboard.cpp diff --git a/src/yuzu/applets/error.cpp b/src/yuzu/applets/error.cpp new file mode 100644 index 000000000..1fb2fe277 --- /dev/null +++ b/src/yuzu/applets/error.cpp @@ -0,0 +1,59 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <QDateTime> +#include "core/hle/lock.h" +#include "yuzu/applets/error.h" +#include "yuzu/main.h" + +QtErrorDisplay::QtErrorDisplay(GMainWindow& parent) { + connect(this, &QtErrorDisplay::MainWindowDisplayError, &parent, + &GMainWindow::ErrorDisplayDisplayError, Qt::QueuedConnection); + connect(&parent, &GMainWindow::ErrorDisplayFinished, this, + &QtErrorDisplay::MainWindowFinishedError, Qt::DirectConnection); +} + +QtErrorDisplay::~QtErrorDisplay() = default; + +void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const { + this->callback = std::move(finished); + emit MainWindowDisplayError( + tr("An error has occured.\nPlease try again or contact the developer of the " + "software.\n\nError Code: %1-%2 (0x%3)") + .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) + .arg(error.description, 4, 10, QChar::fromLatin1('0')) + .arg(error.raw, 8, 16, QChar::fromLatin1('0'))); +} + +void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, + std::function<void()> finished) const { + this->callback = std::move(finished); + emit MainWindowDisplayError( + tr("An error occured on %1 at %2.\nPlease try again or contact the " + "developer of the software.\n\nError Code: %3-%4 (0x%5)") + .arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("dddd, MMMM d, yyyy")) + .arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("h:mm:ss A")) + .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) + .arg(error.description, 4, 10, QChar::fromLatin1('0')) + .arg(error.raw, 8, 16, QChar::fromLatin1('0'))); +} + +void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_text, + std::string fullscreen_text, + std::function<void()> finished) const { + this->callback = std::move(finished); + emit MainWindowDisplayError( + tr("An error has occured.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5") + .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) + .arg(error.description, 4, 10, QChar::fromLatin1('0')) + .arg(error.raw, 8, 16, QChar::fromLatin1('0')) + .arg(QString::fromStdString(dialog_text)) + .arg(QString::fromStdString(fullscreen_text))); +} + +void QtErrorDisplay::MainWindowFinishedError() { + // Acquire the HLE mutex + std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + callback(); +} diff --git a/src/yuzu/applets/error.h b/src/yuzu/applets/error.h new file mode 100644 index 000000000..b0932d895 --- /dev/null +++ b/src/yuzu/applets/error.h @@ -0,0 +1,33 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <QObject> + +#include "core/frontend/applets/error.h" + +class GMainWindow; + +class QtErrorDisplay final : public QObject, public Core::Frontend::ErrorApplet { + Q_OBJECT + +public: + explicit QtErrorDisplay(GMainWindow& parent); + ~QtErrorDisplay() override; + + void ShowError(ResultCode error, std::function<void()> finished) const override; + void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, + std::function<void()> finished) const override; + void ShowCustomErrorText(ResultCode error, std::string dialog_text, std::string fullscreen_text, + std::function<void()> finished) const override; + +signals: + void MainWindowDisplayError(QString error) const; + +private: + void MainWindowFinishedError(); + + mutable std::function<void()> callback; +}; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index bdee44b04..e33e3aaaf 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -8,6 +8,7 @@ #include <thread> // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. +#include "applets/error.h" #include "applets/profile_select.h" #include "applets/software_keyboard.h" #include "applets/web_browser.h" @@ -15,6 +16,7 @@ #include "configuration/configure_per_general.h" #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_real.h" +#include "core/frontend/applets/general_frontend.h" #include "core/frontend/scope_acquire_window_context.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/applets/applets.h" @@ -795,9 +797,13 @@ bool GMainWindow::LoadROM(const QString& filename) { system.SetGPUDebugContext(debug_context); - system.SetProfileSelector(std::make_unique<QtProfileSelector>(*this)); - system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this)); - system.SetWebBrowser(std::make_unique<QtWebBrowser>(*this)); + system.SetAppletFrontendSet({ + std::make_unique<QtErrorDisplay>(*this), + nullptr, + std::make_unique<QtProfileSelector>(*this), + std::make_unique<QtSoftwareKeyboard>(*this), + std::make_unique<QtWebBrowser>(*this), + }); const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; @@ -1583,6 +1589,11 @@ void GMainWindow::OnLoadComplete() { loading_screen->OnLoadComplete(); } +void GMainWindow::ErrorDisplayDisplayError(QString body) { + QMessageBox::critical(this, tr("Error Display"), body); + emit ErrorDisplayFinished(); +} + void GMainWindow::OnMenuReportCompatibility() { if (!Settings::values.yuzu_token.empty() && !Settings::values.yuzu_username.empty()) { CompatDB compatdb{this}; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index ce5045819..fb2a193cb 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -102,6 +102,8 @@ signals: // Signal that tells widgets to update icons to use the current theme void UpdateThemedIcons(); + void ErrorDisplayFinished(); + void ProfileSelectorFinishedSelection(std::optional<Service::Account::UUID> uuid); void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); void SoftwareKeyboardFinishedCheckDialog(); @@ -111,6 +113,7 @@ signals: public slots: void OnLoadComplete(); + void ErrorDisplayDisplayError(QString body); void ProfileSelectorSelectProfile(); void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 7ea4a1b18..a1d7879b1 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -32,11 +32,7 @@ #include "yuzu_cmd/config.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h" -#include <getopt.h> #include "core/file_sys/registered_cache.h" -#ifndef _MSC_VER -#include <unistd.h> -#endif #ifdef _WIN32 // windows.h needs to be included before shellapi.h @@ -45,6 +41,12 @@ #include <shellapi.h> #endif +#undef _UNICODE +#include <getopt.h> +#ifndef _MSC_VER +#include <unistd.h> +#endif + #ifdef _WIN32 extern "C" { // tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable |