diff options
Diffstat (limited to 'src')
105 files changed, 2275 insertions, 807 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6c99dd5e2..a1d87bbbc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -18,15 +18,30 @@ if (MSVC) | |||
| 18 | # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors. | 18 | # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors. |
| 19 | add_definitions(-DWIN32_LEAN_AND_MEAN) | 19 | add_definitions(-DWIN32_LEAN_AND_MEAN) |
| 20 | 20 | ||
| 21 | # /W3 - Level 3 warnings | 21 | # Ensure that projects build with Unicode support. |
| 22 | # /MP - Multi-threaded compilation | 22 | add_definitions(-DUNICODE -D_UNICODE) |
| 23 | # /Zi - Output debugging information | 23 | |
| 24 | # /Zo - enhanced debug info for optimized builds | 24 | # /W3 - Level 3 warnings |
| 25 | # /permissive- - enables stricter C++ standards conformance checks | 25 | # /MP - Multi-threaded compilation |
| 26 | # /EHsc - C++-only exception handling semantics | 26 | # /Zi - Output debugging information |
| 27 | # /Zc:throwingNew - let codegen assume `operator new` will never return null | 27 | # /Zo - Enhanced debug info for optimized builds |
| 28 | # /Zc:inline - let codegen omit inline functions in object files | 28 | # /permissive- - Enables stricter C++ standards conformance checks |
| 29 | add_compile_options(/W3 /MP /Zi /Zo /permissive- /EHsc /std:c++latest /Zc:throwingNew,inline) | 29 | # /EHsc - C++-only exception handling semantics |
| 30 | # /Zc:externConstexpr - Allow extern constexpr variables to have external linkage, like the standard mandates | ||
| 31 | # /Zc:inline - Let codegen omit inline functions in object files | ||
| 32 | # /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null | ||
| 33 | add_compile_options( | ||
| 34 | /W3 | ||
| 35 | /MP | ||
| 36 | /Zi | ||
| 37 | /Zo | ||
| 38 | /permissive- | ||
| 39 | /EHsc | ||
| 40 | /std:c++latest | ||
| 41 | /Zc:externConstexpr | ||
| 42 | /Zc:inline | ||
| 43 | /Zc:throwingNew | ||
| 44 | ) | ||
| 30 | 45 | ||
| 31 | # /GS- - No stack buffer overflow checks | 46 | # /GS- - No stack buffer overflow checks |
| 32 | add_compile_options("$<$<CONFIG:Release>:/GS->") | 47 | add_compile_options("$<$<CONFIG:Release>:/GS->") |
diff --git a/src/common/zstd_compression.cpp b/src/common/zstd_compression.cpp index 60a35c67c..978526492 100644 --- a/src/common/zstd_compression.cpp +++ b/src/common/zstd_compression.cpp | |||
| @@ -2,8 +2,6 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <algorithm> | 5 | #include <algorithm> |
| 8 | #include <zstd.h> | 6 | #include <zstd.h> |
| 9 | 7 | ||
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 | |||
| 88 | file_sys/vfs_vector.h | 88 | file_sys/vfs_vector.h |
| 89 | file_sys/xts_archive.cpp | 89 | file_sys/xts_archive.cpp |
| 90 | file_sys/xts_archive.h | 90 | file_sys/xts_archive.h |
| 91 | frontend/applets/error.cpp | ||
| 92 | frontend/applets/error.h | ||
| 93 | frontend/applets/general_frontend.cpp | ||
| 94 | frontend/applets/general_frontend.h | ||
| 91 | frontend/applets/profile_select.cpp | 95 | frontend/applets/profile_select.cpp |
| 92 | frontend/applets/profile_select.h | 96 | frontend/applets/profile_select.h |
| 93 | frontend/applets/software_keyboard.cpp | 97 | frontend/applets/software_keyboard.cpp |
| @@ -177,12 +181,14 @@ add_library(core STATIC | |||
| 177 | hle/service/am/applet_oe.h | 181 | hle/service/am/applet_oe.h |
| 178 | hle/service/am/applets/applets.cpp | 182 | hle/service/am/applets/applets.cpp |
| 179 | hle/service/am/applets/applets.h | 183 | hle/service/am/applets/applets.h |
| 184 | hle/service/am/applets/error.cpp | ||
| 185 | hle/service/am/applets/error.h | ||
| 186 | hle/service/am/applets/general_backend.cpp | ||
| 187 | hle/service/am/applets/general_backend.h | ||
| 180 | hle/service/am/applets/profile_select.cpp | 188 | hle/service/am/applets/profile_select.cpp |
| 181 | hle/service/am/applets/profile_select.h | 189 | hle/service/am/applets/profile_select.h |
| 182 | hle/service/am/applets/software_keyboard.cpp | 190 | hle/service/am/applets/software_keyboard.cpp |
| 183 | hle/service/am/applets/software_keyboard.h | 191 | hle/service/am/applets/software_keyboard.h |
| 184 | hle/service/am/applets/stub_applet.cpp | ||
| 185 | hle/service/am/applets/stub_applet.h | ||
| 186 | hle/service/am/applets/web_browser.cpp | 192 | hle/service/am/applets/web_browser.cpp |
| 187 | hle/service/am/applets/web_browser.h | 193 | hle/service/am/applets/web_browser.h |
| 188 | hle/service/am/idle.cpp | 194 | 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 @@ | |||
| 18 | #include "core/file_sys/registered_cache.h" | 18 | #include "core/file_sys/registered_cache.h" |
| 19 | #include "core/file_sys/vfs_concat.h" | 19 | #include "core/file_sys/vfs_concat.h" |
| 20 | #include "core/file_sys/vfs_real.h" | 20 | #include "core/file_sys/vfs_real.h" |
| 21 | #include "core/frontend/applets/error.h" | ||
| 22 | #include "core/frontend/applets/general_frontend.h" | ||
| 23 | #include "core/frontend/applets/profile_select.h" | ||
| 24 | #include "core/frontend/applets/software_keyboard.h" | ||
| 25 | #include "core/frontend/applets/web_browser.h" | ||
| 21 | #include "core/gdbstub/gdbstub.h" | 26 | #include "core/gdbstub/gdbstub.h" |
| 22 | #include "core/hle/kernel/client_port.h" | 27 | #include "core/hle/kernel/client_port.h" |
| 23 | #include "core/hle/kernel/kernel.h" | 28 | #include "core/hle/kernel/kernel.h" |
| 24 | #include "core/hle/kernel/process.h" | 29 | #include "core/hle/kernel/process.h" |
| 25 | #include "core/hle/kernel/scheduler.h" | 30 | #include "core/hle/kernel/scheduler.h" |
| 26 | #include "core/hle/kernel/thread.h" | 31 | #include "core/hle/kernel/thread.h" |
| 27 | #include "core/hle/service/am/applets/software_keyboard.h" | 32 | #include "core/hle/service/am/applets/applets.h" |
| 28 | #include "core/hle/service/service.h" | 33 | #include "core/hle/service/service.h" |
| 29 | #include "core/hle/service/sm/sm.h" | 34 | #include "core/hle/service/sm/sm.h" |
| 30 | #include "core/loader/loader.h" | 35 | #include "core/loader/loader.h" |
| @@ -110,12 +115,7 @@ struct System::Impl { | |||
| 110 | content_provider = std::make_unique<FileSys::ContentProviderUnion>(); | 115 | content_provider = std::make_unique<FileSys::ContentProviderUnion>(); |
| 111 | 116 | ||
| 112 | /// Create default implementations of applets if one is not provided. | 117 | /// Create default implementations of applets if one is not provided. |
| 113 | if (profile_selector == nullptr) | 118 | applet_manager.SetDefaultAppletsIfMissing(); |
| 114 | profile_selector = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>(); | ||
| 115 | if (software_keyboard == nullptr) | ||
| 116 | software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>(); | ||
| 117 | if (web_browser == nullptr) | ||
| 118 | web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); | ||
| 119 | 119 | ||
| 120 | telemetry_session = std::make_unique<Core::TelemetrySession>(); | 120 | telemetry_session = std::make_unique<Core::TelemetrySession>(); |
| 121 | service_manager = std::make_shared<Service::SM::ServiceManager>(); | 121 | service_manager = std::make_shared<Service::SM::ServiceManager>(); |
| @@ -223,9 +223,7 @@ struct System::Impl { | |||
| 223 | app_loader.reset(); | 223 | app_loader.reset(); |
| 224 | 224 | ||
| 225 | // Clear all applets | 225 | // Clear all applets |
| 226 | profile_selector.reset(); | 226 | applet_manager.ClearAll(); |
| 227 | software_keyboard.reset(); | ||
| 228 | web_browser.reset(); | ||
| 229 | 227 | ||
| 230 | LOG_DEBUG(Core, "Shutdown OK"); | 228 | LOG_DEBUG(Core, "Shutdown OK"); |
| 231 | } | 229 | } |
| @@ -264,9 +262,7 @@ struct System::Impl { | |||
| 264 | std::unique_ptr<FileSys::CheatEngine> cheat_engine; | 262 | std::unique_ptr<FileSys::CheatEngine> cheat_engine; |
| 265 | 263 | ||
| 266 | /// Frontend applets | 264 | /// Frontend applets |
| 267 | std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector; | 265 | Service::AM::Applets::AppletManager applet_manager; |
| 268 | std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; | ||
| 269 | std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser; | ||
| 270 | 266 | ||
| 271 | /// Service manager | 267 | /// Service manager |
| 272 | std::shared_ptr<Service::SM::ServiceManager> service_manager; | 268 | std::shared_ptr<Service::SM::ServiceManager> service_manager; |
| @@ -476,20 +472,20 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const { | |||
| 476 | return impl->virtual_filesystem; | 472 | return impl->virtual_filesystem; |
| 477 | } | 473 | } |
| 478 | 474 | ||
| 479 | void System::SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet) { | 475 | void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) { |
| 480 | impl->profile_selector = std::move(applet); | 476 | impl->applet_manager.SetAppletFrontendSet(std::move(set)); |
| 481 | } | 477 | } |
| 482 | 478 | ||
| 483 | const Frontend::ProfileSelectApplet& System::GetProfileSelector() const { | 479 | void System::SetDefaultAppletFrontendSet() { |
| 484 | return *impl->profile_selector; | 480 | impl->applet_manager.SetDefaultAppletFrontendSet(); |
| 485 | } | 481 | } |
| 486 | 482 | ||
| 487 | void System::SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet) { | 483 | Service::AM::Applets::AppletManager& System::GetAppletManager() { |
| 488 | impl->software_keyboard = std::move(applet); | 484 | return impl->applet_manager; |
| 489 | } | 485 | } |
| 490 | 486 | ||
| 491 | const Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() const { | 487 | const Service::AM::Applets::AppletManager& System::GetAppletManager() const { |
| 492 | return *impl->software_keyboard; | 488 | return impl->applet_manager; |
| 493 | } | 489 | } |
| 494 | 490 | ||
| 495 | void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) { | 491 | void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) { |
| @@ -513,18 +509,6 @@ void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) { | |||
| 513 | impl->content_provider->ClearSlot(slot); | 509 | impl->content_provider->ClearSlot(slot); |
| 514 | } | 510 | } |
| 515 | 511 | ||
| 516 | void System::SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet) { | ||
| 517 | impl->web_browser = std::move(applet); | ||
| 518 | } | ||
| 519 | |||
| 520 | Frontend::WebBrowserApplet& System::GetWebBrowser() { | ||
| 521 | return *impl->web_browser; | ||
| 522 | } | ||
| 523 | |||
| 524 | const Frontend::WebBrowserApplet& System::GetWebBrowser() const { | ||
| 525 | return *impl->web_browser; | ||
| 526 | } | ||
| 527 | |||
| 528 | System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { | 512 | System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { |
| 529 | return impl->Init(*this, emu_window); | 513 | return impl->Init(*this, emu_window); |
| 530 | } | 514 | } |
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 @@ | |||
| 14 | 14 | ||
| 15 | namespace Core::Frontend { | 15 | namespace Core::Frontend { |
| 16 | class EmuWindow; | 16 | class EmuWindow; |
| 17 | class ProfileSelectApplet; | ||
| 18 | class SoftwareKeyboardApplet; | ||
| 19 | class WebBrowserApplet; | ||
| 20 | } // namespace Core::Frontend | 17 | } // namespace Core::Frontend |
| 21 | 18 | ||
| 22 | namespace FileSys { | 19 | namespace FileSys { |
| @@ -38,9 +35,18 @@ class AppLoader; | |||
| 38 | enum class ResultStatus : u16; | 35 | enum class ResultStatus : u16; |
| 39 | } // namespace Loader | 36 | } // namespace Loader |
| 40 | 37 | ||
| 41 | namespace Service::SM { | 38 | namespace Service { |
| 39 | |||
| 40 | namespace AM::Applets { | ||
| 41 | struct AppletFrontendSet; | ||
| 42 | class AppletManager; | ||
| 43 | } // namespace AM::Applets | ||
| 44 | |||
| 45 | namespace SM { | ||
| 42 | class ServiceManager; | 46 | class ServiceManager; |
| 43 | } // namespace Service::SM | 47 | } // namespace SM |
| 48 | |||
| 49 | } // namespace Service | ||
| 44 | 50 | ||
| 45 | namespace Tegra { | 51 | namespace Tegra { |
| 46 | class DebugContext; | 52 | class DebugContext; |
| @@ -260,18 +266,13 @@ public: | |||
| 260 | void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id, | 266 | void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id, |
| 261 | VAddr code_region_start, VAddr code_region_end); | 267 | VAddr code_region_start, VAddr code_region_end); |
| 262 | 268 | ||
| 263 | void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet); | 269 | void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set); |
| 264 | |||
| 265 | const Frontend::ProfileSelectApplet& GetProfileSelector() const; | ||
| 266 | |||
| 267 | void SetSoftwareKeyboard(std::unique_ptr<Frontend::SoftwareKeyboardApplet> applet); | ||
| 268 | 270 | ||
| 269 | const Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const; | 271 | void SetDefaultAppletFrontendSet(); |
| 270 | 272 | ||
| 271 | void SetWebBrowser(std::unique_ptr<Frontend::WebBrowserApplet> applet); | 273 | Service::AM::Applets::AppletManager& GetAppletManager(); |
| 272 | 274 | ||
| 273 | Frontend::WebBrowserApplet& GetWebBrowser(); | 275 | const Service::AM::Applets::AppletManager& GetAppletManager() const; |
| 274 | const Frontend::WebBrowserApplet& GetWebBrowser() const; | ||
| 275 | 276 | ||
| 276 | void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider); | 277 | void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider); |
| 277 | 278 | ||
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 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/frontend/applets/error.h" | ||
| 6 | |||
| 7 | namespace Core::Frontend { | ||
| 8 | |||
| 9 | ErrorApplet::~ErrorApplet() = default; | ||
| 10 | |||
| 11 | void DefaultErrorApplet::ShowError(ResultCode error, std::function<void()> finished) const { | ||
| 12 | LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})", | ||
| 13 | static_cast<u32>(error.module.Value()), error.description.Value(), error.raw); | ||
| 14 | } | ||
| 15 | |||
| 16 | void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, | ||
| 17 | std::function<void()> finished) const { | ||
| 18 | LOG_CRITICAL( | ||
| 19 | Service_Fatal, | ||
| 20 | "Application requested error display: {:04X}-{:04X} (raw={:08X}) with timestamp={:016X}", | ||
| 21 | static_cast<u32>(error.module.Value()), error.description.Value(), error.raw, time.count()); | ||
| 22 | } | ||
| 23 | |||
| 24 | void DefaultErrorApplet::ShowCustomErrorText(ResultCode error, std::string main_text, | ||
| 25 | std::string detail_text, | ||
| 26 | std::function<void()> finished) const { | ||
| 27 | LOG_CRITICAL(Service_Fatal, | ||
| 28 | "Application requested custom error with error_code={:04X}-{:04X} (raw={:08X})", | ||
| 29 | static_cast<u32>(error.module.Value()), error.description.Value(), error.raw); | ||
| 30 | LOG_CRITICAL(Service_Fatal, " Main Text: {}", main_text); | ||
| 31 | LOG_CRITICAL(Service_Fatal, " Detail Text: {}", detail_text); | ||
| 32 | } | ||
| 33 | |||
| 34 | } // 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 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <chrono> | ||
| 8 | #include <functional> | ||
| 9 | |||
| 10 | #include "core/hle/result.h" | ||
| 11 | |||
| 12 | namespace Core::Frontend { | ||
| 13 | |||
| 14 | class ErrorApplet { | ||
| 15 | public: | ||
| 16 | virtual ~ErrorApplet(); | ||
| 17 | |||
| 18 | virtual void ShowError(ResultCode error, std::function<void()> finished) const = 0; | ||
| 19 | |||
| 20 | virtual void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, | ||
| 21 | std::function<void()> finished) const = 0; | ||
| 22 | |||
| 23 | virtual void ShowCustomErrorText(ResultCode error, std::string dialog_text, | ||
| 24 | std::string fullscreen_text, | ||
| 25 | std::function<void()> finished) const = 0; | ||
| 26 | }; | ||
| 27 | |||
| 28 | class DefaultErrorApplet final : public ErrorApplet { | ||
| 29 | public: | ||
| 30 | void ShowError(ResultCode error, std::function<void()> finished) const override; | ||
| 31 | void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, | ||
| 32 | std::function<void()> finished) const override; | ||
| 33 | void ShowCustomErrorText(ResultCode error, std::string main_text, std::string detail_text, | ||
| 34 | std::function<void()> finished) const override; | ||
| 35 | }; | ||
| 36 | |||
| 37 | } // 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 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/frontend/applets/general_frontend.h" | ||
| 7 | |||
| 8 | namespace Core::Frontend { | ||
| 9 | |||
| 10 | PhotoViewerApplet::~PhotoViewerApplet() = default; | ||
| 11 | |||
| 12 | DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() {} | ||
| 13 | |||
| 14 | void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id, | ||
| 15 | std::function<void()> finished) const { | ||
| 16 | LOG_INFO(Service_AM, | ||
| 17 | "Application requested frontend to display stored photos for title_id={:016X}", | ||
| 18 | title_id); | ||
| 19 | finished(); | ||
| 20 | } | ||
| 21 | |||
| 22 | void DefaultPhotoViewerApplet::ShowAllPhotos(std::function<void()> finished) const { | ||
| 23 | LOG_INFO(Service_AM, "Application requested frontend to display all stored photos."); | ||
| 24 | finished(); | ||
| 25 | } | ||
| 26 | |||
| 27 | } // 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 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <functional> | ||
| 8 | #include "common/common_types.h" | ||
| 9 | |||
| 10 | namespace Core::Frontend { | ||
| 11 | |||
| 12 | class PhotoViewerApplet { | ||
| 13 | public: | ||
| 14 | virtual ~PhotoViewerApplet(); | ||
| 15 | |||
| 16 | virtual void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const = 0; | ||
| 17 | virtual void ShowAllPhotos(std::function<void()> finished) const = 0; | ||
| 18 | }; | ||
| 19 | |||
| 20 | class DefaultPhotoViewerApplet final : public PhotoViewerApplet { | ||
| 21 | public: | ||
| 22 | ~DefaultPhotoViewerApplet() override; | ||
| 23 | |||
| 24 | void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const override; | ||
| 25 | void ShowAllPhotos(std::function<void()> finished) const override; | ||
| 26 | }; | ||
| 27 | |||
| 28 | } // namespace Core::Frontend | ||
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 1320bbe77..eda466a5d 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp | |||
| @@ -10,6 +10,8 @@ | |||
| 10 | 10 | ||
| 11 | namespace Core::Frontend { | 11 | namespace Core::Frontend { |
| 12 | 12 | ||
| 13 | GraphicsContext::~GraphicsContext() = default; | ||
| 14 | |||
| 13 | class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>, | 15 | class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>, |
| 14 | public std::enable_shared_from_this<TouchState> { | 16 | public std::enable_shared_from_this<TouchState> { |
| 15 | public: | 17 | public: |
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 70a522556..e2c290dc1 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -19,6 +19,8 @@ namespace Core::Frontend { | |||
| 19 | */ | 19 | */ |
| 20 | class GraphicsContext { | 20 | class GraphicsContext { |
| 21 | public: | 21 | public: |
| 22 | virtual ~GraphicsContext(); | ||
| 23 | |||
| 22 | /// Makes the graphics context current for the caller thread | 24 | /// Makes the graphics context current for the caller thread |
| 23 | virtual void MakeCurrent() = 0; | 25 | virtual void MakeCurrent() = 0; |
| 24 | 26 | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 8539fabe4..757e5f21f 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -46,8 +46,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_ | |||
| 46 | 46 | ||
| 47 | bool resume = true; | 47 | bool resume = true; |
| 48 | 48 | ||
| 49 | if (thread->GetStatus() == ThreadStatus::WaitSynchAny || | 49 | if (thread->GetStatus() == ThreadStatus::WaitSynch || |
| 50 | thread->GetStatus() == ThreadStatus::WaitSynchAll || | ||
| 51 | thread->GetStatus() == ThreadStatus::WaitHLEEvent) { | 50 | thread->GetStatus() == ThreadStatus::WaitHLEEvent) { |
| 52 | // Remove the thread from each of its waiting objects' waitlists | 51 | // Remove the thread from each of its waiting objects' waitlists |
| 53 | for (const auto& object : thread->GetWaitObjects()) { | 52 | for (const auto& object : thread->GetWaitObjects()) { |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 6d7a7e754..0775a89fb 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -147,8 +147,7 @@ void Process::PrepareForTermination() { | |||
| 147 | continue; | 147 | continue; |
| 148 | 148 | ||
| 149 | // TODO(Subv): When are the other running/ready threads terminated? | 149 | // TODO(Subv): When are the other running/ready threads terminated? |
| 150 | ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynchAny || | 150 | ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynch, |
| 151 | thread->GetStatus() == ThreadStatus::WaitSynchAll, | ||
| 152 | "Exiting processes with non-waiting threads is currently unimplemented"); | 151 | "Exiting processes with non-waiting threads is currently unimplemented"); |
| 153 | 152 | ||
| 154 | thread->Stop(); | 153 | thread->Stop(); |
| @@ -242,7 +241,8 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) { | |||
| 242 | } | 241 | } |
| 243 | 242 | ||
| 244 | Process::Process(Core::System& system) | 243 | Process::Process(Core::System& system) |
| 245 | : WaitObject{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {} | 244 | : WaitObject{system.Kernel()}, vm_manager{system}, |
| 245 | address_arbiter{system}, mutex{system}, system{system} {} | ||
| 246 | 246 | ||
| 247 | Process::~Process() = default; | 247 | Process::~Process() = default; |
| 248 | 248 | ||
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4c763b288..2dcf174c5 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -424,7 +424,7 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han | |||
| 424 | /// Default thread wakeup callback for WaitSynchronization | 424 | /// Default thread wakeup callback for WaitSynchronization |
| 425 | static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, | 425 | static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, |
| 426 | SharedPtr<WaitObject> object, std::size_t index) { | 426 | SharedPtr<WaitObject> object, std::size_t index) { |
| 427 | ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); | 427 | ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); |
| 428 | 428 | ||
| 429 | if (reason == ThreadWakeupReason::Timeout) { | 429 | if (reason == ThreadWakeupReason::Timeout) { |
| 430 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | 430 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); |
| @@ -502,7 +502,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr | |||
| 502 | } | 502 | } |
| 503 | 503 | ||
| 504 | thread->SetWaitObjects(std::move(objects)); | 504 | thread->SetWaitObjects(std::move(objects)); |
| 505 | thread->SetStatus(ThreadStatus::WaitSynchAny); | 505 | thread->SetStatus(ThreadStatus::WaitSynch); |
| 506 | 506 | ||
| 507 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 507 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 508 | thread->WakeAfterDelay(nano_seconds); | 508 | thread->WakeAfterDelay(nano_seconds); |
| @@ -518,16 +518,14 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand | |||
| 518 | LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); | 518 | LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); |
| 519 | 519 | ||
| 520 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | 520 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); |
| 521 | const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); | 521 | SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); |
| 522 | if (!thread) { | 522 | if (!thread) { |
| 523 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", | 523 | LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", |
| 524 | thread_handle); | 524 | thread_handle); |
| 525 | return ERR_INVALID_HANDLE; | 525 | return ERR_INVALID_HANDLE; |
| 526 | } | 526 | } |
| 527 | 527 | ||
| 528 | ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); | 528 | thread->CancelWait(); |
| 529 | thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED); | ||
| 530 | thread->ResumeFromWait(); | ||
| 531 | return RESULT_SUCCESS; | 529 | return RESULT_SUCCESS; |
| 532 | } | 530 | } |
| 533 | 531 | ||
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index ca52267b2..2abf9efca 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -101,8 +101,7 @@ void Thread::ResumeFromWait() { | |||
| 101 | ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects"); | 101 | ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects"); |
| 102 | 102 | ||
| 103 | switch (status) { | 103 | switch (status) { |
| 104 | case ThreadStatus::WaitSynchAll: | 104 | case ThreadStatus::WaitSynch: |
| 105 | case ThreadStatus::WaitSynchAny: | ||
| 106 | case ThreadStatus::WaitHLEEvent: | 105 | case ThreadStatus::WaitHLEEvent: |
| 107 | case ThreadStatus::WaitSleep: | 106 | case ThreadStatus::WaitSleep: |
| 108 | case ThreadStatus::WaitIPC: | 107 | case ThreadStatus::WaitIPC: |
| @@ -142,6 +141,12 @@ void Thread::ResumeFromWait() { | |||
| 142 | ChangeScheduler(); | 141 | ChangeScheduler(); |
| 143 | } | 142 | } |
| 144 | 143 | ||
| 144 | void Thread::CancelWait() { | ||
| 145 | ASSERT(GetStatus() == ThreadStatus::WaitSynch); | ||
| 146 | SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED); | ||
| 147 | ResumeFromWait(); | ||
| 148 | } | ||
| 149 | |||
| 145 | /** | 150 | /** |
| 146 | * Resets a thread context, making it ready to be scheduled and run by the CPU | 151 | * Resets a thread context, making it ready to be scheduled and run by the CPU |
| 147 | * @param context Thread context to reset | 152 | * @param context Thread context to reset |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 411a73b49..f07332f02 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -49,8 +49,7 @@ enum class ThreadStatus { | |||
| 49 | WaitHLEEvent, ///< Waiting for hle event to finish | 49 | WaitHLEEvent, ///< Waiting for hle event to finish |
| 50 | WaitSleep, ///< Waiting due to a SleepThread SVC | 50 | WaitSleep, ///< Waiting due to a SleepThread SVC |
| 51 | WaitIPC, ///< Waiting for the reply from an IPC request | 51 | WaitIPC, ///< Waiting for the reply from an IPC request |
| 52 | WaitSynchAny, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false | 52 | WaitSynch, ///< Waiting due to WaitSynchronization |
| 53 | WaitSynchAll, ///< Waiting due to WaitSynchronizationN with wait_all = true | ||
| 54 | WaitMutex, ///< Waiting due to an ArbitrateLock svc | 53 | WaitMutex, ///< Waiting due to an ArbitrateLock svc |
| 55 | WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc | 54 | WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc |
| 56 | WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc | 55 | WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc |
| @@ -169,11 +168,17 @@ public: | |||
| 169 | return tls_memory; | 168 | return tls_memory; |
| 170 | } | 169 | } |
| 171 | 170 | ||
| 172 | /** | 171 | /// Resumes a thread from waiting |
| 173 | * Resumes a thread from waiting | ||
| 174 | */ | ||
| 175 | void ResumeFromWait(); | 172 | void ResumeFromWait(); |
| 176 | 173 | ||
| 174 | /// Cancels a waiting operation that this thread may or may not be within. | ||
| 175 | /// | ||
| 176 | /// When the thread is within a waiting state, this will set the thread's | ||
| 177 | /// waiting result to signal a canceled wait. The function will then resume | ||
| 178 | /// this thread. | ||
| 179 | /// | ||
| 180 | void CancelWait(); | ||
| 181 | |||
| 177 | /** | 182 | /** |
| 178 | * Schedules an event to wake up the specified thread after the specified delay | 183 | * Schedules an event to wake up the specified thread after the specified delay |
| 179 | * @param nanoseconds The time this thread will be allowed to sleep for | 184 | * @param nanoseconds The time this thread will be allowed to sleep for |
| @@ -184,24 +189,27 @@ public: | |||
| 184 | void CancelWakeupTimer(); | 189 | void CancelWakeupTimer(); |
| 185 | 190 | ||
| 186 | /** | 191 | /** |
| 187 | * Sets the result after the thread awakens (from either WaitSynchronization SVC) | 192 | * Sets the result after the thread awakens (from svcWaitSynchronization) |
| 188 | * @param result Value to set to the returned result | 193 | * @param result Value to set to the returned result |
| 189 | */ | 194 | */ |
| 190 | void SetWaitSynchronizationResult(ResultCode result); | 195 | void SetWaitSynchronizationResult(ResultCode result); |
| 191 | 196 | ||
| 192 | /** | 197 | /** |
| 193 | * Sets the output parameter value after the thread awakens (from WaitSynchronizationN SVC only) | 198 | * Sets the output parameter value after the thread awakens (from svcWaitSynchronization) |
| 194 | * @param output Value to set to the output parameter | 199 | * @param output Value to set to the output parameter |
| 195 | */ | 200 | */ |
| 196 | void SetWaitSynchronizationOutput(s32 output); | 201 | void SetWaitSynchronizationOutput(s32 output); |
| 197 | 202 | ||
| 198 | /** | 203 | /** |
| 199 | * Retrieves the index that this particular object occupies in the list of objects | 204 | * Retrieves the index that this particular object occupies in the list of objects |
| 200 | * that the thread passed to WaitSynchronizationN, starting the search from the last element. | 205 | * that the thread passed to WaitSynchronization, starting the search from the last element. |
| 201 | * It is used to set the output value of WaitSynchronizationN when the thread is awakened. | 206 | * |
| 207 | * It is used to set the output index of WaitSynchronization when the thread is awakened. | ||
| 208 | * | ||
| 202 | * When a thread wakes up due to an object signal, the kernel will use the index of the last | 209 | * When a thread wakes up due to an object signal, the kernel will use the index of the last |
| 203 | * matching object in the wait objects list in case of having multiple instances of the same | 210 | * matching object in the wait objects list in case of having multiple instances of the same |
| 204 | * object in the list. | 211 | * object in the list. |
| 212 | * | ||
| 205 | * @param object Object to query the index of. | 213 | * @param object Object to query the index of. |
| 206 | */ | 214 | */ |
| 207 | s32 GetWaitObjectIndex(const WaitObject* object) const; | 215 | s32 GetWaitObjectIndex(const WaitObject* object) const; |
| @@ -238,13 +246,9 @@ public: | |||
| 238 | */ | 246 | */ |
| 239 | VAddr GetCommandBufferAddress() const; | 247 | VAddr GetCommandBufferAddress() const; |
| 240 | 248 | ||
| 241 | /** | 249 | /// Returns whether this thread is waiting on objects from a WaitSynchronization call. |
| 242 | * Returns whether this thread is waiting for all the objects in | 250 | bool IsSleepingOnWait() const { |
| 243 | * its wait list to become ready, as a result of a WaitSynchronizationN call | 251 | return status == ThreadStatus::WaitSynch; |
| 244 | * with wait_all = true. | ||
| 245 | */ | ||
| 246 | bool IsSleepingOnWaitAll() const { | ||
| 247 | return status == ThreadStatus::WaitSynchAll; | ||
| 248 | } | 252 | } |
| 249 | 253 | ||
| 250 | ThreadContext& GetContext() { | 254 | ThreadContext& GetContext() { |
| @@ -418,7 +422,7 @@ private: | |||
| 418 | Process* owner_process; | 422 | Process* owner_process; |
| 419 | 423 | ||
| 420 | /// Objects that the thread is waiting on, in the same order as they were | 424 | /// Objects that the thread is waiting on, in the same order as they were |
| 421 | /// passed to WaitSynchronization1/N. | 425 | /// passed to WaitSynchronization. |
| 422 | ThreadWaitObjects wait_objects; | 426 | ThreadWaitObjects wait_objects; |
| 423 | 427 | ||
| 424 | /// List of threads that are waiting for a mutex that is held by this thread. | 428 | /// List of threads that are waiting for a mutex that is held by this thread. |
| @@ -441,7 +445,7 @@ private: | |||
| 441 | Handle callback_handle = 0; | 445 | Handle callback_handle = 0; |
| 442 | 446 | ||
| 443 | /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread | 447 | /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread |
| 444 | /// was waiting via WaitSynchronizationN then the object will be the last object that became | 448 | /// was waiting via WaitSynchronization then the object will be the last object that became |
| 445 | /// available. In case of a timeout, the object will be nullptr. | 449 | /// available. In case of a timeout, the object will be nullptr. |
| 446 | WakeupCallback wakeup_callback; | 450 | WakeupCallback wakeup_callback; |
| 447 | 451 | ||
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index f0c0c12fc..48b13cfdd 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -62,7 +62,7 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { | |||
| 62 | return true; | 62 | return true; |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | VMManager::VMManager() { | 65 | VMManager::VMManager(Core::System& system) : system{system} { |
| 66 | // Default to assuming a 39-bit address space. This way we have a sane | 66 | // Default to assuming a 39-bit address space. This way we have a sane |
| 67 | // starting point with executables that don't provide metadata. | 67 | // starting point with executables that don't provide metadata. |
| 68 | Reset(FileSys::ProgramAddressSpaceType::Is39Bit); | 68 | Reset(FileSys::ProgramAddressSpaceType::Is39Bit); |
| @@ -111,7 +111,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, | |||
| 111 | VirtualMemoryArea& final_vma = vma_handle->second; | 111 | VirtualMemoryArea& final_vma = vma_handle->second; |
| 112 | ASSERT(final_vma.size == size); | 112 | ASSERT(final_vma.size == size); |
| 113 | 113 | ||
| 114 | auto& system = Core::System::GetInstance(); | ||
| 115 | system.ArmInterface(0).MapBackingMemory(target, size, block->data() + offset, | 114 | system.ArmInterface(0).MapBackingMemory(target, size, block->data() + offset, |
| 116 | VMAPermission::ReadWriteExecute); | 115 | VMAPermission::ReadWriteExecute); |
| 117 | system.ArmInterface(1).MapBackingMemory(target, size, block->data() + offset, | 116 | system.ArmInterface(1).MapBackingMemory(target, size, block->data() + offset, |
| @@ -140,7 +139,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me | |||
| 140 | VirtualMemoryArea& final_vma = vma_handle->second; | 139 | VirtualMemoryArea& final_vma = vma_handle->second; |
| 141 | ASSERT(final_vma.size == size); | 140 | ASSERT(final_vma.size == size); |
| 142 | 141 | ||
| 143 | auto& system = Core::System::GetInstance(); | ||
| 144 | system.ArmInterface(0).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); | 142 | system.ArmInterface(0).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); |
| 145 | system.ArmInterface(1).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); | 143 | system.ArmInterface(1).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); |
| 146 | system.ArmInterface(2).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); | 144 | system.ArmInterface(2).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute); |
| @@ -223,7 +221,6 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) { | |||
| 223 | 221 | ||
| 224 | ASSERT(FindVMA(target)->second.size >= size); | 222 | ASSERT(FindVMA(target)->second.size >= size); |
| 225 | 223 | ||
| 226 | auto& system = Core::System::GetInstance(); | ||
| 227 | system.ArmInterface(0).UnmapMemory(target, size); | 224 | system.ArmInterface(0).UnmapMemory(target, size); |
| 228 | system.ArmInterface(1).UnmapMemory(target, size); | 225 | system.ArmInterface(1).UnmapMemory(target, size); |
| 229 | system.ArmInterface(2).UnmapMemory(target, size); | 226 | system.ArmInterface(2).UnmapMemory(target, size); |
| @@ -376,7 +373,7 @@ ResultCode VMManager::UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 | |||
| 376 | Reprotect(src_vma_iter, VMAPermission::ReadWrite); | 373 | Reprotect(src_vma_iter, VMAPermission::ReadWrite); |
| 377 | 374 | ||
| 378 | if (dst_memory_state == MemoryState::ModuleCode) { | 375 | if (dst_memory_state == MemoryState::ModuleCode) { |
| 379 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); | 376 | system.InvalidateCpuInstructionCaches(); |
| 380 | } | 377 | } |
| 381 | 378 | ||
| 382 | return unmap_result; | 379 | return unmap_result; |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 288eb9450..ec84d9a70 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -14,6 +14,10 @@ | |||
| 14 | #include "core/hle/result.h" | 14 | #include "core/hle/result.h" |
| 15 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| 16 | 16 | ||
| 17 | namespace Core { | ||
| 18 | class System; | ||
| 19 | } | ||
| 20 | |||
| 17 | namespace FileSys { | 21 | namespace FileSys { |
| 18 | enum class ProgramAddressSpaceType : u8; | 22 | enum class ProgramAddressSpaceType : u8; |
| 19 | } | 23 | } |
| @@ -321,7 +325,7 @@ class VMManager final { | |||
| 321 | public: | 325 | public: |
| 322 | using VMAHandle = VMAMap::const_iterator; | 326 | using VMAHandle = VMAMap::const_iterator; |
| 323 | 327 | ||
| 324 | VMManager(); | 328 | explicit VMManager(Core::System& system); |
| 325 | ~VMManager(); | 329 | ~VMManager(); |
| 326 | 330 | ||
| 327 | /// Clears the address space map, re-initializing with a single free area. | 331 | /// Clears the address space map, re-initializing with a single free area. |
| @@ -712,5 +716,7 @@ private: | |||
| 712 | // The end of the currently allocated heap. This is not an inclusive | 716 | // The end of the currently allocated heap. This is not an inclusive |
| 713 | // end of the range. This is essentially 'base_address + current_size'. | 717 | // end of the range. This is essentially 'base_address + current_size'. |
| 714 | VAddr heap_end = 0; | 718 | VAddr heap_end = 0; |
| 719 | |||
| 720 | Core::System& system; | ||
| 715 | }; | 721 | }; |
| 716 | } // namespace Kernel | 722 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index c8eaf9488..0e96ba872 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp | |||
| @@ -38,8 +38,7 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const { | |||
| 38 | const ThreadStatus thread_status = thread->GetStatus(); | 38 | const ThreadStatus thread_status = thread->GetStatus(); |
| 39 | 39 | ||
| 40 | // The list of waiting threads must not contain threads that are not waiting to be awakened. | 40 | // The list of waiting threads must not contain threads that are not waiting to be awakened. |
| 41 | ASSERT_MSG(thread_status == ThreadStatus::WaitSynchAny || | 41 | ASSERT_MSG(thread_status == ThreadStatus::WaitSynch || |
| 42 | thread_status == ThreadStatus::WaitSynchAll || | ||
| 43 | thread_status == ThreadStatus::WaitHLEEvent, | 42 | thread_status == ThreadStatus::WaitHLEEvent, |
| 44 | "Inconsistent thread statuses in waiting_threads"); | 43 | "Inconsistent thread statuses in waiting_threads"); |
| 45 | 44 | ||
| @@ -49,10 +48,10 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const { | |||
| 49 | if (ShouldWait(thread.get())) | 48 | if (ShouldWait(thread.get())) |
| 50 | continue; | 49 | continue; |
| 51 | 50 | ||
| 52 | // A thread is ready to run if it's either in ThreadStatus::WaitSynchAny or | 51 | // A thread is ready to run if it's either in ThreadStatus::WaitSynch |
| 53 | // in ThreadStatus::WaitSynchAll and the rest of the objects it is waiting on are ready. | 52 | // and the rest of the objects it is waiting on are ready. |
| 54 | bool ready_to_run = true; | 53 | bool ready_to_run = true; |
| 55 | if (thread_status == ThreadStatus::WaitSynchAll) { | 54 | if (thread_status == ThreadStatus::WaitSynch) { |
| 56 | ready_to_run = thread->AllWaitObjectsReady(); | 55 | ready_to_run = thread->AllWaitObjectsReady(); |
| 57 | } | 56 | } |
| 58 | 57 | ||
| @@ -68,33 +67,35 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const { | |||
| 68 | void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) { | 67 | void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) { |
| 69 | ASSERT(!ShouldWait(thread.get())); | 68 | ASSERT(!ShouldWait(thread.get())); |
| 70 | 69 | ||
| 71 | if (!thread) | 70 | if (!thread) { |
| 72 | return; | 71 | return; |
| 72 | } | ||
| 73 | 73 | ||
| 74 | if (!thread->IsSleepingOnWaitAll()) { | 74 | if (thread->IsSleepingOnWait()) { |
| 75 | Acquire(thread.get()); | ||
| 76 | } else { | ||
| 77 | for (const auto& object : thread->GetWaitObjects()) { | 75 | for (const auto& object : thread->GetWaitObjects()) { |
| 78 | ASSERT(!object->ShouldWait(thread.get())); | 76 | ASSERT(!object->ShouldWait(thread.get())); |
| 79 | object->Acquire(thread.get()); | 77 | object->Acquire(thread.get()); |
| 80 | } | 78 | } |
| 79 | } else { | ||
| 80 | Acquire(thread.get()); | ||
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | const std::size_t index = thread->GetWaitObjectIndex(this); | 83 | const std::size_t index = thread->GetWaitObjectIndex(this); |
| 84 | 84 | ||
| 85 | for (const auto& object : thread->GetWaitObjects()) | 85 | for (const auto& object : thread->GetWaitObjects()) { |
| 86 | object->RemoveWaitingThread(thread.get()); | 86 | object->RemoveWaitingThread(thread.get()); |
| 87 | } | ||
| 87 | thread->ClearWaitObjects(); | 88 | thread->ClearWaitObjects(); |
| 88 | 89 | ||
| 89 | thread->CancelWakeupTimer(); | 90 | thread->CancelWakeupTimer(); |
| 90 | 91 | ||
| 91 | bool resume = true; | 92 | bool resume = true; |
| 92 | 93 | if (thread->HasWakeupCallback()) { | |
| 93 | if (thread->HasWakeupCallback()) | ||
| 94 | resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index); | 94 | resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index); |
| 95 | 95 | } | |
| 96 | if (resume) | 96 | if (resume) { |
| 97 | thread->ResumeFromWait(); | 97 | thread->ResumeFromWait(); |
| 98 | } | ||
| 98 | } | 99 | } |
| 99 | 100 | ||
| 100 | void WaitObject::WakeupAllWaitingThreads() { | 101 | void WaitObject::WakeupAllWaitingThreads() { |
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 @@ | |||
| 22 | #include "core/hle/service/am/applets/applets.h" | 22 | #include "core/hle/service/am/applets/applets.h" |
| 23 | #include "core/hle/service/am/applets/profile_select.h" | 23 | #include "core/hle/service/am/applets/profile_select.h" |
| 24 | #include "core/hle/service/am/applets/software_keyboard.h" | 24 | #include "core/hle/service/am/applets/software_keyboard.h" |
| 25 | #include "core/hle/service/am/applets/stub_applet.h" | ||
| 26 | #include "core/hle/service/am/applets/web_browser.h" | 25 | #include "core/hle/service/am/applets/web_browser.h" |
| 27 | #include "core/hle/service/am/idle.h" | 26 | #include "core/hle/service/am/idle.h" |
| 28 | #include "core/hle/service/am/omm.h" | 27 | #include "core/hle/service/am/omm.h" |
| @@ -42,12 +41,6 @@ constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2}; | |||
| 42 | constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3}; | 41 | constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3}; |
| 43 | constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; | 42 | constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; |
| 44 | 43 | ||
| 45 | enum class AppletId : u32 { | ||
| 46 | ProfileSelect = 0x10, | ||
| 47 | SoftwareKeyboard = 0x11, | ||
| 48 | LibAppletOff = 0x17, | ||
| 49 | }; | ||
| 50 | |||
| 51 | constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; | 44 | constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; |
| 52 | 45 | ||
| 53 | struct LaunchParameters { | 46 | struct LaunchParameters { |
| @@ -886,30 +879,16 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple | |||
| 886 | 879 | ||
| 887 | ILibraryAppletCreator::~ILibraryAppletCreator() = default; | 880 | ILibraryAppletCreator::~ILibraryAppletCreator() = default; |
| 888 | 881 | ||
| 889 | static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) { | ||
| 890 | switch (id) { | ||
| 891 | case AppletId::ProfileSelect: | ||
| 892 | return std::make_shared<Applets::ProfileSelect>(); | ||
| 893 | case AppletId::SoftwareKeyboard: | ||
| 894 | return std::make_shared<Applets::SoftwareKeyboard>(); | ||
| 895 | case AppletId::LibAppletOff: | ||
| 896 | return std::make_shared<Applets::WebBrowser>(); | ||
| 897 | default: | ||
| 898 | LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!", | ||
| 899 | static_cast<u32>(id)); | ||
| 900 | return std::make_shared<Applets::StubApplet>(); | ||
| 901 | } | ||
| 902 | } | ||
| 903 | |||
| 904 | void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { | 882 | void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) { |
| 905 | IPC::RequestParser rp{ctx}; | 883 | IPC::RequestParser rp{ctx}; |
| 906 | const auto applet_id = rp.PopRaw<AppletId>(); | 884 | const auto applet_id = rp.PopRaw<Applets::AppletId>(); |
| 907 | const auto applet_mode = rp.PopRaw<u32>(); | 885 | const auto applet_mode = rp.PopRaw<u32>(); |
| 908 | 886 | ||
| 909 | LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", | 887 | LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", |
| 910 | static_cast<u32>(applet_id), applet_mode); | 888 | static_cast<u32>(applet_id), applet_mode); |
| 911 | 889 | ||
| 912 | const auto applet = GetAppletFromId(applet_id); | 890 | const auto& applet_manager{Core::System::GetInstance().GetAppletManager()}; |
| 891 | const auto applet = applet_manager.GetApplet(applet_id); | ||
| 913 | 892 | ||
| 914 | if (applet == nullptr) { | 893 | if (applet == nullptr) { |
| 915 | LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id)); | 894 | 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 @@ | |||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/frontend/applets/error.h" | ||
| 9 | #include "core/frontend/applets/general_frontend.h" | ||
| 10 | #include "core/frontend/applets/profile_select.h" | ||
| 11 | #include "core/frontend/applets/software_keyboard.h" | ||
| 12 | #include "core/frontend/applets/web_browser.h" | ||
| 8 | #include "core/hle/kernel/readable_event.h" | 13 | #include "core/hle/kernel/readable_event.h" |
| 9 | #include "core/hle/kernel/server_session.h" | 14 | #include "core/hle/kernel/server_session.h" |
| 10 | #include "core/hle/kernel/writable_event.h" | 15 | #include "core/hle/kernel/writable_event.h" |
| 11 | #include "core/hle/service/am/am.h" | 16 | #include "core/hle/service/am/am.h" |
| 12 | #include "core/hle/service/am/applets/applets.h" | 17 | #include "core/hle/service/am/applets/applets.h" |
| 18 | #include "core/hle/service/am/applets/error.h" | ||
| 19 | #include "core/hle/service/am/applets/general_backend.h" | ||
| 20 | #include "core/hle/service/am/applets/profile_select.h" | ||
| 21 | #include "core/hle/service/am/applets/software_keyboard.h" | ||
| 22 | #include "core/hle/service/am/applets/web_browser.h" | ||
| 13 | 23 | ||
| 14 | namespace Service::AM::Applets { | 24 | namespace Service::AM::Applets { |
| 15 | 25 | ||
| @@ -111,4 +121,76 @@ void Applet::Initialize() { | |||
| 111 | initialized = true; | 121 | initialized = true; |
| 112 | } | 122 | } |
| 113 | 123 | ||
| 124 | AppletManager::AppletManager() = default; | ||
| 125 | |||
| 126 | AppletManager::~AppletManager() = default; | ||
| 127 | |||
| 128 | void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { | ||
| 129 | if (set.error != nullptr) | ||
| 130 | frontend.error = std::move(set.error); | ||
| 131 | if (set.photo_viewer != nullptr) | ||
| 132 | frontend.photo_viewer = std::move(set.photo_viewer); | ||
| 133 | if (set.profile_select != nullptr) | ||
| 134 | frontend.profile_select = std::move(set.profile_select); | ||
| 135 | if (set.software_keyboard != nullptr) | ||
| 136 | frontend.software_keyboard = std::move(set.software_keyboard); | ||
| 137 | if (set.web_browser != nullptr) | ||
| 138 | frontend.web_browser = std::move(set.web_browser); | ||
| 139 | } | ||
| 140 | |||
| 141 | void AppletManager::SetDefaultAppletFrontendSet() { | ||
| 142 | frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); | ||
| 143 | frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>(); | ||
| 144 | frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>(); | ||
| 145 | frontend.software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>(); | ||
| 146 | frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); | ||
| 147 | } | ||
| 148 | |||
| 149 | void AppletManager::SetDefaultAppletsIfMissing() { | ||
| 150 | if (frontend.error == nullptr) { | ||
| 151 | frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); | ||
| 152 | } | ||
| 153 | |||
| 154 | if (frontend.photo_viewer == nullptr) { | ||
| 155 | frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>(); | ||
| 156 | } | ||
| 157 | |||
| 158 | if (frontend.profile_select == nullptr) { | ||
| 159 | frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>(); | ||
| 160 | } | ||
| 161 | |||
| 162 | if (frontend.software_keyboard == nullptr) { | ||
| 163 | frontend.software_keyboard = | ||
| 164 | std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>(); | ||
| 165 | } | ||
| 166 | |||
| 167 | if (frontend.web_browser == nullptr) { | ||
| 168 | frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | void AppletManager::ClearAll() { | ||
| 173 | frontend = {}; | ||
| 174 | } | ||
| 175 | |||
| 176 | std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const { | ||
| 177 | switch (id) { | ||
| 178 | case AppletId::Error: | ||
| 179 | return std::make_shared<Error>(*frontend.error); | ||
| 180 | case AppletId::ProfileSelect: | ||
| 181 | return std::make_shared<ProfileSelect>(*frontend.profile_select); | ||
| 182 | case AppletId::SoftwareKeyboard: | ||
| 183 | return std::make_shared<SoftwareKeyboard>(*frontend.software_keyboard); | ||
| 184 | case AppletId::PhotoViewer: | ||
| 185 | return std::make_shared<PhotoViewer>(*frontend.photo_viewer); | ||
| 186 | case AppletId::LibAppletOff: | ||
| 187 | return std::make_shared<WebBrowser>(*frontend.web_browser); | ||
| 188 | default: | ||
| 189 | UNIMPLEMENTED_MSG( | ||
| 190 | "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.", | ||
| 191 | static_cast<u8>(id)); | ||
| 192 | return std::make_shared<StubApplet>(); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 114 | } // namespace Service::AM::Applets | 196 | } // 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 @@ | |||
| 12 | 12 | ||
| 13 | union ResultCode; | 13 | union ResultCode; |
| 14 | 14 | ||
| 15 | namespace Core::Frontend { | ||
| 16 | class ErrorApplet; | ||
| 17 | class PhotoViewerApplet; | ||
| 18 | class ProfileSelectApplet; | ||
| 19 | class SoftwareKeyboardApplet; | ||
| 20 | class WebBrowserApplet; | ||
| 21 | } // namespace Core::Frontend | ||
| 22 | |||
| 15 | namespace Service::AM { | 23 | namespace Service::AM { |
| 16 | 24 | ||
| 17 | class IStorage; | 25 | class IStorage; |
| 18 | 26 | ||
| 19 | namespace Applets { | 27 | namespace Applets { |
| 20 | 28 | ||
| 29 | enum class AppletId : u32 { | ||
| 30 | OverlayDisplay = 0x02, | ||
| 31 | QLaunch = 0x03, | ||
| 32 | Starter = 0x04, | ||
| 33 | Auth = 0x0A, | ||
| 34 | Cabinet = 0x0B, | ||
| 35 | Controller = 0x0C, | ||
| 36 | DataErase = 0x0D, | ||
| 37 | Error = 0x0E, | ||
| 38 | NetConnect = 0x0F, | ||
| 39 | ProfileSelect = 0x10, | ||
| 40 | SoftwareKeyboard = 0x11, | ||
| 41 | MiiEdit = 0x12, | ||
| 42 | LibAppletWeb = 0x13, | ||
| 43 | LibAppletShop = 0x14, | ||
| 44 | PhotoViewer = 0x15, | ||
| 45 | Settings = 0x16, | ||
| 46 | LibAppletOff = 0x17, | ||
| 47 | LibAppletWhitelisted = 0x18, | ||
| 48 | LibAppletAuth = 0x19, | ||
| 49 | MyPage = 0x1A, | ||
| 50 | }; | ||
| 51 | |||
| 21 | class AppletDataBroker final { | 52 | class AppletDataBroker final { |
| 22 | public: | 53 | public: |
| 23 | AppletDataBroker(); | 54 | AppletDataBroker(); |
| @@ -105,5 +136,29 @@ protected: | |||
| 105 | bool initialized = false; | 136 | bool initialized = false; |
| 106 | }; | 137 | }; |
| 107 | 138 | ||
| 139 | struct AppletFrontendSet { | ||
| 140 | std::unique_ptr<Core::Frontend::ErrorApplet> error; | ||
| 141 | std::unique_ptr<Core::Frontend::PhotoViewerApplet> photo_viewer; | ||
| 142 | std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_select; | ||
| 143 | std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; | ||
| 144 | std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser; | ||
| 145 | }; | ||
| 146 | |||
| 147 | class AppletManager { | ||
| 148 | public: | ||
| 149 | AppletManager(); | ||
| 150 | ~AppletManager(); | ||
| 151 | |||
| 152 | void SetAppletFrontendSet(AppletFrontendSet set); | ||
| 153 | void SetDefaultAppletFrontendSet(); | ||
| 154 | void SetDefaultAppletsIfMissing(); | ||
| 155 | void ClearAll(); | ||
| 156 | |||
| 157 | std::shared_ptr<Applet> GetApplet(AppletId id) const; | ||
| 158 | |||
| 159 | private: | ||
| 160 | AppletFrontendSet frontend; | ||
| 161 | }; | ||
| 162 | |||
| 108 | } // namespace Applets | 163 | } // namespace Applets |
| 109 | } // namespace Service::AM | 164 | } // 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 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <cstring> | ||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "common/string_util.h" | ||
| 10 | #include "core/core.h" | ||
| 11 | #include "core/frontend/applets/error.h" | ||
| 12 | #include "core/hle/service/am/am.h" | ||
| 13 | #include "core/hle/service/am/applets/error.h" | ||
| 14 | |||
| 15 | namespace Service::AM::Applets { | ||
| 16 | |||
| 17 | #pragma pack(push, 4) | ||
| 18 | struct ShowError { | ||
| 19 | u8 mode; | ||
| 20 | bool jump; | ||
| 21 | INSERT_PADDING_BYTES(4); | ||
| 22 | bool use_64bit_error_code; | ||
| 23 | INSERT_PADDING_BYTES(1); | ||
| 24 | u64 error_code_64; | ||
| 25 | u32 error_code_32; | ||
| 26 | }; | ||
| 27 | static_assert(sizeof(ShowError) == 0x14, "ShowError has incorrect size."); | ||
| 28 | #pragma pack(pop) | ||
| 29 | |||
| 30 | struct ShowErrorRecord { | ||
| 31 | u8 mode; | ||
| 32 | bool jump; | ||
| 33 | INSERT_PADDING_BYTES(6); | ||
| 34 | u64 error_code_64; | ||
| 35 | u64 posix_time; | ||
| 36 | }; | ||
| 37 | static_assert(sizeof(ShowErrorRecord) == 0x18, "ShowErrorRecord has incorrect size."); | ||
| 38 | |||
| 39 | struct SystemErrorArg { | ||
| 40 | u8 mode; | ||
| 41 | bool jump; | ||
| 42 | INSERT_PADDING_BYTES(6); | ||
| 43 | u64 error_code_64; | ||
| 44 | std::array<char, 8> language_code; | ||
| 45 | std::array<char, 0x800> main_text; | ||
| 46 | std::array<char, 0x800> detail_text; | ||
| 47 | }; | ||
| 48 | static_assert(sizeof(SystemErrorArg) == 0x1018, "SystemErrorArg has incorrect size."); | ||
| 49 | |||
| 50 | struct ApplicationErrorArg { | ||
| 51 | u8 mode; | ||
| 52 | bool jump; | ||
| 53 | INSERT_PADDING_BYTES(6); | ||
| 54 | u32 error_code; | ||
| 55 | std::array<char, 8> language_code; | ||
| 56 | std::array<char, 0x800> main_text; | ||
| 57 | std::array<char, 0x800> detail_text; | ||
| 58 | }; | ||
| 59 | static_assert(sizeof(ApplicationErrorArg) == 0x1014, "ApplicationErrorArg has incorrect size."); | ||
| 60 | |||
| 61 | union Error::ErrorArguments { | ||
| 62 | ShowError error; | ||
| 63 | ShowErrorRecord error_record; | ||
| 64 | SystemErrorArg system_error; | ||
| 65 | ApplicationErrorArg application_error; | ||
| 66 | }; | ||
| 67 | |||
| 68 | namespace { | ||
| 69 | template <typename T> | ||
| 70 | void CopyArgumentData(const std::vector<u8>& data, T& variable) { | ||
| 71 | ASSERT(data.size() >= sizeof(T)); | ||
| 72 | std::memcpy(&variable, data.data(), sizeof(T)); | ||
| 73 | } | ||
| 74 | |||
| 75 | ResultCode Decode64BitError(u64 error) { | ||
| 76 | const auto description = (error >> 32) & 0x1FFF; | ||
| 77 | auto module = error & 0x3FF; | ||
| 78 | if (module >= 2000) | ||
| 79 | module -= 2000; | ||
| 80 | module &= 0x1FF; | ||
| 81 | return {static_cast<ErrorModule>(module), static_cast<u32>(description)}; | ||
| 82 | } | ||
| 83 | |||
| 84 | } // Anonymous namespace | ||
| 85 | |||
| 86 | Error::Error(const Core::Frontend::ErrorApplet& frontend) : frontend(frontend) {} | ||
| 87 | |||
| 88 | Error::~Error() = default; | ||
| 89 | |||
| 90 | void Error::Initialize() { | ||
| 91 | Applet::Initialize(); | ||
| 92 | args = std::make_unique<ErrorArguments>(); | ||
| 93 | complete = false; | ||
| 94 | |||
| 95 | const auto storage = broker.PopNormalDataToApplet(); | ||
| 96 | ASSERT(storage != nullptr); | ||
| 97 | const auto data = storage->GetData(); | ||
| 98 | |||
| 99 | ASSERT(!data.empty()); | ||
| 100 | std::memcpy(&mode, data.data(), sizeof(ErrorAppletMode)); | ||
| 101 | |||
| 102 | switch (mode) { | ||
| 103 | case ErrorAppletMode::ShowError: | ||
| 104 | CopyArgumentData(data, args->error); | ||
| 105 | if (args->error.use_64bit_error_code) { | ||
| 106 | error_code = Decode64BitError(args->error.error_code_64); | ||
| 107 | } else { | ||
| 108 | error_code = ResultCode(args->error.error_code_32); | ||
| 109 | } | ||
| 110 | break; | ||
| 111 | case ErrorAppletMode::ShowSystemError: | ||
| 112 | CopyArgumentData(data, args->system_error); | ||
| 113 | error_code = ResultCode(Decode64BitError(args->system_error.error_code_64)); | ||
| 114 | break; | ||
| 115 | case ErrorAppletMode::ShowApplicationError: | ||
| 116 | CopyArgumentData(data, args->application_error); | ||
| 117 | error_code = ResultCode(args->application_error.error_code); | ||
| 118 | break; | ||
| 119 | case ErrorAppletMode::ShowErrorRecord: | ||
| 120 | CopyArgumentData(data, args->error_record); | ||
| 121 | error_code = Decode64BitError(args->error_record.error_code_64); | ||
| 122 | break; | ||
| 123 | default: | ||
| 124 | UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", static_cast<u8>(mode)); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | bool Error::TransactionComplete() const { | ||
| 129 | return complete; | ||
| 130 | } | ||
| 131 | |||
| 132 | ResultCode Error::GetStatus() const { | ||
| 133 | return RESULT_SUCCESS; | ||
| 134 | } | ||
| 135 | |||
| 136 | void Error::ExecuteInteractive() { | ||
| 137 | UNREACHABLE_MSG("Unexpected interactive applet data!"); | ||
| 138 | } | ||
| 139 | |||
| 140 | void Error::Execute() { | ||
| 141 | if (complete) { | ||
| 142 | return; | ||
| 143 | } | ||
| 144 | |||
| 145 | const auto callback = [this] { DisplayCompleted(); }; | ||
| 146 | |||
| 147 | switch (mode) { | ||
| 148 | case ErrorAppletMode::ShowError: | ||
| 149 | frontend.ShowError(error_code, callback); | ||
| 150 | break; | ||
| 151 | case ErrorAppletMode::ShowSystemError: | ||
| 152 | case ErrorAppletMode::ShowApplicationError: { | ||
| 153 | const auto system = mode == ErrorAppletMode::ShowSystemError; | ||
| 154 | const auto& main_text = | ||
| 155 | system ? args->system_error.main_text : args->application_error.main_text; | ||
| 156 | const auto& detail_text = | ||
| 157 | system ? args->system_error.detail_text : args->application_error.detail_text; | ||
| 158 | |||
| 159 | frontend.ShowCustomErrorText( | ||
| 160 | error_code, | ||
| 161 | Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()), | ||
| 162 | Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()), | ||
| 163 | callback); | ||
| 164 | break; | ||
| 165 | } | ||
| 166 | case ErrorAppletMode::ShowErrorRecord: | ||
| 167 | frontend.ShowErrorWithTimestamp( | ||
| 168 | error_code, std::chrono::seconds{args->error_record.posix_time}, callback); | ||
| 169 | break; | ||
| 170 | default: | ||
| 171 | UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", static_cast<u8>(mode)); | ||
| 172 | DisplayCompleted(); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | void Error::DisplayCompleted() { | ||
| 177 | complete = true; | ||
| 178 | broker.PushNormalDataFromApplet(IStorage{{}}); | ||
| 179 | broker.SignalStateChanged(); | ||
| 180 | } | ||
| 181 | |||
| 182 | } // 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 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/result.h" | ||
| 8 | #include "core/hle/service/am/applets/applets.h" | ||
| 9 | |||
| 10 | namespace Service::AM::Applets { | ||
| 11 | |||
| 12 | enum class ErrorAppletMode : u8 { | ||
| 13 | ShowError = 0, | ||
| 14 | ShowSystemError = 1, | ||
| 15 | ShowApplicationError = 2, | ||
| 16 | ShowEula = 3, | ||
| 17 | ShowErrorPctl = 4, | ||
| 18 | ShowErrorRecord = 5, | ||
| 19 | ShowUpdateEula = 8, | ||
| 20 | }; | ||
| 21 | |||
| 22 | class Error final : public Applet { | ||
| 23 | public: | ||
| 24 | explicit Error(const Core::Frontend::ErrorApplet& frontend); | ||
| 25 | ~Error() override; | ||
| 26 | |||
| 27 | void Initialize() override; | ||
| 28 | |||
| 29 | bool TransactionComplete() const override; | ||
| 30 | ResultCode GetStatus() const override; | ||
| 31 | void ExecuteInteractive() override; | ||
| 32 | void Execute() override; | ||
| 33 | |||
| 34 | void DisplayCompleted(); | ||
| 35 | |||
| 36 | private: | ||
| 37 | union ErrorArguments; | ||
| 38 | |||
| 39 | const Core::Frontend::ErrorApplet& frontend; | ||
| 40 | ResultCode error_code = RESULT_SUCCESS; | ||
| 41 | ErrorAppletMode mode = ErrorAppletMode::ShowError; | ||
| 42 | std::unique_ptr<ErrorArguments> args; | ||
| 43 | |||
| 44 | bool complete = false; | ||
| 45 | }; | ||
| 46 | |||
| 47 | } // 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 @@ | |||
| 4 | 4 | ||
| 5 | #include <string> | 5 | #include <string> |
| 6 | 6 | ||
| 7 | #include "common/assert.h" | ||
| 7 | #include "common/hex_util.h" | 8 | #include "common/hex_util.h" |
| 8 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "core/core.h" | ||
| 11 | #include "core/frontend/applets/general_frontend.h" | ||
| 12 | #include "core/hle/kernel/process.h" | ||
| 9 | #include "core/hle/result.h" | 13 | #include "core/hle/result.h" |
| 10 | #include "core/hle/service/am/am.h" | 14 | #include "core/hle/service/am/am.h" |
| 11 | #include "core/hle/service/am/applets/stub_applet.h" | 15 | #include "core/hle/service/am/applets/general_backend.h" |
| 12 | 16 | ||
| 13 | namespace Service::AM::Applets { | 17 | namespace Service::AM::Applets { |
| 14 | 18 | ||
| @@ -30,6 +34,55 @@ static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) { | |||
| 30 | } | 34 | } |
| 31 | } | 35 | } |
| 32 | 36 | ||
| 37 | PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {} | ||
| 38 | |||
| 39 | PhotoViewer::~PhotoViewer() = default; | ||
| 40 | |||
| 41 | void PhotoViewer::Initialize() { | ||
| 42 | Applet::Initialize(); | ||
| 43 | complete = false; | ||
| 44 | |||
| 45 | const auto storage = broker.PopNormalDataToApplet(); | ||
| 46 | ASSERT(storage != nullptr); | ||
| 47 | const auto data = storage->GetData(); | ||
| 48 | ASSERT(!data.empty()); | ||
| 49 | mode = static_cast<PhotoViewerAppletMode>(data[0]); | ||
| 50 | } | ||
| 51 | |||
| 52 | bool PhotoViewer::TransactionComplete() const { | ||
| 53 | return complete; | ||
| 54 | } | ||
| 55 | |||
| 56 | ResultCode PhotoViewer::GetStatus() const { | ||
| 57 | return RESULT_SUCCESS; | ||
| 58 | } | ||
| 59 | |||
| 60 | void PhotoViewer::ExecuteInteractive() { | ||
| 61 | UNREACHABLE_MSG("Unexpected interactive applet data."); | ||
| 62 | } | ||
| 63 | |||
| 64 | void PhotoViewer::Execute() { | ||
| 65 | if (complete) | ||
| 66 | return; | ||
| 67 | |||
| 68 | const auto callback = [this] { ViewFinished(); }; | ||
| 69 | switch (mode) { | ||
| 70 | case PhotoViewerAppletMode::CurrentApp: | ||
| 71 | frontend.ShowPhotosForApplication(Core::CurrentProcess()->GetTitleID(), callback); | ||
| 72 | break; | ||
| 73 | case PhotoViewerAppletMode::AllApps: | ||
| 74 | frontend.ShowAllPhotos(callback); | ||
| 75 | break; | ||
| 76 | default: | ||
| 77 | UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", static_cast<u8>(mode)); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | void PhotoViewer::ViewFinished() { | ||
| 82 | broker.PushNormalDataFromApplet(IStorage{{}}); | ||
| 83 | broker.SignalStateChanged(); | ||
| 84 | } | ||
| 85 | |||
| 33 | StubApplet::StubApplet() = default; | 86 | StubApplet::StubApplet() = default; |
| 34 | 87 | ||
| 35 | StubApplet::~StubApplet() = default; | 88 | StubApplet::~StubApplet() = default; |
| @@ -67,4 +120,5 @@ void StubApplet::Execute() { | |||
| 67 | broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)}); | 120 | broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)}); |
| 68 | broker.SignalStateChanged(); | 121 | broker.SignalStateChanged(); |
| 69 | } | 122 | } |
| 123 | |||
| 70 | } // namespace Service::AM::Applets | 124 | } // 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 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/am/applets/applets.h" | ||
| 8 | |||
| 9 | namespace Service::AM::Applets { | ||
| 10 | |||
| 11 | enum class PhotoViewerAppletMode : u8 { | ||
| 12 | CurrentApp = 0, | ||
| 13 | AllApps = 1, | ||
| 14 | }; | ||
| 15 | |||
| 16 | class PhotoViewer final : public Applet { | ||
| 17 | public: | ||
| 18 | explicit PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend); | ||
| 19 | ~PhotoViewer() override; | ||
| 20 | |||
| 21 | void Initialize() override; | ||
| 22 | bool TransactionComplete() const override; | ||
| 23 | ResultCode GetStatus() const override; | ||
| 24 | void ExecuteInteractive() override; | ||
| 25 | void Execute() override; | ||
| 26 | |||
| 27 | void ViewFinished(); | ||
| 28 | |||
| 29 | private: | ||
| 30 | const Core::Frontend::PhotoViewerApplet& frontend; | ||
| 31 | bool complete = false; | ||
| 32 | PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp; | ||
| 33 | }; | ||
| 34 | |||
| 35 | class StubApplet final : public Applet { | ||
| 36 | public: | ||
| 37 | StubApplet(); | ||
| 38 | ~StubApplet() override; | ||
| 39 | |||
| 40 | void Initialize() override; | ||
| 41 | |||
| 42 | bool TransactionComplete() const override; | ||
| 43 | ResultCode GetStatus() const override; | ||
| 44 | void ExecuteInteractive() override; | ||
| 45 | void Execute() override; | ||
| 46 | }; | ||
| 47 | |||
| 48 | } // 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 { | |||
| 15 | 15 | ||
| 16 | constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; | 16 | constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; |
| 17 | 17 | ||
| 18 | ProfileSelect::ProfileSelect() = default; | 18 | ProfileSelect::ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend) |
| 19 | : frontend(frontend) {} | ||
| 20 | |||
| 19 | ProfileSelect::~ProfileSelect() = default; | 21 | ProfileSelect::~ProfileSelect() = default; |
| 20 | 22 | ||
| 21 | void ProfileSelect::Initialize() { | 23 | void ProfileSelect::Initialize() { |
| @@ -51,8 +53,6 @@ void ProfileSelect::Execute() { | |||
| 51 | return; | 53 | return; |
| 52 | } | 54 | } |
| 53 | 55 | ||
| 54 | const auto& frontend{Core::System::GetInstance().GetProfileSelector()}; | ||
| 55 | |||
| 56 | frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); }); | 56 | frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); }); |
| 57 | } | 57 | } |
| 58 | 58 | ||
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 | |||
| 28 | 28 | ||
| 29 | class ProfileSelect final : public Applet { | 29 | class ProfileSelect final : public Applet { |
| 30 | public: | 30 | public: |
| 31 | ProfileSelect(); | 31 | explicit ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend); |
| 32 | ~ProfileSelect() override; | 32 | ~ProfileSelect() override; |
| 33 | 33 | ||
| 34 | void Initialize() override; | 34 | void Initialize() override; |
| @@ -41,6 +41,8 @@ public: | |||
| 41 | void SelectionComplete(std::optional<Account::UUID> uuid); | 41 | void SelectionComplete(std::optional<Account::UUID> uuid); |
| 42 | 42 | ||
| 43 | private: | 43 | private: |
| 44 | const Core::Frontend::ProfileSelectApplet& frontend; | ||
| 45 | |||
| 44 | UserSelectionConfig config; | 46 | UserSelectionConfig config; |
| 45 | bool complete = false; | 47 | bool complete = false; |
| 46 | ResultCode status = RESULT_SUCCESS; | 48 | 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( | |||
| 39 | return params; | 39 | return params; |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | SoftwareKeyboard::SoftwareKeyboard() = default; | 42 | SoftwareKeyboard::SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend) |
| 43 | : frontend(frontend) {} | ||
| 43 | 44 | ||
| 44 | SoftwareKeyboard::~SoftwareKeyboard() = default; | 45 | SoftwareKeyboard::~SoftwareKeyboard() = default; |
| 45 | 46 | ||
| @@ -90,8 +91,6 @@ void SoftwareKeyboard::ExecuteInteractive() { | |||
| 90 | if (status == INTERACTIVE_STATUS_OK) { | 91 | if (status == INTERACTIVE_STATUS_OK) { |
| 91 | complete = true; | 92 | complete = true; |
| 92 | } else { | 93 | } else { |
| 93 | const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()}; | ||
| 94 | |||
| 95 | std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; | 94 | std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; |
| 96 | std::memcpy(string.data(), data.data() + 4, string.size() * 2); | 95 | std::memcpy(string.data(), data.data() + 4, string.size() * 2); |
| 97 | frontend.SendTextCheckDialog( | 96 | frontend.SendTextCheckDialog( |
| @@ -106,8 +105,6 @@ void SoftwareKeyboard::Execute() { | |||
| 106 | return; | 105 | return; |
| 107 | } | 106 | } |
| 108 | 107 | ||
| 109 | const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()}; | ||
| 110 | |||
| 111 | const auto parameters = ConvertToFrontendParameters(config, initial_text); | 108 | const auto parameters = ConvertToFrontendParameters(config, initial_text); |
| 112 | 109 | ||
| 113 | frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); }, | 110 | 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 | |||
| 55 | 55 | ||
| 56 | class SoftwareKeyboard final : public Applet { | 56 | class SoftwareKeyboard final : public Applet { |
| 57 | public: | 57 | public: |
| 58 | SoftwareKeyboard(); | 58 | explicit SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend); |
| 59 | ~SoftwareKeyboard() override; | 59 | ~SoftwareKeyboard() override; |
| 60 | 60 | ||
| 61 | void Initialize() override; | 61 | void Initialize() override; |
| @@ -68,6 +68,8 @@ public: | |||
| 68 | void WriteText(std::optional<std::u16string> text); | 68 | void WriteText(std::optional<std::u16string> text); |
| 69 | 69 | ||
| 70 | private: | 70 | private: |
| 71 | const Core::Frontend::SoftwareKeyboardApplet& frontend; | ||
| 72 | |||
| 71 | KeyboardConfig config; | 73 | KeyboardConfig config; |
| 72 | std::u16string initial_text; | 74 | std::u16string initial_text; |
| 73 | bool complete = false; | 75 | 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 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/am/applets/applets.h" | ||
| 8 | |||
| 9 | namespace Service::AM::Applets { | ||
| 10 | |||
| 11 | class StubApplet final : public Applet { | ||
| 12 | public: | ||
| 13 | StubApplet(); | ||
| 14 | ~StubApplet() override; | ||
| 15 | |||
| 16 | void Initialize() override; | ||
| 17 | |||
| 18 | bool TransactionComplete() const override; | ||
| 19 | ResultCode GetStatus() const override; | ||
| 20 | void ExecuteInteractive() override; | ||
| 21 | void Execute() override; | ||
| 22 | }; | ||
| 23 | |||
| 24 | } // 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() { | |||
| 95 | return nullptr; | 95 | return nullptr; |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | WebBrowser::WebBrowser() = default; | 98 | WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend) : frontend(frontend) {} |
| 99 | 99 | ||
| 100 | WebBrowser::~WebBrowser() = default; | 100 | WebBrowser::~WebBrowser() = default; |
| 101 | 101 | ||
| @@ -152,8 +152,6 @@ void WebBrowser::Execute() { | |||
| 152 | return; | 152 | return; |
| 153 | } | 153 | } |
| 154 | 154 | ||
| 155 | auto& frontend{Core::System::GetInstance().GetWebBrowser()}; | ||
| 156 | |||
| 157 | frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); | 155 | frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); |
| 158 | } | 156 | } |
| 159 | 157 | ||
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 { | |||
| 12 | 12 | ||
| 13 | class WebBrowser final : public Applet { | 13 | class WebBrowser final : public Applet { |
| 14 | public: | 14 | public: |
| 15 | WebBrowser(); | 15 | WebBrowser(Core::Frontend::WebBrowserApplet& frontend); |
| 16 | ~WebBrowser() override; | 16 | ~WebBrowser() override; |
| 17 | 17 | ||
| 18 | void Initialize() override; | 18 | void Initialize() override; |
| @@ -32,6 +32,8 @@ public: | |||
| 32 | void Finalize(); | 32 | void Finalize(); |
| 33 | 33 | ||
| 34 | private: | 34 | private: |
| 35 | Core::Frontend::WebBrowserApplet& frontend; | ||
| 36 | |||
| 35 | bool complete = false; | 37 | bool complete = false; |
| 36 | bool unpacked = false; | 38 | bool unpacked = false; |
| 37 | ResultCode status = RESULT_SUCCESS; | 39 | ResultCode status = RESULT_SUCCESS; |
diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp index b6b71f966..6a01d4d29 100644 --- a/src/core/hle/service/audio/audctl.cpp +++ b/src/core/hle/service/audio/audctl.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | ||
| 5 | #include "core/hle/service/audio/audctl.h" | 7 | #include "core/hle/service/audio/audctl.h" |
| 6 | 8 | ||
| 7 | namespace Service::Audio { | 9 | namespace Service::Audio { |
| @@ -11,8 +13,8 @@ AudCtl::AudCtl() : ServiceFramework{"audctl"} { | |||
| 11 | static const FunctionInfo functions[] = { | 13 | static const FunctionInfo functions[] = { |
| 12 | {0, nullptr, "GetTargetVolume"}, | 14 | {0, nullptr, "GetTargetVolume"}, |
| 13 | {1, nullptr, "SetTargetVolume"}, | 15 | {1, nullptr, "SetTargetVolume"}, |
| 14 | {2, nullptr, "GetTargetVolumeMin"}, | 16 | {2, &AudCtl::GetTargetVolumeMin, "GetTargetVolumeMin"}, |
| 15 | {3, nullptr, "GetTargetVolumeMax"}, | 17 | {3, &AudCtl::GetTargetVolumeMax, "GetTargetVolumeMax"}, |
| 16 | {4, nullptr, "IsTargetMute"}, | 18 | {4, nullptr, "IsTargetMute"}, |
| 17 | {5, nullptr, "SetTargetMute"}, | 19 | {5, nullptr, "SetTargetMute"}, |
| 18 | {6, nullptr, "IsTargetConnected"}, | 20 | {6, nullptr, "IsTargetConnected"}, |
| @@ -44,4 +46,28 @@ AudCtl::AudCtl() : ServiceFramework{"audctl"} { | |||
| 44 | 46 | ||
| 45 | AudCtl::~AudCtl() = default; | 47 | AudCtl::~AudCtl() = default; |
| 46 | 48 | ||
| 49 | void AudCtl::GetTargetVolumeMin(Kernel::HLERequestContext& ctx) { | ||
| 50 | LOG_DEBUG(Audio, "called."); | ||
| 51 | |||
| 52 | // This service function is currently hardcoded on the | ||
| 53 | // actual console to this value (as of 8.0.0). | ||
| 54 | constexpr s32 target_min_volume = 0; | ||
| 55 | |||
| 56 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 57 | rb.Push(RESULT_SUCCESS); | ||
| 58 | rb.Push(target_min_volume); | ||
| 59 | } | ||
| 60 | |||
| 61 | void AudCtl::GetTargetVolumeMax(Kernel::HLERequestContext& ctx) { | ||
| 62 | LOG_DEBUG(Audio, "called."); | ||
| 63 | |||
| 64 | // This service function is currently hardcoded on the | ||
| 65 | // actual console to this value (as of 8.0.0). | ||
| 66 | constexpr s32 target_max_volume = 15; | ||
| 67 | |||
| 68 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 69 | rb.Push(RESULT_SUCCESS); | ||
| 70 | rb.Push(target_max_volume); | ||
| 71 | } | ||
| 72 | |||
| 47 | } // namespace Service::Audio | 73 | } // 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> { | |||
| 12 | public: | 12 | public: |
| 13 | explicit AudCtl(); | 13 | explicit AudCtl(); |
| 14 | ~AudCtl() override; | 14 | ~AudCtl() override; |
| 15 | |||
| 16 | private: | ||
| 17 | void GetTargetVolumeMin(Kernel::HLERequestContext& ctx); | ||
| 18 | void GetTargetVolumeMax(Kernel::HLERequestContext& ctx); | ||
| 15 | }; | 19 | }; |
| 16 | 20 | ||
| 17 | } // namespace Service::Audio | 21 | } // namespace Service::Audio |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index a86653204..8592b1f44 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -21,8 +21,6 @@ | |||
| 21 | #include "core/memory.h" | 21 | #include "core/memory.h" |
| 22 | #include "core/settings.h" | 22 | #include "core/settings.h" |
| 23 | 23 | ||
| 24 | #pragma optimize("", off) | ||
| 25 | |||
| 26 | namespace Loader { | 24 | namespace Loader { |
| 27 | namespace { | 25 | namespace { |
| 28 | struct MODHeader { | 26 | struct MODHeader { |
diff --git a/src/core/memory.h b/src/core/memory.h index b9fa18b1d..04e2c5f1d 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -72,15 +72,6 @@ u8* GetPointer(VAddr vaddr); | |||
| 72 | 72 | ||
| 73 | std::string ReadCString(VAddr vaddr, std::size_t max_length); | 73 | std::string ReadCString(VAddr vaddr, std::size_t max_length); |
| 74 | 74 | ||
| 75 | enum class FlushMode { | ||
| 76 | /// Write back modified surfaces to RAM | ||
| 77 | Flush, | ||
| 78 | /// Remove region from the cache | ||
| 79 | Invalidate, | ||
| 80 | /// Write back modified surfaces to RAM, and also remove them from the cache | ||
| 81 | FlushAndInvalidate, | ||
| 82 | }; | ||
| 83 | |||
| 84 | /** | 75 | /** |
| 85 | * Mark each page touching the region as cached. | 76 | * Mark each page touching the region as cached. |
| 86 | */ | 77 | */ |
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 6d32ebea3..c1365879b 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -90,6 +90,7 @@ void LogSettings() { | |||
| 90 | LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor); | 90 | LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor); |
| 91 | LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); | 91 | LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); |
| 92 | LogSetting("Renderer_FrameLimit", Settings::values.frame_limit); | 92 | LogSetting("Renderer_FrameLimit", Settings::values.frame_limit); |
| 93 | LogSetting("Renderer_UseCompatibilityProfile", Settings::values.use_compatibility_profile); | ||
| 93 | LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); | 94 | LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache); |
| 94 | LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation); | 95 | LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation); |
| 95 | LogSetting("Renderer_UseAsynchronousGpuEmulation", | 96 | LogSetting("Renderer_UseAsynchronousGpuEmulation", |
diff --git a/src/core/settings.h b/src/core/settings.h index b84390745..5ff3634aa 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -390,6 +390,7 @@ struct Values { | |||
| 390 | float resolution_factor; | 390 | float resolution_factor; |
| 391 | bool use_frame_limit; | 391 | bool use_frame_limit; |
| 392 | u16 frame_limit; | 392 | u16 frame_limit; |
| 393 | bool use_compatibility_profile; | ||
| 393 | bool use_disk_shader_cache; | 394 | bool use_disk_shader_cache; |
| 394 | bool use_accurate_gpu_emulation; | 395 | bool use_accurate_gpu_emulation; |
| 395 | bool use_asynchronous_gpu_emulation; | 396 | bool use_asynchronous_gpu_emulation; |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index e1db06811..4b17bada5 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -102,12 +102,6 @@ bool VerifyLogin(const std::string& username, const std::string& token) { | |||
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | TelemetrySession::TelemetrySession() { | 104 | TelemetrySession::TelemetrySession() { |
| 105 | #ifdef ENABLE_WEB_SERVICE | ||
| 106 | backend = std::make_unique<WebService::TelemetryJson>( | ||
| 107 | Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token); | ||
| 108 | #else | ||
| 109 | backend = std::make_unique<Telemetry::NullVisitor>(); | ||
| 110 | #endif | ||
| 111 | // Log one-time top-level information | 105 | // Log one-time top-level information |
| 112 | AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId()); | 106 | AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId()); |
| 113 | 107 | ||
| @@ -175,9 +169,14 @@ TelemetrySession::~TelemetrySession() { | |||
| 175 | .count()}; | 169 | .count()}; |
| 176 | AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time); | 170 | AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time); |
| 177 | 171 | ||
| 172 | #ifdef ENABLE_WEB_SERVICE | ||
| 173 | auto backend = std::make_unique<WebService::TelemetryJson>( | ||
| 174 | Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token); | ||
| 175 | #else | ||
| 176 | auto backend = std::make_unique<Telemetry::NullVisitor>(); | ||
| 177 | #endif | ||
| 178 | |||
| 178 | // Complete the session, submitting to web service if necessary | 179 | // Complete the session, submitting to web service if necessary |
| 179 | // This is just a placeholder to wrap up the session once the core completes and this is | ||
| 180 | // destroyed. This will be moved elsewhere once we are actually doing real I/O with the service. | ||
| 181 | field_collection.Accept(*backend); | 180 | field_collection.Accept(*backend); |
| 182 | if (Settings::values.enable_telemetry) | 181 | if (Settings::values.enable_telemetry) |
| 183 | backend->Complete(); | 182 | backend->Complete(); |
| @@ -186,6 +185,8 @@ TelemetrySession::~TelemetrySession() { | |||
| 186 | 185 | ||
| 187 | bool TelemetrySession::SubmitTestcase() { | 186 | bool TelemetrySession::SubmitTestcase() { |
| 188 | #ifdef ENABLE_WEB_SERVICE | 187 | #ifdef ENABLE_WEB_SERVICE |
| 188 | auto backend = std::make_unique<WebService::TelemetryJson>( | ||
| 189 | Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token); | ||
| 189 | field_collection.Accept(*backend); | 190 | field_collection.Accept(*backend); |
| 190 | return backend->SubmitTestcase(); | 191 | return backend->SubmitTestcase(); |
| 191 | #else | 192 | #else |
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h index 023612b79..cae5a45a0 100644 --- a/src/core/telemetry_session.h +++ b/src/core/telemetry_session.h | |||
| @@ -39,7 +39,6 @@ public: | |||
| 39 | 39 | ||
| 40 | private: | 40 | private: |
| 41 | Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session | 41 | Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session |
| 42 | std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields | ||
| 43 | }; | 42 | }; |
| 44 | 43 | ||
| 45 | /** | 44 | /** |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 1e31a2900..1e010e4da 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -3,6 +3,8 @@ add_library(video_core STATIC | |||
| 3 | dma_pusher.h | 3 | dma_pusher.h |
| 4 | debug_utils/debug_utils.cpp | 4 | debug_utils/debug_utils.cpp |
| 5 | debug_utils/debug_utils.h | 5 | debug_utils/debug_utils.h |
| 6 | engines/engine_upload.cpp | ||
| 7 | engines/engine_upload.h | ||
| 6 | engines/fermi_2d.cpp | 8 | engines/fermi_2d.cpp |
| 7 | engines/fermi_2d.h | 9 | engines/fermi_2d.h |
| 8 | engines/kepler_compute.cpp | 10 | engines/kepler_compute.cpp |
| @@ -36,6 +38,8 @@ add_library(video_core STATIC | |||
| 36 | renderer_base.h | 38 | renderer_base.h |
| 37 | renderer_opengl/gl_buffer_cache.cpp | 39 | renderer_opengl/gl_buffer_cache.cpp |
| 38 | renderer_opengl/gl_buffer_cache.h | 40 | renderer_opengl/gl_buffer_cache.h |
| 41 | renderer_opengl/gl_device.cpp | ||
| 42 | renderer_opengl/gl_device.h | ||
| 39 | renderer_opengl/gl_global_cache.cpp | 43 | renderer_opengl/gl_global_cache.cpp |
| 40 | renderer_opengl/gl_global_cache.h | 44 | renderer_opengl/gl_global_cache.h |
| 41 | renderer_opengl/gl_primitive_assembler.cpp | 45 | renderer_opengl/gl_primitive_assembler.cpp |
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 046d047cb..036e66f05 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp | |||
| @@ -57,8 +57,8 @@ bool DmaPusher::Step() { | |||
| 57 | 57 | ||
| 58 | // Push buffer non-empty, read a word | 58 | // Push buffer non-empty, read a word |
| 59 | command_headers.resize(command_list_header.size); | 59 | command_headers.resize(command_list_header.size); |
| 60 | gpu.MemoryManager().ReadBlock(dma_get, command_headers.data(), | 60 | gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(), |
| 61 | command_list_header.size * sizeof(u32)); | 61 | command_list_header.size * sizeof(u32)); |
| 62 | 62 | ||
| 63 | for (const CommandHeader& command_header : command_headers) { | 63 | for (const CommandHeader& command_header : command_headers) { |
| 64 | 64 | ||
| @@ -105,6 +105,8 @@ bool DmaPusher::Step() { | |||
| 105 | dma_state.non_incrementing = false; | 105 | dma_state.non_incrementing = false; |
| 106 | dma_increment_once = true; | 106 | dma_increment_once = true; |
| 107 | break; | 107 | break; |
| 108 | default: | ||
| 109 | break; | ||
| 108 | } | 110 | } |
| 109 | } | 111 | } |
| 110 | } | 112 | } |
diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp new file mode 100644 index 000000000..f8aa4ff55 --- /dev/null +++ b/src/video_core/engines/engine_upload.cpp | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "video_core/engines/engine_upload.h" | ||
| 7 | #include "video_core/memory_manager.h" | ||
| 8 | #include "video_core/textures/decoders.h" | ||
| 9 | |||
| 10 | namespace Tegra::Engines::Upload { | ||
| 11 | |||
| 12 | State::State(MemoryManager& memory_manager, Registers& regs) | ||
| 13 | : memory_manager(memory_manager), regs(regs) {} | ||
| 14 | |||
| 15 | void State::ProcessExec(const bool is_linear) { | ||
| 16 | write_offset = 0; | ||
| 17 | copy_size = regs.line_length_in * regs.line_count; | ||
| 18 | inner_buffer.resize(copy_size); | ||
| 19 | this->is_linear = is_linear; | ||
| 20 | } | ||
| 21 | |||
| 22 | void State::ProcessData(const u32 data, const bool is_last_call) { | ||
| 23 | const u32 sub_copy_size = std::min(4U, copy_size - write_offset); | ||
| 24 | std::memcpy(&inner_buffer[write_offset], &data, sub_copy_size); | ||
| 25 | write_offset += sub_copy_size; | ||
| 26 | if (!is_last_call) { | ||
| 27 | return; | ||
| 28 | } | ||
| 29 | const GPUVAddr address{regs.dest.Address()}; | ||
| 30 | if (is_linear) { | ||
| 31 | memory_manager.WriteBlock(address, inner_buffer.data(), copy_size); | ||
| 32 | } else { | ||
| 33 | UNIMPLEMENTED_IF(regs.dest.z != 0); | ||
| 34 | UNIMPLEMENTED_IF(regs.dest.depth != 1); | ||
| 35 | UNIMPLEMENTED_IF(regs.dest.BlockWidth() != 1); | ||
| 36 | UNIMPLEMENTED_IF(regs.dest.BlockDepth() != 1); | ||
| 37 | const std::size_t dst_size = Tegra::Texture::CalculateSize( | ||
| 38 | true, 1, regs.dest.width, regs.dest.height, 1, regs.dest.BlockHeight(), 1); | ||
| 39 | tmp_buffer.resize(dst_size); | ||
| 40 | memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size); | ||
| 41 | Tegra::Texture::SwizzleKepler(regs.dest.width, regs.dest.height, regs.dest.x, regs.dest.y, | ||
| 42 | regs.dest.BlockHeight(), copy_size, inner_buffer.data(), | ||
| 43 | tmp_buffer.data()); | ||
| 44 | memory_manager.WriteBlock(address, tmp_buffer.data(), dst_size); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | } // namespace Tegra::Engines::Upload | ||
diff --git a/src/video_core/engines/engine_upload.h b/src/video_core/engines/engine_upload.h new file mode 100644 index 000000000..9c6e0d21c --- /dev/null +++ b/src/video_core/engines/engine_upload.h | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | #include <vector> | ||
| 9 | #include "common/bit_field.h" | ||
| 10 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 13 | namespace Tegra { | ||
| 14 | class MemoryManager; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace Tegra::Engines::Upload { | ||
| 18 | |||
| 19 | struct Registers { | ||
| 20 | u32 line_length_in; | ||
| 21 | u32 line_count; | ||
| 22 | |||
| 23 | struct { | ||
| 24 | u32 address_high; | ||
| 25 | u32 address_low; | ||
| 26 | u32 pitch; | ||
| 27 | union { | ||
| 28 | BitField<0, 4, u32> block_width; | ||
| 29 | BitField<4, 4, u32> block_height; | ||
| 30 | BitField<8, 4, u32> block_depth; | ||
| 31 | }; | ||
| 32 | u32 width; | ||
| 33 | u32 height; | ||
| 34 | u32 depth; | ||
| 35 | u32 z; | ||
| 36 | u32 x; | ||
| 37 | u32 y; | ||
| 38 | |||
| 39 | GPUVAddr Address() const { | ||
| 40 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low); | ||
| 41 | } | ||
| 42 | |||
| 43 | u32 BlockWidth() const { | ||
| 44 | return 1U << block_width.Value(); | ||
| 45 | } | ||
| 46 | |||
| 47 | u32 BlockHeight() const { | ||
| 48 | return 1U << block_height.Value(); | ||
| 49 | } | ||
| 50 | |||
| 51 | u32 BlockDepth() const { | ||
| 52 | return 1U << block_depth.Value(); | ||
| 53 | } | ||
| 54 | } dest; | ||
| 55 | }; | ||
| 56 | |||
| 57 | class State { | ||
| 58 | public: | ||
| 59 | State(MemoryManager& memory_manager, Registers& regs); | ||
| 60 | ~State() = default; | ||
| 61 | |||
| 62 | void ProcessExec(const bool is_linear); | ||
| 63 | void ProcessData(const u32 data, const bool is_last_call); | ||
| 64 | |||
| 65 | private: | ||
| 66 | u32 write_offset = 0; | ||
| 67 | u32 copy_size = 0; | ||
| 68 | std::vector<u8> inner_buffer; | ||
| 69 | std::vector<u8> tmp_buffer; | ||
| 70 | bool is_linear = false; | ||
| 71 | Registers& regs; | ||
| 72 | MemoryManager& memory_manager; | ||
| 73 | }; | ||
| 74 | |||
| 75 | } // namespace Tegra::Engines::Upload | ||
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h index 2e51b7f13..45f59a4d9 100644 --- a/src/video_core/engines/fermi_2d.h +++ b/src/video_core/engines/fermi_2d.h | |||
| @@ -21,6 +21,12 @@ class RasterizerInterface; | |||
| 21 | 21 | ||
| 22 | namespace Tegra::Engines { | 22 | namespace Tegra::Engines { |
| 23 | 23 | ||
| 24 | /** | ||
| 25 | * This Engine is known as G80_2D. Documentation can be found in: | ||
| 26 | * https://github.com/envytools/envytools/blob/master/rnndb/graph/g80_2d.xml | ||
| 27 | * https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_2d.xml.h | ||
| 28 | */ | ||
| 29 | |||
| 24 | #define FERMI2D_REG_INDEX(field_name) \ | 30 | #define FERMI2D_REG_INDEX(field_name) \ |
| 25 | (offsetof(Tegra::Engines::Fermi2D::Regs, field_name) / sizeof(u32)) | 31 | (offsetof(Tegra::Engines::Fermi2D::Regs, field_name) / sizeof(u32)) |
| 26 | 32 | ||
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index b1d950460..7404a8163 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp | |||
| @@ -4,12 +4,21 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "core/core.h" | ||
| 7 | #include "video_core/engines/kepler_compute.h" | 8 | #include "video_core/engines/kepler_compute.h" |
| 9 | #include "video_core/engines/maxwell_3d.h" | ||
| 8 | #include "video_core/memory_manager.h" | 10 | #include "video_core/memory_manager.h" |
| 11 | #include "video_core/rasterizer_interface.h" | ||
| 12 | #include "video_core/renderer_base.h" | ||
| 13 | #include "video_core/textures/decoders.h" | ||
| 9 | 14 | ||
| 10 | namespace Tegra::Engines { | 15 | namespace Tegra::Engines { |
| 11 | 16 | ||
| 12 | KeplerCompute::KeplerCompute(MemoryManager& memory_manager) : memory_manager{memory_manager} {} | 17 | KeplerCompute::KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer, |
| 18 | MemoryManager& memory_manager) | ||
| 19 | : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, upload_state{ | ||
| 20 | memory_manager, | ||
| 21 | regs.upload} {} | ||
| 13 | 22 | ||
| 14 | KeplerCompute::~KeplerCompute() = default; | 23 | KeplerCompute::~KeplerCompute() = default; |
| 15 | 24 | ||
| @@ -20,14 +29,34 @@ void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) { | |||
| 20 | regs.reg_array[method_call.method] = method_call.argument; | 29 | regs.reg_array[method_call.method] = method_call.argument; |
| 21 | 30 | ||
| 22 | switch (method_call.method) { | 31 | switch (method_call.method) { |
| 32 | case KEPLER_COMPUTE_REG_INDEX(exec_upload): { | ||
| 33 | upload_state.ProcessExec(regs.exec_upload.linear != 0); | ||
| 34 | break; | ||
| 35 | } | ||
| 36 | case KEPLER_COMPUTE_REG_INDEX(data_upload): { | ||
| 37 | const bool is_last_call = method_call.IsLastCall(); | ||
| 38 | upload_state.ProcessData(method_call.argument, is_last_call); | ||
| 39 | if (is_last_call) { | ||
| 40 | system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); | ||
| 41 | } | ||
| 42 | break; | ||
| 43 | } | ||
| 23 | case KEPLER_COMPUTE_REG_INDEX(launch): | 44 | case KEPLER_COMPUTE_REG_INDEX(launch): |
| 24 | // Abort execution since compute shaders can be used to alter game memory (e.g. CUDA | 45 | ProcessLaunch(); |
| 25 | // kernels) | ||
| 26 | UNREACHABLE_MSG("Compute shaders are not implemented"); | ||
| 27 | break; | 46 | break; |
| 28 | default: | 47 | default: |
| 29 | break; | 48 | break; |
| 30 | } | 49 | } |
| 31 | } | 50 | } |
| 32 | 51 | ||
| 52 | void KeplerCompute::ProcessLaunch() { | ||
| 53 | |||
| 54 | const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address(); | ||
| 55 | memory_manager.ReadBlockUnsafe(launch_desc_loc, &launch_description, | ||
| 56 | LaunchParams::NUM_LAUNCH_PARAMETERS * sizeof(u32)); | ||
| 57 | |||
| 58 | const GPUVAddr code_loc = regs.code_loc.Address() + launch_description.program_start; | ||
| 59 | LOG_WARNING(HW_GPU, "Compute Kernel Execute at Address 0x{:016x}, STUBBED", code_loc); | ||
| 60 | } | ||
| 61 | |||
| 33 | } // namespace Tegra::Engines | 62 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index fb6cdf432..5250b8d9b 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h | |||
| @@ -6,22 +6,40 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <cstddef> | 8 | #include <cstddef> |
| 9 | #include <vector> | ||
| 10 | #include "common/bit_field.h" | ||
| 9 | #include "common/common_funcs.h" | 11 | #include "common/common_funcs.h" |
| 10 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "video_core/engines/engine_upload.h" | ||
| 11 | #include "video_core/gpu.h" | 14 | #include "video_core/gpu.h" |
| 12 | 15 | ||
| 16 | namespace Core { | ||
| 17 | class System; | ||
| 18 | } | ||
| 19 | |||
| 13 | namespace Tegra { | 20 | namespace Tegra { |
| 14 | class MemoryManager; | 21 | class MemoryManager; |
| 15 | } | 22 | } |
| 16 | 23 | ||
| 24 | namespace VideoCore { | ||
| 25 | class RasterizerInterface; | ||
| 26 | } | ||
| 27 | |||
| 17 | namespace Tegra::Engines { | 28 | namespace Tegra::Engines { |
| 18 | 29 | ||
| 30 | /** | ||
| 31 | * This Engine is known as GK104_Compute. Documentation can be found in: | ||
| 32 | * https://github.com/envytools/envytools/blob/master/rnndb/graph/gk104_compute.xml | ||
| 33 | * https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nvc0/nve4_compute.xml.h | ||
| 34 | */ | ||
| 35 | |||
| 19 | #define KEPLER_COMPUTE_REG_INDEX(field_name) \ | 36 | #define KEPLER_COMPUTE_REG_INDEX(field_name) \ |
| 20 | (offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32)) | 37 | (offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32)) |
| 21 | 38 | ||
| 22 | class KeplerCompute final { | 39 | class KeplerCompute final { |
| 23 | public: | 40 | public: |
| 24 | explicit KeplerCompute(MemoryManager& memory_manager); | 41 | explicit KeplerCompute(Core::System& system, VideoCore::RasterizerInterface& rasterizer, |
| 42 | MemoryManager& memory_manager); | ||
| 25 | ~KeplerCompute(); | 43 | ~KeplerCompute(); |
| 26 | 44 | ||
| 27 | static constexpr std::size_t NumConstBuffers = 8; | 45 | static constexpr std::size_t NumConstBuffers = 8; |
| @@ -31,30 +49,181 @@ public: | |||
| 31 | 49 | ||
| 32 | union { | 50 | union { |
| 33 | struct { | 51 | struct { |
| 34 | INSERT_PADDING_WORDS(0xAF); | 52 | INSERT_PADDING_WORDS(0x60); |
| 53 | |||
| 54 | Upload::Registers upload; | ||
| 55 | |||
| 56 | struct { | ||
| 57 | union { | ||
| 58 | BitField<0, 1, u32> linear; | ||
| 59 | }; | ||
| 60 | } exec_upload; | ||
| 61 | |||
| 62 | u32 data_upload; | ||
| 63 | |||
| 64 | INSERT_PADDING_WORDS(0x3F); | ||
| 65 | |||
| 66 | struct { | ||
| 67 | u32 address; | ||
| 68 | GPUVAddr Address() const { | ||
| 69 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address) << 8)); | ||
| 70 | } | ||
| 71 | } launch_desc_loc; | ||
| 72 | |||
| 73 | INSERT_PADDING_WORDS(0x1); | ||
| 35 | 74 | ||
| 36 | u32 launch; | 75 | u32 launch; |
| 37 | 76 | ||
| 38 | INSERT_PADDING_WORDS(0xC48); | 77 | INSERT_PADDING_WORDS(0x4A7); |
| 78 | |||
| 79 | struct { | ||
| 80 | u32 address_high; | ||
| 81 | u32 address_low; | ||
| 82 | u32 limit; | ||
| 83 | GPUVAddr Address() const { | ||
| 84 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | | ||
| 85 | address_low); | ||
| 86 | } | ||
| 87 | } tsc; | ||
| 88 | |||
| 89 | INSERT_PADDING_WORDS(0x3); | ||
| 90 | |||
| 91 | struct { | ||
| 92 | u32 address_high; | ||
| 93 | u32 address_low; | ||
| 94 | u32 limit; | ||
| 95 | GPUVAddr Address() const { | ||
| 96 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | | ||
| 97 | address_low); | ||
| 98 | } | ||
| 99 | } tic; | ||
| 100 | |||
| 101 | INSERT_PADDING_WORDS(0x22); | ||
| 102 | |||
| 103 | struct { | ||
| 104 | u32 address_high; | ||
| 105 | u32 address_low; | ||
| 106 | GPUVAddr Address() const { | ||
| 107 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | | ||
| 108 | address_low); | ||
| 109 | } | ||
| 110 | } code_loc; | ||
| 111 | |||
| 112 | INSERT_PADDING_WORDS(0x3FE); | ||
| 113 | |||
| 114 | u32 texture_const_buffer_index; | ||
| 115 | |||
| 116 | INSERT_PADDING_WORDS(0x374); | ||
| 39 | }; | 117 | }; |
| 40 | std::array<u32, NUM_REGS> reg_array; | 118 | std::array<u32, NUM_REGS> reg_array; |
| 41 | }; | 119 | }; |
| 42 | } regs{}; | 120 | } regs{}; |
| 121 | |||
| 122 | struct LaunchParams { | ||
| 123 | static constexpr std::size_t NUM_LAUNCH_PARAMETERS = 0x40; | ||
| 124 | |||
| 125 | INSERT_PADDING_WORDS(0x8); | ||
| 126 | |||
| 127 | u32 program_start; | ||
| 128 | |||
| 129 | INSERT_PADDING_WORDS(0x2); | ||
| 130 | |||
| 131 | BitField<30, 1, u32> linked_tsc; | ||
| 132 | |||
| 133 | BitField<0, 31, u32> grid_dim_x; | ||
| 134 | union { | ||
| 135 | BitField<0, 16, u32> grid_dim_y; | ||
| 136 | BitField<16, 16, u32> grid_dim_z; | ||
| 137 | }; | ||
| 138 | |||
| 139 | INSERT_PADDING_WORDS(0x3); | ||
| 140 | |||
| 141 | BitField<0, 16, u32> shared_alloc; | ||
| 142 | |||
| 143 | BitField<0, 31, u32> block_dim_x; | ||
| 144 | union { | ||
| 145 | BitField<0, 16, u32> block_dim_y; | ||
| 146 | BitField<16, 16, u32> block_dim_z; | ||
| 147 | }; | ||
| 148 | |||
| 149 | union { | ||
| 150 | BitField<0, 8, u32> const_buffer_enable_mask; | ||
| 151 | BitField<29, 2, u32> cache_layout; | ||
| 152 | } memory_config; | ||
| 153 | |||
| 154 | INSERT_PADDING_WORDS(0x8); | ||
| 155 | |||
| 156 | struct { | ||
| 157 | u32 address_low; | ||
| 158 | union { | ||
| 159 | BitField<0, 8, u32> address_high; | ||
| 160 | BitField<15, 17, u32> size; | ||
| 161 | }; | ||
| 162 | GPUVAddr Address() const { | ||
| 163 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high.Value()) << 32) | | ||
| 164 | address_low); | ||
| 165 | } | ||
| 166 | } const_buffer_config[8]; | ||
| 167 | |||
| 168 | union { | ||
| 169 | BitField<0, 20, u32> local_pos_alloc; | ||
| 170 | BitField<27, 5, u32> barrier_alloc; | ||
| 171 | }; | ||
| 172 | |||
| 173 | union { | ||
| 174 | BitField<0, 20, u32> local_neg_alloc; | ||
| 175 | BitField<24, 5, u32> gpr_alloc; | ||
| 176 | }; | ||
| 177 | |||
| 178 | INSERT_PADDING_WORDS(0x11); | ||
| 179 | } launch_description; | ||
| 180 | |||
| 181 | struct { | ||
| 182 | u32 write_offset = 0; | ||
| 183 | u32 copy_size = 0; | ||
| 184 | std::vector<u8> inner_buffer; | ||
| 185 | } state{}; | ||
| 186 | |||
| 43 | static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), | 187 | static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), |
| 44 | "KeplerCompute Regs has wrong size"); | 188 | "KeplerCompute Regs has wrong size"); |
| 45 | 189 | ||
| 190 | static_assert(sizeof(LaunchParams) == LaunchParams::NUM_LAUNCH_PARAMETERS * sizeof(u32), | ||
| 191 | "KeplerCompute LaunchParams has wrong size"); | ||
| 192 | |||
| 46 | /// Write the value to the register identified by method. | 193 | /// Write the value to the register identified by method. |
| 47 | void CallMethod(const GPU::MethodCall& method_call); | 194 | void CallMethod(const GPU::MethodCall& method_call); |
| 48 | 195 | ||
| 49 | private: | 196 | private: |
| 197 | Core::System& system; | ||
| 198 | VideoCore::RasterizerInterface& rasterizer; | ||
| 50 | MemoryManager& memory_manager; | 199 | MemoryManager& memory_manager; |
| 200 | Upload::State upload_state; | ||
| 201 | |||
| 202 | void ProcessLaunch(); | ||
| 51 | }; | 203 | }; |
| 52 | 204 | ||
| 53 | #define ASSERT_REG_POSITION(field_name, position) \ | 205 | #define ASSERT_REG_POSITION(field_name, position) \ |
| 54 | static_assert(offsetof(KeplerCompute::Regs, field_name) == position * 4, \ | 206 | static_assert(offsetof(KeplerCompute::Regs, field_name) == position * 4, \ |
| 55 | "Field " #field_name " has invalid position") | 207 | "Field " #field_name " has invalid position") |
| 56 | 208 | ||
| 209 | #define ASSERT_LAUNCH_PARAM_POSITION(field_name, position) \ | ||
| 210 | static_assert(offsetof(KeplerCompute::LaunchParams, field_name) == position * 4, \ | ||
| 211 | "Field " #field_name " has invalid position") | ||
| 212 | |||
| 213 | ASSERT_REG_POSITION(upload, 0x60); | ||
| 214 | ASSERT_REG_POSITION(exec_upload, 0x6C); | ||
| 215 | ASSERT_REG_POSITION(data_upload, 0x6D); | ||
| 57 | ASSERT_REG_POSITION(launch, 0xAF); | 216 | ASSERT_REG_POSITION(launch, 0xAF); |
| 217 | ASSERT_REG_POSITION(tsc, 0x557); | ||
| 218 | ASSERT_REG_POSITION(tic, 0x55D); | ||
| 219 | ASSERT_REG_POSITION(code_loc, 0x582); | ||
| 220 | ASSERT_REG_POSITION(texture_const_buffer_index, 0x982); | ||
| 221 | ASSERT_LAUNCH_PARAM_POSITION(program_start, 0x8); | ||
| 222 | ASSERT_LAUNCH_PARAM_POSITION(grid_dim_x, 0xC); | ||
| 223 | ASSERT_LAUNCH_PARAM_POSITION(shared_alloc, 0x11); | ||
| 224 | ASSERT_LAUNCH_PARAM_POSITION(block_dim_x, 0x12); | ||
| 225 | ASSERT_LAUNCH_PARAM_POSITION(memory_config, 0x14); | ||
| 226 | ASSERT_LAUNCH_PARAM_POSITION(const_buffer_config, 0x1D); | ||
| 58 | 227 | ||
| 59 | #undef ASSERT_REG_POSITION | 228 | #undef ASSERT_REG_POSITION |
| 60 | 229 | ||
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp index cd51a31d7..0561f676c 100644 --- a/src/video_core/engines/kepler_memory.cpp +++ b/src/video_core/engines/kepler_memory.cpp | |||
| @@ -10,12 +10,12 @@ | |||
| 10 | #include "video_core/memory_manager.h" | 10 | #include "video_core/memory_manager.h" |
| 11 | #include "video_core/rasterizer_interface.h" | 11 | #include "video_core/rasterizer_interface.h" |
| 12 | #include "video_core/renderer_base.h" | 12 | #include "video_core/renderer_base.h" |
| 13 | #include "video_core/textures/decoders.h" | ||
| 13 | 14 | ||
| 14 | namespace Tegra::Engines { | 15 | namespace Tegra::Engines { |
| 15 | 16 | ||
| 16 | KeplerMemory::KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | 17 | KeplerMemory::KeplerMemory(Core::System& system, MemoryManager& memory_manager) |
| 17 | MemoryManager& memory_manager) | 18 | : system{system}, memory_manager{memory_manager}, upload_state{memory_manager, regs.upload} {} |
| 18 | : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager} {} | ||
| 19 | 19 | ||
| 20 | KeplerMemory::~KeplerMemory() = default; | 20 | KeplerMemory::~KeplerMemory() = default; |
| 21 | 21 | ||
| @@ -27,30 +27,18 @@ void KeplerMemory::CallMethod(const GPU::MethodCall& method_call) { | |||
| 27 | 27 | ||
| 28 | switch (method_call.method) { | 28 | switch (method_call.method) { |
| 29 | case KEPLERMEMORY_REG_INDEX(exec): { | 29 | case KEPLERMEMORY_REG_INDEX(exec): { |
| 30 | state.write_offset = 0; | 30 | upload_state.ProcessExec(regs.exec.linear != 0); |
| 31 | break; | 31 | break; |
| 32 | } | 32 | } |
| 33 | case KEPLERMEMORY_REG_INDEX(data): { | 33 | case KEPLERMEMORY_REG_INDEX(data): { |
| 34 | ProcessData(method_call.argument); | 34 | const bool is_last_call = method_call.IsLastCall(); |
| 35 | upload_state.ProcessData(method_call.argument, is_last_call); | ||
| 36 | if (is_last_call) { | ||
| 37 | system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); | ||
| 38 | } | ||
| 35 | break; | 39 | break; |
| 36 | } | 40 | } |
| 37 | } | 41 | } |
| 38 | } | 42 | } |
| 39 | 43 | ||
| 40 | void KeplerMemory::ProcessData(u32 data) { | ||
| 41 | ASSERT_MSG(regs.exec.linear, "Non-linear uploads are not supported"); | ||
| 42 | ASSERT(regs.dest.x == 0 && regs.dest.y == 0 && regs.dest.z == 0); | ||
| 43 | |||
| 44 | // We have to invalidate the destination region to evict any outdated surfaces from the cache. | ||
| 45 | // We do this before actually writing the new data because the destination address might | ||
| 46 | // contain a dirty surface that will have to be written back to memory. | ||
| 47 | const GPUVAddr address{regs.dest.Address() + state.write_offset * sizeof(u32)}; | ||
| 48 | rasterizer.InvalidateRegion(ToCacheAddr(memory_manager.GetPointer(address)), sizeof(u32)); | ||
| 49 | memory_manager.Write<u32>(address, data); | ||
| 50 | |||
| 51 | system.GPU().Maxwell3D().dirty_flags.OnMemoryWrite(); | ||
| 52 | |||
| 53 | state.write_offset++; | ||
| 54 | } | ||
| 55 | |||
| 56 | } // namespace Tegra::Engines | 44 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h index 78b6c3e45..f3bc675a9 100644 --- a/src/video_core/engines/kepler_memory.h +++ b/src/video_core/engines/kepler_memory.h | |||
| @@ -6,9 +6,11 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <cstddef> | 8 | #include <cstddef> |
| 9 | #include <vector> | ||
| 9 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 10 | #include "common/common_funcs.h" | 11 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "video_core/engines/engine_upload.h" | ||
| 12 | #include "video_core/gpu.h" | 14 | #include "video_core/gpu.h" |
| 13 | 15 | ||
| 14 | namespace Core { | 16 | namespace Core { |
| @@ -19,19 +21,20 @@ namespace Tegra { | |||
| 19 | class MemoryManager; | 21 | class MemoryManager; |
| 20 | } | 22 | } |
| 21 | 23 | ||
| 22 | namespace VideoCore { | ||
| 23 | class RasterizerInterface; | ||
| 24 | } | ||
| 25 | |||
| 26 | namespace Tegra::Engines { | 24 | namespace Tegra::Engines { |
| 27 | 25 | ||
| 26 | /** | ||
| 27 | * This Engine is known as P2MF. Documentation can be found in: | ||
| 28 | * https://github.com/envytools/envytools/blob/master/rnndb/graph/gk104_p2mf.xml | ||
| 29 | * https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nvc0/nve4_p2mf.xml.h | ||
| 30 | */ | ||
| 31 | |||
| 28 | #define KEPLERMEMORY_REG_INDEX(field_name) \ | 32 | #define KEPLERMEMORY_REG_INDEX(field_name) \ |
| 29 | (offsetof(Tegra::Engines::KeplerMemory::Regs, field_name) / sizeof(u32)) | 33 | (offsetof(Tegra::Engines::KeplerMemory::Regs, field_name) / sizeof(u32)) |
| 30 | 34 | ||
| 31 | class KeplerMemory final { | 35 | class KeplerMemory final { |
| 32 | public: | 36 | public: |
| 33 | KeplerMemory(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | 37 | KeplerMemory(Core::System& system, MemoryManager& memory_manager); |
| 34 | MemoryManager& memory_manager); | ||
| 35 | ~KeplerMemory(); | 38 | ~KeplerMemory(); |
| 36 | 39 | ||
| 37 | /// Write the value to the register identified by method. | 40 | /// Write the value to the register identified by method. |
| @@ -44,26 +47,7 @@ public: | |||
| 44 | struct { | 47 | struct { |
| 45 | INSERT_PADDING_WORDS(0x60); | 48 | INSERT_PADDING_WORDS(0x60); |
| 46 | 49 | ||
| 47 | u32 line_length_in; | 50 | Upload::Registers upload; |
| 48 | u32 line_count; | ||
| 49 | |||
| 50 | struct { | ||
| 51 | u32 address_high; | ||
| 52 | u32 address_low; | ||
| 53 | u32 pitch; | ||
| 54 | u32 block_dimensions; | ||
| 55 | u32 width; | ||
| 56 | u32 height; | ||
| 57 | u32 depth; | ||
| 58 | u32 z; | ||
| 59 | u32 x; | ||
| 60 | u32 y; | ||
| 61 | |||
| 62 | GPUVAddr Address() const { | ||
| 63 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | | ||
| 64 | address_low); | ||
| 65 | } | ||
| 66 | } dest; | ||
| 67 | 51 | ||
| 68 | struct { | 52 | struct { |
| 69 | union { | 53 | union { |
| @@ -79,25 +63,17 @@ public: | |||
| 79 | }; | 63 | }; |
| 80 | } regs{}; | 64 | } regs{}; |
| 81 | 65 | ||
| 82 | struct { | ||
| 83 | u32 write_offset = 0; | ||
| 84 | } state{}; | ||
| 85 | |||
| 86 | private: | 66 | private: |
| 87 | Core::System& system; | 67 | Core::System& system; |
| 88 | VideoCore::RasterizerInterface& rasterizer; | ||
| 89 | MemoryManager& memory_manager; | 68 | MemoryManager& memory_manager; |
| 90 | 69 | Upload::State upload_state; | |
| 91 | void ProcessData(u32 data); | ||
| 92 | }; | 70 | }; |
| 93 | 71 | ||
| 94 | #define ASSERT_REG_POSITION(field_name, position) \ | 72 | #define ASSERT_REG_POSITION(field_name, position) \ |
| 95 | static_assert(offsetof(KeplerMemory::Regs, field_name) == position * 4, \ | 73 | static_assert(offsetof(KeplerMemory::Regs, field_name) == position * 4, \ |
| 96 | "Field " #field_name " has invalid position") | 74 | "Field " #field_name " has invalid position") |
| 97 | 75 | ||
| 98 | ASSERT_REG_POSITION(line_length_in, 0x60); | 76 | ASSERT_REG_POSITION(upload, 0x60); |
| 99 | ASSERT_REG_POSITION(line_count, 0x61); | ||
| 100 | ASSERT_REG_POSITION(dest, 0x62); | ||
| 101 | ASSERT_REG_POSITION(exec, 0x6C); | 77 | ASSERT_REG_POSITION(exec, 0x6C); |
| 102 | ASSERT_REG_POSITION(data, 0x6D); | 78 | ASSERT_REG_POSITION(data, 0x6D); |
| 103 | #undef ASSERT_REG_POSITION | 79 | #undef ASSERT_REG_POSITION |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index b198793bc..d7b586db9 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -20,8 +20,8 @@ constexpr u32 MacroRegistersStart = 0xE00; | |||
| 20 | 20 | ||
| 21 | Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | 21 | Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& rasterizer, |
| 22 | MemoryManager& memory_manager) | 22 | MemoryManager& memory_manager) |
| 23 | : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, macro_interpreter{ | 23 | : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager}, |
| 24 | *this} { | 24 | macro_interpreter{*this}, upload_state{memory_manager, regs.upload} { |
| 25 | InitializeRegisterDefaults(); | 25 | InitializeRegisterDefaults(); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| @@ -253,6 +253,18 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { | |||
| 253 | ProcessSyncPoint(); | 253 | ProcessSyncPoint(); |
| 254 | break; | 254 | break; |
| 255 | } | 255 | } |
| 256 | case MAXWELL3D_REG_INDEX(exec_upload): { | ||
| 257 | upload_state.ProcessExec(regs.exec_upload.linear != 0); | ||
| 258 | break; | ||
| 259 | } | ||
| 260 | case MAXWELL3D_REG_INDEX(data_upload): { | ||
| 261 | const bool is_last_call = method_call.IsLastCall(); | ||
| 262 | upload_state.ProcessData(method_call.argument, is_last_call); | ||
| 263 | if (is_last_call) { | ||
| 264 | dirty_flags.OnMemoryWrite(); | ||
| 265 | } | ||
| 266 | break; | ||
| 267 | } | ||
| 256 | default: | 268 | default: |
| 257 | break; | 269 | break; |
| 258 | } | 270 | } |
| @@ -418,7 +430,7 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { | |||
| 418 | const GPUVAddr tic_address_gpu{regs.tic.TICAddress() + tic_index * sizeof(Texture::TICEntry)}; | 430 | const GPUVAddr tic_address_gpu{regs.tic.TICAddress() + tic_index * sizeof(Texture::TICEntry)}; |
| 419 | 431 | ||
| 420 | Texture::TICEntry tic_entry; | 432 | Texture::TICEntry tic_entry; |
| 421 | memory_manager.ReadBlock(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry)); | 433 | memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry)); |
| 422 | 434 | ||
| 423 | ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear || | 435 | ASSERT_MSG(tic_entry.header_version == Texture::TICHeaderVersion::BlockLinear || |
| 424 | tic_entry.header_version == Texture::TICHeaderVersion::Pitch, | 436 | tic_entry.header_version == Texture::TICHeaderVersion::Pitch, |
| @@ -439,7 +451,7 @@ Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const { | |||
| 439 | const GPUVAddr tsc_address_gpu{regs.tsc.TSCAddress() + tsc_index * sizeof(Texture::TSCEntry)}; | 451 | const GPUVAddr tsc_address_gpu{regs.tsc.TSCAddress() + tsc_index * sizeof(Texture::TSCEntry)}; |
| 440 | 452 | ||
| 441 | Texture::TSCEntry tsc_entry; | 453 | Texture::TSCEntry tsc_entry; |
| 442 | memory_manager.ReadBlock(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry)); | 454 | memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry)); |
| 443 | return tsc_entry; | 455 | return tsc_entry; |
| 444 | } | 456 | } |
| 445 | 457 | ||
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index cc2424d38..4883b582a 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "common/common_funcs.h" | 14 | #include "common/common_funcs.h" |
| 15 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 16 | #include "common/math_util.h" | 16 | #include "common/math_util.h" |
| 17 | #include "video_core/engines/engine_upload.h" | ||
| 17 | #include "video_core/gpu.h" | 18 | #include "video_core/gpu.h" |
| 18 | #include "video_core/macro_interpreter.h" | 19 | #include "video_core/macro_interpreter.h" |
| 19 | #include "video_core/textures/texture.h" | 20 | #include "video_core/textures/texture.h" |
| @@ -32,6 +33,12 @@ class RasterizerInterface; | |||
| 32 | 33 | ||
| 33 | namespace Tegra::Engines { | 34 | namespace Tegra::Engines { |
| 34 | 35 | ||
| 36 | /** | ||
| 37 | * This Engine is known as GF100_3D. Documentation can be found in: | ||
| 38 | * https://github.com/envytools/envytools/blob/master/rnndb/graph/gf100_3d.xml | ||
| 39 | * https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nvc0/nvc0_3d.xml.h | ||
| 40 | */ | ||
| 41 | |||
| 35 | #define MAXWELL3D_REG_INDEX(field_name) \ | 42 | #define MAXWELL3D_REG_INDEX(field_name) \ |
| 36 | (offsetof(Tegra::Engines::Maxwell3D::Regs, field_name) / sizeof(u32)) | 43 | (offsetof(Tegra::Engines::Maxwell3D::Regs, field_name) / sizeof(u32)) |
| 37 | 44 | ||
| @@ -243,9 +250,10 @@ public: | |||
| 243 | return "10_10_10_2"; | 250 | return "10_10_10_2"; |
| 244 | case Size::Size_11_11_10: | 251 | case Size::Size_11_11_10: |
| 245 | return "11_11_10"; | 252 | return "11_11_10"; |
| 253 | default: | ||
| 254 | UNREACHABLE(); | ||
| 255 | return {}; | ||
| 246 | } | 256 | } |
| 247 | UNREACHABLE(); | ||
| 248 | return {}; | ||
| 249 | } | 257 | } |
| 250 | 258 | ||
| 251 | std::string TypeString() const { | 259 | std::string TypeString() const { |
| @@ -579,7 +587,18 @@ public: | |||
| 579 | u32 bind; | 587 | u32 bind; |
| 580 | } macros; | 588 | } macros; |
| 581 | 589 | ||
| 582 | INSERT_PADDING_WORDS(0x69); | 590 | INSERT_PADDING_WORDS(0x17); |
| 591 | |||
| 592 | Upload::Registers upload; | ||
| 593 | struct { | ||
| 594 | union { | ||
| 595 | BitField<0, 1, u32> linear; | ||
| 596 | }; | ||
| 597 | } exec_upload; | ||
| 598 | |||
| 599 | u32 data_upload; | ||
| 600 | |||
| 601 | INSERT_PADDING_WORDS(0x44); | ||
| 583 | 602 | ||
| 584 | struct { | 603 | struct { |
| 585 | union { | 604 | union { |
| @@ -1175,6 +1194,8 @@ private: | |||
| 1175 | /// Interpreter for the macro codes uploaded to the GPU. | 1194 | /// Interpreter for the macro codes uploaded to the GPU. |
| 1176 | MacroInterpreter macro_interpreter; | 1195 | MacroInterpreter macro_interpreter; |
| 1177 | 1196 | ||
| 1197 | Upload::State upload_state; | ||
| 1198 | |||
| 1178 | /// Retrieves information about a specific TIC entry from the TIC buffer. | 1199 | /// Retrieves information about a specific TIC entry from the TIC buffer. |
| 1179 | Texture::TICEntry GetTICEntry(u32 tic_index) const; | 1200 | Texture::TICEntry GetTICEntry(u32 tic_index) const; |
| 1180 | 1201 | ||
| @@ -1218,6 +1239,9 @@ private: | |||
| 1218 | "Field " #field_name " has invalid position") | 1239 | "Field " #field_name " has invalid position") |
| 1219 | 1240 | ||
| 1220 | ASSERT_REG_POSITION(macros, 0x45); | 1241 | ASSERT_REG_POSITION(macros, 0x45); |
| 1242 | ASSERT_REG_POSITION(upload, 0x60); | ||
| 1243 | ASSERT_REG_POSITION(exec_upload, 0x6C); | ||
| 1244 | ASSERT_REG_POSITION(data_upload, 0x6D); | ||
| 1221 | ASSERT_REG_POSITION(sync_info, 0xB2); | 1245 | ASSERT_REG_POSITION(sync_info, 0xB2); |
| 1222 | ASSERT_REG_POSITION(tfb_enabled, 0x1D1); | 1246 | ASSERT_REG_POSITION(tfb_enabled, 0x1D1); |
| 1223 | ASSERT_REG_POSITION(rt, 0x200); | 1247 | ASSERT_REG_POSITION(rt, 0x200); |
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index 2426d0067..3a5dfef0c 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp | |||
| @@ -83,57 +83,66 @@ void MaxwellDMA::HandleCopy() { | |||
| 83 | 83 | ||
| 84 | ASSERT(regs.exec.enable_2d == 1); | 84 | ASSERT(regs.exec.enable_2d == 1); |
| 85 | 85 | ||
| 86 | const std::size_t copy_size = regs.x_count * regs.y_count; | 86 | if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { |
| 87 | ASSERT(regs.src_params.size_z == 1); | ||
| 88 | // If the input is tiled and the output is linear, deswizzle the input and copy it over. | ||
| 89 | const u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x; | ||
| 90 | const std::size_t src_size = Texture::CalculateSize( | ||
| 91 | true, src_bytes_per_pixel, regs.src_params.size_x, regs.src_params.size_y, | ||
| 92 | regs.src_params.size_z, regs.src_params.BlockHeight(), regs.src_params.BlockDepth()); | ||
| 87 | 93 | ||
| 88 | auto source_ptr{memory_manager.GetPointer(source)}; | 94 | const std::size_t dst_size = regs.dst_pitch * regs.y_count; |
| 89 | auto dst_ptr{memory_manager.GetPointer(dest)}; | ||
| 90 | 95 | ||
| 91 | if (!source_ptr) { | 96 | if (read_buffer.size() < src_size) { |
| 92 | LOG_ERROR(HW_GPU, "source_ptr is invalid"); | 97 | read_buffer.resize(src_size); |
| 93 | return; | 98 | } |
| 94 | } | ||
| 95 | 99 | ||
| 96 | if (!dst_ptr) { | 100 | if (write_buffer.size() < dst_size) { |
| 97 | LOG_ERROR(HW_GPU, "dst_ptr is invalid"); | 101 | write_buffer.resize(dst_size); |
| 98 | return; | 102 | } |
| 99 | } | ||
| 100 | 103 | ||
| 101 | const auto FlushAndInvalidate = [&](u32 src_size, u64 dst_size) { | 104 | memory_manager.ReadBlock(source, read_buffer.data(), src_size); |
| 102 | // TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated | 105 | memory_manager.ReadBlock(dest, write_buffer.data(), dst_size); |
| 103 | // copying. | ||
| 104 | rasterizer.FlushRegion(ToCacheAddr(source_ptr), src_size); | ||
| 105 | 106 | ||
| 106 | // We have to invalidate the destination region to evict any outdated surfaces from the | 107 | Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch, |
| 107 | // cache. We do this before actually writing the new data because the destination address | 108 | regs.src_params.size_x, src_bytes_per_pixel, read_buffer.data(), |
| 108 | // might contain a dirty surface that will have to be written back to memory. | 109 | write_buffer.data(), regs.src_params.BlockHeight(), |
| 109 | rasterizer.InvalidateRegion(ToCacheAddr(dst_ptr), dst_size); | 110 | regs.src_params.pos_x, regs.src_params.pos_y); |
| 110 | }; | ||
| 111 | 111 | ||
| 112 | if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) { | 112 | memory_manager.WriteBlock(dest, write_buffer.data(), dst_size); |
| 113 | ASSERT(regs.src_params.size_z == 1); | 113 | } else { |
| 114 | // If the input is tiled and the output is linear, deswizzle the input and copy it over. | 114 | ASSERT(regs.dst_params.BlockDepth() == 1); |
| 115 | 115 | ||
| 116 | const u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x; | 116 | const u32 src_bytes_per_pixel = regs.src_pitch / regs.x_count; |
| 117 | 117 | ||
| 118 | FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y, | 118 | const std::size_t dst_size = Texture::CalculateSize( |
| 119 | copy_size * src_bytes_per_pixel); | 119 | true, src_bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y, |
| 120 | regs.dst_params.size_z, regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth()); | ||
| 120 | 121 | ||
| 121 | Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch, | 122 | const std::size_t dst_layer_size = Texture::CalculateSize( |
| 122 | regs.src_params.size_x, src_bytes_per_pixel, source_ptr, dst_ptr, | 123 | true, src_bytes_per_pixel, regs.dst_params.size_x, regs.dst_params.size_y, 1, |
| 123 | regs.src_params.BlockHeight(), regs.src_params.pos_x, | 124 | regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth()); |
| 124 | regs.src_params.pos_y); | ||
| 125 | } else { | ||
| 126 | ASSERT(regs.dst_params.size_z == 1); | ||
| 127 | ASSERT(regs.src_pitch == regs.x_count); | ||
| 128 | 125 | ||
| 129 | const u32 src_bpp = regs.src_pitch / regs.x_count; | 126 | const std::size_t src_size = regs.src_pitch * regs.y_count; |
| 130 | 127 | ||
| 131 | FlushAndInvalidate(regs.src_pitch * regs.y_count, | 128 | if (read_buffer.size() < src_size) { |
| 132 | regs.dst_params.size_x * regs.dst_params.size_y * src_bpp); | 129 | read_buffer.resize(src_size); |
| 130 | } | ||
| 131 | |||
| 132 | if (write_buffer.size() < dst_size) { | ||
| 133 | write_buffer.resize(dst_size); | ||
| 134 | } | ||
| 135 | |||
| 136 | memory_manager.ReadBlock(source, read_buffer.data(), src_size); | ||
| 137 | memory_manager.ReadBlock(dest, write_buffer.data(), dst_size); | ||
| 133 | 138 | ||
| 134 | // If the input is linear and the output is tiled, swizzle the input and copy it over. | 139 | // If the input is linear and the output is tiled, swizzle the input and copy it over. |
| 135 | Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x, | 140 | Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x, |
| 136 | src_bpp, dst_ptr, source_ptr, regs.dst_params.BlockHeight()); | 141 | src_bytes_per_pixel, |
| 142 | write_buffer.data() + dst_layer_size * regs.dst_params.pos_z, | ||
| 143 | read_buffer.data(), regs.dst_params.BlockHeight()); | ||
| 144 | |||
| 145 | memory_manager.WriteBlock(dest, write_buffer.data(), dst_size); | ||
| 137 | } | 146 | } |
| 138 | } | 147 | } |
| 139 | 148 | ||
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index c6b649842..e5942f671 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <cstddef> | 8 | #include <cstddef> |
| 9 | #include <vector> | ||
| 9 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 10 | #include "common/common_funcs.h" | 11 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| @@ -25,6 +26,11 @@ class RasterizerInterface; | |||
| 25 | 26 | ||
| 26 | namespace Tegra::Engines { | 27 | namespace Tegra::Engines { |
| 27 | 28 | ||
| 29 | /** | ||
| 30 | * This Engine is known as GK104_Copy. Documentation can be found in: | ||
| 31 | * https://github.com/envytools/envytools/blob/master/rnndb/fifo/gk104_copy.xml | ||
| 32 | */ | ||
| 33 | |||
| 28 | class MaxwellDMA final { | 34 | class MaxwellDMA final { |
| 29 | public: | 35 | public: |
| 30 | explicit MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | 36 | explicit MaxwellDMA(Core::System& system, VideoCore::RasterizerInterface& rasterizer, |
| @@ -63,6 +69,16 @@ public: | |||
| 63 | 69 | ||
| 64 | static_assert(sizeof(Parameters) == 24, "Parameters has wrong size"); | 70 | static_assert(sizeof(Parameters) == 24, "Parameters has wrong size"); |
| 65 | 71 | ||
| 72 | enum class ComponentMode : u32 { | ||
| 73 | Src0 = 0, | ||
| 74 | Src1 = 1, | ||
| 75 | Src2 = 2, | ||
| 76 | Src3 = 3, | ||
| 77 | Const0 = 4, | ||
| 78 | Const1 = 5, | ||
| 79 | Zero = 6, | ||
| 80 | }; | ||
| 81 | |||
| 66 | enum class CopyMode : u32 { | 82 | enum class CopyMode : u32 { |
| 67 | None = 0, | 83 | None = 0, |
| 68 | Unk1 = 1, | 84 | Unk1 = 1, |
| @@ -128,7 +144,26 @@ public: | |||
| 128 | u32 x_count; | 144 | u32 x_count; |
| 129 | u32 y_count; | 145 | u32 y_count; |
| 130 | 146 | ||
| 131 | INSERT_PADDING_WORDS(0xBB); | 147 | INSERT_PADDING_WORDS(0xB8); |
| 148 | |||
| 149 | u32 const0; | ||
| 150 | u32 const1; | ||
| 151 | union { | ||
| 152 | BitField<0, 4, ComponentMode> component0; | ||
| 153 | BitField<4, 4, ComponentMode> component1; | ||
| 154 | BitField<8, 4, ComponentMode> component2; | ||
| 155 | BitField<12, 4, ComponentMode> component3; | ||
| 156 | BitField<16, 2, u32> component_size; | ||
| 157 | BitField<20, 3, u32> src_num_components; | ||
| 158 | BitField<24, 3, u32> dst_num_components; | ||
| 159 | |||
| 160 | u32 SrcBytePerPixel() const { | ||
| 161 | return src_num_components.Value() * component_size.Value(); | ||
| 162 | } | ||
| 163 | u32 DstBytePerPixel() const { | ||
| 164 | return dst_num_components.Value() * component_size.Value(); | ||
| 165 | } | ||
| 166 | } swizzle_config; | ||
| 132 | 167 | ||
| 133 | Parameters dst_params; | 168 | Parameters dst_params; |
| 134 | 169 | ||
| @@ -149,6 +184,9 @@ private: | |||
| 149 | 184 | ||
| 150 | MemoryManager& memory_manager; | 185 | MemoryManager& memory_manager; |
| 151 | 186 | ||
| 187 | std::vector<u8> read_buffer; | ||
| 188 | std::vector<u8> write_buffer; | ||
| 189 | |||
| 152 | /// Performs the copy from the source buffer to the destination buffer as configured in the | 190 | /// Performs the copy from the source buffer to the destination buffer as configured in the |
| 153 | /// registers. | 191 | /// registers. |
| 154 | void HandleCopy(); | 192 | void HandleCopy(); |
| @@ -165,6 +203,9 @@ ASSERT_REG_POSITION(src_pitch, 0x104); | |||
| 165 | ASSERT_REG_POSITION(dst_pitch, 0x105); | 203 | ASSERT_REG_POSITION(dst_pitch, 0x105); |
| 166 | ASSERT_REG_POSITION(x_count, 0x106); | 204 | ASSERT_REG_POSITION(x_count, 0x106); |
| 167 | ASSERT_REG_POSITION(y_count, 0x107); | 205 | ASSERT_REG_POSITION(y_count, 0x107); |
| 206 | ASSERT_REG_POSITION(const0, 0x1C0); | ||
| 207 | ASSERT_REG_POSITION(const1, 0x1C1); | ||
| 208 | ASSERT_REG_POSITION(swizzle_config, 0x1C2); | ||
| 168 | ASSERT_REG_POSITION(dst_params, 0x1C3); | 209 | ASSERT_REG_POSITION(dst_params, 0x1C3); |
| 169 | ASSERT_REG_POSITION(src_params, 0x1CA); | 210 | ASSERT_REG_POSITION(src_params, 0x1CA); |
| 170 | 211 | ||
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index fce9733b9..e5b4eadea 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -937,21 +937,34 @@ union Instruction { | |||
| 937 | } iset; | 937 | } iset; |
| 938 | 938 | ||
| 939 | union { | 939 | union { |
| 940 | BitField<8, 2, Register::Size> dest_size; | 940 | BitField<41, 2, u64> selector; // i2i and i2f only |
| 941 | BitField<10, 2, Register::Size> src_size; | ||
| 942 | BitField<12, 1, u64> is_output_signed; | ||
| 943 | BitField<13, 1, u64> is_input_signed; | ||
| 944 | BitField<41, 2, u64> selector; | ||
| 945 | BitField<45, 1, u64> negate_a; | 941 | BitField<45, 1, u64> negate_a; |
| 946 | BitField<49, 1, u64> abs_a; | 942 | BitField<49, 1, u64> abs_a; |
| 943 | BitField<10, 2, Register::Size> src_size; | ||
| 944 | BitField<13, 1, u64> is_input_signed; | ||
| 945 | BitField<8, 2, Register::Size> dst_size; | ||
| 946 | BitField<12, 1, u64> is_output_signed; | ||
| 947 | |||
| 948 | union { | ||
| 949 | BitField<39, 2, u64> tab5cb8_2; | ||
| 950 | } i2f; | ||
| 947 | 951 | ||
| 948 | union { | 952 | union { |
| 949 | BitField<39, 2, F2iRoundingOp> rounding; | 953 | BitField<39, 2, F2iRoundingOp> rounding; |
| 950 | } f2i; | 954 | } f2i; |
| 951 | 955 | ||
| 952 | union { | 956 | union { |
| 953 | BitField<39, 4, F2fRoundingOp> rounding; | 957 | BitField<8, 2, Register::Size> src_size; |
| 958 | BitField<10, 2, Register::Size> dst_size; | ||
| 959 | BitField<39, 4, u64> rounding; | ||
| 960 | // H0, H1 extract for F16 missing | ||
| 961 | BitField<41, 1, u64> selector; // Guessed as some games set it, TODO: reverse this value | ||
| 962 | F2fRoundingOp GetRoundingMode() const { | ||
| 963 | constexpr u64 rounding_mask = 0x0B; | ||
| 964 | return static_cast<F2fRoundingOp>(rounding.Value() & rounding_mask); | ||
| 965 | } | ||
| 954 | } f2f; | 966 | } f2f; |
| 967 | |||
| 955 | } conversion; | 968 | } conversion; |
| 956 | 969 | ||
| 957 | union { | 970 | union { |
| @@ -1734,7 +1747,7 @@ private: | |||
| 1734 | INST("0011100-00101---", Id::SHR_IMM, Type::Shift, "SHR_IMM"), | 1747 | INST("0011100-00101---", Id::SHR_IMM, Type::Shift, "SHR_IMM"), |
| 1735 | INST("0100110011100---", Id::I2I_C, Type::Conversion, "I2I_C"), | 1748 | INST("0100110011100---", Id::I2I_C, Type::Conversion, "I2I_C"), |
| 1736 | INST("0101110011100---", Id::I2I_R, Type::Conversion, "I2I_R"), | 1749 | INST("0101110011100---", Id::I2I_R, Type::Conversion, "I2I_R"), |
| 1737 | INST("01110001-1000---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"), | 1750 | INST("0011101-11100---", Id::I2I_IMM, Type::Conversion, "I2I_IMM"), |
| 1738 | INST("0100110010111---", Id::I2F_C, Type::Conversion, "I2F_C"), | 1751 | INST("0100110010111---", Id::I2F_C, Type::Conversion, "I2F_C"), |
| 1739 | INST("0101110010111---", Id::I2F_R, Type::Conversion, "I2F_R"), | 1752 | INST("0101110010111---", Id::I2F_R, Type::Conversion, "I2F_R"), |
| 1740 | INST("0011100-10111---", Id::I2F_IMM, Type::Conversion, "I2F_IMM"), | 1753 | INST("0011100-10111---", Id::I2F_IMM, Type::Conversion, "I2F_IMM"), |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 4461083ff..52706505b 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -35,9 +35,9 @@ GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{ren | |||
| 35 | dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); | 35 | dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); |
| 36 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); | 36 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); |
| 37 | fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager); | 37 | fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager); |
| 38 | kepler_compute = std::make_unique<Engines::KeplerCompute>(*memory_manager); | 38 | kepler_compute = std::make_unique<Engines::KeplerCompute>(system, rasterizer, *memory_manager); |
| 39 | maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, rasterizer, *memory_manager); | 39 | maxwell_dma = std::make_unique<Engines::MaxwellDMA>(system, rasterizer, *memory_manager); |
| 40 | kepler_memory = std::make_unique<Engines::KeplerMemory>(system, rasterizer, *memory_manager); | 40 | kepler_memory = std::make_unique<Engines::KeplerMemory>(system, *memory_manager); |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | GPU::~GPU() = default; | 43 | GPU::~GPU() = default; |
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 { | |||
| 199 | return {}; | 199 | return {}; |
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const { | 202 | bool MemoryManager::IsBlockContinous(const GPUVAddr start, const std::size_t size) { |
| 203 | const GPUVAddr end = start + size; | ||
| 204 | const auto host_ptr_start = reinterpret_cast<std::uintptr_t>(GetPointer(start)); | ||
| 205 | const auto host_ptr_end = reinterpret_cast<std::uintptr_t>(GetPointer(end)); | ||
| 206 | const std::size_t range = static_cast<std::size_t>(host_ptr_end - host_ptr_start); | ||
| 207 | return range == size; | ||
| 208 | } | ||
| 209 | |||
| 210 | void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const { | ||
| 203 | std::size_t remaining_size{size}; | 211 | std::size_t remaining_size{size}; |
| 204 | std::size_t page_index{src_addr >> page_bits}; | 212 | std::size_t page_index{src_addr >> page_bits}; |
| 205 | std::size_t page_offset{src_addr & page_mask}; | 213 | 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 | |||
| 226 | } | 234 | } |
| 227 | } | 235 | } |
| 228 | 236 | ||
| 229 | void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) { | 237 | void MemoryManager::ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, |
| 238 | const std::size_t size) const { | ||
| 239 | std::size_t remaining_size{size}; | ||
| 240 | std::size_t page_index{src_addr >> page_bits}; | ||
| 241 | std::size_t page_offset{src_addr & page_mask}; | ||
| 242 | |||
| 243 | while (remaining_size > 0) { | ||
| 244 | const std::size_t copy_amount{ | ||
| 245 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | ||
| 246 | const u8* page_pointer = page_table.pointers[page_index]; | ||
| 247 | if (page_pointer) { | ||
| 248 | const u8* src_ptr{page_pointer + page_offset}; | ||
| 249 | std::memcpy(dest_buffer, src_ptr, copy_amount); | ||
| 250 | } else { | ||
| 251 | std::memset(dest_buffer, 0, copy_amount); | ||
| 252 | } | ||
| 253 | page_index++; | ||
| 254 | page_offset = 0; | ||
| 255 | dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; | ||
| 256 | remaining_size -= copy_amount; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size) { | ||
| 230 | std::size_t remaining_size{size}; | 261 | std::size_t remaining_size{size}; |
| 231 | std::size_t page_index{dest_addr >> page_bits}; | 262 | std::size_t page_index{dest_addr >> page_bits}; |
| 232 | std::size_t page_offset{dest_addr & page_mask}; | 263 | std::size_t page_offset{dest_addr & page_mask}; |
| @@ -253,7 +284,28 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std:: | |||
| 253 | } | 284 | } |
| 254 | } | 285 | } |
| 255 | 286 | ||
| 256 | void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size) { | 287 | void MemoryManager::WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, |
| 288 | const std::size_t size) { | ||
| 289 | std::size_t remaining_size{size}; | ||
| 290 | std::size_t page_index{dest_addr >> page_bits}; | ||
| 291 | std::size_t page_offset{dest_addr & page_mask}; | ||
| 292 | |||
| 293 | while (remaining_size > 0) { | ||
| 294 | const std::size_t copy_amount{ | ||
| 295 | std::min(static_cast<std::size_t>(page_size) - page_offset, remaining_size)}; | ||
| 296 | u8* page_pointer = page_table.pointers[page_index]; | ||
| 297 | if (page_pointer) { | ||
| 298 | u8* dest_ptr{page_pointer + page_offset}; | ||
| 299 | std::memcpy(dest_ptr, src_buffer, copy_amount); | ||
| 300 | } | ||
| 301 | page_index++; | ||
| 302 | page_offset = 0; | ||
| 303 | src_buffer = static_cast<const u8*>(src_buffer) + copy_amount; | ||
| 304 | remaining_size -= copy_amount; | ||
| 305 | } | ||
| 306 | } | ||
| 307 | |||
| 308 | void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) { | ||
| 257 | std::size_t remaining_size{size}; | 309 | std::size_t remaining_size{size}; |
| 258 | std::size_t page_index{src_addr >> page_bits}; | 310 | std::size_t page_index{src_addr >> page_bits}; |
| 259 | std::size_t page_offset{src_addr & page_mask}; | 311 | 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 | |||
| 281 | } | 333 | } |
| 282 | } | 334 | } |
| 283 | 335 | ||
| 336 | void MemoryManager::CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size) { | ||
| 337 | std::vector<u8> tmp_buffer(size); | ||
| 338 | ReadBlockUnsafe(src_addr, tmp_buffer.data(), size); | ||
| 339 | WriteBlockUnsafe(dest_addr, tmp_buffer.data(), size); | ||
| 340 | } | ||
| 341 | |||
| 284 | void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, | 342 | void MemoryManager::MapPages(GPUVAddr base, u64 size, u8* memory, Common::PageType type, |
| 285 | VAddr backing_addr) { | 343 | VAddr backing_addr) { |
| 286 | LOG_DEBUG(HW_GPU, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * page_size, | 344 | 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: | |||
| 65 | u8* GetPointer(GPUVAddr addr); | 65 | u8* GetPointer(GPUVAddr addr); |
| 66 | const u8* GetPointer(GPUVAddr addr) const; | 66 | const u8* GetPointer(GPUVAddr addr) const; |
| 67 | 67 | ||
| 68 | void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const; | 68 | // Returns true if the block is continous in host memory, false otherwise |
| 69 | void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size); | 69 | bool IsBlockContinous(const GPUVAddr start, const std::size_t size); |
| 70 | void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size); | 70 | |
| 71 | /** | ||
| 72 | * ReadBlock and WriteBlock are full read and write operations over virtual | ||
| 73 | * GPU Memory. It's important to use these when GPU memory may not be continous | ||
| 74 | * in the Host Memory counterpart. Note: This functions cause Host GPU Memory | ||
| 75 | * Flushes and Invalidations, respectively to each operation. | ||
| 76 | */ | ||
| 77 | void ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const; | ||
| 78 | void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size); | ||
| 79 | void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size); | ||
| 80 | |||
| 81 | /** | ||
| 82 | * ReadBlockUnsafe and WriteBlockUnsafe are special versions of ReadBlock and | ||
| 83 | * WriteBlock respectively. In this versions, no flushing or invalidation is actually | ||
| 84 | * done and their performance is similar to a memcpy. This functions can be used | ||
| 85 | * on either of this 2 scenarios instead of their safe counterpart: | ||
| 86 | * - Memory which is sure to never be represented in the Host GPU. | ||
| 87 | * - Memory Managed by a Cache Manager. Example: Texture Flushing should use | ||
| 88 | * WriteBlockUnsafe instead of WriteBlock since it shouldn't invalidate the texture | ||
| 89 | * being flushed. | ||
| 90 | */ | ||
| 91 | void ReadBlockUnsafe(GPUVAddr src_addr, void* dest_buffer, const std::size_t size) const; | ||
| 92 | void WriteBlockUnsafe(GPUVAddr dest_addr, const void* src_buffer, const std::size_t size); | ||
| 93 | void CopyBlockUnsafe(GPUVAddr dest_addr, GPUVAddr src_addr, const std::size_t size); | ||
| 71 | 94 | ||
| 72 | private: | 95 | private: |
| 73 | using VMAMap = std::map<GPUVAddr, VirtualMemoryArea>; | 96 | 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 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstddef> | ||
| 6 | #include <glad/glad.h> | ||
| 7 | |||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "video_core/renderer_opengl/gl_device.h" | ||
| 10 | |||
| 11 | namespace OpenGL { | ||
| 12 | |||
| 13 | namespace { | ||
| 14 | template <typename T> | ||
| 15 | T GetInteger(GLenum pname) { | ||
| 16 | GLint temporary; | ||
| 17 | glGetIntegerv(pname, &temporary); | ||
| 18 | return static_cast<T>(temporary); | ||
| 19 | } | ||
| 20 | } // Anonymous namespace | ||
| 21 | |||
| 22 | Device::Device() { | ||
| 23 | uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); | ||
| 24 | has_variable_aoffi = TestVariableAoffi(); | ||
| 25 | } | ||
| 26 | |||
| 27 | bool Device::TestVariableAoffi() { | ||
| 28 | const GLchar* AOFFI_TEST = R"(#version 430 core | ||
| 29 | uniform sampler2D tex; | ||
| 30 | uniform ivec2 variable_offset; | ||
| 31 | void main() { | ||
| 32 | gl_Position = textureOffset(tex, vec2(0), variable_offset); | ||
| 33 | } | ||
| 34 | )"; | ||
| 35 | const GLuint shader{glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &AOFFI_TEST)}; | ||
| 36 | GLint link_status{}; | ||
| 37 | glGetProgramiv(shader, GL_LINK_STATUS, &link_status); | ||
| 38 | glDeleteProgram(shader); | ||
| 39 | |||
| 40 | const bool supported{link_status == GL_TRUE}; | ||
| 41 | LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", supported); | ||
| 42 | return supported; | ||
| 43 | } | ||
| 44 | |||
| 45 | } // 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 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | |||
| 9 | namespace OpenGL { | ||
| 10 | |||
| 11 | class Device { | ||
| 12 | public: | ||
| 13 | Device(); | ||
| 14 | |||
| 15 | std::size_t GetUniformBufferAlignment() const { | ||
| 16 | return uniform_buffer_alignment; | ||
| 17 | } | ||
| 18 | |||
| 19 | bool HasVariableAoffi() const { | ||
| 20 | return has_variable_aoffi; | ||
| 21 | } | ||
| 22 | |||
| 23 | private: | ||
| 24 | static bool TestVariableAoffi(); | ||
| 25 | |||
| 26 | std::size_t uniform_buffer_alignment{}; | ||
| 27 | bool has_variable_aoffi{}; | ||
| 28 | }; | ||
| 29 | |||
| 30 | } // namespace OpenGL | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 6034dc489..3cc945235 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 { | |||
| 99 | }; | 99 | }; |
| 100 | 100 | ||
| 101 | RasterizerOpenGL::RasterizerOpenGL(Core::System& system, ScreenInfo& info) | 101 | RasterizerOpenGL::RasterizerOpenGL(Core::System& system, ScreenInfo& info) |
| 102 | : res_cache{*this}, shader_cache{*this, system}, global_cache{*this}, system{system}, | 102 | : res_cache{*this}, shader_cache{*this, system, device}, global_cache{*this}, system{system}, |
| 103 | screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) { | 103 | screen_info{info}, buffer_cache(*this, STREAM_BUFFER_SIZE) { |
| 104 | OpenGLState::ApplyDefaultState(); | 104 | OpenGLState::ApplyDefaultState(); |
| 105 | 105 | ||
| @@ -107,8 +107,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, ScreenInfo& info) | |||
| 107 | state.draw.shader_program = 0; | 107 | state.draw.shader_program = 0; |
| 108 | state.Apply(); | 108 | state.Apply(); |
| 109 | 109 | ||
| 110 | glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); | ||
| 111 | |||
| 112 | LOG_DEBUG(Render_OpenGL, "Sync fixed function OpenGL state here"); | 110 | LOG_DEBUG(Render_OpenGL, "Sync fixed function OpenGL state here"); |
| 113 | CheckExtensions(); | 111 | CheckExtensions(); |
| 114 | } | 112 | } |
| @@ -307,6 +305,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | |||
| 307 | case Maxwell::ShaderProgram::Geometry: | 305 | case Maxwell::ShaderProgram::Geometry: |
| 308 | shader_program_manager->UseTrivialGeometryShader(); | 306 | shader_program_manager->UseTrivialGeometryShader(); |
| 309 | break; | 307 | break; |
| 308 | default: | ||
| 309 | break; | ||
| 310 | } | 310 | } |
| 311 | continue; | 311 | continue; |
| 312 | } | 312 | } |
| @@ -315,8 +315,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { | |||
| 315 | 315 | ||
| 316 | GLShader::MaxwellUniformData ubo{}; | 316 | GLShader::MaxwellUniformData ubo{}; |
| 317 | ubo.SetFromRegs(gpu, stage); | 317 | ubo.SetFromRegs(gpu, stage); |
| 318 | const GLintptr offset = buffer_cache.UploadHostMemory( | 318 | const GLintptr offset = |
| 319 | &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); | 319 | buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment()); |
| 320 | 320 | ||
| 321 | // Bind the emulation info buffer | 321 | // Bind the emulation info buffer |
| 322 | bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), offset, | 322 | bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), offset, |
| @@ -700,23 +700,24 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 700 | // Add space for index buffer (keeping in mind non-core primitives) | 700 | // Add space for index buffer (keeping in mind non-core primitives) |
| 701 | switch (regs.draw.topology) { | 701 | switch (regs.draw.topology) { |
| 702 | case Maxwell::PrimitiveTopology::Quads: | 702 | case Maxwell::PrimitiveTopology::Quads: |
| 703 | buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + | 703 | buffer_size = Common::AlignUp(buffer_size, 4) + |
| 704 | primitive_assembler.CalculateQuadSize(regs.vertex_buffer.count); | 704 | primitive_assembler.CalculateQuadSize(regs.vertex_buffer.count); |
| 705 | break; | 705 | break; |
| 706 | default: | 706 | default: |
| 707 | if (is_indexed) { | 707 | if (is_indexed) { |
| 708 | buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + CalculateIndexBufferSize(); | 708 | buffer_size = Common::AlignUp(buffer_size, 4) + CalculateIndexBufferSize(); |
| 709 | } | 709 | } |
| 710 | break; | 710 | break; |
| 711 | } | 711 | } |
| 712 | 712 | ||
| 713 | // Uniform space for the 5 shader stages | 713 | // Uniform space for the 5 shader stages |
| 714 | buffer_size = | 714 | buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + |
| 715 | Common::AlignUp<std::size_t>(buffer_size, 4) + | 715 | (sizeof(GLShader::MaxwellUniformData) + device.GetUniformBufferAlignment()) * |
| 716 | (sizeof(GLShader::MaxwellUniformData) + uniform_buffer_alignment) * Maxwell::MaxShaderStage; | 716 | Maxwell::MaxShaderStage; |
| 717 | 717 | ||
| 718 | // Add space for at least 18 constant buffers | 718 | // Add space for at least 18 constant buffers |
| 719 | buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment); | 719 | buffer_size += |
| 720 | Maxwell::MaxConstBuffers * (MaxConstbufferSize + device.GetUniformBufferAlignment()); | ||
| 720 | 721 | ||
| 721 | const bool invalidate = buffer_cache.Map(buffer_size); | 722 | const bool invalidate = buffer_cache.Map(buffer_size); |
| 722 | if (invalidate) { | 723 | if (invalidate) { |
| @@ -848,8 +849,8 @@ void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::Shader | |||
| 848 | size = Common::AlignUp(size, sizeof(GLvec4)); | 849 | size = Common::AlignUp(size, sizeof(GLvec4)); |
| 849 | ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big"); | 850 | ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big"); |
| 850 | 851 | ||
| 851 | const GLintptr const_buffer_offset = buffer_cache.UploadMemory( | 852 | const GLintptr const_buffer_offset = |
| 852 | buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment)); | 853 | buffer_cache.UploadMemory(buffer.address, size, device.GetUniformBufferAlignment()); |
| 853 | 854 | ||
| 854 | bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), const_buffer_offset, size); | 855 | bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), const_buffer_offset, size); |
| 855 | } | 856 | } |
| @@ -921,8 +922,8 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { | |||
| 921 | viewport.y = viewport_rect.bottom; | 922 | viewport.y = viewport_rect.bottom; |
| 922 | viewport.width = viewport_rect.GetWidth(); | 923 | viewport.width = viewport_rect.GetWidth(); |
| 923 | viewport.height = viewport_rect.GetHeight(); | 924 | viewport.height = viewport_rect.GetHeight(); |
| 924 | viewport.depth_range_far = regs.viewports[i].depth_range_far; | 925 | viewport.depth_range_far = src.depth_range_far; |
| 925 | viewport.depth_range_near = regs.viewports[i].depth_range_near; | 926 | viewport.depth_range_near = src.depth_range_near; |
| 926 | } | 927 | } |
| 927 | state.depth_clamp.far_plane = regs.view_volume_clip_control.depth_clamp_far != 0; | 928 | state.depth_clamp.far_plane = regs.view_volume_clip_control.depth_clamp_far != 0; |
| 928 | state.depth_clamp.near_plane = regs.view_volume_clip_control.depth_clamp_near != 0; | 929 | state.depth_clamp.near_plane = regs.view_volume_clip_control.depth_clamp_near != 0; |
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 @@ | |||
| 21 | #include "video_core/rasterizer_cache.h" | 21 | #include "video_core/rasterizer_cache.h" |
| 22 | #include "video_core/rasterizer_interface.h" | 22 | #include "video_core/rasterizer_interface.h" |
| 23 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | 23 | #include "video_core/renderer_opengl/gl_buffer_cache.h" |
| 24 | #include "video_core/renderer_opengl/gl_device.h" | ||
| 24 | #include "video_core/renderer_opengl/gl_global_cache.h" | 25 | #include "video_core/renderer_opengl/gl_global_cache.h" |
| 25 | #include "video_core/renderer_opengl/gl_primitive_assembler.h" | 26 | #include "video_core/renderer_opengl/gl_primitive_assembler.h" |
| 26 | #include "video_core/renderer_opengl/gl_rasterizer_cache.h" | 27 | #include "video_core/renderer_opengl/gl_rasterizer_cache.h" |
| @@ -172,6 +173,7 @@ private: | |||
| 172 | /// but are needed for correct emulation | 173 | /// but are needed for correct emulation |
| 173 | void CheckExtensions(); | 174 | void CheckExtensions(); |
| 174 | 175 | ||
| 176 | const Device device; | ||
| 175 | OpenGLState state; | 177 | OpenGLState state; |
| 176 | 178 | ||
| 177 | RasterizerCacheOpenGL res_cache; | 179 | RasterizerCacheOpenGL res_cache; |
| @@ -180,7 +182,6 @@ private: | |||
| 180 | SamplerCacheOpenGL sampler_cache; | 182 | SamplerCacheOpenGL sampler_cache; |
| 181 | 183 | ||
| 182 | Core::System& system; | 184 | Core::System& system; |
| 183 | |||
| 184 | ScreenInfo& screen_info; | 185 | ScreenInfo& screen_info; |
| 185 | 186 | ||
| 186 | std::unique_ptr<GLShader::ProgramManager> shader_program_manager; | 187 | std::unique_ptr<GLShader::ProgramManager> shader_program_manager; |
| @@ -196,7 +197,6 @@ private: | |||
| 196 | static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; | 197 | static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; |
| 197 | OGLBufferCache buffer_cache; | 198 | OGLBufferCache buffer_cache; |
| 198 | PrimitiveAssembler primitive_assembler{buffer_cache}; | 199 | PrimitiveAssembler primitive_assembler{buffer_cache}; |
| 199 | GLint uniform_buffer_alignment; | ||
| 200 | 200 | ||
| 201 | BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER}; | 201 | BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER}; |
| 202 | BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER}; | 202 | 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 67f99a568..7d8fb670e 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -642,13 +642,16 @@ void CachedSurface::LoadGLBuffer(RasterizerTemporaryMemory& res_cache_tmp_mem) { | |||
| 642 | SwizzleFunc(MortonSwizzleMode::MortonToLinear, params, gl_buffer[i], i); | 642 | SwizzleFunc(MortonSwizzleMode::MortonToLinear, params, gl_buffer[i], i); |
| 643 | } else { | 643 | } else { |
| 644 | const u32 bpp = params.GetFormatBpp() / 8; | 644 | const u32 bpp = params.GetFormatBpp() / 8; |
| 645 | const u32 copy_size = params.width * bpp; | 645 | const u32 copy_size = (params.width * bpp + GetDefaultBlockWidth(params.pixel_format) - 1) / |
| 646 | GetDefaultBlockWidth(params.pixel_format); | ||
| 646 | if (params.pitch == copy_size) { | 647 | if (params.pitch == copy_size) { |
| 647 | std::memcpy(gl_buffer[0].data(), params.host_ptr, params.size_in_bytes_gl); | 648 | std::memcpy(gl_buffer[0].data(), params.host_ptr, params.size_in_bytes_gl); |
| 648 | } else { | 649 | } else { |
| 650 | const u32 height = (params.height + GetDefaultBlockHeight(params.pixel_format) - 1) / | ||
| 651 | GetDefaultBlockHeight(params.pixel_format); | ||
| 649 | const u8* start{params.host_ptr}; | 652 | const u8* start{params.host_ptr}; |
| 650 | u8* write_to = gl_buffer[0].data(); | 653 | u8* write_to = gl_buffer[0].data(); |
| 651 | for (u32 h = params.height; h > 0; h--) { | 654 | for (u32 h = height; h > 0; h--) { |
| 652 | std::memcpy(write_to, start, copy_size); | 655 | std::memcpy(write_to, start, copy_size); |
| 653 | start += params.pitch; | 656 | start += params.pitch; |
| 654 | write_to += copy_size; | 657 | 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..b1c8f7c35 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) { | |||
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | /// Gets the shader program code from memory for the specified address | 40 | /// Gets the shader program code from memory for the specified address |
| 41 | ProgramCode GetShaderCode(const u8* host_ptr) { | 41 | ProgramCode GetShaderCode(Tegra::MemoryManager& memory_manager, const GPUVAddr gpu_addr, |
| 42 | const u8* host_ptr) { | ||
| 42 | ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH); | 43 | ProgramCode program_code(VideoCommon::Shader::MAX_PROGRAM_LENGTH); |
| 43 | ASSERT_OR_EXECUTE(host_ptr != nullptr, { | 44 | ASSERT_OR_EXECUTE(host_ptr != nullptr, { |
| 44 | std::fill(program_code.begin(), program_code.end(), 0); | 45 | std::fill(program_code.begin(), program_code.end(), 0); |
| 45 | return program_code; | 46 | return program_code; |
| 46 | }); | 47 | }); |
| 47 | std::memcpy(program_code.data(), host_ptr, program_code.size() * sizeof(u64)); | 48 | memory_manager.ReadBlockUnsafe(gpu_addr, program_code.data(), |
| 49 | program_code.size() * sizeof(u64)); | ||
| 48 | return program_code; | 50 | return program_code; |
| 49 | } | 51 | } |
| 50 | 52 | ||
| @@ -134,8 +136,8 @@ u64 GetUniqueIdentifier(Maxwell::ShaderProgram program_type, const ProgramCode& | |||
| 134 | } | 136 | } |
| 135 | 137 | ||
| 136 | /// Creates an unspecialized program from code streams | 138 | /// Creates an unspecialized program from code streams |
| 137 | GLShader::ProgramResult CreateProgram(Maxwell::ShaderProgram program_type, ProgramCode program_code, | 139 | GLShader::ProgramResult CreateProgram(const Device& device, Maxwell::ShaderProgram program_type, |
| 138 | ProgramCode program_code_b) { | 140 | ProgramCode program_code, ProgramCode program_code_b) { |
| 139 | GLShader::ShaderSetup setup(program_code); | 141 | GLShader::ShaderSetup setup(program_code); |
| 140 | if (program_type == Maxwell::ShaderProgram::VertexA) { | 142 | if (program_type == Maxwell::ShaderProgram::VertexA) { |
| 141 | // VertexB is always enabled, so when VertexA is enabled, we have two vertex shaders. | 143 | // 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 | |||
| 149 | switch (program_type) { | 151 | switch (program_type) { |
| 150 | case Maxwell::ShaderProgram::VertexA: | 152 | case Maxwell::ShaderProgram::VertexA: |
| 151 | case Maxwell::ShaderProgram::VertexB: | 153 | case Maxwell::ShaderProgram::VertexB: |
| 152 | return GLShader::GenerateVertexShader(setup); | 154 | return GLShader::GenerateVertexShader(device, setup); |
| 153 | case Maxwell::ShaderProgram::Geometry: | 155 | case Maxwell::ShaderProgram::Geometry: |
| 154 | return GLShader::GenerateGeometryShader(setup); | 156 | return GLShader::GenerateGeometryShader(device, setup); |
| 155 | case Maxwell::ShaderProgram::Fragment: | 157 | case Maxwell::ShaderProgram::Fragment: |
| 156 | return GLShader::GenerateFragmentShader(setup); | 158 | return GLShader::GenerateFragmentShader(device, setup); |
| 157 | default: | 159 | default: |
| 158 | LOG_CRITICAL(HW_GPU, "Unimplemented program_type={}", static_cast<u32>(program_type)); | 160 | LOG_CRITICAL(HW_GPU, "Unimplemented program_type={}", static_cast<u32>(program_type)); |
| 159 | UNREACHABLE(); | 161 | UNREACHABLE(); |
| @@ -212,22 +214,20 @@ std::set<GLenum> GetSupportedFormats() { | |||
| 212 | return supported_formats; | 214 | return supported_formats; |
| 213 | } | 215 | } |
| 214 | 216 | ||
| 215 | } // namespace | 217 | } // Anonymous namespace |
| 216 | 218 | ||
| 217 | CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier, | 219 | CachedShader::CachedShader(const Device& device, VAddr cpu_addr, u64 unique_identifier, |
| 218 | Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache, | 220 | Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache, |
| 219 | const PrecompiledPrograms& precompiled_programs, | 221 | const PrecompiledPrograms& precompiled_programs, |
| 220 | ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr) | 222 | ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr) |
| 221 | : RasterizerCacheObject{host_ptr}, host_ptr{host_ptr}, cpu_addr{cpu_addr}, | 223 | : RasterizerCacheObject{host_ptr}, host_ptr{host_ptr}, cpu_addr{cpu_addr}, |
| 222 | unique_identifier{unique_identifier}, program_type{program_type}, disk_cache{disk_cache}, | 224 | unique_identifier{unique_identifier}, program_type{program_type}, disk_cache{disk_cache}, |
| 223 | precompiled_programs{precompiled_programs} { | 225 | precompiled_programs{precompiled_programs} { |
| 224 | 226 | const std::size_t code_size{CalculateProgramSize(program_code)}; | |
| 225 | const std::size_t code_size = CalculateProgramSize(program_code); | 227 | const std::size_t code_size_b{program_code_b.empty() ? 0 |
| 226 | const std::size_t code_size_b = | 228 | : CalculateProgramSize(program_code_b)}; |
| 227 | program_code_b.empty() ? 0 : CalculateProgramSize(program_code_b); | 229 | GLShader::ProgramResult program_result{ |
| 228 | 230 | CreateProgram(device, program_type, program_code, program_code_b)}; | |
| 229 | GLShader::ProgramResult program_result = | ||
| 230 | CreateProgram(program_type, program_code, program_code_b); | ||
| 231 | if (program_result.first.empty()) { | 231 | if (program_result.first.empty()) { |
| 232 | // TODO(Rodrigo): Unimplemented shader stages hit here, avoid using these for now | 232 | // TODO(Rodrigo): Unimplemented shader stages hit here, avoid using these for now |
| 233 | return; | 233 | return; |
| @@ -251,7 +251,6 @@ CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier, | |||
| 251 | : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier}, | 251 | : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier}, |
| 252 | program_type{program_type}, disk_cache{disk_cache}, precompiled_programs{ | 252 | program_type{program_type}, disk_cache{disk_cache}, precompiled_programs{ |
| 253 | precompiled_programs} { | 253 | precompiled_programs} { |
| 254 | |||
| 255 | code = std::move(result.first); | 254 | code = std::move(result.first); |
| 256 | entries = result.second; | 255 | entries = result.second; |
| 257 | shader_length = entries.shader_length; | 256 | shader_length = entries.shader_length; |
| @@ -344,8 +343,9 @@ ShaderDiskCacheUsage CachedShader::GetUsage(GLenum primitive_mode, | |||
| 344 | return {unique_identifier, base_bindings, primitive_mode}; | 343 | return {unique_identifier, base_bindings, primitive_mode}; |
| 345 | } | 344 | } |
| 346 | 345 | ||
| 347 | ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system) | 346 | ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, |
| 348 | : RasterizerCache{rasterizer}, disk_cache{system} {} | 347 | const Device& device) |
| 348 | : RasterizerCache{rasterizer}, disk_cache{system}, device{device} {} | ||
| 349 | 349 | ||
| 350 | void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, | 350 | void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, |
| 351 | const VideoCore::DiskResourceLoadCallback& callback) { | 351 | const VideoCore::DiskResourceLoadCallback& callback) { |
| @@ -363,6 +363,10 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
| 363 | if (stop_loading) | 363 | if (stop_loading) |
| 364 | return; | 364 | return; |
| 365 | 365 | ||
| 366 | // Track if precompiled cache was altered during loading to know if we have to serialize the | ||
| 367 | // virtual precompiled cache file back to the hard drive | ||
| 368 | bool precompiled_cache_altered = false; | ||
| 369 | |||
| 366 | // Build shaders | 370 | // Build shaders |
| 367 | if (callback) | 371 | if (callback) |
| 368 | callback(VideoCore::LoadCallbackStage::Build, 0, usages.size()); | 372 | callback(VideoCore::LoadCallbackStage::Build, 0, usages.size()); |
| @@ -384,6 +388,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
| 384 | if (!shader) { | 388 | if (!shader) { |
| 385 | // Invalidate the precompiled cache if a shader dumped shader was rejected | 389 | // Invalidate the precompiled cache if a shader dumped shader was rejected |
| 386 | disk_cache.InvalidatePrecompiled(); | 390 | disk_cache.InvalidatePrecompiled(); |
| 391 | precompiled_cache_altered = true; | ||
| 387 | dumps.clear(); | 392 | dumps.clear(); |
| 388 | } | 393 | } |
| 389 | } | 394 | } |
| @@ -405,8 +410,13 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading, | |||
| 405 | if (dumps.find(usage) == dumps.end()) { | 410 | if (dumps.find(usage) == dumps.end()) { |
| 406 | const auto& program = precompiled_programs.at(usage); | 411 | const auto& program = precompiled_programs.at(usage); |
| 407 | disk_cache.SaveDump(usage, program->handle); | 412 | disk_cache.SaveDump(usage, program->handle); |
| 413 | precompiled_cache_altered = true; | ||
| 408 | } | 414 | } |
| 409 | } | 415 | } |
| 416 | |||
| 417 | if (precompiled_cache_altered) { | ||
| 418 | disk_cache.SaveVirtualPrecompiledFile(); | ||
| 419 | } | ||
| 410 | } | 420 | } |
| 411 | 421 | ||
| 412 | CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram( | 422 | CachedProgram ShaderCacheOpenGL::GeneratePrecompiledProgram( |
| @@ -439,17 +449,18 @@ std::unordered_map<u64, UnspecializedShader> ShaderCacheOpenGL::GenerateUnspecia | |||
| 439 | const std::unordered_map<u64, ShaderDiskCacheDecompiled>& decompiled) { | 449 | const std::unordered_map<u64, ShaderDiskCacheDecompiled>& decompiled) { |
| 440 | std::unordered_map<u64, UnspecializedShader> unspecialized; | 450 | std::unordered_map<u64, UnspecializedShader> unspecialized; |
| 441 | 451 | ||
| 442 | if (callback) | 452 | if (callback) { |
| 443 | callback(VideoCore::LoadCallbackStage::Decompile, 0, raws.size()); | 453 | callback(VideoCore::LoadCallbackStage::Decompile, 0, raws.size()); |
| 454 | } | ||
| 444 | 455 | ||
| 445 | for (std::size_t i = 0; i < raws.size(); ++i) { | 456 | for (std::size_t i = 0; i < raws.size(); ++i) { |
| 446 | if (stop_loading) | 457 | if (stop_loading) { |
| 447 | return {}; | 458 | return {}; |
| 448 | 459 | } | |
| 449 | const auto& raw{raws[i]}; | 460 | const auto& raw{raws[i]}; |
| 450 | const u64 unique_identifier = raw.GetUniqueIdentifier(); | 461 | const u64 unique_identifier{raw.GetUniqueIdentifier()}; |
| 451 | const u64 calculated_hash = | 462 | const u64 calculated_hash{ |
| 452 | GetUniqueIdentifier(raw.GetProgramType(), raw.GetProgramCode(), raw.GetProgramCodeB()); | 463 | GetUniqueIdentifier(raw.GetProgramType(), raw.GetProgramCode(), raw.GetProgramCodeB())}; |
| 453 | if (unique_identifier != calculated_hash) { | 464 | if (unique_identifier != calculated_hash) { |
| 454 | LOG_ERROR( | 465 | LOG_ERROR( |
| 455 | Render_OpenGL, | 466 | Render_OpenGL, |
| @@ -466,8 +477,8 @@ std::unordered_map<u64, UnspecializedShader> ShaderCacheOpenGL::GenerateUnspecia | |||
| 466 | result = {stored_decompiled.code, stored_decompiled.entries}; | 477 | result = {stored_decompiled.code, stored_decompiled.entries}; |
| 467 | } else { | 478 | } else { |
| 468 | // Otherwise decompile the shader at boot and save the result to the decompiled file | 479 | // Otherwise decompile the shader at boot and save the result to the decompiled file |
| 469 | result = | 480 | result = CreateProgram(device, raw.GetProgramType(), raw.GetProgramCode(), |
| 470 | CreateProgram(raw.GetProgramType(), raw.GetProgramCode(), raw.GetProgramCodeB()); | 481 | raw.GetProgramCodeB()); |
| 471 | disk_cache.SaveDecompiled(unique_identifier, result.first, result.second); | 482 | disk_cache.SaveDecompiled(unique_identifier, result.first, result.second); |
| 472 | } | 483 | } |
| 473 | 484 | ||
| @@ -477,8 +488,9 @@ std::unordered_map<u64, UnspecializedShader> ShaderCacheOpenGL::GenerateUnspecia | |||
| 477 | {raw.GetUniqueIdentifier(), | 488 | {raw.GetUniqueIdentifier(), |
| 478 | {std::move(result.first), std::move(result.second), raw.GetProgramType()}}); | 489 | {std::move(result.first), std::move(result.second), raw.GetProgramType()}}); |
| 479 | 490 | ||
| 480 | if (callback) | 491 | if (callback) { |
| 481 | callback(VideoCore::LoadCallbackStage::Decompile, i, raws.size()); | 492 | callback(VideoCore::LoadCallbackStage::Decompile, i, raws.size()); |
| 493 | } | ||
| 482 | } | 494 | } |
| 483 | return unspecialized; | 495 | return unspecialized; |
| 484 | } | 496 | } |
| @@ -497,11 +509,12 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | |||
| 497 | 509 | ||
| 498 | if (!shader) { | 510 | if (!shader) { |
| 499 | // No shader found - create a new one | 511 | // No shader found - create a new one |
| 500 | ProgramCode program_code{GetShaderCode(host_ptr)}; | 512 | ProgramCode program_code{GetShaderCode(memory_manager, program_addr, host_ptr)}; |
| 501 | ProgramCode program_code_b; | 513 | ProgramCode program_code_b; |
| 502 | if (program == Maxwell::ShaderProgram::VertexA) { | 514 | if (program == Maxwell::ShaderProgram::VertexA) { |
| 503 | program_code_b = GetShaderCode( | 515 | const GPUVAddr program_addr_b{GetShaderAddress(Maxwell::ShaderProgram::VertexB)}; |
| 504 | memory_manager.GetPointer(GetShaderAddress(Maxwell::ShaderProgram::VertexB))); | 516 | program_code_b = GetShaderCode(memory_manager, program_addr_b, |
| 517 | memory_manager.GetPointer(program_addr_b)); | ||
| 505 | } | 518 | } |
| 506 | const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b); | 519 | const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b); |
| 507 | const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)}; | 520 | const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)}; |
| @@ -512,7 +525,7 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | |||
| 512 | precompiled_programs, found->second, host_ptr); | 525 | precompiled_programs, found->second, host_ptr); |
| 513 | } else { | 526 | } else { |
| 514 | shader = std::make_shared<CachedShader>( | 527 | shader = std::make_shared<CachedShader>( |
| 515 | cpu_addr, unique_identifier, program, disk_cache, precompiled_programs, | 528 | device, cpu_addr, unique_identifier, program, disk_cache, precompiled_programs, |
| 516 | std::move(program_code), std::move(program_code_b), host_ptr); | 529 | std::move(program_code), std::move(program_code_b), host_ptr); |
| 517 | } | 530 | } |
| 518 | Register(shader); | 531 | 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 eae771c08..31b979987 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; | |||
| 27 | namespace OpenGL { | 27 | namespace OpenGL { |
| 28 | 28 | ||
| 29 | class CachedShader; | 29 | class CachedShader; |
| 30 | class Device; | ||
| 30 | class RasterizerOpenGL; | 31 | class RasterizerOpenGL; |
| 31 | struct UnspecializedShader; | 32 | struct UnspecializedShader; |
| 32 | 33 | ||
| @@ -38,7 +39,7 @@ using PrecompiledShaders = std::unordered_map<u64, GLShader::ProgramResult>; | |||
| 38 | 39 | ||
| 39 | class CachedShader final : public RasterizerCacheObject { | 40 | class CachedShader final : public RasterizerCacheObject { |
| 40 | public: | 41 | public: |
| 41 | explicit CachedShader(VAddr cpu_addr, u64 unique_identifier, | 42 | explicit CachedShader(const Device& device, VAddr cpu_addr, u64 unique_identifier, |
| 42 | Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache, | 43 | Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache, |
| 43 | const PrecompiledPrograms& precompiled_programs, | 44 | const PrecompiledPrograms& precompiled_programs, |
| 44 | ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr); | 45 | ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr); |
| @@ -109,7 +110,8 @@ private: | |||
| 109 | 110 | ||
| 110 | class ShaderCacheOpenGL final : public RasterizerCache<Shader> { | 111 | class ShaderCacheOpenGL final : public RasterizerCache<Shader> { |
| 111 | public: | 112 | public: |
| 112 | explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system); | 113 | explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system, |
| 114 | const Device& device); | ||
| 113 | 115 | ||
| 114 | /// Loads disk cache for the current game | 116 | /// Loads disk cache for the current game |
| 115 | void LoadDiskCache(const std::atomic_bool& stop_loading, | 117 | void LoadDiskCache(const std::atomic_bool& stop_loading, |
| @@ -131,6 +133,8 @@ private: | |||
| 131 | CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump, | 133 | CachedProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump, |
| 132 | const std::set<GLenum>& supported_formats); | 134 | const std::set<GLenum>& supported_formats); |
| 133 | 135 | ||
| 136 | const Device& device; | ||
| 137 | |||
| 134 | std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; | 138 | std::array<Shader, Maxwell::MaxShaderProgram> last_shaders; |
| 135 | 139 | ||
| 136 | ShaderDiskCacheOpenGL disk_cache; | 140 | 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 445048daf..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 @@ | |||
| 15 | #include "common/assert.h" | 15 | #include "common/assert.h" |
| 16 | #include "common/common_types.h" | 16 | #include "common/common_types.h" |
| 17 | #include "video_core/engines/maxwell_3d.h" | 17 | #include "video_core/engines/maxwell_3d.h" |
| 18 | #include "video_core/renderer_opengl/gl_device.h" | ||
| 18 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 19 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 19 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 20 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| 20 | #include "video_core/shader/shader_ir.h" | 21 | #include "video_core/shader/shader_ir.h" |
| @@ -119,14 +120,10 @@ std::string GetTopologyName(Tegra::Shader::OutputTopology topology) { | |||
| 119 | 120 | ||
| 120 | /// Returns true if an object has to be treated as precise | 121 | /// Returns true if an object has to be treated as precise |
| 121 | bool IsPrecise(Operation operand) { | 122 | bool IsPrecise(Operation operand) { |
| 122 | const auto& meta = operand.GetMeta(); | 123 | const auto& meta{operand.GetMeta()}; |
| 123 | |||
| 124 | if (const auto arithmetic = std::get_if<MetaArithmetic>(&meta)) { | 124 | if (const auto arithmetic = std::get_if<MetaArithmetic>(&meta)) { |
| 125 | return arithmetic->precise; | 125 | return arithmetic->precise; |
| 126 | } | 126 | } |
| 127 | if (const auto half_arithmetic = std::get_if<MetaHalfArithmetic>(&meta)) { | ||
| 128 | return half_arithmetic->precise; | ||
| 129 | } | ||
| 130 | return false; | 127 | return false; |
| 131 | } | 128 | } |
| 132 | 129 | ||
| @@ -139,8 +136,9 @@ bool IsPrecise(Node node) { | |||
| 139 | 136 | ||
| 140 | class GLSLDecompiler final { | 137 | class GLSLDecompiler final { |
| 141 | public: | 138 | public: |
| 142 | explicit GLSLDecompiler(const ShaderIR& ir, ShaderStage stage, std::string suffix) | 139 | explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderStage stage, |
| 143 | : ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {} | 140 | std::string suffix) |
| 141 | : device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {} | ||
| 144 | 142 | ||
| 145 | void Decompile() { | 143 | void Decompile() { |
| 146 | DeclareVertex(); | 144 | DeclareVertex(); |
| @@ -627,28 +625,7 @@ private: | |||
| 627 | } | 625 | } |
| 628 | 626 | ||
| 629 | std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) { | 627 | std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) { |
| 630 | std::string value = VisitOperand(operation, operand_index); | 628 | return CastOperand(VisitOperand(operation, operand_index), type); |
| 631 | switch (type) { | ||
| 632 | case Type::HalfFloat: { | ||
| 633 | const auto half_meta = std::get_if<MetaHalfArithmetic>(&operation.GetMeta()); | ||
| 634 | if (!half_meta) { | ||
| 635 | value = "toHalf2(" + value + ')'; | ||
| 636 | } | ||
| 637 | |||
| 638 | switch (half_meta->types.at(operand_index)) { | ||
| 639 | case Tegra::Shader::HalfType::H0_H1: | ||
| 640 | return "toHalf2(" + value + ')'; | ||
| 641 | case Tegra::Shader::HalfType::F32: | ||
| 642 | return "vec2(" + value + ')'; | ||
| 643 | case Tegra::Shader::HalfType::H0_H0: | ||
| 644 | return "vec2(toHalf2(" + value + ")[0])"; | ||
| 645 | case Tegra::Shader::HalfType::H1_H1: | ||
| 646 | return "vec2(toHalf2(" + value + ")[1])"; | ||
| 647 | } | ||
| 648 | } | ||
| 649 | default: | ||
| 650 | return CastOperand(value, type); | ||
| 651 | } | ||
| 652 | } | 629 | } |
| 653 | 630 | ||
| 654 | std::string CastOperand(const std::string& value, Type type) const { | 631 | std::string CastOperand(const std::string& value, Type type) const { |
| @@ -662,9 +639,7 @@ private: | |||
| 662 | case Type::Uint: | 639 | case Type::Uint: |
| 663 | return "ftou(" + value + ')'; | 640 | return "ftou(" + value + ')'; |
| 664 | case Type::HalfFloat: | 641 | case Type::HalfFloat: |
| 665 | // Can't be handled as a stand-alone value | 642 | return "toHalf2(" + value + ')'; |
| 666 | UNREACHABLE(); | ||
| 667 | return value; | ||
| 668 | } | 643 | } |
| 669 | UNREACHABLE(); | 644 | UNREACHABLE(); |
| 670 | return value; | 645 | return value; |
| @@ -829,8 +804,12 @@ private: | |||
| 829 | // Inline the string as an immediate integer in GLSL (AOFFI arguments are required | 804 | // Inline the string as an immediate integer in GLSL (AOFFI arguments are required |
| 830 | // to be constant by the standard). | 805 | // to be constant by the standard). |
| 831 | expr += std::to_string(static_cast<s32>(immediate->GetValue())); | 806 | expr += std::to_string(static_cast<s32>(immediate->GetValue())); |
| 832 | } else { | 807 | } else if (device.HasVariableAoffi()) { |
| 808 | // Avoid using variable AOFFI on unsupported devices. | ||
| 833 | expr += "ftoi(" + Visit(operand) + ')'; | 809 | expr += "ftoi(" + Visit(operand) + ')'; |
| 810 | } else { | ||
| 811 | // Insert 0 on devices not supporting variable AOFFI. | ||
| 812 | expr += '0'; | ||
| 834 | } | 813 | } |
| 835 | if (index + 1 < aoffi.size()) { | 814 | if (index + 1 < aoffi.size()) { |
| 836 | expr += ", "; | 815 | expr += ", "; |
| @@ -1083,13 +1062,40 @@ private: | |||
| 1083 | return BitwiseCastResult(value, Type::HalfFloat); | 1062 | return BitwiseCastResult(value, Type::HalfFloat); |
| 1084 | } | 1063 | } |
| 1085 | 1064 | ||
| 1065 | std::string HClamp(Operation operation) { | ||
| 1066 | const std::string value = VisitOperand(operation, 0, Type::HalfFloat); | ||
| 1067 | const std::string min = VisitOperand(operation, 1, Type::Float); | ||
| 1068 | const std::string max = VisitOperand(operation, 2, Type::Float); | ||
| 1069 | const std::string clamped = "clamp(" + value + ", vec2(" + min + "), vec2(" + max + "))"; | ||
| 1070 | return ApplyPrecise(operation, BitwiseCastResult(clamped, Type::HalfFloat)); | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | std::string HUnpack(Operation operation) { | ||
| 1074 | const std::string operand{VisitOperand(operation, 0, Type::HalfFloat)}; | ||
| 1075 | const auto value = [&]() -> std::string { | ||
| 1076 | switch (std::get<Tegra::Shader::HalfType>(operation.GetMeta())) { | ||
| 1077 | case Tegra::Shader::HalfType::H0_H1: | ||
| 1078 | return operand; | ||
| 1079 | case Tegra::Shader::HalfType::F32: | ||
| 1080 | return "vec2(fromHalf2(" + operand + "))"; | ||
| 1081 | case Tegra::Shader::HalfType::H0_H0: | ||
| 1082 | return "vec2(" + operand + "[0])"; | ||
| 1083 | case Tegra::Shader::HalfType::H1_H1: | ||
| 1084 | return "vec2(" + operand + "[1])"; | ||
| 1085 | } | ||
| 1086 | UNREACHABLE(); | ||
| 1087 | return "0"; | ||
| 1088 | }(); | ||
| 1089 | return "fromHalf2(" + value + ')'; | ||
| 1090 | } | ||
| 1091 | |||
| 1086 | std::string HMergeF32(Operation operation) { | 1092 | std::string HMergeF32(Operation operation) { |
| 1087 | return "float(toHalf2(" + Visit(operation[0]) + ")[0])"; | 1093 | return "float(toHalf2(" + Visit(operation[0]) + ")[0])"; |
| 1088 | } | 1094 | } |
| 1089 | 1095 | ||
| 1090 | std::string HMergeH0(Operation operation) { | 1096 | std::string HMergeH0(Operation operation) { |
| 1091 | return "fromHalf2(vec2(toHalf2(" + Visit(operation[0]) + ")[1], toHalf2(" + | 1097 | return "fromHalf2(vec2(toHalf2(" + Visit(operation[1]) + ")[0], toHalf2(" + |
| 1092 | Visit(operation[1]) + ")[0]))"; | 1098 | Visit(operation[0]) + ")[1]))"; |
| 1093 | } | 1099 | } |
| 1094 | 1100 | ||
| 1095 | std::string HMergeH1(Operation operation) { | 1101 | std::string HMergeH1(Operation operation) { |
| @@ -1189,34 +1195,46 @@ private: | |||
| 1189 | return GenerateUnary(operation, "any", Type::Bool, Type::Bool2); | 1195 | return GenerateUnary(operation, "any", Type::Bool, Type::Bool2); |
| 1190 | } | 1196 | } |
| 1191 | 1197 | ||
| 1198 | template <bool with_nan> | ||
| 1199 | std::string GenerateHalfComparison(Operation operation, std::string compare_op) { | ||
| 1200 | std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2, | ||
| 1201 | Type::HalfFloat, Type::HalfFloat)}; | ||
| 1202 | if constexpr (!with_nan) { | ||
| 1203 | return comparison; | ||
| 1204 | } | ||
| 1205 | return "halfFloatNanComparison(" + comparison + ", " + | ||
| 1206 | VisitOperand(operation, 0, Type::HalfFloat) + ", " + | ||
| 1207 | VisitOperand(operation, 1, Type::HalfFloat) + ')'; | ||
| 1208 | } | ||
| 1209 | |||
| 1210 | template <bool with_nan> | ||
| 1192 | std::string Logical2HLessThan(Operation operation) { | 1211 | std::string Logical2HLessThan(Operation operation) { |
| 1193 | return GenerateBinaryCall(operation, "lessThan", Type::Bool2, Type::HalfFloat, | 1212 | return GenerateHalfComparison<with_nan>(operation, "lessThan"); |
| 1194 | Type::HalfFloat); | ||
| 1195 | } | 1213 | } |
| 1196 | 1214 | ||
| 1215 | template <bool with_nan> | ||
| 1197 | std::string Logical2HEqual(Operation operation) { | 1216 | std::string Logical2HEqual(Operation operation) { |
| 1198 | return GenerateBinaryCall(operation, "equal", Type::Bool2, Type::HalfFloat, | 1217 | return GenerateHalfComparison<with_nan>(operation, "equal"); |
| 1199 | Type::HalfFloat); | ||
| 1200 | } | 1218 | } |
| 1201 | 1219 | ||
| 1220 | template <bool with_nan> | ||
| 1202 | std::string Logical2HLessEqual(Operation operation) { | 1221 | std::string Logical2HLessEqual(Operation operation) { |
| 1203 | return GenerateBinaryCall(operation, "lessThanEqual", Type::Bool2, Type::HalfFloat, | 1222 | return GenerateHalfComparison<with_nan>(operation, "lessThanEqual"); |
| 1204 | Type::HalfFloat); | ||
| 1205 | } | 1223 | } |
| 1206 | 1224 | ||
| 1225 | template <bool with_nan> | ||
| 1207 | std::string Logical2HGreaterThan(Operation operation) { | 1226 | std::string Logical2HGreaterThan(Operation operation) { |
| 1208 | return GenerateBinaryCall(operation, "greaterThan", Type::Bool2, Type::HalfFloat, | 1227 | return GenerateHalfComparison<with_nan>(operation, "greaterThan"); |
| 1209 | Type::HalfFloat); | ||
| 1210 | } | 1228 | } |
| 1211 | 1229 | ||
| 1230 | template <bool with_nan> | ||
| 1212 | std::string Logical2HNotEqual(Operation operation) { | 1231 | std::string Logical2HNotEqual(Operation operation) { |
| 1213 | return GenerateBinaryCall(operation, "notEqual", Type::Bool2, Type::HalfFloat, | 1232 | return GenerateHalfComparison<with_nan>(operation, "notEqual"); |
| 1214 | Type::HalfFloat); | ||
| 1215 | } | 1233 | } |
| 1216 | 1234 | ||
| 1235 | template <bool with_nan> | ||
| 1217 | std::string Logical2HGreaterEqual(Operation operation) { | 1236 | std::string Logical2HGreaterEqual(Operation operation) { |
| 1218 | return GenerateBinaryCall(operation, "greaterThanEqual", Type::Bool2, Type::HalfFloat, | 1237 | return GenerateHalfComparison<with_nan>(operation, "greaterThanEqual"); |
| 1219 | Type::HalfFloat); | ||
| 1220 | } | 1238 | } |
| 1221 | 1239 | ||
| 1222 | std::string Texture(Operation operation) { | 1240 | std::string Texture(Operation operation) { |
| @@ -1505,6 +1523,8 @@ private: | |||
| 1505 | &GLSLDecompiler::Fma<Type::HalfFloat>, | 1523 | &GLSLDecompiler::Fma<Type::HalfFloat>, |
| 1506 | &GLSLDecompiler::Absolute<Type::HalfFloat>, | 1524 | &GLSLDecompiler::Absolute<Type::HalfFloat>, |
| 1507 | &GLSLDecompiler::HNegate, | 1525 | &GLSLDecompiler::HNegate, |
| 1526 | &GLSLDecompiler::HClamp, | ||
| 1527 | &GLSLDecompiler::HUnpack, | ||
| 1508 | &GLSLDecompiler::HMergeF32, | 1528 | &GLSLDecompiler::HMergeF32, |
| 1509 | &GLSLDecompiler::HMergeH0, | 1529 | &GLSLDecompiler::HMergeH0, |
| 1510 | &GLSLDecompiler::HMergeH1, | 1530 | &GLSLDecompiler::HMergeH1, |
| @@ -1541,12 +1561,18 @@ private: | |||
| 1541 | &GLSLDecompiler::LogicalNotEqual<Type::Uint>, | 1561 | &GLSLDecompiler::LogicalNotEqual<Type::Uint>, |
| 1542 | &GLSLDecompiler::LogicalGreaterEqual<Type::Uint>, | 1562 | &GLSLDecompiler::LogicalGreaterEqual<Type::Uint>, |
| 1543 | 1563 | ||
| 1544 | &GLSLDecompiler::Logical2HLessThan, | 1564 | &GLSLDecompiler::Logical2HLessThan<false>, |
| 1545 | &GLSLDecompiler::Logical2HEqual, | 1565 | &GLSLDecompiler::Logical2HEqual<false>, |
| 1546 | &GLSLDecompiler::Logical2HLessEqual, | 1566 | &GLSLDecompiler::Logical2HLessEqual<false>, |
| 1547 | &GLSLDecompiler::Logical2HGreaterThan, | 1567 | &GLSLDecompiler::Logical2HGreaterThan<false>, |
| 1548 | &GLSLDecompiler::Logical2HNotEqual, | 1568 | &GLSLDecompiler::Logical2HNotEqual<false>, |
| 1549 | &GLSLDecompiler::Logical2HGreaterEqual, | 1569 | &GLSLDecompiler::Logical2HGreaterEqual<false>, |
| 1570 | &GLSLDecompiler::Logical2HLessThan<true>, | ||
| 1571 | &GLSLDecompiler::Logical2HEqual<true>, | ||
| 1572 | &GLSLDecompiler::Logical2HLessEqual<true>, | ||
| 1573 | &GLSLDecompiler::Logical2HGreaterThan<true>, | ||
| 1574 | &GLSLDecompiler::Logical2HNotEqual<true>, | ||
| 1575 | &GLSLDecompiler::Logical2HGreaterEqual<true>, | ||
| 1550 | 1576 | ||
| 1551 | &GLSLDecompiler::Texture, | 1577 | &GLSLDecompiler::Texture, |
| 1552 | &GLSLDecompiler::TextureLod, | 1578 | &GLSLDecompiler::TextureLod, |
| @@ -1625,6 +1651,7 @@ private: | |||
| 1625 | return name + '_' + std::to_string(index) + '_' + suffix; | 1651 | return name + '_' + std::to_string(index) + '_' + suffix; |
| 1626 | } | 1652 | } |
| 1627 | 1653 | ||
| 1654 | const Device& device; | ||
| 1628 | const ShaderIR& ir; | 1655 | const ShaderIR& ir; |
| 1629 | const ShaderStage stage; | 1656 | const ShaderStage stage; |
| 1630 | const std::string suffix; | 1657 | const std::string suffix; |
| @@ -1647,11 +1674,18 @@ std::string GetCommonDeclarations() { | |||
| 1647 | "}\n\n" | 1674 | "}\n\n" |
| 1648 | "vec2 toHalf2(float value) {\n" | 1675 | "vec2 toHalf2(float value) {\n" |
| 1649 | " return unpackHalf2x16(ftou(value));\n" | 1676 | " return unpackHalf2x16(ftou(value));\n" |
| 1677 | "}\n\n" | ||
| 1678 | "bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {\n" | ||
| 1679 | " bvec2 is_nan1 = isnan(pair1);\n" | ||
| 1680 | " bvec2 is_nan2 = isnan(pair2);\n" | ||
| 1681 | " return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || " | ||
| 1682 | "is_nan2.y);\n" | ||
| 1650 | "}\n"; | 1683 | "}\n"; |
| 1651 | } | 1684 | } |
| 1652 | 1685 | ||
| 1653 | ProgramResult Decompile(const ShaderIR& ir, Maxwell::ShaderStage stage, const std::string& suffix) { | 1686 | ProgramResult Decompile(const Device& device, const ShaderIR& ir, Maxwell::ShaderStage stage, |
| 1654 | GLSLDecompiler decompiler(ir, stage, suffix); | 1687 | const std::string& suffix) { |
| 1688 | GLSLDecompiler decompiler(device, ir, stage, suffix); | ||
| 1655 | decompiler.Decompile(); | 1689 | decompiler.Decompile(); |
| 1656 | return {decompiler.GetResult(), decompiler.GetShaderEntries()}; | 1690 | return {decompiler.GetResult(), decompiler.GetShaderEntries()}; |
| 1657 | } | 1691 | } |
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 @@ | |||
| 12 | #include "video_core/engines/maxwell_3d.h" | 12 | #include "video_core/engines/maxwell_3d.h" |
| 13 | #include "video_core/shader/shader_ir.h" | 13 | #include "video_core/shader/shader_ir.h" |
| 14 | 14 | ||
| 15 | namespace OpenGL { | ||
| 16 | class Device; | ||
| 17 | } | ||
| 18 | |||
| 15 | namespace VideoCommon::Shader { | 19 | namespace VideoCommon::Shader { |
| 16 | class ShaderIR; | 20 | class ShaderIR; |
| 17 | } | 21 | } |
| @@ -77,7 +81,7 @@ struct ShaderEntries { | |||
| 77 | 81 | ||
| 78 | std::string GetCommonDeclarations(); | 82 | std::string GetCommonDeclarations(); |
| 79 | 83 | ||
| 80 | ProgramResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage, | 84 | ProgramResult Decompile(const Device& device, const VideoCommon::Shader::ShaderIR& ir, |
| 81 | const std::string& suffix); | 85 | Maxwell::ShaderStage stage, const std::string& suffix); |
| 82 | 86 | ||
| 83 | } // namespace OpenGL::GLShader | 87 | } // namespace OpenGL::GLShader |
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index 53752b38d..254c0d499 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp | |||
| @@ -104,7 +104,8 @@ bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const { | |||
| 104 | return true; | 104 | return true; |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {} | 107 | ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) |
| 108 | : system{system}, precompiled_cache_virtual_file_offset{0} {} | ||
| 108 | 109 | ||
| 109 | std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> | 110 | std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskCacheUsage>>> |
| 110 | ShaderDiskCacheOpenGL::LoadTransferable() { | 111 | ShaderDiskCacheOpenGL::LoadTransferable() { |
| @@ -177,6 +178,7 @@ ShaderDiskCacheOpenGL::LoadTransferable() { | |||
| 177 | return {}; | 178 | return {}; |
| 178 | } | 179 | } |
| 179 | } | 180 | } |
| 181 | |||
| 180 | return {{raws, usages}}; | 182 | return {{raws, usages}}; |
| 181 | } | 183 | } |
| 182 | 184 | ||
| @@ -208,59 +210,64 @@ ShaderDiskCacheOpenGL::LoadPrecompiled() { | |||
| 208 | std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, | 210 | std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, |
| 209 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> | 211 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> |
| 210 | ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { | 212 | ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { |
| 213 | // Read compressed file from disk and decompress to virtual precompiled cache file | ||
| 214 | std::vector<u8> compressed(file.GetSize()); | ||
| 215 | file.ReadBytes(compressed.data(), compressed.size()); | ||
| 216 | const std::vector<u8> decompressed = Common::Compression::DecompressDataZSTD(compressed); | ||
| 217 | SaveArrayToPrecompiled(decompressed.data(), decompressed.size()); | ||
| 218 | precompiled_cache_virtual_file_offset = 0; | ||
| 219 | |||
| 211 | ShaderCacheVersionHash file_hash{}; | 220 | ShaderCacheVersionHash file_hash{}; |
| 212 | if (file.ReadArray(file_hash.data(), file_hash.size()) != file_hash.size()) { | 221 | if (!LoadArrayFromPrecompiled(file_hash.data(), file_hash.size())) { |
| 222 | precompiled_cache_virtual_file_offset = 0; | ||
| 213 | return {}; | 223 | return {}; |
| 214 | } | 224 | } |
| 215 | if (GetShaderCacheVersionHash() != file_hash) { | 225 | if (GetShaderCacheVersionHash() != file_hash) { |
| 216 | LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator"); | 226 | LOG_INFO(Render_OpenGL, "Precompiled cache is from another version of the emulator"); |
| 227 | precompiled_cache_virtual_file_offset = 0; | ||
| 217 | return {}; | 228 | return {}; |
| 218 | } | 229 | } |
| 219 | 230 | ||
| 220 | std::unordered_map<u64, ShaderDiskCacheDecompiled> decompiled; | 231 | std::unordered_map<u64, ShaderDiskCacheDecompiled> decompiled; |
| 221 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump> dumps; | 232 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump> dumps; |
| 222 | while (file.Tell() < file.GetSize()) { | 233 | while (precompiled_cache_virtual_file_offset < precompiled_cache_virtual_file.GetSize()) { |
| 223 | PrecompiledEntryKind kind{}; | 234 | PrecompiledEntryKind kind{}; |
| 224 | if (file.ReadBytes(&kind, sizeof(u32)) != sizeof(u32)) { | 235 | if (!LoadObjectFromPrecompiled(kind)) { |
| 225 | return {}; | 236 | return {}; |
| 226 | } | 237 | } |
| 227 | 238 | ||
| 228 | switch (kind) { | 239 | switch (kind) { |
| 229 | case PrecompiledEntryKind::Decompiled: { | 240 | case PrecompiledEntryKind::Decompiled: { |
| 230 | u64 unique_identifier{}; | 241 | u64 unique_identifier{}; |
| 231 | if (file.ReadBytes(&unique_identifier, sizeof(u64)) != sizeof(u64)) | 242 | if (!LoadObjectFromPrecompiled(unique_identifier)) { |
| 232 | return {}; | 243 | return {}; |
| 244 | } | ||
| 233 | 245 | ||
| 234 | const auto entry = LoadDecompiledEntry(file); | 246 | const auto entry = LoadDecompiledEntry(); |
| 235 | if (!entry) | 247 | if (!entry) { |
| 236 | return {}; | 248 | return {}; |
| 249 | } | ||
| 237 | decompiled.insert({unique_identifier, std::move(*entry)}); | 250 | decompiled.insert({unique_identifier, std::move(*entry)}); |
| 238 | break; | 251 | break; |
| 239 | } | 252 | } |
| 240 | case PrecompiledEntryKind::Dump: { | 253 | case PrecompiledEntryKind::Dump: { |
| 241 | ShaderDiskCacheUsage usage; | 254 | ShaderDiskCacheUsage usage; |
| 242 | if (file.ReadBytes(&usage, sizeof(usage)) != sizeof(usage)) | 255 | if (!LoadObjectFromPrecompiled(usage)) { |
| 243 | return {}; | 256 | return {}; |
| 257 | } | ||
| 244 | 258 | ||
| 245 | ShaderDiskCacheDump dump; | 259 | ShaderDiskCacheDump dump; |
| 246 | if (file.ReadBytes(&dump.binary_format, sizeof(u32)) != sizeof(u32)) | 260 | if (!LoadObjectFromPrecompiled(dump.binary_format)) { |
| 247 | return {}; | ||
| 248 | |||
| 249 | u32 binary_length{}; | ||
| 250 | u32 compressed_size{}; | ||
| 251 | if (file.ReadBytes(&binary_length, sizeof(u32)) != sizeof(u32) || | ||
| 252 | file.ReadBytes(&compressed_size, sizeof(u32)) != sizeof(u32)) { | ||
| 253 | return {}; | 261 | return {}; |
| 254 | } | 262 | } |
| 255 | 263 | ||
| 256 | std::vector<u8> compressed_binary(compressed_size); | 264 | u32 binary_length{}; |
| 257 | if (file.ReadArray(compressed_binary.data(), compressed_binary.size()) != | 265 | if (!LoadObjectFromPrecompiled(binary_length)) { |
| 258 | compressed_binary.size()) { | ||
| 259 | return {}; | 266 | return {}; |
| 260 | } | 267 | } |
| 261 | 268 | ||
| 262 | dump.binary = Common::Compression::DecompressDataZSTD(compressed_binary); | 269 | dump.binary.resize(binary_length); |
| 263 | if (dump.binary.empty()) { | 270 | if (!LoadArrayFromPrecompiled(dump.binary.data(), dump.binary.size())) { |
| 264 | return {}; | 271 | return {}; |
| 265 | } | 272 | } |
| 266 | 273 | ||
| @@ -274,45 +281,41 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { | |||
| 274 | return {{decompiled, dumps}}; | 281 | return {{decompiled, dumps}}; |
| 275 | } | 282 | } |
| 276 | 283 | ||
| 277 | std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEntry( | 284 | std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEntry() { |
| 278 | FileUtil::IOFile& file) { | ||
| 279 | u32 code_size{}; | 285 | u32 code_size{}; |
| 280 | u32 compressed_code_size{}; | 286 | if (!LoadObjectFromPrecompiled(code_size)) { |
| 281 | if (file.ReadBytes(&code_size, sizeof(u32)) != sizeof(u32) || | ||
| 282 | file.ReadBytes(&compressed_code_size, sizeof(u32)) != sizeof(u32)) { | ||
| 283 | return {}; | 287 | return {}; |
| 284 | } | 288 | } |
| 285 | 289 | ||
| 286 | std::vector<u8> compressed_code(compressed_code_size); | 290 | std::vector<u8> code(code_size); |
| 287 | if (file.ReadArray(compressed_code.data(), compressed_code.size()) != compressed_code.size()) { | 291 | if (!LoadArrayFromPrecompiled(code.data(), code.size())) { |
| 288 | return {}; | 292 | return {}; |
| 289 | } | 293 | } |
| 290 | 294 | ||
| 291 | const std::vector<u8> code = Common::Compression::DecompressDataZSTD(compressed_code); | ||
| 292 | if (code.empty()) { | ||
| 293 | return {}; | ||
| 294 | } | ||
| 295 | ShaderDiskCacheDecompiled entry; | 295 | ShaderDiskCacheDecompiled entry; |
| 296 | entry.code = std::string(reinterpret_cast<const char*>(code.data()), code_size); | 296 | entry.code = std::string(reinterpret_cast<const char*>(code.data()), code_size); |
| 297 | 297 | ||
| 298 | u32 const_buffers_count{}; | 298 | u32 const_buffers_count{}; |
| 299 | if (file.ReadBytes(&const_buffers_count, sizeof(u32)) != sizeof(u32)) | 299 | if (!LoadObjectFromPrecompiled(const_buffers_count)) { |
| 300 | return {}; | 300 | return {}; |
| 301 | } | ||
| 302 | |||
| 301 | for (u32 i = 0; i < const_buffers_count; ++i) { | 303 | for (u32 i = 0; i < const_buffers_count; ++i) { |
| 302 | u32 max_offset{}; | 304 | u32 max_offset{}; |
| 303 | u32 index{}; | 305 | u32 index{}; |
| 304 | u8 is_indirect{}; | 306 | u8 is_indirect{}; |
| 305 | if (file.ReadBytes(&max_offset, sizeof(u32)) != sizeof(u32) || | 307 | if (!LoadObjectFromPrecompiled(max_offset) || !LoadObjectFromPrecompiled(index) || |
| 306 | file.ReadBytes(&index, sizeof(u32)) != sizeof(u32) || | 308 | !LoadObjectFromPrecompiled(is_indirect)) { |
| 307 | file.ReadBytes(&is_indirect, sizeof(u8)) != sizeof(u8)) { | ||
| 308 | return {}; | 309 | return {}; |
| 309 | } | 310 | } |
| 310 | entry.entries.const_buffers.emplace_back(max_offset, is_indirect != 0, index); | 311 | entry.entries.const_buffers.emplace_back(max_offset, is_indirect != 0, index); |
| 311 | } | 312 | } |
| 312 | 313 | ||
| 313 | u32 samplers_count{}; | 314 | u32 samplers_count{}; |
| 314 | if (file.ReadBytes(&samplers_count, sizeof(u32)) != sizeof(u32)) | 315 | if (!LoadObjectFromPrecompiled(samplers_count)) { |
| 315 | return {}; | 316 | return {}; |
| 317 | } | ||
| 318 | |||
| 316 | for (u32 i = 0; i < samplers_count; ++i) { | 319 | for (u32 i = 0; i < samplers_count; ++i) { |
| 317 | u64 offset{}; | 320 | u64 offset{}; |
| 318 | u64 index{}; | 321 | u64 index{}; |
| @@ -320,12 +323,9 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn | |||
| 320 | u8 is_array{}; | 323 | u8 is_array{}; |
| 321 | u8 is_shadow{}; | 324 | u8 is_shadow{}; |
| 322 | u8 is_bindless{}; | 325 | u8 is_bindless{}; |
| 323 | if (file.ReadBytes(&offset, sizeof(u64)) != sizeof(u64) || | 326 | if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) || |
| 324 | file.ReadBytes(&index, sizeof(u64)) != sizeof(u64) || | 327 | !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_array) || |
| 325 | file.ReadBytes(&type, sizeof(u32)) != sizeof(u32) || | 328 | !LoadObjectFromPrecompiled(is_shadow) || !LoadObjectFromPrecompiled(is_bindless)) { |
| 326 | file.ReadBytes(&is_array, sizeof(u8)) != sizeof(u8) || | ||
| 327 | file.ReadBytes(&is_shadow, sizeof(u8)) != sizeof(u8) || | ||
| 328 | file.ReadBytes(&is_bindless, sizeof(u8)) != sizeof(u8)) { | ||
| 329 | return {}; | 329 | return {}; |
| 330 | } | 330 | } |
| 331 | entry.entries.samplers.emplace_back(static_cast<std::size_t>(offset), | 331 | entry.entries.samplers.emplace_back(static_cast<std::size_t>(offset), |
| @@ -335,17 +335,17 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn | |||
| 335 | } | 335 | } |
| 336 | 336 | ||
| 337 | u32 global_memory_count{}; | 337 | u32 global_memory_count{}; |
| 338 | if (file.ReadBytes(&global_memory_count, sizeof(u32)) != sizeof(u32)) | 338 | if (!LoadObjectFromPrecompiled(global_memory_count)) { |
| 339 | return {}; | 339 | return {}; |
| 340 | } | ||
| 341 | |||
| 340 | for (u32 i = 0; i < global_memory_count; ++i) { | 342 | for (u32 i = 0; i < global_memory_count; ++i) { |
| 341 | u32 cbuf_index{}; | 343 | u32 cbuf_index{}; |
| 342 | u32 cbuf_offset{}; | 344 | u32 cbuf_offset{}; |
| 343 | u8 is_read{}; | 345 | u8 is_read{}; |
| 344 | u8 is_written{}; | 346 | u8 is_written{}; |
| 345 | if (file.ReadBytes(&cbuf_index, sizeof(u32)) != sizeof(u32) || | 347 | if (!LoadObjectFromPrecompiled(cbuf_index) || !LoadObjectFromPrecompiled(cbuf_offset) || |
| 346 | file.ReadBytes(&cbuf_offset, sizeof(u32)) != sizeof(u32) || | 348 | !LoadObjectFromPrecompiled(is_read) || !LoadObjectFromPrecompiled(is_written)) { |
| 347 | file.ReadBytes(&is_read, sizeof(u8)) != sizeof(u8) || | ||
| 348 | file.ReadBytes(&is_written, sizeof(u8)) != sizeof(u8)) { | ||
| 349 | return {}; | 349 | return {}; |
| 350 | } | 350 | } |
| 351 | entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read != 0, | 351 | entry.entries.global_memory_entries.emplace_back(cbuf_index, cbuf_offset, is_read != 0, |
| @@ -354,74 +354,81 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn | |||
| 354 | 354 | ||
| 355 | for (auto& clip_distance : entry.entries.clip_distances) { | 355 | for (auto& clip_distance : entry.entries.clip_distances) { |
| 356 | u8 clip_distance_raw{}; | 356 | u8 clip_distance_raw{}; |
| 357 | if (file.ReadBytes(&clip_distance_raw, sizeof(u8)) != sizeof(u8)) | 357 | if (!LoadObjectFromPrecompiled(clip_distance_raw)) |
| 358 | return {}; | 358 | return {}; |
| 359 | clip_distance = clip_distance_raw != 0; | 359 | clip_distance = clip_distance_raw != 0; |
| 360 | } | 360 | } |
| 361 | 361 | ||
| 362 | u64 shader_length{}; | 362 | u64 shader_length{}; |
| 363 | if (file.ReadBytes(&shader_length, sizeof(u64)) != sizeof(u64)) | 363 | if (!LoadObjectFromPrecompiled(shader_length)) { |
| 364 | return {}; | 364 | return {}; |
| 365 | } | ||
| 366 | |||
| 365 | entry.entries.shader_length = static_cast<std::size_t>(shader_length); | 367 | entry.entries.shader_length = static_cast<std::size_t>(shader_length); |
| 366 | 368 | ||
| 367 | return entry; | 369 | return entry; |
| 368 | } | 370 | } |
| 369 | 371 | ||
| 370 | bool ShaderDiskCacheOpenGL::SaveDecompiledFile(FileUtil::IOFile& file, u64 unique_identifier, | 372 | bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std::string& code, |
| 371 | const std::string& code, | ||
| 372 | const std::vector<u8>& compressed_code, | ||
| 373 | const GLShader::ShaderEntries& entries) { | 373 | const GLShader::ShaderEntries& entries) { |
| 374 | if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Decompiled)) != 1 || | 374 | if (!SaveObjectToPrecompiled(static_cast<u32>(PrecompiledEntryKind::Decompiled)) || |
| 375 | file.WriteObject(unique_identifier) != 1 || | 375 | !SaveObjectToPrecompiled(unique_identifier) || |
| 376 | file.WriteObject(static_cast<u32>(code.size())) != 1 || | 376 | !SaveObjectToPrecompiled(static_cast<u32>(code.size())) || |
| 377 | file.WriteObject(static_cast<u32>(compressed_code.size())) != 1 || | 377 | !SaveArrayToPrecompiled(code.data(), code.size())) { |
| 378 | file.WriteArray(compressed_code.data(), compressed_code.size()) != compressed_code.size()) { | ||
| 379 | return false; | 378 | return false; |
| 380 | } | 379 | } |
| 381 | 380 | ||
| 382 | if (file.WriteObject(static_cast<u32>(entries.const_buffers.size())) != 1) | 381 | if (!SaveObjectToPrecompiled(static_cast<u32>(entries.const_buffers.size()))) { |
| 383 | return false; | 382 | return false; |
| 383 | } | ||
| 384 | for (const auto& cbuf : entries.const_buffers) { | 384 | for (const auto& cbuf : entries.const_buffers) { |
| 385 | if (file.WriteObject(static_cast<u32>(cbuf.GetMaxOffset())) != 1 || | 385 | if (!SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetMaxOffset())) || |
| 386 | file.WriteObject(static_cast<u32>(cbuf.GetIndex())) != 1 || | 386 | !SaveObjectToPrecompiled(static_cast<u32>(cbuf.GetIndex())) || |
| 387 | file.WriteObject(static_cast<u8>(cbuf.IsIndirect() ? 1 : 0)) != 1) { | 387 | !SaveObjectToPrecompiled(static_cast<u8>(cbuf.IsIndirect() ? 1 : 0))) { |
| 388 | return false; | 388 | return false; |
| 389 | } | 389 | } |
| 390 | } | 390 | } |
| 391 | 391 | ||
| 392 | if (file.WriteObject(static_cast<u32>(entries.samplers.size())) != 1) | 392 | if (!SaveObjectToPrecompiled(static_cast<u32>(entries.samplers.size()))) { |
| 393 | return false; | 393 | return false; |
| 394 | } | ||
| 394 | for (const auto& sampler : entries.samplers) { | 395 | for (const auto& sampler : entries.samplers) { |
| 395 | if (file.WriteObject(static_cast<u64>(sampler.GetOffset())) != 1 || | 396 | if (!SaveObjectToPrecompiled(static_cast<u64>(sampler.GetOffset())) || |
| 396 | file.WriteObject(static_cast<u64>(sampler.GetIndex())) != 1 || | 397 | !SaveObjectToPrecompiled(static_cast<u64>(sampler.GetIndex())) || |
| 397 | file.WriteObject(static_cast<u32>(sampler.GetType())) != 1 || | 398 | !SaveObjectToPrecompiled(static_cast<u32>(sampler.GetType())) || |
| 398 | file.WriteObject(static_cast<u8>(sampler.IsArray() ? 1 : 0)) != 1 || | 399 | !SaveObjectToPrecompiled(static_cast<u8>(sampler.IsArray() ? 1 : 0)) || |
| 399 | file.WriteObject(static_cast<u8>(sampler.IsShadow() ? 1 : 0)) != 1 || | 400 | !SaveObjectToPrecompiled(static_cast<u8>(sampler.IsShadow() ? 1 : 0)) || |
| 400 | file.WriteObject(static_cast<u8>(sampler.IsBindless() ? 1 : 0)) != 1) { | 401 | !SaveObjectToPrecompiled(static_cast<u8>(sampler.IsBindless() ? 1 : 0))) { |
| 401 | return false; | 402 | return false; |
| 402 | } | 403 | } |
| 403 | } | 404 | } |
| 404 | 405 | ||
| 405 | if (file.WriteObject(static_cast<u32>(entries.global_memory_entries.size())) != 1) | 406 | if (!SaveObjectToPrecompiled(static_cast<u32>(entries.global_memory_entries.size()))) { |
| 406 | return false; | 407 | return false; |
| 408 | } | ||
| 407 | for (const auto& gmem : entries.global_memory_entries) { | 409 | for (const auto& gmem : entries.global_memory_entries) { |
| 408 | if (file.WriteObject(static_cast<u32>(gmem.GetCbufIndex())) != 1 || | 410 | if (!SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufIndex())) || |
| 409 | file.WriteObject(static_cast<u32>(gmem.GetCbufOffset())) != 1 || | 411 | !SaveObjectToPrecompiled(static_cast<u32>(gmem.GetCbufOffset())) || |
| 410 | file.WriteObject(static_cast<u8>(gmem.IsRead() ? 1 : 0)) != 1 || | 412 | !SaveObjectToPrecompiled(static_cast<u8>(gmem.IsRead() ? 1 : 0)) || |
| 411 | file.WriteObject(static_cast<u8>(gmem.IsWritten() ? 1 : 0)) != 1) { | 413 | !SaveObjectToPrecompiled(static_cast<u8>(gmem.IsWritten() ? 1 : 0))) { |
| 412 | return false; | 414 | return false; |
| 413 | } | 415 | } |
| 414 | } | 416 | } |
| 415 | 417 | ||
| 416 | for (const bool clip_distance : entries.clip_distances) { | 418 | for (const bool clip_distance : entries.clip_distances) { |
| 417 | if (file.WriteObject(static_cast<u8>(clip_distance ? 1 : 0)) != 1) | 419 | if (!SaveObjectToPrecompiled(static_cast<u8>(clip_distance ? 1 : 0))) { |
| 418 | return false; | 420 | return false; |
| 421 | } | ||
| 419 | } | 422 | } |
| 420 | 423 | ||
| 421 | return file.WriteObject(static_cast<u64>(entries.shader_length)) == 1; | 424 | if (!SaveObjectToPrecompiled(static_cast<u64>(entries.shader_length))) { |
| 425 | return false; | ||
| 426 | } | ||
| 427 | |||
| 428 | return true; | ||
| 422 | } | 429 | } |
| 423 | 430 | ||
| 424 | void ShaderDiskCacheOpenGL::InvalidateTransferable() const { | 431 | void ShaderDiskCacheOpenGL::InvalidateTransferable() { |
| 425 | if (!FileUtil::Delete(GetTransferablePath())) { | 432 | if (!FileUtil::Delete(GetTransferablePath())) { |
| 426 | LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}", | 433 | LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}", |
| 427 | GetTransferablePath()); | 434 | GetTransferablePath()); |
| @@ -429,7 +436,10 @@ void ShaderDiskCacheOpenGL::InvalidateTransferable() const { | |||
| 429 | InvalidatePrecompiled(); | 436 | InvalidatePrecompiled(); |
| 430 | } | 437 | } |
| 431 | 438 | ||
| 432 | void ShaderDiskCacheOpenGL::InvalidatePrecompiled() const { | 439 | void ShaderDiskCacheOpenGL::InvalidatePrecompiled() { |
| 440 | // Clear virtaul precompiled cache file | ||
| 441 | precompiled_cache_virtual_file.Resize(0); | ||
| 442 | |||
| 433 | if (!FileUtil::Delete(GetPrecompiledPath())) { | 443 | if (!FileUtil::Delete(GetPrecompiledPath())) { |
| 434 | LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath()); | 444 | LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath()); |
| 435 | } | 445 | } |
| @@ -465,7 +475,10 @@ void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) { | |||
| 465 | ASSERT_MSG(it != transferable.end(), "Saving shader usage without storing raw previously"); | 475 | ASSERT_MSG(it != transferable.end(), "Saving shader usage without storing raw previously"); |
| 466 | 476 | ||
| 467 | auto& usages{it->second}; | 477 | auto& usages{it->second}; |
| 468 | ASSERT(usages.find(usage) == usages.end()); | 478 | if (usages.find(usage) != usages.end()) { |
| 479 | // Skip this variant since the shader is already stored. | ||
| 480 | return; | ||
| 481 | } | ||
| 469 | usages.insert(usage); | 482 | usages.insert(usage); |
| 470 | 483 | ||
| 471 | FileUtil::IOFile file = AppendTransferableFile(); | 484 | FileUtil::IOFile file = AppendTransferableFile(); |
| @@ -485,22 +498,13 @@ void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::str | |||
| 485 | if (!IsUsable()) | 498 | if (!IsUsable()) |
| 486 | return; | 499 | return; |
| 487 | 500 | ||
| 488 | const std::vector<u8> compressed_code{Common::Compression::CompressDataZSTDDefault( | 501 | if (precompiled_cache_virtual_file.GetSize() == 0) { |
| 489 | reinterpret_cast<const u8*>(code.data()), code.size())}; | 502 | SavePrecompiledHeaderToVirtualPrecompiledCache(); |
| 490 | if (compressed_code.empty()) { | ||
| 491 | LOG_ERROR(Render_OpenGL, "Failed to compress GLSL code - skipping shader {:016x}", | ||
| 492 | unique_identifier); | ||
| 493 | return; | ||
| 494 | } | 503 | } |
| 495 | 504 | ||
| 496 | FileUtil::IOFile file = AppendPrecompiledFile(); | 505 | if (!SaveDecompiledFile(unique_identifier, code, entries)) { |
| 497 | if (!file.IsOpen()) | ||
| 498 | return; | ||
| 499 | |||
| 500 | if (!SaveDecompiledFile(file, unique_identifier, code, compressed_code, entries)) { | ||
| 501 | LOG_ERROR(Render_OpenGL, | 506 | LOG_ERROR(Render_OpenGL, |
| 502 | "Failed to save decompiled entry to the precompiled file - removing"); | 507 | "Failed to save decompiled entry to the precompiled file - removing"); |
| 503 | file.Close(); | ||
| 504 | InvalidatePrecompiled(); | 508 | InvalidatePrecompiled(); |
| 505 | } | 509 | } |
| 506 | } | 510 | } |
| @@ -516,28 +520,13 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p | |||
| 516 | std::vector<u8> binary(binary_length); | 520 | std::vector<u8> binary(binary_length); |
| 517 | glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data()); | 521 | glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data()); |
| 518 | 522 | ||
| 519 | const std::vector<u8> compressed_binary = | 523 | if (!SaveObjectToPrecompiled(static_cast<u32>(PrecompiledEntryKind::Dump)) || |
| 520 | Common::Compression::CompressDataZSTDDefault(binary.data(), binary.size()); | 524 | !SaveObjectToPrecompiled(usage) || |
| 521 | 525 | !SaveObjectToPrecompiled(static_cast<u32>(binary_format)) || | |
| 522 | if (compressed_binary.empty()) { | 526 | !SaveObjectToPrecompiled(static_cast<u32>(binary_length)) || |
| 523 | LOG_ERROR(Render_OpenGL, "Failed to compress binary program in shader={:016x}", | 527 | !SaveArrayToPrecompiled(binary.data(), binary.size())) { |
| 524 | usage.unique_identifier); | ||
| 525 | return; | ||
| 526 | } | ||
| 527 | |||
| 528 | FileUtil::IOFile file = AppendPrecompiledFile(); | ||
| 529 | if (!file.IsOpen()) | ||
| 530 | return; | ||
| 531 | |||
| 532 | if (file.WriteObject(static_cast<u32>(PrecompiledEntryKind::Dump)) != 1 || | ||
| 533 | file.WriteObject(usage) != 1 || file.WriteObject(static_cast<u32>(binary_format)) != 1 || | ||
| 534 | file.WriteObject(static_cast<u32>(binary_length)) != 1 || | ||
| 535 | file.WriteObject(static_cast<u32>(compressed_binary.size())) != 1 || | ||
| 536 | file.WriteArray(compressed_binary.data(), compressed_binary.size()) != | ||
| 537 | compressed_binary.size()) { | ||
| 538 | LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016x} - removing", | 528 | LOG_ERROR(Render_OpenGL, "Failed to save binary program file in shader={:016x} - removing", |
| 539 | usage.unique_identifier); | 529 | usage.unique_identifier); |
| 540 | file.Close(); | ||
| 541 | InvalidatePrecompiled(); | 530 | InvalidatePrecompiled(); |
| 542 | return; | 531 | return; |
| 543 | } | 532 | } |
| @@ -570,28 +559,33 @@ FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const { | |||
| 570 | return file; | 559 | return file; |
| 571 | } | 560 | } |
| 572 | 561 | ||
| 573 | FileUtil::IOFile ShaderDiskCacheOpenGL::AppendPrecompiledFile() const { | 562 | void ShaderDiskCacheOpenGL::SavePrecompiledHeaderToVirtualPrecompiledCache() { |
| 574 | if (!EnsureDirectories()) | 563 | const auto hash{GetShaderCacheVersionHash()}; |
| 575 | return {}; | 564 | if (!SaveArrayToPrecompiled(hash.data(), hash.size())) { |
| 565 | LOG_ERROR( | ||
| 566 | Render_OpenGL, | ||
| 567 | "Failed to write precompiled cache version hash to virtual precompiled cache file"); | ||
| 568 | } | ||
| 569 | } | ||
| 570 | |||
| 571 | void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() { | ||
| 572 | precompiled_cache_virtual_file_offset = 0; | ||
| 573 | const std::vector<u8>& uncompressed = precompiled_cache_virtual_file.ReadAllBytes(); | ||
| 574 | const std::vector<u8>& compressed = | ||
| 575 | Common::Compression::CompressDataZSTDDefault(uncompressed.data(), uncompressed.size()); | ||
| 576 | 576 | ||
| 577 | const auto precompiled_path{GetPrecompiledPath()}; | 577 | const auto precompiled_path{GetPrecompiledPath()}; |
| 578 | const bool existed = FileUtil::Exists(precompiled_path); | 578 | FileUtil::IOFile file(precompiled_path, "wb"); |
| 579 | 579 | ||
| 580 | FileUtil::IOFile file(precompiled_path, "ab"); | ||
| 581 | if (!file.IsOpen()) { | 580 | if (!file.IsOpen()) { |
| 582 | LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path); | 581 | LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path); |
| 583 | return {}; | 582 | return; |
| 584 | } | 583 | } |
| 585 | 584 | if (file.WriteBytes(compressed.data(), compressed.size()) != compressed.size()) { | |
| 586 | if (!existed || file.GetSize() == 0) { | 585 | LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version in path={}", |
| 587 | const auto hash{GetShaderCacheVersionHash()}; | 586 | precompiled_path); |
| 588 | if (file.WriteArray(hash.data(), hash.size()) != hash.size()) { | 587 | return; |
| 589 | LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version hash in path={}", | ||
| 590 | precompiled_path); | ||
| 591 | return {}; | ||
| 592 | } | ||
| 593 | } | 588 | } |
| 594 | return file; | ||
| 595 | } | 589 | } |
| 596 | 590 | ||
| 597 | bool ShaderDiskCacheOpenGL::EnsureDirectories() const { | 591 | bool ShaderDiskCacheOpenGL::EnsureDirectories() const { |
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h index 6be0c0547..0142b2e3b 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | 16 | ||
| 17 | #include "common/assert.h" | 17 | #include "common/assert.h" |
| 18 | #include "common/common_types.h" | 18 | #include "common/common_types.h" |
| 19 | #include "core/file_sys/vfs_vector.h" | ||
| 19 | #include "video_core/engines/maxwell_3d.h" | 20 | #include "video_core/engines/maxwell_3d.h" |
| 20 | #include "video_core/renderer_opengl/gl_shader_gen.h" | 21 | #include "video_core/renderer_opengl/gl_shader_gen.h" |
| 21 | 22 | ||
| @@ -172,10 +173,10 @@ public: | |||
| 172 | LoadPrecompiled(); | 173 | LoadPrecompiled(); |
| 173 | 174 | ||
| 174 | /// Removes the transferable (and precompiled) cache file. | 175 | /// Removes the transferable (and precompiled) cache file. |
| 175 | void InvalidateTransferable() const; | 176 | void InvalidateTransferable(); |
| 176 | 177 | ||
| 177 | /// Removes the precompiled cache file. | 178 | /// Removes the precompiled cache file and clears virtual precompiled cache file. |
| 178 | void InvalidatePrecompiled() const; | 179 | void InvalidatePrecompiled(); |
| 179 | 180 | ||
| 180 | /// Saves a raw dump to the transferable file. Checks for collisions. | 181 | /// Saves a raw dump to the transferable file. Checks for collisions. |
| 181 | void SaveRaw(const ShaderDiskCacheRaw& entry); | 182 | void SaveRaw(const ShaderDiskCacheRaw& entry); |
| @@ -190,18 +191,21 @@ public: | |||
| 190 | /// Saves a dump entry to the precompiled file. Does not check for collisions. | 191 | /// Saves a dump entry to the precompiled file. Does not check for collisions. |
| 191 | void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program); | 192 | void SaveDump(const ShaderDiskCacheUsage& usage, GLuint program); |
| 192 | 193 | ||
| 194 | /// Serializes virtual precompiled shader cache file to real file | ||
| 195 | void SaveVirtualPrecompiledFile(); | ||
| 196 | |||
| 193 | private: | 197 | private: |
| 194 | /// Loads the transferable cache. Returns empty on failure. | 198 | /// Loads the transferable cache. Returns empty on failure. |
| 195 | std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, | 199 | std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, |
| 196 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> | 200 | std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>>> |
| 197 | LoadPrecompiledFile(FileUtil::IOFile& file); | 201 | LoadPrecompiledFile(FileUtil::IOFile& file); |
| 198 | 202 | ||
| 199 | /// Loads a decompiled cache entry from the passed file. Returns empty on failure. | 203 | /// Loads a decompiled cache entry from m_precompiled_cache_virtual_file. Returns empty on |
| 200 | std::optional<ShaderDiskCacheDecompiled> LoadDecompiledEntry(FileUtil::IOFile& file); | 204 | /// failure. |
| 205 | std::optional<ShaderDiskCacheDecompiled> LoadDecompiledEntry(); | ||
| 201 | 206 | ||
| 202 | /// Saves a decompiled entry to the passed file. Returns true on success. | 207 | /// Saves a decompiled entry to the passed file. Returns true on success. |
| 203 | bool SaveDecompiledFile(FileUtil::IOFile& file, u64 unique_identifier, const std::string& code, | 208 | bool SaveDecompiledFile(u64 unique_identifier, const std::string& code, |
| 204 | const std::vector<u8>& compressed_code, | ||
| 205 | const GLShader::ShaderEntries& entries); | 209 | const GLShader::ShaderEntries& entries); |
| 206 | 210 | ||
| 207 | /// Returns if the cache can be used | 211 | /// Returns if the cache can be used |
| @@ -210,8 +214,8 @@ private: | |||
| 210 | /// Opens current game's transferable file and write it's header if it doesn't exist | 214 | /// Opens current game's transferable file and write it's header if it doesn't exist |
| 211 | FileUtil::IOFile AppendTransferableFile() const; | 215 | FileUtil::IOFile AppendTransferableFile() const; |
| 212 | 216 | ||
| 213 | /// Opens current game's precompiled file and write it's header if it doesn't exist | 217 | /// Save precompiled header to precompiled_cache_in_memory |
| 214 | FileUtil::IOFile AppendPrecompiledFile() const; | 218 | void SavePrecompiledHeaderToVirtualPrecompiledCache(); |
| 215 | 219 | ||
| 216 | /// Create shader disk cache directories. Returns true on success. | 220 | /// Create shader disk cache directories. Returns true on success. |
| 217 | bool EnsureDirectories() const; | 221 | bool EnsureDirectories() const; |
| @@ -234,10 +238,42 @@ private: | |||
| 234 | /// Get current game's title id | 238 | /// Get current game's title id |
| 235 | std::string GetTitleID() const; | 239 | std::string GetTitleID() const; |
| 236 | 240 | ||
| 241 | template <typename T> | ||
| 242 | bool SaveArrayToPrecompiled(const T* data, std::size_t length) { | ||
| 243 | const std::size_t write_length = precompiled_cache_virtual_file.WriteArray( | ||
| 244 | data, length, precompiled_cache_virtual_file_offset); | ||
| 245 | precompiled_cache_virtual_file_offset += write_length; | ||
| 246 | return write_length == sizeof(T) * length; | ||
| 247 | } | ||
| 248 | |||
| 249 | template <typename T> | ||
| 250 | bool LoadArrayFromPrecompiled(T* data, std::size_t length) { | ||
| 251 | const std::size_t read_length = precompiled_cache_virtual_file.ReadArray( | ||
| 252 | data, length, precompiled_cache_virtual_file_offset); | ||
| 253 | precompiled_cache_virtual_file_offset += read_length; | ||
| 254 | return read_length == sizeof(T) * length; | ||
| 255 | } | ||
| 256 | |||
| 257 | template <typename T> | ||
| 258 | bool SaveObjectToPrecompiled(const T& object) { | ||
| 259 | return SaveArrayToPrecompiled(&object, 1); | ||
| 260 | } | ||
| 261 | |||
| 262 | template <typename T> | ||
| 263 | bool LoadObjectFromPrecompiled(T& object) { | ||
| 264 | return LoadArrayFromPrecompiled(&object, 1); | ||
| 265 | } | ||
| 266 | |||
| 237 | // Copre system | 267 | // Copre system |
| 238 | Core::System& system; | 268 | Core::System& system; |
| 239 | // Stored transferable shaders | 269 | // Stored transferable shaders |
| 240 | std::map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable; | 270 | std::map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable; |
| 271 | // Stores whole precompiled cache which will be read from or saved to the precompiled chache | ||
| 272 | // file | ||
| 273 | FileSys::VectorVfsFile precompiled_cache_virtual_file; | ||
| 274 | // Stores the current offset of the precompiled cache file for IO purposes | ||
| 275 | std::size_t precompiled_cache_virtual_file_offset; | ||
| 276 | |||
| 241 | // The cache has been loaded at boot | 277 | // The cache has been loaded at boot |
| 242 | bool tried_to_load{}; | 278 | bool tried_to_load{}; |
| 243 | }; | 279 | }; |
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; | |||
| 16 | 16 | ||
| 17 | static constexpr u32 PROGRAM_OFFSET{10}; | 17 | static constexpr u32 PROGRAM_OFFSET{10}; |
| 18 | 18 | ||
| 19 | ProgramResult GenerateVertexShader(const ShaderSetup& setup) { | 19 | ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup) { |
| 20 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); | 20 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); |
| 21 | 21 | ||
| 22 | std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; | 22 | 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 { | |||
| 34 | 34 | ||
| 35 | )"; | 35 | )"; |
| 36 | ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); | 36 | ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); |
| 37 | ProgramResult program = Decompile(program_ir, Maxwell3D::Regs::ShaderStage::Vertex, "vertex"); | 37 | ProgramResult program = |
| 38 | Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Vertex, "vertex"); | ||
| 38 | 39 | ||
| 39 | out += program.first; | 40 | out += program.first; |
| 40 | 41 | ||
| 41 | if (setup.IsDualProgram()) { | 42 | if (setup.IsDualProgram()) { |
| 42 | ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET); | 43 | ShaderIR program_ir_b(setup.program.code_b, PROGRAM_OFFSET); |
| 43 | ProgramResult program_b = | 44 | ProgramResult program_b = |
| 44 | Decompile(program_ir_b, Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b"); | 45 | Decompile(device, program_ir_b, Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b"); |
| 45 | 46 | ||
| 46 | out += program_b.first; | 47 | out += program_b.first; |
| 47 | } | 48 | } |
| @@ -57,6 +58,9 @@ void main() { | |||
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | out += R"( | 60 | out += R"( |
| 61 | |||
| 62 | // Set Position Y direction | ||
| 63 | position.y *= utof(config_pack[2]); | ||
| 60 | // Check if the flip stage is VertexB | 64 | // Check if the flip stage is VertexB |
| 61 | // Config pack's second value is flip_stage | 65 | // Config pack's second value is flip_stage |
| 62 | if (config_pack[1] == 1) { | 66 | if (config_pack[1] == 1) { |
| @@ -75,7 +79,7 @@ void main() { | |||
| 75 | return {out, program.second}; | 79 | return {out, program.second}; |
| 76 | } | 80 | } |
| 77 | 81 | ||
| 78 | ProgramResult GenerateGeometryShader(const ShaderSetup& setup) { | 82 | ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& setup) { |
| 79 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); | 83 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); |
| 80 | 84 | ||
| 81 | std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; | 85 | 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 { | |||
| 95 | )"; | 99 | )"; |
| 96 | ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); | 100 | ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); |
| 97 | ProgramResult program = | 101 | ProgramResult program = |
| 98 | Decompile(program_ir, Maxwell3D::Regs::ShaderStage::Geometry, "geometry"); | 102 | Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Geometry, "geometry"); |
| 99 | out += program.first; | 103 | out += program.first; |
| 100 | 104 | ||
| 101 | out += R"( | 105 | out += R"( |
| @@ -106,7 +110,7 @@ void main() { | |||
| 106 | return {out, program.second}; | 110 | return {out, program.second}; |
| 107 | } | 111 | } |
| 108 | 112 | ||
| 109 | ProgramResult GenerateFragmentShader(const ShaderSetup& setup) { | 113 | ProgramResult GenerateFragmentShader(const Device& device, const ShaderSetup& setup) { |
| 110 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); | 114 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); |
| 111 | 115 | ||
| 112 | std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; | 116 | std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; |
| @@ -158,7 +162,7 @@ bool AlphaFunc(in float value) { | |||
| 158 | )"; | 162 | )"; |
| 159 | ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); | 163 | ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET); |
| 160 | ProgramResult program = | 164 | ProgramResult program = |
| 161 | Decompile(program_ir, Maxwell3D::Regs::ShaderStage::Fragment, "fragment"); | 165 | Decompile(device, program_ir, Maxwell3D::Regs::ShaderStage::Fragment, "fragment"); |
| 162 | 166 | ||
| 163 | out += program.first; | 167 | out += program.first; |
| 164 | 168 | ||
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 @@ | |||
| 10 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 10 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| 11 | #include "video_core/shader/shader_ir.h" | 11 | #include "video_core/shader/shader_ir.h" |
| 12 | 12 | ||
| 13 | namespace OpenGL { | ||
| 14 | class Device; | ||
| 15 | } | ||
| 16 | |||
| 13 | namespace OpenGL::GLShader { | 17 | namespace OpenGL::GLShader { |
| 14 | 18 | ||
| 15 | using VideoCommon::Shader::ProgramCode; | 19 | using VideoCommon::Shader::ProgramCode; |
| @@ -39,22 +43,13 @@ private: | |||
| 39 | bool has_program_b{}; | 43 | bool has_program_b{}; |
| 40 | }; | 44 | }; |
| 41 | 45 | ||
| 42 | /** | 46 | /// Generates the GLSL vertex shader program source code for the given VS program |
| 43 | * Generates the GLSL vertex shader program source code for the given VS program | 47 | ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setup); |
| 44 | * @returns String of the shader source code | 48 | |
| 45 | */ | 49 | /// Generates the GLSL geometry shader program source code for the given GS program |
| 46 | ProgramResult GenerateVertexShader(const ShaderSetup& setup); | 50 | ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& setup); |
| 47 | 51 | ||
| 48 | /** | 52 | /// Generates the GLSL fragment shader program source code for the given FS program |
| 49 | * Generates the GLSL geometry shader program source code for the given GS program | 53 | ProgramResult GenerateFragmentShader(const Device& device, const ShaderSetup& setup); |
| 50 | * @returns String of the shader source code | ||
| 51 | */ | ||
| 52 | ProgramResult GenerateGeometryShader(const ShaderSetup& setup); | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Generates the GLSL fragment shader program source code for the given FS program | ||
| 56 | * @returns String of the shader source code | ||
| 57 | */ | ||
| 58 | ProgramResult GenerateFragmentShader(const ShaderSetup& setup); | ||
| 59 | 54 | ||
| 60 | } // namespace OpenGL::GLShader | 55 | } // 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 { | |||
| 471 | const auto& texture_unit = texture_units[i]; | 471 | const auto& texture_unit = texture_units[i]; |
| 472 | auto& cur_state_texture_unit = cur_state.texture_units[i]; | 472 | auto& cur_state_texture_unit = cur_state.texture_units[i]; |
| 473 | textures[i] = texture_unit.texture; | 473 | textures[i] = texture_unit.texture; |
| 474 | if (cur_state_texture_unit.texture == textures[i]) | 474 | if (cur_state_texture_unit.texture == textures[i]) { |
| 475 | continue; | 475 | continue; |
| 476 | } | ||
| 476 | cur_state_texture_unit.texture = textures[i]; | 477 | cur_state_texture_unit.texture = textures[i]; |
| 477 | if (!has_delta) { | 478 | if (!has_delta) { |
| 478 | first = i; | 479 | first = i; |
| @@ -493,10 +494,11 @@ void OpenGLState::ApplySamplers() const { | |||
| 493 | std::array<GLuint, Maxwell::NumTextureSamplers> samplers; | 494 | std::array<GLuint, Maxwell::NumTextureSamplers> samplers; |
| 494 | 495 | ||
| 495 | for (std::size_t i = 0; i < std::size(samplers); ++i) { | 496 | for (std::size_t i = 0; i < std::size(samplers); ++i) { |
| 496 | if (cur_state.texture_units[i].sampler == texture_units[i].sampler) | 497 | samplers[i] = texture_units[i].sampler; |
| 498 | if (cur_state.texture_units[i].sampler == texture_units[i].sampler) { | ||
| 497 | continue; | 499 | continue; |
| 500 | } | ||
| 498 | cur_state.texture_units[i].sampler = texture_units[i].sampler; | 501 | cur_state.texture_units[i].sampler = texture_units[i].sampler; |
| 499 | samplers[i] = texture_units[i].sampler; | ||
| 500 | if (!has_delta) { | 502 | if (!has_delta) { |
| 501 | first = i; | 503 | first = i; |
| 502 | has_delta = true; | 504 | has_delta = true; |
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index a8833c06e..95b773135 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -27,8 +27,7 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs; | |||
| 27 | inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | 27 | inline GLenum VertexType(Maxwell::VertexAttribute attrib) { |
| 28 | switch (attrib.type) { | 28 | switch (attrib.type) { |
| 29 | case Maxwell::VertexAttribute::Type::UnsignedInt: | 29 | case Maxwell::VertexAttribute::Type::UnsignedInt: |
| 30 | case Maxwell::VertexAttribute::Type::UnsignedNorm: { | 30 | case Maxwell::VertexAttribute::Type::UnsignedNorm: |
| 31 | |||
| 32 | switch (attrib.size) { | 31 | switch (attrib.size) { |
| 33 | case Maxwell::VertexAttribute::Size::Size_8: | 32 | case Maxwell::VertexAttribute::Size::Size_8: |
| 34 | case Maxwell::VertexAttribute::Size::Size_8_8: | 33 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| @@ -47,16 +46,13 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 47 | return GL_UNSIGNED_INT; | 46 | return GL_UNSIGNED_INT; |
| 48 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 47 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 49 | return GL_UNSIGNED_INT_2_10_10_10_REV; | 48 | return GL_UNSIGNED_INT_2_10_10_10_REV; |
| 49 | default: | ||
| 50 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 51 | UNREACHABLE(); | ||
| 52 | return {}; | ||
| 50 | } | 53 | } |
| 51 | |||
| 52 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 53 | UNREACHABLE(); | ||
| 54 | return {}; | ||
| 55 | } | ||
| 56 | |||
| 57 | case Maxwell::VertexAttribute::Type::SignedInt: | 54 | case Maxwell::VertexAttribute::Type::SignedInt: |
| 58 | case Maxwell::VertexAttribute::Type::SignedNorm: { | 55 | case Maxwell::VertexAttribute::Type::SignedNorm: |
| 59 | |||
| 60 | switch (attrib.size) { | 56 | switch (attrib.size) { |
| 61 | case Maxwell::VertexAttribute::Size::Size_8: | 57 | case Maxwell::VertexAttribute::Size::Size_8: |
| 62 | case Maxwell::VertexAttribute::Size::Size_8_8: | 58 | case Maxwell::VertexAttribute::Size::Size_8_8: |
| @@ -75,14 +71,12 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 75 | return GL_INT; | 71 | return GL_INT; |
| 76 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 72 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 77 | return GL_INT_2_10_10_10_REV; | 73 | return GL_INT_2_10_10_10_REV; |
| 74 | default: | ||
| 75 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 76 | UNREACHABLE(); | ||
| 77 | return {}; | ||
| 78 | } | 78 | } |
| 79 | 79 | case Maxwell::VertexAttribute::Type::Float: | |
| 80 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 81 | UNREACHABLE(); | ||
| 82 | return {}; | ||
| 83 | } | ||
| 84 | |||
| 85 | case Maxwell::VertexAttribute::Type::Float: { | ||
| 86 | switch (attrib.size) { | 80 | switch (attrib.size) { |
| 87 | case Maxwell::VertexAttribute::Size::Size_16: | 81 | case Maxwell::VertexAttribute::Size::Size_16: |
| 88 | case Maxwell::VertexAttribute::Size::Size_16_16: | 82 | case Maxwell::VertexAttribute::Size::Size_16_16: |
| @@ -94,13 +88,16 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 94 | case Maxwell::VertexAttribute::Size::Size_32_32_32: | 88 | case Maxwell::VertexAttribute::Size::Size_32_32_32: |
| 95 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | 89 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: |
| 96 | return GL_FLOAT; | 90 | return GL_FLOAT; |
| 91 | default: | ||
| 92 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 93 | UNREACHABLE(); | ||
| 94 | return {}; | ||
| 97 | } | 95 | } |
| 96 | default: | ||
| 97 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); | ||
| 98 | UNREACHABLE(); | ||
| 99 | return {}; | ||
| 98 | } | 100 | } |
| 99 | } | ||
| 100 | |||
| 101 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); | ||
| 102 | UNREACHABLE(); | ||
| 103 | return {}; | ||
| 104 | } | 101 | } |
| 105 | 102 | ||
| 106 | inline GLenum IndexFormat(Maxwell::IndexFormat index_format) { | 103 | inline GLenum IndexFormat(Maxwell::IndexFormat index_format) { |
| @@ -129,10 +126,11 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { | |||
| 129 | return GL_TRIANGLES; | 126 | return GL_TRIANGLES; |
| 130 | case Maxwell::PrimitiveTopology::TriangleStrip: | 127 | case Maxwell::PrimitiveTopology::TriangleStrip: |
| 131 | return GL_TRIANGLE_STRIP; | 128 | return GL_TRIANGLE_STRIP; |
| 129 | default: | ||
| 130 | LOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology)); | ||
| 131 | UNREACHABLE(); | ||
| 132 | return {}; | ||
| 132 | } | 133 | } |
| 133 | LOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology)); | ||
| 134 | UNREACHABLE(); | ||
| 135 | return {}; | ||
| 136 | } | 134 | } |
| 137 | 135 | ||
| 138 | inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, | 136 | inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, |
| @@ -186,9 +184,10 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) { | |||
| 186 | } else { | 184 | } else { |
| 187 | return GL_MIRROR_CLAMP_TO_EDGE; | 185 | return GL_MIRROR_CLAMP_TO_EDGE; |
| 188 | } | 186 | } |
| 187 | default: | ||
| 188 | LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); | ||
| 189 | return GL_REPEAT; | ||
| 189 | } | 190 | } |
| 190 | LOG_ERROR(Render_OpenGL, "Unimplemented texture wrap mode={}", static_cast<u32>(wrap_mode)); | ||
| 191 | return GL_REPEAT; | ||
| 192 | } | 191 | } |
| 193 | 192 | ||
| 194 | inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { | 193 | inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) { |
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 34bf26ff2..9fe1e3280 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp | |||
| @@ -62,9 +62,10 @@ vk::SamplerAddressMode WrapMode(Tegra::Texture::WrapMode wrap_mode) { | |||
| 62 | case Tegra::Texture::WrapMode::MirrorOnceBorder: | 62 | case Tegra::Texture::WrapMode::MirrorOnceBorder: |
| 63 | UNIMPLEMENTED(); | 63 | UNIMPLEMENTED(); |
| 64 | return vk::SamplerAddressMode::eMirrorClampToEdge; | 64 | return vk::SamplerAddressMode::eMirrorClampToEdge; |
| 65 | default: | ||
| 66 | UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", static_cast<u32>(wrap_mode)); | ||
| 67 | return {}; | ||
| 65 | } | 68 | } |
| 66 | UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", static_cast<u32>(wrap_mode)); | ||
| 67 | return {}; | ||
| 68 | } | 69 | } |
| 69 | 70 | ||
| 70 | vk::CompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func) { | 71 | vk::CompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func) { |
| @@ -225,9 +226,10 @@ vk::PrimitiveTopology PrimitiveTopology(Maxwell::PrimitiveTopology topology) { | |||
| 225 | return vk::PrimitiveTopology::eTriangleList; | 226 | return vk::PrimitiveTopology::eTriangleList; |
| 226 | case Maxwell::PrimitiveTopology::TriangleStrip: | 227 | case Maxwell::PrimitiveTopology::TriangleStrip: |
| 227 | return vk::PrimitiveTopology::eTriangleStrip; | 228 | return vk::PrimitiveTopology::eTriangleStrip; |
| 229 | default: | ||
| 230 | UNIMPLEMENTED_MSG("Unimplemented topology={}", static_cast<u32>(topology)); | ||
| 231 | return {}; | ||
| 228 | } | 232 | } |
| 229 | UNIMPLEMENTED_MSG("Unimplemented topology={}", static_cast<u32>(topology)); | ||
| 230 | return {}; | ||
| 231 | } | 233 | } |
| 232 | 234 | ||
| 233 | vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { | 235 | vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { |
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 25500f9a3..23d9b10db 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -76,14 +76,10 @@ constexpr u32 GetGenericAttributeLocation(Attribute::Index attribute) { | |||
| 76 | 76 | ||
| 77 | /// Returns true if an object has to be treated as precise | 77 | /// Returns true if an object has to be treated as precise |
| 78 | bool IsPrecise(Operation operand) { | 78 | bool IsPrecise(Operation operand) { |
| 79 | const auto& meta = operand.GetMeta(); | 79 | const auto& meta{operand.GetMeta()}; |
| 80 | |||
| 81 | if (std::holds_alternative<MetaArithmetic>(meta)) { | 80 | if (std::holds_alternative<MetaArithmetic>(meta)) { |
| 82 | return std::get<MetaArithmetic>(meta).precise; | 81 | return std::get<MetaArithmetic>(meta).precise; |
| 83 | } | 82 | } |
| 84 | if (std::holds_alternative<MetaHalfArithmetic>(meta)) { | ||
| 85 | return std::get<MetaHalfArithmetic>(meta).precise; | ||
| 86 | } | ||
| 87 | return false; | 83 | return false; |
| 88 | } | 84 | } |
| 89 | 85 | ||
| @@ -746,6 +742,16 @@ private: | |||
| 746 | return {}; | 742 | return {}; |
| 747 | } | 743 | } |
| 748 | 744 | ||
| 745 | Id HClamp(Operation operation) { | ||
| 746 | UNIMPLEMENTED(); | ||
| 747 | return {}; | ||
| 748 | } | ||
| 749 | |||
| 750 | Id HUnpack(Operation operation) { | ||
| 751 | UNIMPLEMENTED(); | ||
| 752 | return {}; | ||
| 753 | } | ||
| 754 | |||
| 749 | Id HMergeF32(Operation operation) { | 755 | Id HMergeF32(Operation operation) { |
| 750 | UNIMPLEMENTED(); | 756 | UNIMPLEMENTED(); |
| 751 | return {}; | 757 | return {}; |
| @@ -1218,6 +1224,8 @@ private: | |||
| 1218 | &SPIRVDecompiler::Ternary<&Module::OpFma, Type::HalfFloat>, | 1224 | &SPIRVDecompiler::Ternary<&Module::OpFma, Type::HalfFloat>, |
| 1219 | &SPIRVDecompiler::Unary<&Module::OpFAbs, Type::HalfFloat>, | 1225 | &SPIRVDecompiler::Unary<&Module::OpFAbs, Type::HalfFloat>, |
| 1220 | &SPIRVDecompiler::HNegate, | 1226 | &SPIRVDecompiler::HNegate, |
| 1227 | &SPIRVDecompiler::HClamp, | ||
| 1228 | &SPIRVDecompiler::HUnpack, | ||
| 1221 | &SPIRVDecompiler::HMergeF32, | 1229 | &SPIRVDecompiler::HMergeF32, |
| 1222 | &SPIRVDecompiler::HMergeH0, | 1230 | &SPIRVDecompiler::HMergeH0, |
| 1223 | &SPIRVDecompiler::HMergeH1, | 1231 | &SPIRVDecompiler::HMergeH1, |
| @@ -1260,6 +1268,13 @@ private: | |||
| 1260 | &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::HalfFloat>, | 1268 | &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::HalfFloat>, |
| 1261 | &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::HalfFloat>, | 1269 | &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::HalfFloat>, |
| 1262 | &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::HalfFloat>, | 1270 | &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::HalfFloat>, |
| 1271 | // TODO(Rodrigo): Should these use the OpFUnord* variants? | ||
| 1272 | &SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool, Type::HalfFloat>, | ||
| 1273 | &SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool, Type::HalfFloat>, | ||
| 1274 | &SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool, Type::HalfFloat>, | ||
| 1275 | &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::HalfFloat>, | ||
| 1276 | &SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::HalfFloat>, | ||
| 1277 | &SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::HalfFloat>, | ||
| 1263 | 1278 | ||
| 1264 | &SPIRVDecompiler::Texture, | 1279 | &SPIRVDecompiler::Texture, |
| 1265 | &SPIRVDecompiler::TextureLod, | 1280 | &SPIRVDecompiler::TextureLod, |
diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp index e4c438792..2da595c0d 100644 --- a/src/video_core/shader/decode.cpp +++ b/src/video_core/shader/decode.cpp | |||
| @@ -116,6 +116,8 @@ ExitMethod ShaderIR::Scan(u32 begin, u32 end, std::set<u32>& labels) { | |||
| 116 | // Continue scanning for an exit method. | 116 | // Continue scanning for an exit method. |
| 117 | break; | 117 | break; |
| 118 | } | 118 | } |
| 119 | default: | ||
| 120 | break; | ||
| 119 | } | 121 | } |
| 120 | } | 122 | } |
| 121 | return exit_method = ExitMethod::AlwaysReturn; | 123 | return exit_method = ExitMethod::AlwaysReturn; |
| @@ -206,4 +208,4 @@ u32 ShaderIR::DecodeInstr(NodeBlock& bb, u32 pc) { | |||
| 206 | return pc + 1; | 208 | return pc + 1; |
| 207 | } | 209 | } |
| 208 | 210 | ||
| 209 | } // namespace VideoCommon::Shader \ No newline at end of file | 211 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode/arithmetic_half.cpp b/src/video_core/shader/decode/arithmetic_half.cpp index baee89107..2098c1170 100644 --- a/src/video_core/shader/decode/arithmetic_half.cpp +++ b/src/video_core/shader/decode/arithmetic_half.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | namespace VideoCommon::Shader { | 10 | namespace VideoCommon::Shader { |
| 11 | 11 | ||
| 12 | using Tegra::Shader::HalfType; | ||
| 12 | using Tegra::Shader::Instruction; | 13 | using Tegra::Shader::Instruction; |
| 13 | using Tegra::Shader::OpCode; | 14 | using Tegra::Shader::OpCode; |
| 14 | 15 | ||
| @@ -18,48 +19,50 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) { | |||
| 18 | 19 | ||
| 19 | if (opcode->get().GetId() == OpCode::Id::HADD2_C || | 20 | if (opcode->get().GetId() == OpCode::Id::HADD2_C || |
| 20 | opcode->get().GetId() == OpCode::Id::HADD2_R) { | 21 | opcode->get().GetId() == OpCode::Id::HADD2_R) { |
| 21 | UNIMPLEMENTED_IF(instr.alu_half.ftz != 0); | 22 | if (instr.alu_half.ftz != 0) { |
| 23 | LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName()); | ||
| 24 | } | ||
| 22 | } | 25 | } |
| 23 | UNIMPLEMENTED_IF_MSG(instr.alu_half.saturate != 0, "Half float saturation not implemented"); | ||
| 24 | 26 | ||
| 25 | const bool negate_a = | 27 | const bool negate_a = |
| 26 | opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; | 28 | opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; |
| 27 | const bool negate_b = | 29 | const bool negate_b = |
| 28 | opcode->get().GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0; | 30 | opcode->get().GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0; |
| 29 | 31 | ||
| 30 | const Node op_a = GetOperandAbsNegHalf(GetRegister(instr.gpr8), instr.alu_half.abs_a, negate_a); | 32 | Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half.type_a); |
| 31 | 33 | op_a = GetOperandAbsNegHalf(op_a, instr.alu_half.abs_a, negate_a); | |
| 32 | // instr.alu_half.type_a | ||
| 33 | 34 | ||
| 34 | Node op_b = [&]() { | 35 | auto [type_b, op_b] = [&]() -> std::tuple<HalfType, Node> { |
| 35 | switch (opcode->get().GetId()) { | 36 | switch (opcode->get().GetId()) { |
| 36 | case OpCode::Id::HADD2_C: | 37 | case OpCode::Id::HADD2_C: |
| 37 | case OpCode::Id::HMUL2_C: | 38 | case OpCode::Id::HMUL2_C: |
| 38 | return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); | 39 | return {HalfType::F32, GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())}; |
| 39 | case OpCode::Id::HADD2_R: | 40 | case OpCode::Id::HADD2_R: |
| 40 | case OpCode::Id::HMUL2_R: | 41 | case OpCode::Id::HMUL2_R: |
| 41 | return GetRegister(instr.gpr20); | 42 | return {instr.alu_half.type_b, GetRegister(instr.gpr20)}; |
| 42 | default: | 43 | default: |
| 43 | UNREACHABLE(); | 44 | UNREACHABLE(); |
| 44 | return Immediate(0); | 45 | return {HalfType::F32, Immediate(0)}; |
| 45 | } | 46 | } |
| 46 | }(); | 47 | }(); |
| 47 | op_b = GetOperandAbsNegHalf(op_b, instr.alu_half.abs_b, negate_b); | 48 | op_b = UnpackHalfFloat(op_b, type_b); |
| 49 | // redeclaration to avoid a bug in clang with reusing local bindings in lambdas | ||
| 50 | Node op_b_alt = GetOperandAbsNegHalf(op_b, instr.alu_half.abs_b, negate_b); | ||
| 48 | 51 | ||
| 49 | Node value = [&]() { | 52 | Node value = [&]() { |
| 50 | MetaHalfArithmetic meta{true, {instr.alu_half_imm.type_a, instr.alu_half.type_b}}; | ||
| 51 | switch (opcode->get().GetId()) { | 53 | switch (opcode->get().GetId()) { |
| 52 | case OpCode::Id::HADD2_C: | 54 | case OpCode::Id::HADD2_C: |
| 53 | case OpCode::Id::HADD2_R: | 55 | case OpCode::Id::HADD2_R: |
| 54 | return Operation(OperationCode::HAdd, meta, op_a, op_b); | 56 | return Operation(OperationCode::HAdd, PRECISE, op_a, op_b_alt); |
| 55 | case OpCode::Id::HMUL2_C: | 57 | case OpCode::Id::HMUL2_C: |
| 56 | case OpCode::Id::HMUL2_R: | 58 | case OpCode::Id::HMUL2_R: |
| 57 | return Operation(OperationCode::HMul, meta, op_a, op_b); | 59 | return Operation(OperationCode::HMul, PRECISE, op_a, op_b_alt); |
| 58 | default: | 60 | default: |
| 59 | UNIMPLEMENTED_MSG("Unhandled half float instruction: {}", opcode->get().GetName()); | 61 | UNIMPLEMENTED_MSG("Unhandled half float instruction: {}", opcode->get().GetName()); |
| 60 | return Immediate(0); | 62 | return Immediate(0); |
| 61 | } | 63 | } |
| 62 | }(); | 64 | }(); |
| 65 | value = GetSaturatedHalfFloat(value, instr.alu_half.saturate); | ||
| 63 | value = HalfMerge(GetRegister(instr.gpr0), value, instr.alu_half.merge); | 66 | value = HalfMerge(GetRegister(instr.gpr0), value, instr.alu_half.merge); |
| 64 | 67 | ||
| 65 | SetRegister(bb, instr.gpr0, value); | 68 | SetRegister(bb, instr.gpr0, value); |
| @@ -67,4 +70,4 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) { | |||
| 67 | return pc; | 70 | return pc; |
| 68 | } | 71 | } |
| 69 | 72 | ||
| 70 | } // namespace VideoCommon::Shader \ No newline at end of file | 73 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode/arithmetic_half_immediate.cpp b/src/video_core/shader/decode/arithmetic_half_immediate.cpp index c2164ba50..fbcd35b18 100644 --- a/src/video_core/shader/decode/arithmetic_half_immediate.cpp +++ b/src/video_core/shader/decode/arithmetic_half_immediate.cpp | |||
| @@ -17,34 +17,33 @@ u32 ShaderIR::DecodeArithmeticHalfImmediate(NodeBlock& bb, u32 pc) { | |||
| 17 | const auto opcode = OpCode::Decode(instr); | 17 | const auto opcode = OpCode::Decode(instr); |
| 18 | 18 | ||
| 19 | if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) { | 19 | if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) { |
| 20 | UNIMPLEMENTED_IF(instr.alu_half_imm.ftz != 0); | 20 | if (instr.alu_half_imm.ftz != 0) { |
| 21 | LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName()); | ||
| 22 | } | ||
| 21 | } else { | 23 | } else { |
| 22 | UNIMPLEMENTED_IF(instr.alu_half_imm.precision != Tegra::Shader::HalfPrecision::None); | 24 | UNIMPLEMENTED_IF(instr.alu_half_imm.precision != Tegra::Shader::HalfPrecision::None); |
| 23 | } | 25 | } |
| 24 | UNIMPLEMENTED_IF_MSG(instr.alu_half_imm.saturate != 0, | ||
| 25 | "Half float immediate saturation not implemented"); | ||
| 26 | 26 | ||
| 27 | Node op_a = GetRegister(instr.gpr8); | 27 | Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half_imm.type_a); |
| 28 | op_a = GetOperandAbsNegHalf(op_a, instr.alu_half_imm.abs_a, instr.alu_half_imm.negate_a); | 28 | op_a = GetOperandAbsNegHalf(op_a, instr.alu_half_imm.abs_a, instr.alu_half_imm.negate_a); |
| 29 | 29 | ||
| 30 | const Node op_b = UnpackHalfImmediate(instr, true); | 30 | const Node op_b = UnpackHalfImmediate(instr, true); |
| 31 | 31 | ||
| 32 | Node value = [&]() { | 32 | Node value = [&]() { |
| 33 | MetaHalfArithmetic meta{true, {instr.alu_half_imm.type_a}}; | ||
| 34 | switch (opcode->get().GetId()) { | 33 | switch (opcode->get().GetId()) { |
| 35 | case OpCode::Id::HADD2_IMM: | 34 | case OpCode::Id::HADD2_IMM: |
| 36 | return Operation(OperationCode::HAdd, meta, op_a, op_b); | 35 | return Operation(OperationCode::HAdd, PRECISE, op_a, op_b); |
| 37 | case OpCode::Id::HMUL2_IMM: | 36 | case OpCode::Id::HMUL2_IMM: |
| 38 | return Operation(OperationCode::HMul, meta, op_a, op_b); | 37 | return Operation(OperationCode::HMul, PRECISE, op_a, op_b); |
| 39 | default: | 38 | default: |
| 40 | UNREACHABLE(); | 39 | UNREACHABLE(); |
| 41 | return Immediate(0); | 40 | return Immediate(0); |
| 42 | } | 41 | } |
| 43 | }(); | 42 | }(); |
| 44 | value = HalfMerge(GetRegister(instr.gpr0), value, instr.alu_half_imm.merge); | ||
| 45 | 43 | ||
| 44 | value = GetSaturatedHalfFloat(value, instr.alu_half_imm.saturate); | ||
| 45 | value = HalfMerge(GetRegister(instr.gpr0), value, instr.alu_half_imm.merge); | ||
| 46 | SetRegister(bb, instr.gpr0, value); | 46 | SetRegister(bb, instr.gpr0, value); |
| 47 | |||
| 48 | return pc; | 47 | return pc; |
| 49 | } | 48 | } |
| 50 | 49 | ||
diff --git a/src/video_core/shader/decode/conversion.cpp b/src/video_core/shader/decode/conversion.cpp index 55a6fbbf2..b5ec9a6f5 100644 --- a/src/video_core/shader/decode/conversion.cpp +++ b/src/video_core/shader/decode/conversion.cpp | |||
| @@ -18,13 +18,29 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) { | |||
| 18 | const auto opcode = OpCode::Decode(instr); | 18 | const auto opcode = OpCode::Decode(instr); |
| 19 | 19 | ||
| 20 | switch (opcode->get().GetId()) { | 20 | switch (opcode->get().GetId()) { |
| 21 | case OpCode::Id::I2I_R: { | 21 | case OpCode::Id::I2I_R: |
| 22 | case OpCode::Id::I2I_C: | ||
| 23 | case OpCode::Id::I2I_IMM: { | ||
| 22 | UNIMPLEMENTED_IF(instr.conversion.selector); | 24 | UNIMPLEMENTED_IF(instr.conversion.selector); |
| 25 | UNIMPLEMENTED_IF(instr.conversion.dst_size != Register::Size::Word); | ||
| 26 | UNIMPLEMENTED_IF(instr.alu.saturate_d); | ||
| 23 | 27 | ||
| 24 | const bool input_signed = instr.conversion.is_input_signed; | 28 | const bool input_signed = instr.conversion.is_input_signed; |
| 25 | const bool output_signed = instr.conversion.is_output_signed; | 29 | const bool output_signed = instr.conversion.is_output_signed; |
| 26 | 30 | ||
| 27 | Node value = GetRegister(instr.gpr20); | 31 | Node value = [&]() { |
| 32 | switch (opcode->get().GetId()) { | ||
| 33 | case OpCode::Id::I2I_R: | ||
| 34 | return GetRegister(instr.gpr20); | ||
| 35 | case OpCode::Id::I2I_C: | ||
| 36 | return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); | ||
| 37 | case OpCode::Id::I2I_IMM: | ||
| 38 | return Immediate(instr.alu.GetSignedImm20_20()); | ||
| 39 | default: | ||
| 40 | UNREACHABLE(); | ||
| 41 | return Immediate(0); | ||
| 42 | } | ||
| 43 | }(); | ||
| 28 | value = ConvertIntegerSize(value, instr.conversion.src_size, input_signed); | 44 | value = ConvertIntegerSize(value, instr.conversion.src_size, input_signed); |
| 29 | 45 | ||
| 30 | value = GetOperandAbsNegInteger(value, instr.conversion.abs_a, instr.conversion.negate_a, | 46 | value = GetOperandAbsNegInteger(value, instr.conversion.abs_a, instr.conversion.negate_a, |
| @@ -38,17 +54,24 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) { | |||
| 38 | break; | 54 | break; |
| 39 | } | 55 | } |
| 40 | case OpCode::Id::I2F_R: | 56 | case OpCode::Id::I2F_R: |
| 41 | case OpCode::Id::I2F_C: { | 57 | case OpCode::Id::I2F_C: |
| 42 | UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word); | 58 | case OpCode::Id::I2F_IMM: { |
| 59 | UNIMPLEMENTED_IF(instr.conversion.dst_size != Register::Size::Word); | ||
| 43 | UNIMPLEMENTED_IF(instr.conversion.selector); | 60 | UNIMPLEMENTED_IF(instr.conversion.selector); |
| 44 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | 61 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, |
| 45 | "Condition codes generation in I2F is not implemented"); | 62 | "Condition codes generation in I2F is not implemented"); |
| 46 | 63 | ||
| 47 | Node value = [&]() { | 64 | Node value = [&]() { |
| 48 | if (instr.is_b_gpr) { | 65 | switch (opcode->get().GetId()) { |
| 66 | case OpCode::Id::I2F_R: | ||
| 49 | return GetRegister(instr.gpr20); | 67 | return GetRegister(instr.gpr20); |
| 50 | } else { | 68 | case OpCode::Id::I2F_C: |
| 51 | return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); | 69 | return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); |
| 70 | case OpCode::Id::I2F_IMM: | ||
| 71 | return Immediate(instr.alu.GetSignedImm20_20()); | ||
| 72 | default: | ||
| 73 | UNREACHABLE(); | ||
| 74 | return Immediate(0); | ||
| 52 | } | 75 | } |
| 53 | }(); | 76 | }(); |
| 54 | const bool input_signed = instr.conversion.is_input_signed; | 77 | const bool input_signed = instr.conversion.is_input_signed; |
| @@ -62,24 +85,31 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) { | |||
| 62 | break; | 85 | break; |
| 63 | } | 86 | } |
| 64 | case OpCode::Id::F2F_R: | 87 | case OpCode::Id::F2F_R: |
| 65 | case OpCode::Id::F2F_C: { | 88 | case OpCode::Id::F2F_C: |
| 66 | UNIMPLEMENTED_IF(instr.conversion.dest_size != Register::Size::Word); | 89 | case OpCode::Id::F2F_IMM: { |
| 67 | UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word); | 90 | UNIMPLEMENTED_IF(instr.conversion.f2f.dst_size != Register::Size::Word); |
| 91 | UNIMPLEMENTED_IF(instr.conversion.f2f.src_size != Register::Size::Word); | ||
| 68 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | 92 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, |
| 69 | "Condition codes generation in F2F is not implemented"); | 93 | "Condition codes generation in F2F is not implemented"); |
| 70 | 94 | ||
| 71 | Node value = [&]() { | 95 | Node value = [&]() { |
| 72 | if (instr.is_b_gpr) { | 96 | switch (opcode->get().GetId()) { |
| 97 | case OpCode::Id::F2F_R: | ||
| 73 | return GetRegister(instr.gpr20); | 98 | return GetRegister(instr.gpr20); |
| 74 | } else { | 99 | case OpCode::Id::F2F_C: |
| 75 | return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); | 100 | return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); |
| 101 | case OpCode::Id::F2F_IMM: | ||
| 102 | return GetImmediate19(instr); | ||
| 103 | default: | ||
| 104 | UNREACHABLE(); | ||
| 105 | return Immediate(0); | ||
| 76 | } | 106 | } |
| 77 | }(); | 107 | }(); |
| 78 | 108 | ||
| 79 | value = GetOperandAbsNegFloat(value, instr.conversion.abs_a, instr.conversion.negate_a); | 109 | value = GetOperandAbsNegFloat(value, instr.conversion.abs_a, instr.conversion.negate_a); |
| 80 | 110 | ||
| 81 | value = [&]() { | 111 | value = [&]() { |
| 82 | switch (instr.conversion.f2f.rounding) { | 112 | switch (instr.conversion.f2f.GetRoundingMode()) { |
| 83 | case Tegra::Shader::F2fRoundingOp::None: | 113 | case Tegra::Shader::F2fRoundingOp::None: |
| 84 | return value; | 114 | return value; |
| 85 | case Tegra::Shader::F2fRoundingOp::Round: | 115 | case Tegra::Shader::F2fRoundingOp::Round: |
| @@ -90,10 +120,11 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) { | |||
| 90 | return Operation(OperationCode::FCeil, PRECISE, value); | 120 | return Operation(OperationCode::FCeil, PRECISE, value); |
| 91 | case Tegra::Shader::F2fRoundingOp::Trunc: | 121 | case Tegra::Shader::F2fRoundingOp::Trunc: |
| 92 | return Operation(OperationCode::FTrunc, PRECISE, value); | 122 | return Operation(OperationCode::FTrunc, PRECISE, value); |
| 123 | default: | ||
| 124 | UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}", | ||
| 125 | static_cast<u32>(instr.conversion.f2f.rounding.Value())); | ||
| 126 | return Immediate(0); | ||
| 93 | } | 127 | } |
| 94 | UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}", | ||
| 95 | static_cast<u32>(instr.conversion.f2f.rounding.Value())); | ||
| 96 | return Immediate(0); | ||
| 97 | }(); | 128 | }(); |
| 98 | value = GetSaturatedFloat(value, instr.alu.saturate_d); | 129 | value = GetSaturatedFloat(value, instr.alu.saturate_d); |
| 99 | 130 | ||
| @@ -102,15 +133,22 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) { | |||
| 102 | break; | 133 | break; |
| 103 | } | 134 | } |
| 104 | case OpCode::Id::F2I_R: | 135 | case OpCode::Id::F2I_R: |
| 105 | case OpCode::Id::F2I_C: { | 136 | case OpCode::Id::F2I_C: |
| 137 | case OpCode::Id::F2I_IMM: { | ||
| 106 | UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word); | 138 | UNIMPLEMENTED_IF(instr.conversion.src_size != Register::Size::Word); |
| 107 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, | 139 | UNIMPLEMENTED_IF_MSG(instr.generates_cc, |
| 108 | "Condition codes generation in F2I is not implemented"); | 140 | "Condition codes generation in F2I is not implemented"); |
| 109 | Node value = [&]() { | 141 | Node value = [&]() { |
| 110 | if (instr.is_b_gpr) { | 142 | switch (opcode->get().GetId()) { |
| 143 | case OpCode::Id::F2I_R: | ||
| 111 | return GetRegister(instr.gpr20); | 144 | return GetRegister(instr.gpr20); |
| 112 | } else { | 145 | case OpCode::Id::F2I_C: |
| 113 | return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); | 146 | return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); |
| 147 | case OpCode::Id::F2I_IMM: | ||
| 148 | return GetImmediate19(instr); | ||
| 149 | default: | ||
| 150 | UNREACHABLE(); | ||
| 151 | return Immediate(0); | ||
| 114 | } | 152 | } |
| 115 | }(); | 153 | }(); |
| 116 | 154 | ||
| @@ -134,7 +172,7 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) { | |||
| 134 | }(); | 172 | }(); |
| 135 | const bool is_signed = instr.conversion.is_output_signed; | 173 | const bool is_signed = instr.conversion.is_output_signed; |
| 136 | value = SignedOperation(OperationCode::ICastFloat, is_signed, PRECISE, value); | 174 | value = SignedOperation(OperationCode::ICastFloat, is_signed, PRECISE, value); |
| 137 | value = ConvertIntegerSize(value, instr.conversion.dest_size, is_signed); | 175 | value = ConvertIntegerSize(value, instr.conversion.dst_size, is_signed); |
| 138 | 176 | ||
| 139 | SetRegister(bb, instr.gpr0, value); | 177 | SetRegister(bb, instr.gpr0, value); |
| 140 | break; | 178 | break; |
diff --git a/src/video_core/shader/decode/half_set.cpp b/src/video_core/shader/decode/half_set.cpp index 748368555..1dd94bf9d 100644 --- a/src/video_core/shader/decode/half_set.cpp +++ b/src/video_core/shader/decode/half_set.cpp | |||
| @@ -18,11 +18,13 @@ u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) { | |||
| 18 | const Instruction instr = {program_code[pc]}; | 18 | const Instruction instr = {program_code[pc]}; |
| 19 | const auto opcode = OpCode::Decode(instr); | 19 | const auto opcode = OpCode::Decode(instr); |
| 20 | 20 | ||
| 21 | UNIMPLEMENTED_IF(instr.hset2.ftz != 0); | 21 | if (instr.hset2.ftz != 0) { |
| 22 | LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName()); | ||
| 23 | } | ||
| 24 | |||
| 25 | Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hset2.type_a); | ||
| 26 | op_a = GetOperandAbsNegHalf(op_a, instr.hset2.abs_a, instr.hset2.negate_a); | ||
| 22 | 27 | ||
| 23 | // instr.hset2.type_a | ||
| 24 | // instr.hset2.type_b | ||
| 25 | Node op_a = GetRegister(instr.gpr8); | ||
| 26 | Node op_b = [&]() { | 28 | Node op_b = [&]() { |
| 27 | switch (opcode->get().GetId()) { | 29 | switch (opcode->get().GetId()) { |
| 28 | case OpCode::Id::HSET2_R: | 30 | case OpCode::Id::HSET2_R: |
| @@ -32,14 +34,12 @@ u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) { | |||
| 32 | return Immediate(0); | 34 | return Immediate(0); |
| 33 | } | 35 | } |
| 34 | }(); | 36 | }(); |
| 35 | 37 | op_b = UnpackHalfFloat(op_b, instr.hset2.type_b); | |
| 36 | op_a = GetOperandAbsNegHalf(op_a, instr.hset2.abs_a, instr.hset2.negate_a); | ||
| 37 | op_b = GetOperandAbsNegHalf(op_b, instr.hset2.abs_b, instr.hset2.negate_b); | 38 | op_b = GetOperandAbsNegHalf(op_b, instr.hset2.abs_b, instr.hset2.negate_b); |
| 38 | 39 | ||
| 39 | const Node second_pred = GetPredicate(instr.hset2.pred39, instr.hset2.neg_pred); | 40 | const Node second_pred = GetPredicate(instr.hset2.pred39, instr.hset2.neg_pred); |
| 40 | 41 | ||
| 41 | MetaHalfArithmetic meta{false, {instr.hset2.type_a, instr.hset2.type_b}}; | 42 | const Node comparison_pair = GetPredicateComparisonHalf(instr.hset2.cond, op_a, op_b); |
| 42 | const Node comparison_pair = GetPredicateComparisonHalf(instr.hset2.cond, meta, op_a, op_b); | ||
| 43 | 43 | ||
| 44 | const OperationCode combiner = GetPredicateCombiner(instr.hset2.op); | 44 | const OperationCode combiner = GetPredicateCombiner(instr.hset2.op); |
| 45 | 45 | ||
diff --git a/src/video_core/shader/decode/half_set_predicate.cpp b/src/video_core/shader/decode/half_set_predicate.cpp index e68512692..6e59eb650 100644 --- a/src/video_core/shader/decode/half_set_predicate.cpp +++ b/src/video_core/shader/decode/half_set_predicate.cpp | |||
| @@ -19,10 +19,10 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) { | |||
| 19 | 19 | ||
| 20 | UNIMPLEMENTED_IF(instr.hsetp2.ftz != 0); | 20 | UNIMPLEMENTED_IF(instr.hsetp2.ftz != 0); |
| 21 | 21 | ||
| 22 | Node op_a = GetRegister(instr.gpr8); | 22 | Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hsetp2.type_a); |
| 23 | op_a = GetOperandAbsNegHalf(op_a, instr.hsetp2.abs_a, instr.hsetp2.negate_a); | 23 | op_a = GetOperandAbsNegHalf(op_a, instr.hsetp2.abs_a, instr.hsetp2.negate_a); |
| 24 | 24 | ||
| 25 | const Node op_b = [&]() { | 25 | Node op_b = [&]() { |
| 26 | switch (opcode->get().GetId()) { | 26 | switch (opcode->get().GetId()) { |
| 27 | case OpCode::Id::HSETP2_R: | 27 | case OpCode::Id::HSETP2_R: |
| 28 | return GetOperandAbsNegHalf(GetRegister(instr.gpr20), instr.hsetp2.abs_a, | 28 | return GetOperandAbsNegHalf(GetRegister(instr.gpr20), instr.hsetp2.abs_a, |
| @@ -32,6 +32,7 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) { | |||
| 32 | return Immediate(0); | 32 | return Immediate(0); |
| 33 | } | 33 | } |
| 34 | }(); | 34 | }(); |
| 35 | op_b = UnpackHalfFloat(op_b, instr.hsetp2.type_b); | ||
| 35 | 36 | ||
| 36 | // We can't use the constant predicate as destination. | 37 | // We can't use the constant predicate as destination. |
| 37 | ASSERT(instr.hsetp2.pred3 != static_cast<u64>(Pred::UnusedIndex)); | 38 | ASSERT(instr.hsetp2.pred3 != static_cast<u64>(Pred::UnusedIndex)); |
| @@ -42,8 +43,7 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) { | |||
| 42 | const OperationCode pair_combiner = | 43 | const OperationCode pair_combiner = |
| 43 | instr.hsetp2.h_and ? OperationCode::LogicalAll2 : OperationCode::LogicalAny2; | 44 | instr.hsetp2.h_and ? OperationCode::LogicalAll2 : OperationCode::LogicalAny2; |
| 44 | 45 | ||
| 45 | MetaHalfArithmetic meta = {false, {instr.hsetp2.type_a, instr.hsetp2.type_b}}; | 46 | const Node comparison = GetPredicateComparisonHalf(instr.hsetp2.cond, op_a, op_b); |
| 46 | const Node comparison = GetPredicateComparisonHalf(instr.hsetp2.cond, meta, op_a, op_b); | ||
| 47 | const Node first_pred = Operation(pair_combiner, comparison); | 47 | const Node first_pred = Operation(pair_combiner, comparison); |
| 48 | 48 | ||
| 49 | // Set the primary predicate to the result of Predicate OP SecondPredicate | 49 | // Set the primary predicate to the result of Predicate OP SecondPredicate |
diff --git a/src/video_core/shader/decode/hfma2.cpp b/src/video_core/shader/decode/hfma2.cpp index 7a07c5ec6..a425f9eb7 100644 --- a/src/video_core/shader/decode/hfma2.cpp +++ b/src/video_core/shader/decode/hfma2.cpp | |||
| @@ -27,10 +27,6 @@ u32 ShaderIR::DecodeHfma2(NodeBlock& bb, u32 pc) { | |||
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | constexpr auto identity = HalfType::H0_H1; | 29 | constexpr auto identity = HalfType::H0_H1; |
| 30 | |||
| 31 | const HalfType type_a = instr.hfma2.type_a; | ||
| 32 | const Node op_a = GetRegister(instr.gpr8); | ||
| 33 | |||
| 34 | bool neg_b{}, neg_c{}; | 30 | bool neg_b{}, neg_c{}; |
| 35 | auto [saturate, type_b, op_b, type_c, | 31 | auto [saturate, type_b, op_b, type_c, |
| 36 | op_c] = [&]() -> std::tuple<bool, HalfType, Node, HalfType, Node> { | 32 | op_c] = [&]() -> std::tuple<bool, HalfType, Node, HalfType, Node> { |
| @@ -38,15 +34,14 @@ u32 ShaderIR::DecodeHfma2(NodeBlock& bb, u32 pc) { | |||
| 38 | case OpCode::Id::HFMA2_CR: | 34 | case OpCode::Id::HFMA2_CR: |
| 39 | neg_b = instr.hfma2.negate_b; | 35 | neg_b = instr.hfma2.negate_b; |
| 40 | neg_c = instr.hfma2.negate_c; | 36 | neg_c = instr.hfma2.negate_c; |
| 41 | return {instr.hfma2.saturate, instr.hfma2.type_b, | 37 | return {instr.hfma2.saturate, HalfType::F32, |
| 42 | GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()), | 38 | GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()), |
| 43 | instr.hfma2.type_reg39, GetRegister(instr.gpr39)}; | 39 | instr.hfma2.type_reg39, GetRegister(instr.gpr39)}; |
| 44 | case OpCode::Id::HFMA2_RC: | 40 | case OpCode::Id::HFMA2_RC: |
| 45 | neg_b = instr.hfma2.negate_b; | 41 | neg_b = instr.hfma2.negate_b; |
| 46 | neg_c = instr.hfma2.negate_c; | 42 | neg_c = instr.hfma2.negate_c; |
| 47 | return {instr.hfma2.saturate, instr.hfma2.type_reg39, GetRegister(instr.gpr39), | 43 | return {instr.hfma2.saturate, instr.hfma2.type_reg39, GetRegister(instr.gpr39), |
| 48 | instr.hfma2.type_b, | 44 | HalfType::F32, GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())}; |
| 49 | GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())}; | ||
| 50 | case OpCode::Id::HFMA2_RR: | 45 | case OpCode::Id::HFMA2_RR: |
| 51 | neg_b = instr.hfma2.rr.negate_b; | 46 | neg_b = instr.hfma2.rr.negate_b; |
| 52 | neg_c = instr.hfma2.rr.negate_c; | 47 | neg_c = instr.hfma2.rr.negate_c; |
| @@ -60,13 +55,13 @@ u32 ShaderIR::DecodeHfma2(NodeBlock& bb, u32 pc) { | |||
| 60 | return {false, identity, Immediate(0), identity, Immediate(0)}; | 55 | return {false, identity, Immediate(0), identity, Immediate(0)}; |
| 61 | } | 56 | } |
| 62 | }(); | 57 | }(); |
| 63 | UNIMPLEMENTED_IF_MSG(saturate, "HFMA2 saturation is not implemented"); | ||
| 64 | 58 | ||
| 65 | op_b = GetOperandAbsNegHalf(op_b, false, neg_b); | 59 | const Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hfma2.type_a); |
| 66 | op_c = GetOperandAbsNegHalf(op_c, false, neg_c); | 60 | op_b = GetOperandAbsNegHalf(UnpackHalfFloat(op_b, type_b), false, neg_b); |
| 61 | op_c = GetOperandAbsNegHalf(UnpackHalfFloat(op_c, type_c), false, neg_c); | ||
| 67 | 62 | ||
| 68 | MetaHalfArithmetic meta{true, {type_a, type_b, type_c}}; | 63 | Node value = Operation(OperationCode::HFma, PRECISE, op_a, op_b, op_c); |
| 69 | Node value = Operation(OperationCode::HFma, meta, op_a, op_b, op_c); | 64 | value = GetSaturatedHalfFloat(value, saturate); |
| 70 | value = HalfMerge(GetRegister(instr.gpr0), value, instr.hfma2.merge); | 65 | value = HalfMerge(GetRegister(instr.gpr0), value, instr.hfma2.merge); |
| 71 | 66 | ||
| 72 | SetRegister(bb, instr.gpr0, value); | 67 | SetRegister(bb, instr.gpr0, value); |
| @@ -74,4 +69,4 @@ u32 ShaderIR::DecodeHfma2(NodeBlock& bb, u32 pc) { | |||
| 74 | return pc; | 69 | return pc; |
| 75 | } | 70 | } |
| 76 | 71 | ||
| 77 | } // namespace VideoCommon::Shader \ No newline at end of file | 72 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp index fa65ac9a9..8b574d4e5 100644 --- a/src/video_core/shader/decode/texture.cpp +++ b/src/video_core/shader/decode/texture.cpp | |||
| @@ -296,7 +296,7 @@ const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg, | |||
| 296 | ASSERT(cbuf_offset_imm != nullptr); | 296 | ASSERT(cbuf_offset_imm != nullptr); |
| 297 | const auto cbuf_offset = cbuf_offset_imm->GetValue(); | 297 | const auto cbuf_offset = cbuf_offset_imm->GetValue(); |
| 298 | const auto cbuf_index = cbuf->GetIndex(); | 298 | const auto cbuf_index = cbuf->GetIndex(); |
| 299 | const u64 cbuf_key = (cbuf_index << 32) | cbuf_offset; | 299 | const auto cbuf_key = (static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset); |
| 300 | 300 | ||
| 301 | // If this sampler has already been used, return the existing mapping. | 301 | // If this sampler has already been used, return the existing mapping. |
| 302 | const auto itr = | 302 | const auto itr = |
| @@ -541,7 +541,6 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de | |||
| 541 | bool is_array, bool is_aoffi) { | 541 | bool is_array, bool is_aoffi) { |
| 542 | const std::size_t coord_count = GetCoordCount(texture_type); | 542 | const std::size_t coord_count = GetCoordCount(texture_type); |
| 543 | const std::size_t total_coord_count = coord_count + (is_array ? 1 : 0); | 543 | const std::size_t total_coord_count = coord_count + (is_array ? 1 : 0); |
| 544 | const std::size_t total_reg_count = total_coord_count + (depth_compare ? 1 : 0); | ||
| 545 | 544 | ||
| 546 | // If enabled arrays index is always stored in the gpr8 field | 545 | // If enabled arrays index is always stored in the gpr8 field |
| 547 | const u64 array_register = instr.gpr8.Value(); | 546 | const u64 array_register = instr.gpr8.Value(); |
diff --git a/src/video_core/shader/decode/xmad.cpp b/src/video_core/shader/decode/xmad.cpp index db15c0718..04a776398 100644 --- a/src/video_core/shader/decode/xmad.cpp +++ b/src/video_core/shader/decode/xmad.cpp | |||
| @@ -56,9 +56,10 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) { | |||
| 56 | instr.xmad.mode, | 56 | instr.xmad.mode, |
| 57 | Immediate(static_cast<u32>(instr.xmad.imm20_16)), | 57 | Immediate(static_cast<u32>(instr.xmad.imm20_16)), |
| 58 | GetRegister(instr.gpr39)}; | 58 | GetRegister(instr.gpr39)}; |
| 59 | default: | ||
| 60 | UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName()); | ||
| 61 | return {false, false, false, Tegra::Shader::XmadMode::None, Immediate(0), Immediate(0)}; | ||
| 59 | } | 62 | } |
| 60 | UNIMPLEMENTED_MSG("Unhandled XMAD instruction: {}", opcode->get().GetName()); | ||
| 61 | return {false, false, false, Tegra::Shader::XmadMode::None, Immediate(0), Immediate(0)}; | ||
| 62 | }(); | 63 | }(); |
| 63 | 64 | ||
| 64 | op_a = BitfieldExtract(op_a, instr.xmad.high_a ? 16 : 0, 16); | 65 | op_a = BitfieldExtract(op_a, instr.xmad.high_a ? 16 : 0, 16); |
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index ac5112d78..e4eb0dfd9 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp | |||
| @@ -189,7 +189,11 @@ Node ShaderIR::UnpackHalfImmediate(Instruction instr, bool has_negation) { | |||
| 189 | const Node first_negate = GetPredicate(instr.half_imm.first_negate != 0); | 189 | const Node first_negate = GetPredicate(instr.half_imm.first_negate != 0); |
| 190 | const Node second_negate = GetPredicate(instr.half_imm.second_negate != 0); | 190 | const Node second_negate = GetPredicate(instr.half_imm.second_negate != 0); |
| 191 | 191 | ||
| 192 | return Operation(OperationCode::HNegate, HALF_NO_PRECISE, value, first_negate, second_negate); | 192 | return Operation(OperationCode::HNegate, NO_PRECISE, value, first_negate, second_negate); |
| 193 | } | ||
| 194 | |||
| 195 | Node ShaderIR::UnpackHalfFloat(Node value, Tegra::Shader::HalfType type) { | ||
| 196 | return Operation(OperationCode::HUnpack, type, value); | ||
| 193 | } | 197 | } |
| 194 | 198 | ||
| 195 | Node ShaderIR::HalfMerge(Node dest, Node src, Tegra::Shader::HalfMerge merge) { | 199 | Node ShaderIR::HalfMerge(Node dest, Node src, Tegra::Shader::HalfMerge merge) { |
| @@ -209,17 +213,26 @@ Node ShaderIR::HalfMerge(Node dest, Node src, Tegra::Shader::HalfMerge merge) { | |||
| 209 | 213 | ||
| 210 | Node ShaderIR::GetOperandAbsNegHalf(Node value, bool absolute, bool negate) { | 214 | Node ShaderIR::GetOperandAbsNegHalf(Node value, bool absolute, bool negate) { |
| 211 | if (absolute) { | 215 | if (absolute) { |
| 212 | value = Operation(OperationCode::HAbsolute, HALF_NO_PRECISE, value); | 216 | value = Operation(OperationCode::HAbsolute, NO_PRECISE, value); |
| 213 | } | 217 | } |
| 214 | if (negate) { | 218 | if (negate) { |
| 215 | value = Operation(OperationCode::HNegate, HALF_NO_PRECISE, value, GetPredicate(true), | 219 | value = Operation(OperationCode::HNegate, NO_PRECISE, value, GetPredicate(true), |
| 216 | GetPredicate(true)); | 220 | GetPredicate(true)); |
| 217 | } | 221 | } |
| 218 | return value; | 222 | return value; |
| 219 | } | 223 | } |
| 220 | 224 | ||
| 225 | Node ShaderIR::GetSaturatedHalfFloat(Node value, bool saturate) { | ||
| 226 | if (!saturate) { | ||
| 227 | return value; | ||
| 228 | } | ||
| 229 | const Node positive_zero = Immediate(std::copysignf(0, 1)); | ||
| 230 | const Node positive_one = Immediate(1.0f); | ||
| 231 | return Operation(OperationCode::HClamp, NO_PRECISE, value, positive_zero, positive_one); | ||
| 232 | } | ||
| 233 | |||
| 221 | Node ShaderIR::GetPredicateComparisonFloat(PredCondition condition, Node op_a, Node op_b) { | 234 | Node ShaderIR::GetPredicateComparisonFloat(PredCondition condition, Node op_a, Node op_b) { |
| 222 | static const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = { | 235 | const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = { |
| 223 | {PredCondition::LessThan, OperationCode::LogicalFLessThan}, | 236 | {PredCondition::LessThan, OperationCode::LogicalFLessThan}, |
| 224 | {PredCondition::Equal, OperationCode::LogicalFEqual}, | 237 | {PredCondition::Equal, OperationCode::LogicalFEqual}, |
| 225 | {PredCondition::LessEqual, OperationCode::LogicalFLessEqual}, | 238 | {PredCondition::LessEqual, OperationCode::LogicalFLessEqual}, |
| @@ -255,7 +268,7 @@ Node ShaderIR::GetPredicateComparisonFloat(PredCondition condition, Node op_a, N | |||
| 255 | 268 | ||
| 256 | Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_signed, Node op_a, | 269 | Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_signed, Node op_a, |
| 257 | Node op_b) { | 270 | Node op_b) { |
| 258 | static const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = { | 271 | const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = { |
| 259 | {PredCondition::LessThan, OperationCode::LogicalILessThan}, | 272 | {PredCondition::LessThan, OperationCode::LogicalILessThan}, |
| 260 | {PredCondition::Equal, OperationCode::LogicalIEqual}, | 273 | {PredCondition::Equal, OperationCode::LogicalIEqual}, |
| 261 | {PredCondition::LessEqual, OperationCode::LogicalILessEqual}, | 274 | {PredCondition::LessEqual, OperationCode::LogicalILessEqual}, |
| @@ -283,40 +296,32 @@ Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_si | |||
| 283 | return predicate; | 296 | return predicate; |
| 284 | } | 297 | } |
| 285 | 298 | ||
| 286 | Node ShaderIR::GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, | 299 | Node ShaderIR::GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, Node op_a, |
| 287 | const MetaHalfArithmetic& meta, Node op_a, Node op_b) { | 300 | Node op_b) { |
| 288 | 301 | const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = { | |
| 289 | UNIMPLEMENTED_IF_MSG(condition == PredCondition::LessThanWithNan || | ||
| 290 | condition == PredCondition::NotEqualWithNan || | ||
| 291 | condition == PredCondition::LessEqualWithNan || | ||
| 292 | condition == PredCondition::GreaterThanWithNan || | ||
| 293 | condition == PredCondition::GreaterEqualWithNan, | ||
| 294 | "Unimplemented NaN comparison for half floats"); | ||
| 295 | |||
| 296 | static const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = { | ||
| 297 | {PredCondition::LessThan, OperationCode::Logical2HLessThan}, | 302 | {PredCondition::LessThan, OperationCode::Logical2HLessThan}, |
| 298 | {PredCondition::Equal, OperationCode::Logical2HEqual}, | 303 | {PredCondition::Equal, OperationCode::Logical2HEqual}, |
| 299 | {PredCondition::LessEqual, OperationCode::Logical2HLessEqual}, | 304 | {PredCondition::LessEqual, OperationCode::Logical2HLessEqual}, |
| 300 | {PredCondition::GreaterThan, OperationCode::Logical2HGreaterThan}, | 305 | {PredCondition::GreaterThan, OperationCode::Logical2HGreaterThan}, |
| 301 | {PredCondition::NotEqual, OperationCode::Logical2HNotEqual}, | 306 | {PredCondition::NotEqual, OperationCode::Logical2HNotEqual}, |
| 302 | {PredCondition::GreaterEqual, OperationCode::Logical2HGreaterEqual}, | 307 | {PredCondition::GreaterEqual, OperationCode::Logical2HGreaterEqual}, |
| 303 | {PredCondition::LessThanWithNan, OperationCode::Logical2HLessThan}, | 308 | {PredCondition::LessThanWithNan, OperationCode::Logical2HLessThanWithNan}, |
| 304 | {PredCondition::NotEqualWithNan, OperationCode::Logical2HNotEqual}, | 309 | {PredCondition::NotEqualWithNan, OperationCode::Logical2HNotEqualWithNan}, |
| 305 | {PredCondition::LessEqualWithNan, OperationCode::Logical2HLessEqual}, | 310 | {PredCondition::LessEqualWithNan, OperationCode::Logical2HLessEqualWithNan}, |
| 306 | {PredCondition::GreaterThanWithNan, OperationCode::Logical2HGreaterThan}, | 311 | {PredCondition::GreaterThanWithNan, OperationCode::Logical2HGreaterThanWithNan}, |
| 307 | {PredCondition::GreaterEqualWithNan, OperationCode::Logical2HGreaterEqual}}; | 312 | {PredCondition::GreaterEqualWithNan, OperationCode::Logical2HGreaterEqualWithNan}}; |
| 308 | 313 | ||
| 309 | const auto comparison{PredicateComparisonTable.find(condition)}; | 314 | const auto comparison{PredicateComparisonTable.find(condition)}; |
| 310 | UNIMPLEMENTED_IF_MSG(comparison == PredicateComparisonTable.end(), | 315 | UNIMPLEMENTED_IF_MSG(comparison == PredicateComparisonTable.end(), |
| 311 | "Unknown predicate comparison operation"); | 316 | "Unknown predicate comparison operation"); |
| 312 | 317 | ||
| 313 | const Node predicate = Operation(comparison->second, meta, op_a, op_b); | 318 | const Node predicate = Operation(comparison->second, NO_PRECISE, op_a, op_b); |
| 314 | 319 | ||
| 315 | return predicate; | 320 | return predicate; |
| 316 | } | 321 | } |
| 317 | 322 | ||
| 318 | OperationCode ShaderIR::GetPredicateCombiner(PredOperation operation) { | 323 | OperationCode ShaderIR::GetPredicateCombiner(PredOperation operation) { |
| 319 | static const std::unordered_map<PredOperation, OperationCode> PredicateOperationTable = { | 324 | const std::unordered_map<PredOperation, OperationCode> PredicateOperationTable = { |
| 320 | {PredOperation::And, OperationCode::LogicalAnd}, | 325 | {PredOperation::And, OperationCode::LogicalAnd}, |
| 321 | {PredOperation::Or, OperationCode::LogicalOr}, | 326 | {PredOperation::Or, OperationCode::LogicalOr}, |
| 322 | {PredOperation::Xor, OperationCode::LogicalXor}, | 327 | {PredOperation::Xor, OperationCode::LogicalXor}, |
| @@ -434,11 +439,14 @@ Node ShaderIR::BitfieldExtract(Node value, u32 offset, u32 bits) { | |||
| 434 | return OperationCode::LogicalUGreaterEqual; | 439 | return OperationCode::LogicalUGreaterEqual; |
| 435 | case OperationCode::INegate: | 440 | case OperationCode::INegate: |
| 436 | UNREACHABLE_MSG("Can't negate an unsigned integer"); | 441 | UNREACHABLE_MSG("Can't negate an unsigned integer"); |
| 442 | return {}; | ||
| 437 | case OperationCode::IAbsolute: | 443 | case OperationCode::IAbsolute: |
| 438 | UNREACHABLE_MSG("Can't apply absolute to an unsigned integer"); | 444 | UNREACHABLE_MSG("Can't apply absolute to an unsigned integer"); |
| 445 | return {}; | ||
| 446 | default: | ||
| 447 | UNREACHABLE_MSG("Unknown signed operation with code={}", static_cast<u32>(operation_code)); | ||
| 448 | return {}; | ||
| 439 | } | 449 | } |
| 440 | UNREACHABLE_MSG("Unknown signed operation with code={}", static_cast<u32>(operation_code)); | ||
| 441 | return {}; | ||
| 442 | } | 450 | } |
| 443 | 451 | ||
| 444 | } // namespace VideoCommon::Shader \ No newline at end of file | 452 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 57af8b10f..65f1e1de9 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h | |||
| @@ -109,11 +109,13 @@ enum class OperationCode { | |||
| 109 | UBitfieldExtract, /// (MetaArithmetic, uint value, int offset, int offset) -> uint | 109 | UBitfieldExtract, /// (MetaArithmetic, uint value, int offset, int offset) -> uint |
| 110 | UBitCount, /// (MetaArithmetic, uint) -> uint | 110 | UBitCount, /// (MetaArithmetic, uint) -> uint |
| 111 | 111 | ||
| 112 | HAdd, /// (MetaHalfArithmetic, f16vec2 a, f16vec2 b) -> f16vec2 | 112 | HAdd, /// (MetaArithmetic, f16vec2 a, f16vec2 b) -> f16vec2 |
| 113 | HMul, /// (MetaHalfArithmetic, f16vec2 a, f16vec2 b) -> f16vec2 | 113 | HMul, /// (MetaArithmetic, f16vec2 a, f16vec2 b) -> f16vec2 |
| 114 | HFma, /// (MetaHalfArithmetic, f16vec2 a, f16vec2 b, f16vec2 c) -> f16vec2 | 114 | HFma, /// (MetaArithmetic, f16vec2 a, f16vec2 b, f16vec2 c) -> f16vec2 |
| 115 | HAbsolute, /// (f16vec2 a) -> f16vec2 | 115 | HAbsolute, /// (f16vec2 a) -> f16vec2 |
| 116 | HNegate, /// (f16vec2 a, bool first, bool second) -> f16vec2 | 116 | HNegate, /// (f16vec2 a, bool first, bool second) -> f16vec2 |
| 117 | HClamp, /// (f16vec2 src, float min, float max) -> f16vec2 | ||
| 118 | HUnpack, /// (Tegra::Shader::HalfType, T value) -> f16vec2 | ||
| 117 | HMergeF32, /// (f16vec2 src) -> float | 119 | HMergeF32, /// (f16vec2 src) -> float |
| 118 | HMergeH0, /// (f16vec2 dest, f16vec2 src) -> f16vec2 | 120 | HMergeH0, /// (f16vec2 dest, f16vec2 src) -> f16vec2 |
| 119 | HMergeH1, /// (f16vec2 dest, f16vec2 src) -> f16vec2 | 121 | HMergeH1, /// (f16vec2 dest, f16vec2 src) -> f16vec2 |
| @@ -150,12 +152,18 @@ enum class OperationCode { | |||
| 150 | LogicalUNotEqual, /// (uint a, uint b) -> bool | 152 | LogicalUNotEqual, /// (uint a, uint b) -> bool |
| 151 | LogicalUGreaterEqual, /// (uint a, uint b) -> bool | 153 | LogicalUGreaterEqual, /// (uint a, uint b) -> bool |
| 152 | 154 | ||
| 153 | Logical2HLessThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 | 155 | Logical2HLessThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 |
| 154 | Logical2HEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 | 156 | Logical2HEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 |
| 155 | Logical2HLessEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 | 157 | Logical2HLessEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 |
| 156 | Logical2HGreaterThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 | 158 | Logical2HGreaterThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 |
| 157 | Logical2HNotEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 | 159 | Logical2HNotEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 |
| 158 | Logical2HGreaterEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 | 160 | Logical2HGreaterEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 |
| 161 | Logical2HLessThanWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 | ||
| 162 | Logical2HEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 | ||
| 163 | Logical2HLessEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 | ||
| 164 | Logical2HGreaterThanWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 | ||
| 165 | Logical2HNotEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 | ||
| 166 | Logical2HGreaterEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 | ||
| 159 | 167 | ||
| 160 | Texture, /// (MetaTexture, float[N] coords) -> float4 | 168 | Texture, /// (MetaTexture, float[N] coords) -> float4 |
| 161 | TextureLod, /// (MetaTexture, float[N] coords) -> float4 | 169 | TextureLod, /// (MetaTexture, float[N] coords) -> float4 |
| @@ -243,8 +251,9 @@ public: | |||
| 243 | } | 251 | } |
| 244 | 252 | ||
| 245 | bool operator<(const Sampler& rhs) const { | 253 | bool operator<(const Sampler& rhs) const { |
| 246 | return std::tie(offset, index, type, is_array, is_shadow) < | 254 | return std::tie(index, offset, type, is_array, is_shadow, is_bindless) < |
| 247 | std::tie(rhs.offset, rhs.index, rhs.type, rhs.is_array, rhs.is_shadow); | 255 | std::tie(rhs.index, rhs.offset, rhs.type, rhs.is_array, rhs.is_shadow, |
| 256 | rhs.is_bindless); | ||
| 248 | } | 257 | } |
| 249 | 258 | ||
| 250 | private: | 259 | private: |
| @@ -308,13 +317,6 @@ struct MetaArithmetic { | |||
| 308 | bool precise{}; | 317 | bool precise{}; |
| 309 | }; | 318 | }; |
| 310 | 319 | ||
| 311 | struct MetaHalfArithmetic { | ||
| 312 | bool precise{}; | ||
| 313 | std::array<Tegra::Shader::HalfType, 3> types = {Tegra::Shader::HalfType::H0_H1, | ||
| 314 | Tegra::Shader::HalfType::H0_H1, | ||
| 315 | Tegra::Shader::HalfType::H0_H1}; | ||
| 316 | }; | ||
| 317 | |||
| 318 | struct MetaTexture { | 320 | struct MetaTexture { |
| 319 | const Sampler& sampler; | 321 | const Sampler& sampler; |
| 320 | Node array{}; | 322 | Node array{}; |
| @@ -326,11 +328,10 @@ struct MetaTexture { | |||
| 326 | u32 element{}; | 328 | u32 element{}; |
| 327 | }; | 329 | }; |
| 328 | 330 | ||
| 329 | constexpr MetaArithmetic PRECISE = {true}; | 331 | inline constexpr MetaArithmetic PRECISE = {true}; |
| 330 | constexpr MetaArithmetic NO_PRECISE = {false}; | 332 | inline constexpr MetaArithmetic NO_PRECISE = {false}; |
| 331 | constexpr MetaHalfArithmetic HALF_NO_PRECISE = {false}; | ||
| 332 | 333 | ||
| 333 | using Meta = std::variant<MetaArithmetic, MetaHalfArithmetic, MetaTexture>; | 334 | using Meta = std::variant<MetaArithmetic, MetaTexture, Tegra::Shader::HalfType>; |
| 334 | 335 | ||
| 335 | /// Holds any kind of operation that can be done in the IR | 336 | /// Holds any kind of operation that can be done in the IR |
| 336 | class OperationNode final { | 337 | class OperationNode final { |
| @@ -734,10 +735,14 @@ private: | |||
| 734 | 735 | ||
| 735 | /// Unpacks a half immediate from an instruction | 736 | /// Unpacks a half immediate from an instruction |
| 736 | Node UnpackHalfImmediate(Tegra::Shader::Instruction instr, bool has_negation); | 737 | Node UnpackHalfImmediate(Tegra::Shader::Instruction instr, bool has_negation); |
| 738 | /// Unpacks a binary value into a half float pair with a type format | ||
| 739 | Node UnpackHalfFloat(Node value, Tegra::Shader::HalfType type); | ||
| 737 | /// Merges a half pair into another value | 740 | /// Merges a half pair into another value |
| 738 | Node HalfMerge(Node dest, Node src, Tegra::Shader::HalfMerge merge); | 741 | Node HalfMerge(Node dest, Node src, Tegra::Shader::HalfMerge merge); |
| 739 | /// Conditionally absolute/negated half float pair. Absolute is applied first | 742 | /// Conditionally absolute/negated half float pair. Absolute is applied first |
| 740 | Node GetOperandAbsNegHalf(Node value, bool absolute, bool negate); | 743 | Node GetOperandAbsNegHalf(Node value, bool absolute, bool negate); |
| 744 | /// Conditionally saturates a half float pair | ||
| 745 | Node GetSaturatedHalfFloat(Node value, bool saturate = true); | ||
| 741 | 746 | ||
| 742 | /// Returns a predicate comparing two floats | 747 | /// Returns a predicate comparing two floats |
| 743 | Node GetPredicateComparisonFloat(Tegra::Shader::PredCondition condition, Node op_a, Node op_b); | 748 | Node GetPredicateComparisonFloat(Tegra::Shader::PredCondition condition, Node op_a, Node op_b); |
| @@ -745,8 +750,7 @@ private: | |||
| 745 | Node GetPredicateComparisonInteger(Tegra::Shader::PredCondition condition, bool is_signed, | 750 | Node GetPredicateComparisonInteger(Tegra::Shader::PredCondition condition, bool is_signed, |
| 746 | Node op_a, Node op_b); | 751 | Node op_a, Node op_b); |
| 747 | /// Returns a predicate comparing two half floats. meta consumes how both pairs will be compared | 752 | /// Returns a predicate comparing two half floats. meta consumes how both pairs will be compared |
| 748 | Node GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, | 753 | Node GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, Node op_a, Node op_b); |
| 749 | const MetaHalfArithmetic& meta, Node op_a, Node op_b); | ||
| 750 | 754 | ||
| 751 | /// Returns a predicate combiner operation | 755 | /// Returns a predicate combiner operation |
| 752 | OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation); | 756 | OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation); |
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index 3b022a456..6384fa8d2 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp | |||
| @@ -178,39 +178,44 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, | |||
| 178 | return PixelFormat::ABGR8S; | 178 | return PixelFormat::ABGR8S; |
| 179 | case Tegra::Texture::ComponentType::UINT: | 179 | case Tegra::Texture::ComponentType::UINT: |
| 180 | return PixelFormat::ABGR8UI; | 180 | return PixelFormat::ABGR8UI; |
| 181 | default: | ||
| 182 | break; | ||
| 181 | } | 183 | } |
| 182 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 184 | break; |
| 183 | UNREACHABLE(); | ||
| 184 | case Tegra::Texture::TextureFormat::B5G6R5: | 185 | case Tegra::Texture::TextureFormat::B5G6R5: |
| 185 | switch (component_type) { | 186 | switch (component_type) { |
| 186 | case Tegra::Texture::ComponentType::UNORM: | 187 | case Tegra::Texture::ComponentType::UNORM: |
| 187 | return PixelFormat::B5G6R5U; | 188 | return PixelFormat::B5G6R5U; |
| 189 | default: | ||
| 190 | break; | ||
| 188 | } | 191 | } |
| 189 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 192 | break; |
| 190 | UNREACHABLE(); | ||
| 191 | case Tegra::Texture::TextureFormat::A2B10G10R10: | 193 | case Tegra::Texture::TextureFormat::A2B10G10R10: |
| 192 | switch (component_type) { | 194 | switch (component_type) { |
| 193 | case Tegra::Texture::ComponentType::UNORM: | 195 | case Tegra::Texture::ComponentType::UNORM: |
| 194 | return PixelFormat::A2B10G10R10U; | 196 | return PixelFormat::A2B10G10R10U; |
| 197 | default: | ||
| 198 | break; | ||
| 195 | } | 199 | } |
| 196 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 200 | break; |
| 197 | UNREACHABLE(); | ||
| 198 | case Tegra::Texture::TextureFormat::A1B5G5R5: | 201 | case Tegra::Texture::TextureFormat::A1B5G5R5: |
| 199 | switch (component_type) { | 202 | switch (component_type) { |
| 200 | case Tegra::Texture::ComponentType::UNORM: | 203 | case Tegra::Texture::ComponentType::UNORM: |
| 201 | return PixelFormat::A1B5G5R5U; | 204 | return PixelFormat::A1B5G5R5U; |
| 205 | default: | ||
| 206 | break; | ||
| 202 | } | 207 | } |
| 203 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 208 | break; |
| 204 | UNREACHABLE(); | ||
| 205 | case Tegra::Texture::TextureFormat::R8: | 209 | case Tegra::Texture::TextureFormat::R8: |
| 206 | switch (component_type) { | 210 | switch (component_type) { |
| 207 | case Tegra::Texture::ComponentType::UNORM: | 211 | case Tegra::Texture::ComponentType::UNORM: |
| 208 | return PixelFormat::R8U; | 212 | return PixelFormat::R8U; |
| 209 | case Tegra::Texture::ComponentType::UINT: | 213 | case Tegra::Texture::ComponentType::UINT: |
| 210 | return PixelFormat::R8UI; | 214 | return PixelFormat::R8UI; |
| 215 | default: | ||
| 216 | break; | ||
| 211 | } | 217 | } |
| 212 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 218 | break; |
| 213 | UNREACHABLE(); | ||
| 214 | case Tegra::Texture::TextureFormat::G8R8: | 219 | case Tegra::Texture::TextureFormat::G8R8: |
| 215 | // TextureFormat::G8R8 is actually ordered red then green, as such we can use | 220 | // TextureFormat::G8R8 is actually ordered red then green, as such we can use |
| 216 | // PixelFormat::RG8U and PixelFormat::RG8S. This was tested with The Legend of Zelda: Breath | 221 | // PixelFormat::RG8U and PixelFormat::RG8S. This was tested with The Legend of Zelda: Breath |
| @@ -220,50 +225,55 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, | |||
| 220 | return PixelFormat::RG8U; | 225 | return PixelFormat::RG8U; |
| 221 | case Tegra::Texture::ComponentType::SNORM: | 226 | case Tegra::Texture::ComponentType::SNORM: |
| 222 | return PixelFormat::RG8S; | 227 | return PixelFormat::RG8S; |
| 228 | default: | ||
| 229 | break; | ||
| 223 | } | 230 | } |
| 224 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 231 | break; |
| 225 | UNREACHABLE(); | ||
| 226 | case Tegra::Texture::TextureFormat::R16_G16_B16_A16: | 232 | case Tegra::Texture::TextureFormat::R16_G16_B16_A16: |
| 227 | switch (component_type) { | 233 | switch (component_type) { |
| 228 | case Tegra::Texture::ComponentType::UNORM: | 234 | case Tegra::Texture::ComponentType::UNORM: |
| 229 | return PixelFormat::RGBA16U; | 235 | return PixelFormat::RGBA16U; |
| 230 | case Tegra::Texture::ComponentType::FLOAT: | 236 | case Tegra::Texture::ComponentType::FLOAT: |
| 231 | return PixelFormat::RGBA16F; | 237 | return PixelFormat::RGBA16F; |
| 238 | default: | ||
| 239 | break; | ||
| 232 | } | 240 | } |
| 233 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 241 | break; |
| 234 | UNREACHABLE(); | ||
| 235 | case Tegra::Texture::TextureFormat::BF10GF11RF11: | 242 | case Tegra::Texture::TextureFormat::BF10GF11RF11: |
| 236 | switch (component_type) { | 243 | switch (component_type) { |
| 237 | case Tegra::Texture::ComponentType::FLOAT: | 244 | case Tegra::Texture::ComponentType::FLOAT: |
| 238 | return PixelFormat::R11FG11FB10F; | 245 | return PixelFormat::R11FG11FB10F; |
| 246 | default: | ||
| 247 | break; | ||
| 239 | } | 248 | } |
| 240 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | ||
| 241 | UNREACHABLE(); | ||
| 242 | case Tegra::Texture::TextureFormat::R32_G32_B32_A32: | 249 | case Tegra::Texture::TextureFormat::R32_G32_B32_A32: |
| 243 | switch (component_type) { | 250 | switch (component_type) { |
| 244 | case Tegra::Texture::ComponentType::FLOAT: | 251 | case Tegra::Texture::ComponentType::FLOAT: |
| 245 | return PixelFormat::RGBA32F; | 252 | return PixelFormat::RGBA32F; |
| 246 | case Tegra::Texture::ComponentType::UINT: | 253 | case Tegra::Texture::ComponentType::UINT: |
| 247 | return PixelFormat::RGBA32UI; | 254 | return PixelFormat::RGBA32UI; |
| 255 | default: | ||
| 256 | break; | ||
| 248 | } | 257 | } |
| 249 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 258 | break; |
| 250 | UNREACHABLE(); | ||
| 251 | case Tegra::Texture::TextureFormat::R32_G32: | 259 | case Tegra::Texture::TextureFormat::R32_G32: |
| 252 | switch (component_type) { | 260 | switch (component_type) { |
| 253 | case Tegra::Texture::ComponentType::FLOAT: | 261 | case Tegra::Texture::ComponentType::FLOAT: |
| 254 | return PixelFormat::RG32F; | 262 | return PixelFormat::RG32F; |
| 255 | case Tegra::Texture::ComponentType::UINT: | 263 | case Tegra::Texture::ComponentType::UINT: |
| 256 | return PixelFormat::RG32UI; | 264 | return PixelFormat::RG32UI; |
| 265 | default: | ||
| 266 | break; | ||
| 257 | } | 267 | } |
| 258 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 268 | break; |
| 259 | UNREACHABLE(); | ||
| 260 | case Tegra::Texture::TextureFormat::R32_G32_B32: | 269 | case Tegra::Texture::TextureFormat::R32_G32_B32: |
| 261 | switch (component_type) { | 270 | switch (component_type) { |
| 262 | case Tegra::Texture::ComponentType::FLOAT: | 271 | case Tegra::Texture::ComponentType::FLOAT: |
| 263 | return PixelFormat::RGB32F; | 272 | return PixelFormat::RGB32F; |
| 273 | default: | ||
| 274 | break; | ||
| 264 | } | 275 | } |
| 265 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 276 | break; |
| 266 | UNREACHABLE(); | ||
| 267 | case Tegra::Texture::TextureFormat::R16: | 277 | case Tegra::Texture::TextureFormat::R16: |
| 268 | switch (component_type) { | 278 | switch (component_type) { |
| 269 | case Tegra::Texture::ComponentType::FLOAT: | 279 | case Tegra::Texture::ComponentType::FLOAT: |
| @@ -276,18 +286,20 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, | |||
| 276 | return PixelFormat::R16UI; | 286 | return PixelFormat::R16UI; |
| 277 | case Tegra::Texture::ComponentType::SINT: | 287 | case Tegra::Texture::ComponentType::SINT: |
| 278 | return PixelFormat::R16I; | 288 | return PixelFormat::R16I; |
| 289 | default: | ||
| 290 | break; | ||
| 279 | } | 291 | } |
| 280 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 292 | break; |
| 281 | UNREACHABLE(); | ||
| 282 | case Tegra::Texture::TextureFormat::R32: | 293 | case Tegra::Texture::TextureFormat::R32: |
| 283 | switch (component_type) { | 294 | switch (component_type) { |
| 284 | case Tegra::Texture::ComponentType::FLOAT: | 295 | case Tegra::Texture::ComponentType::FLOAT: |
| 285 | return PixelFormat::R32F; | 296 | return PixelFormat::R32F; |
| 286 | case Tegra::Texture::ComponentType::UINT: | 297 | case Tegra::Texture::ComponentType::UINT: |
| 287 | return PixelFormat::R32UI; | 298 | return PixelFormat::R32UI; |
| 299 | default: | ||
| 300 | break; | ||
| 288 | } | 301 | } |
| 289 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 302 | break; |
| 290 | UNREACHABLE(); | ||
| 291 | case Tegra::Texture::TextureFormat::ZF32: | 303 | case Tegra::Texture::TextureFormat::ZF32: |
| 292 | return PixelFormat::Z32F; | 304 | return PixelFormat::Z32F; |
| 293 | case Tegra::Texture::TextureFormat::Z16: | 305 | case Tegra::Texture::TextureFormat::Z16: |
| @@ -310,9 +322,10 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, | |||
| 310 | return PixelFormat::DXN2UNORM; | 322 | return PixelFormat::DXN2UNORM; |
| 311 | case Tegra::Texture::ComponentType::SNORM: | 323 | case Tegra::Texture::ComponentType::SNORM: |
| 312 | return PixelFormat::DXN2SNORM; | 324 | return PixelFormat::DXN2SNORM; |
| 325 | default: | ||
| 326 | break; | ||
| 313 | } | 327 | } |
| 314 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 328 | break; |
| 315 | UNREACHABLE(); | ||
| 316 | case Tegra::Texture::TextureFormat::BC7U: | 329 | case Tegra::Texture::TextureFormat::BC7U: |
| 317 | return is_srgb ? PixelFormat::BC7U_SRGB : PixelFormat::BC7U; | 330 | return is_srgb ? PixelFormat::BC7U_SRGB : PixelFormat::BC7U; |
| 318 | case Tegra::Texture::TextureFormat::BC6H_UF16: | 331 | case Tegra::Texture::TextureFormat::BC6H_UF16: |
| @@ -343,15 +356,17 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, | |||
| 343 | return PixelFormat::RG16UI; | 356 | return PixelFormat::RG16UI; |
| 344 | case Tegra::Texture::ComponentType::SINT: | 357 | case Tegra::Texture::ComponentType::SINT: |
| 345 | return PixelFormat::RG16I; | 358 | return PixelFormat::RG16I; |
| 359 | default: | ||
| 360 | break; | ||
| 346 | } | 361 | } |
| 347 | LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); | 362 | break; |
| 348 | UNREACHABLE(); | ||
| 349 | default: | 363 | default: |
| 350 | LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format), | 364 | break; |
| 351 | static_cast<u32>(component_type)); | ||
| 352 | UNREACHABLE(); | ||
| 353 | return PixelFormat::ABGR8U; | ||
| 354 | } | 365 | } |
| 366 | LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format), | ||
| 367 | static_cast<u32>(component_type)); | ||
| 368 | UNREACHABLE(); | ||
| 369 | return PixelFormat::ABGR8U; | ||
| 355 | } | 370 | } |
| 356 | 371 | ||
| 357 | ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) { | 372 | ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) { |
| @@ -513,8 +528,9 @@ bool IsFormatBCn(PixelFormat format) { | |||
| 513 | case PixelFormat::DXT45_SRGB: | 528 | case PixelFormat::DXT45_SRGB: |
| 514 | case PixelFormat::BC7U_SRGB: | 529 | case PixelFormat::BC7U_SRGB: |
| 515 | return true; | 530 | return true; |
| 531 | default: | ||
| 532 | return false; | ||
| 516 | } | 533 | } |
| 517 | return false; | ||
| 518 | } | 534 | } |
| 519 | 535 | ||
| 520 | } // namespace VideoCore::Surface | 536 | } // namespace VideoCore::Surface |
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp index b508d64e9..eafb6b73a 100644 --- a/src/video_core/textures/astc.cpp +++ b/src/video_core/textures/astc.cpp | |||
| @@ -1616,6 +1616,7 @@ namespace Tegra::Texture::ASTC { | |||
| 1616 | std::vector<uint8_t> Decompress(const uint8_t* data, uint32_t width, uint32_t height, | 1616 | std::vector<uint8_t> Decompress(const uint8_t* data, uint32_t width, uint32_t height, |
| 1617 | uint32_t depth, uint32_t block_width, uint32_t block_height) { | 1617 | uint32_t depth, uint32_t block_width, uint32_t block_height) { |
| 1618 | uint32_t blockIdx = 0; | 1618 | uint32_t blockIdx = 0; |
| 1619 | std::size_t depth_offset = 0; | ||
| 1619 | std::vector<uint8_t> outData(height * width * depth * 4); | 1620 | std::vector<uint8_t> outData(height * width * depth * 4); |
| 1620 | for (uint32_t k = 0; k < depth; k++) { | 1621 | for (uint32_t k = 0; k < depth; k++) { |
| 1621 | for (uint32_t j = 0; j < height; j += block_height) { | 1622 | for (uint32_t j = 0; j < height; j += block_height) { |
| @@ -1630,7 +1631,7 @@ std::vector<uint8_t> Decompress(const uint8_t* data, uint32_t width, uint32_t he | |||
| 1630 | uint32_t decompWidth = std::min(block_width, width - i); | 1631 | uint32_t decompWidth = std::min(block_width, width - i); |
| 1631 | uint32_t decompHeight = std::min(block_height, height - j); | 1632 | uint32_t decompHeight = std::min(block_height, height - j); |
| 1632 | 1633 | ||
| 1633 | uint8_t* outRow = outData.data() + (j * width + i) * 4; | 1634 | uint8_t* outRow = depth_offset + outData.data() + (j * width + i) * 4; |
| 1634 | for (uint32_t jj = 0; jj < decompHeight; jj++) { | 1635 | for (uint32_t jj = 0; jj < decompHeight; jj++) { |
| 1635 | memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4); | 1636 | memcpy(outRow + jj * width * 4, uncompData + jj * block_width, decompWidth * 4); |
| 1636 | } | 1637 | } |
| @@ -1638,6 +1639,7 @@ std::vector<uint8_t> Decompress(const uint8_t* data, uint32_t width, uint32_t he | |||
| 1638 | blockIdx++; | 1639 | blockIdx++; |
| 1639 | } | 1640 | } |
| 1640 | } | 1641 | } |
| 1642 | depth_offset += height * width * 4; | ||
| 1641 | } | 1643 | } |
| 1642 | 1644 | ||
| 1643 | return outData; | 1645 | return outData; |
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 | |||
| 288 | } | 288 | } |
| 289 | } | 289 | } |
| 290 | 290 | ||
| 291 | void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y, | ||
| 292 | const u32 block_height, const std::size_t copy_size, const u8* source_data, | ||
| 293 | u8* swizzle_data) { | ||
| 294 | const u32 image_width_in_gobs{(width + gob_size_x - 1) / gob_size_x}; | ||
| 295 | std::size_t count = 0; | ||
| 296 | for (std::size_t y = dst_y; y < height && count < copy_size; ++y) { | ||
| 297 | const std::size_t gob_address_y = | ||
| 298 | (y / (gob_size_y * block_height)) * gob_size * block_height * image_width_in_gobs + | ||
| 299 | ((y % (gob_size_y * block_height)) / gob_size_y) * gob_size; | ||
| 300 | const auto& table = legacy_swizzle_table[y % gob_size_y]; | ||
| 301 | for (std::size_t x = dst_x; x < width && count < copy_size; ++x) { | ||
| 302 | const std::size_t gob_address = | ||
| 303 | gob_address_y + (x / gob_size_x) * gob_size * block_height; | ||
| 304 | const std::size_t swizzled_offset = gob_address + table[x % gob_size_x]; | ||
| 305 | const u8* source_line = source_data + count; | ||
| 306 | u8* dest_addr = swizzle_data + swizzled_offset; | ||
| 307 | count++; | ||
| 308 | |||
| 309 | std::memcpy(dest_addr, source_line, 1); | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 291 | std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, | 314 | std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width, |
| 292 | u32 height) { | 315 | u32 height) { |
| 293 | std::vector<u8> rgba_data; | 316 | 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 | |||
| 51 | u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height, | 51 | u32 bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, u32 block_height, |
| 52 | u32 offset_x, u32 offset_y); | 52 | u32 offset_x, u32 offset_y); |
| 53 | 53 | ||
| 54 | void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y, | ||
| 55 | const u32 block_height, const std::size_t copy_size, const u8* source_data, | ||
| 56 | u8* swizzle_data); | ||
| 57 | |||
| 54 | } // namespace Tegra::Texture | 58 | } // 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 | |||
| 7 | Info.plist | 7 | Info.plist |
| 8 | about_dialog.cpp | 8 | about_dialog.cpp |
| 9 | about_dialog.h | 9 | about_dialog.h |
| 10 | applets/error.cpp | ||
| 11 | applets/error.h | ||
| 10 | applets/profile_select.cpp | 12 | applets/profile_select.cpp |
| 11 | applets/profile_select.h | 13 | applets/profile_select.h |
| 12 | applets/software_keyboard.cpp | 14 | 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 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <QDateTime> | ||
| 6 | #include "core/hle/lock.h" | ||
| 7 | #include "yuzu/applets/error.h" | ||
| 8 | #include "yuzu/main.h" | ||
| 9 | |||
| 10 | QtErrorDisplay::QtErrorDisplay(GMainWindow& parent) { | ||
| 11 | connect(this, &QtErrorDisplay::MainWindowDisplayError, &parent, | ||
| 12 | &GMainWindow::ErrorDisplayDisplayError, Qt::QueuedConnection); | ||
| 13 | connect(&parent, &GMainWindow::ErrorDisplayFinished, this, | ||
| 14 | &QtErrorDisplay::MainWindowFinishedError, Qt::DirectConnection); | ||
| 15 | } | ||
| 16 | |||
| 17 | QtErrorDisplay::~QtErrorDisplay() = default; | ||
| 18 | |||
| 19 | void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const { | ||
| 20 | this->callback = std::move(finished); | ||
| 21 | emit MainWindowDisplayError( | ||
| 22 | tr("An error has occured.\nPlease try again or contact the developer of the " | ||
| 23 | "software.\n\nError Code: %1-%2 (0x%3)") | ||
| 24 | .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) | ||
| 25 | .arg(error.description, 4, 10, QChar::fromLatin1('0')) | ||
| 26 | .arg(error.raw, 8, 16, QChar::fromLatin1('0'))); | ||
| 27 | } | ||
| 28 | |||
| 29 | void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, | ||
| 30 | std::function<void()> finished) const { | ||
| 31 | this->callback = std::move(finished); | ||
| 32 | emit MainWindowDisplayError( | ||
| 33 | tr("An error occured on %1 at %2.\nPlease try again or contact the " | ||
| 34 | "developer of the software.\n\nError Code: %3-%4 (0x%5)") | ||
| 35 | .arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("dddd, MMMM d, yyyy")) | ||
| 36 | .arg(QDateTime::fromSecsSinceEpoch(time.count()).toString("h:mm:ss A")) | ||
| 37 | .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) | ||
| 38 | .arg(error.description, 4, 10, QChar::fromLatin1('0')) | ||
| 39 | .arg(error.raw, 8, 16, QChar::fromLatin1('0'))); | ||
| 40 | } | ||
| 41 | |||
| 42 | void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_text, | ||
| 43 | std::string fullscreen_text, | ||
| 44 | std::function<void()> finished) const { | ||
| 45 | this->callback = std::move(finished); | ||
| 46 | emit MainWindowDisplayError( | ||
| 47 | tr("An error has occured.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5") | ||
| 48 | .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0')) | ||
| 49 | .arg(error.description, 4, 10, QChar::fromLatin1('0')) | ||
| 50 | .arg(error.raw, 8, 16, QChar::fromLatin1('0')) | ||
| 51 | .arg(QString::fromStdString(dialog_text)) | ||
| 52 | .arg(QString::fromStdString(fullscreen_text))); | ||
| 53 | } | ||
| 54 | |||
| 55 | void QtErrorDisplay::MainWindowFinishedError() { | ||
| 56 | // Acquire the HLE mutex | ||
| 57 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 58 | callback(); | ||
| 59 | } | ||
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 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <QObject> | ||
| 8 | |||
| 9 | #include "core/frontend/applets/error.h" | ||
| 10 | |||
| 11 | class GMainWindow; | ||
| 12 | |||
| 13 | class QtErrorDisplay final : public QObject, public Core::Frontend::ErrorApplet { | ||
| 14 | Q_OBJECT | ||
| 15 | |||
| 16 | public: | ||
| 17 | explicit QtErrorDisplay(GMainWindow& parent); | ||
| 18 | ~QtErrorDisplay() override; | ||
| 19 | |||
| 20 | void ShowError(ResultCode error, std::function<void()> finished) const override; | ||
| 21 | void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time, | ||
| 22 | std::function<void()> finished) const override; | ||
| 23 | void ShowCustomErrorText(ResultCode error, std::string dialog_text, std::string fullscreen_text, | ||
| 24 | std::function<void()> finished) const override; | ||
| 25 | |||
| 26 | signals: | ||
| 27 | void MainWindowDisplayError(QString error) const; | ||
| 28 | |||
| 29 | private: | ||
| 30 | void MainWindowFinishedError(); | ||
| 31 | |||
| 32 | mutable std::function<void()> callback; | ||
| 33 | }; | ||
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 7eed9fcf3..5c98636c5 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -377,7 +377,11 @@ void GRenderWindow::InitRenderTarget() { | |||
| 377 | // WA_DontShowOnScreen, WA_DeleteOnClose | 377 | // WA_DontShowOnScreen, WA_DeleteOnClose |
| 378 | QSurfaceFormat fmt; | 378 | QSurfaceFormat fmt; |
| 379 | fmt.setVersion(4, 3); | 379 | fmt.setVersion(4, 3); |
| 380 | fmt.setProfile(QSurfaceFormat::CoreProfile); | 380 | if (Settings::values.use_compatibility_profile) { |
| 381 | fmt.setProfile(QSurfaceFormat::CompatibilityProfile); | ||
| 382 | } else { | ||
| 383 | fmt.setProfile(QSurfaceFormat::CoreProfile); | ||
| 384 | } | ||
| 381 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | 385 | // TODO: expose a setting for buffer value (ie default/single/double/triple) |
| 382 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | 386 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); |
| 383 | shared_context = std::make_unique<QOpenGLContext>(); | 387 | shared_context = std::make_unique<QOpenGLContext>(); |
diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp index c8b0a5ec0..5477f050c 100644 --- a/src/yuzu/compatdb.cpp +++ b/src/yuzu/compatdb.cpp | |||
| @@ -58,7 +58,7 @@ void CompatDB::Submit() { | |||
| 58 | 58 | ||
| 59 | button(NextButton)->setEnabled(false); | 59 | button(NextButton)->setEnabled(false); |
| 60 | button(NextButton)->setText(tr("Submitting")); | 60 | button(NextButton)->setText(tr("Submitting")); |
| 61 | button(QWizard::CancelButton)->setVisible(false); | 61 | button(CancelButton)->setVisible(false); |
| 62 | 62 | ||
| 63 | testcase_watcher.setFuture(QtConcurrent::run( | 63 | testcase_watcher.setFuture(QtConcurrent::run( |
| 64 | [] { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); })); | 64 | [] { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); })); |
| @@ -74,12 +74,12 @@ void CompatDB::OnTestcaseSubmitted() { | |||
| 74 | tr("An error occured while sending the Testcase")); | 74 | tr("An error occured while sending the Testcase")); |
| 75 | button(NextButton)->setEnabled(true); | 75 | button(NextButton)->setEnabled(true); |
| 76 | button(NextButton)->setText(tr("Next")); | 76 | button(NextButton)->setText(tr("Next")); |
| 77 | button(QWizard::CancelButton)->setVisible(true); | 77 | button(CancelButton)->setVisible(true); |
| 78 | } else { | 78 | } else { |
| 79 | next(); | 79 | next(); |
| 80 | // older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a | 80 | // older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a |
| 81 | // workaround | 81 | // workaround |
| 82 | button(QWizard::CancelButton)->setVisible(false); | 82 | button(CancelButton)->setVisible(false); |
| 83 | } | 83 | } |
| 84 | } | 84 | } |
| 85 | 85 | ||
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 8725a78dc..6c6f047d8 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -389,6 +389,8 @@ void Config::ReadValues() { | |||
| 389 | Settings::values.resolution_factor = ReadSetting("resolution_factor", 1.0).toFloat(); | 389 | Settings::values.resolution_factor = ReadSetting("resolution_factor", 1.0).toFloat(); |
| 390 | Settings::values.use_frame_limit = ReadSetting("use_frame_limit", true).toBool(); | 390 | Settings::values.use_frame_limit = ReadSetting("use_frame_limit", true).toBool(); |
| 391 | Settings::values.frame_limit = ReadSetting("frame_limit", 100).toInt(); | 391 | Settings::values.frame_limit = ReadSetting("frame_limit", 100).toInt(); |
| 392 | Settings::values.use_compatibility_profile = | ||
| 393 | ReadSetting("use_compatibility_profile", true).toBool(); | ||
| 392 | Settings::values.use_disk_shader_cache = ReadSetting("use_disk_shader_cache", true).toBool(); | 394 | Settings::values.use_disk_shader_cache = ReadSetting("use_disk_shader_cache", true).toBool(); |
| 393 | Settings::values.use_accurate_gpu_emulation = | 395 | Settings::values.use_accurate_gpu_emulation = |
| 394 | ReadSetting("use_accurate_gpu_emulation", false).toBool(); | 396 | ReadSetting("use_accurate_gpu_emulation", false).toBool(); |
| @@ -661,6 +663,7 @@ void Config::SaveValues() { | |||
| 661 | WriteSetting("resolution_factor", (double)Settings::values.resolution_factor, 1.0); | 663 | WriteSetting("resolution_factor", (double)Settings::values.resolution_factor, 1.0); |
| 662 | WriteSetting("use_frame_limit", Settings::values.use_frame_limit, true); | 664 | WriteSetting("use_frame_limit", Settings::values.use_frame_limit, true); |
| 663 | WriteSetting("frame_limit", Settings::values.frame_limit, 100); | 665 | WriteSetting("frame_limit", Settings::values.frame_limit, 100); |
| 666 | WriteSetting("use_compatibility_profile", Settings::values.use_compatibility_profile, true); | ||
| 664 | WriteSetting("use_disk_shader_cache", Settings::values.use_disk_shader_cache, true); | 667 | WriteSetting("use_disk_shader_cache", Settings::values.use_disk_shader_cache, true); |
| 665 | WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false); | 668 | WriteSetting("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation, false); |
| 666 | WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation, | 669 | WriteSetting("use_asynchronous_gpu_emulation", Settings::values.use_asynchronous_gpu_emulation, |
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index a5218b051..32c05b797 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp | |||
| @@ -17,8 +17,12 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) | |||
| 17 | ui->hotkeysTab->Populate(registry); | 17 | ui->hotkeysTab->Populate(registry); |
| 18 | this->setConfiguration(); | 18 | this->setConfiguration(); |
| 19 | this->PopulateSelectionList(); | 19 | this->PopulateSelectionList(); |
| 20 | |||
| 21 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||
| 22 | |||
| 20 | connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, | 23 | connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, |
| 21 | &ConfigureDialog::UpdateVisibleTabs); | 24 | &ConfigureDialog::UpdateVisibleTabs); |
| 25 | |||
| 22 | adjustSize(); | 26 | adjustSize(); |
| 23 | ui->selectorList->setCurrentRow(0); | 27 | ui->selectorList->setCurrentRow(0); |
| 24 | 28 | ||
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 0a9883d37..c299c0b5b 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -73,6 +73,7 @@ void ConfigureGraphics::setConfiguration() { | |||
| 73 | static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); | 73 | static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); |
| 74 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); | 74 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); |
| 75 | ui->frame_limit->setValue(Settings::values.frame_limit); | 75 | ui->frame_limit->setValue(Settings::values.frame_limit); |
| 76 | ui->use_compatibility_profile->setChecked(Settings::values.use_compatibility_profile); | ||
| 76 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); | 77 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); |
| 77 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); | 78 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); |
| 78 | ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | 79 | ui->use_asynchronous_gpu_emulation->setEnabled(!Core::System::GetInstance().IsPoweredOn()); |
| @@ -88,6 +89,7 @@ void ConfigureGraphics::applyConfiguration() { | |||
| 88 | ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); | 89 | ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); |
| 89 | Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); | 90 | Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); |
| 90 | Settings::values.frame_limit = ui->frame_limit->value(); | 91 | Settings::values.frame_limit = ui->frame_limit->value(); |
| 92 | Settings::values.use_compatibility_profile = ui->use_compatibility_profile->isChecked(); | ||
| 91 | Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); | 93 | Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); |
| 92 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); | 94 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); |
| 93 | Settings::values.use_asynchronous_gpu_emulation = | 95 | Settings::values.use_asynchronous_gpu_emulation = |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 15ab18ecd..0f6f6c003 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -50,6 +50,13 @@ | |||
| 50 | </layout> | 50 | </layout> |
| 51 | </item> | 51 | </item> |
| 52 | <item> | 52 | <item> |
| 53 | <widget class="QCheckBox" name="use_compatibility_profile"> | ||
| 54 | <property name="text"> | ||
| 55 | <string>Use OpenGL compatibility profile</string> | ||
| 56 | </property> | ||
| 57 | </widget> | ||
| 58 | </item> | ||
| 59 | <item> | ||
| 53 | <widget class="QCheckBox" name="use_disk_shader_cache"> | 60 | <widget class="QCheckBox" name="use_disk_shader_cache"> |
| 54 | <property name="text"> | 61 | <property name="text"> |
| 55 | <string>Use disk shader cache</string> | 62 | <string>Use disk shader cache</string> |
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 593bb681f..85b095688 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -227,8 +227,7 @@ QString WaitTreeThread::GetText() const { | |||
| 227 | case Kernel::ThreadStatus::WaitIPC: | 227 | case Kernel::ThreadStatus::WaitIPC: |
| 228 | status = tr("waiting for IPC reply"); | 228 | status = tr("waiting for IPC reply"); |
| 229 | break; | 229 | break; |
| 230 | case Kernel::ThreadStatus::WaitSynchAll: | 230 | case Kernel::ThreadStatus::WaitSynch: |
| 231 | case Kernel::ThreadStatus::WaitSynchAny: | ||
| 232 | status = tr("waiting for objects"); | 231 | status = tr("waiting for objects"); |
| 233 | break; | 232 | break; |
| 234 | case Kernel::ThreadStatus::WaitMutex: | 233 | case Kernel::ThreadStatus::WaitMutex: |
| @@ -269,8 +268,7 @@ QColor WaitTreeThread::GetColor() const { | |||
| 269 | return QColor(Qt::GlobalColor::darkRed); | 268 | return QColor(Qt::GlobalColor::darkRed); |
| 270 | case Kernel::ThreadStatus::WaitSleep: | 269 | case Kernel::ThreadStatus::WaitSleep: |
| 271 | return QColor(Qt::GlobalColor::darkYellow); | 270 | return QColor(Qt::GlobalColor::darkYellow); |
| 272 | case Kernel::ThreadStatus::WaitSynchAll: | 271 | case Kernel::ThreadStatus::WaitSynch: |
| 273 | case Kernel::ThreadStatus::WaitSynchAny: | ||
| 274 | case Kernel::ThreadStatus::WaitMutex: | 272 | case Kernel::ThreadStatus::WaitMutex: |
| 275 | case Kernel::ThreadStatus::WaitCondVar: | 273 | case Kernel::ThreadStatus::WaitCondVar: |
| 276 | case Kernel::ThreadStatus::WaitArb: | 274 | case Kernel::ThreadStatus::WaitArb: |
| @@ -325,10 +323,9 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { | |||
| 325 | list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex"))); | 323 | list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex"))); |
| 326 | } | 324 | } |
| 327 | 325 | ||
| 328 | if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynchAny || | 326 | if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) { |
| 329 | thread.GetStatus() == Kernel::ThreadStatus::WaitSynchAll) { | ||
| 330 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjects(), | 327 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjects(), |
| 331 | thread.IsSleepingOnWaitAll())); | 328 | thread.IsSleepingOnWait())); |
| 332 | } | 329 | } |
| 333 | 330 | ||
| 334 | list.push_back(std::make_unique<WaitTreeCallstack>(thread)); | 331 | list.push_back(std::make_unique<WaitTreeCallstack>(thread)); |
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h index 4f526dc7e..248fadaf3 100644 --- a/src/yuzu/hotkeys.h +++ b/src/yuzu/hotkeys.h | |||
| @@ -67,8 +67,6 @@ public: | |||
| 67 | 67 | ||
| 68 | private: | 68 | private: |
| 69 | struct Hotkey { | 69 | struct Hotkey { |
| 70 | Hotkey() : shortcut(nullptr), context(Qt::WindowShortcut) {} | ||
| 71 | |||
| 72 | QKeySequence keyseq; | 70 | QKeySequence keyseq; |
| 73 | QShortcut* shortcut = nullptr; | 71 | QShortcut* shortcut = nullptr; |
| 74 | Qt::ShortcutContext context = Qt::WindowShortcut; | 72 | Qt::ShortcutContext context = Qt::WindowShortcut; |
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 @@ | |||
| 8 | #include <thread> | 8 | #include <thread> |
| 9 | 9 | ||
| 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. | 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. |
| 11 | #include "applets/error.h" | ||
| 11 | #include "applets/profile_select.h" | 12 | #include "applets/profile_select.h" |
| 12 | #include "applets/software_keyboard.h" | 13 | #include "applets/software_keyboard.h" |
| 13 | #include "applets/web_browser.h" | 14 | #include "applets/web_browser.h" |
| @@ -15,6 +16,7 @@ | |||
| 15 | #include "configuration/configure_per_general.h" | 16 | #include "configuration/configure_per_general.h" |
| 16 | #include "core/file_sys/vfs.h" | 17 | #include "core/file_sys/vfs.h" |
| 17 | #include "core/file_sys/vfs_real.h" | 18 | #include "core/file_sys/vfs_real.h" |
| 19 | #include "core/frontend/applets/general_frontend.h" | ||
| 18 | #include "core/frontend/scope_acquire_window_context.h" | 20 | #include "core/frontend/scope_acquire_window_context.h" |
| 19 | #include "core/hle/service/acc/profile_manager.h" | 21 | #include "core/hle/service/acc/profile_manager.h" |
| 20 | #include "core/hle/service/am/applets/applets.h" | 22 | #include "core/hle/service/am/applets/applets.h" |
| @@ -795,9 +797,13 @@ bool GMainWindow::LoadROM(const QString& filename) { | |||
| 795 | 797 | ||
| 796 | system.SetGPUDebugContext(debug_context); | 798 | system.SetGPUDebugContext(debug_context); |
| 797 | 799 | ||
| 798 | system.SetProfileSelector(std::make_unique<QtProfileSelector>(*this)); | 800 | system.SetAppletFrontendSet({ |
| 799 | system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this)); | 801 | std::make_unique<QtErrorDisplay>(*this), |
| 800 | system.SetWebBrowser(std::make_unique<QtWebBrowser>(*this)); | 802 | nullptr, |
| 803 | std::make_unique<QtProfileSelector>(*this), | ||
| 804 | std::make_unique<QtSoftwareKeyboard>(*this), | ||
| 805 | std::make_unique<QtWebBrowser>(*this), | ||
| 806 | }); | ||
| 801 | 807 | ||
| 802 | const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; | 808 | const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; |
| 803 | 809 | ||
| @@ -1583,6 +1589,11 @@ void GMainWindow::OnLoadComplete() { | |||
| 1583 | loading_screen->OnLoadComplete(); | 1589 | loading_screen->OnLoadComplete(); |
| 1584 | } | 1590 | } |
| 1585 | 1591 | ||
| 1592 | void GMainWindow::ErrorDisplayDisplayError(QString body) { | ||
| 1593 | QMessageBox::critical(this, tr("Error Display"), body); | ||
| 1594 | emit ErrorDisplayFinished(); | ||
| 1595 | } | ||
| 1596 | |||
| 1586 | void GMainWindow::OnMenuReportCompatibility() { | 1597 | void GMainWindow::OnMenuReportCompatibility() { |
| 1587 | if (!Settings::values.yuzu_token.empty() && !Settings::values.yuzu_username.empty()) { | 1598 | if (!Settings::values.yuzu_token.empty() && !Settings::values.yuzu_username.empty()) { |
| 1588 | CompatDB compatdb{this}; | 1599 | 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: | |||
| 102 | // Signal that tells widgets to update icons to use the current theme | 102 | // Signal that tells widgets to update icons to use the current theme |
| 103 | void UpdateThemedIcons(); | 103 | void UpdateThemedIcons(); |
| 104 | 104 | ||
| 105 | void ErrorDisplayFinished(); | ||
| 106 | |||
| 105 | void ProfileSelectorFinishedSelection(std::optional<Service::Account::UUID> uuid); | 107 | void ProfileSelectorFinishedSelection(std::optional<Service::Account::UUID> uuid); |
| 106 | void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); | 108 | void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); |
| 107 | void SoftwareKeyboardFinishedCheckDialog(); | 109 | void SoftwareKeyboardFinishedCheckDialog(); |
| @@ -111,6 +113,7 @@ signals: | |||
| 111 | 113 | ||
| 112 | public slots: | 114 | public slots: |
| 113 | void OnLoadComplete(); | 115 | void OnLoadComplete(); |
| 116 | void ErrorDisplayDisplayError(QString body); | ||
| 114 | void ProfileSelectorSelectProfile(); | 117 | void ProfileSelectorSelectProfile(); |
| 115 | void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); | 118 | void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); |
| 116 | void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); | 119 | void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index f24cc77fe..d0ae058fd 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -349,6 +349,8 @@ void Config::ReadValues() { | |||
| 349 | Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); | 349 | Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); |
| 350 | Settings::values.frame_limit = | 350 | Settings::values.frame_limit = |
| 351 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); | 351 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); |
| 352 | Settings::values.use_compatibility_profile = | ||
| 353 | sdl2_config->GetBoolean("Renderer", "use_compatibility_profile", true); | ||
| 352 | Settings::values.use_disk_shader_cache = | 354 | Settings::values.use_disk_shader_cache = |
| 353 | sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); | 355 | sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false); |
| 354 | Settings::values.use_accurate_gpu_emulation = | 356 | Settings::values.use_accurate_gpu_emulation = |
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 @@ | |||
| 32 | #include "yuzu_cmd/config.h" | 32 | #include "yuzu_cmd/config.h" |
| 33 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 33 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 34 | 34 | ||
| 35 | #include <getopt.h> | ||
| 36 | #include "core/file_sys/registered_cache.h" | 35 | #include "core/file_sys/registered_cache.h" |
| 37 | #ifndef _MSC_VER | ||
| 38 | #include <unistd.h> | ||
| 39 | #endif | ||
| 40 | 36 | ||
| 41 | #ifdef _WIN32 | 37 | #ifdef _WIN32 |
| 42 | // windows.h needs to be included before shellapi.h | 38 | // windows.h needs to be included before shellapi.h |
| @@ -45,6 +41,12 @@ | |||
| 45 | #include <shellapi.h> | 41 | #include <shellapi.h> |
| 46 | #endif | 42 | #endif |
| 47 | 43 | ||
| 44 | #undef _UNICODE | ||
| 45 | #include <getopt.h> | ||
| 46 | #ifndef _MSC_VER | ||
| 47 | #include <unistd.h> | ||
| 48 | #endif | ||
| 49 | |||
| 48 | #ifdef _WIN32 | 50 | #ifdef _WIN32 |
| 49 | extern "C" { | 51 | extern "C" { |
| 50 | // tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable | 52 | // tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable |