diff options
82 files changed, 1380 insertions, 797 deletions
diff --git a/.travis/clang-format/script.sh b/.travis/clang-format/script.sh index 0f6e8e6e4..3eff6322f 100755 --- a/.travis/clang-format/script.sh +++ b/.travis/clang-format/script.sh @@ -1,6 +1,6 @@  #!/bin/bash -ex -if grep -nr '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .travis* dist/*.desktop \ +if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .travis* dist/*.desktop \                   dist/*.svg dist/*.xml; then      echo Trailing whitespace found, aborting      exit 1 diff --git a/src/common/web_result.h b/src/common/web_result.h index 969926674..8bfa2141d 100644 --- a/src/common/web_result.h +++ b/src/common/web_result.h @@ -5,6 +5,7 @@  #pragma once  #include <string> +#include "common/common_types.h"  namespace Common {  struct WebResult { diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4fddaafd1..78986deb5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -400,8 +400,8 @@ create_target_directory_groups(core)  target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)  target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives)  if (ENABLE_WEB_SERVICE) -    add_definitions(-DENABLE_WEB_SERVICE) -    target_link_libraries(core PUBLIC json-headers web_service) +    target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) +    target_link_libraries(core PRIVATE web_service)  endif()  if (ARCHITECTURE_x86_64) diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 0762321a9..4d2491870 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -144,7 +144,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {      // Multi-process state      config.processor_id = core_index; -    config.global_monitor = &exclusive_monitor->monitor; +    config.global_monitor = &exclusive_monitor.monitor;      // System registers      config.tpidrro_el0 = &cb->tpidrro_el0; @@ -171,10 +171,9 @@ void ARM_Dynarmic::Step() {      cb->InterpreterFallback(jit->GetPC(), 1);  } -ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, -                           std::size_t core_index) +ARM_Dynarmic::ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index)      : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index}, -      exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} { +      exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {      ThreadContext ctx{};      inner_unicorn.SaveContext(ctx);      PageTableChanged(); diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index 4ee92ee27..512bf8ce9 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -23,7 +23,7 @@ class DynarmicExclusiveMonitor;  class ARM_Dynarmic final : public ARM_Interface {  public: -    ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, std::size_t core_index); +    ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index);      ~ARM_Dynarmic();      void MapBackingMemory(VAddr address, std::size_t size, u8* memory, @@ -62,7 +62,7 @@ private:      ARM_Unicorn inner_unicorn;      std::size_t core_index; -    std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor; +    DynarmicExclusiveMonitor& exclusive_monitor;      Memory::PageTable* current_page_table = nullptr;  }; diff --git a/src/core/core.cpp b/src/core/core.cpp index e2fb9e038..3c57a62ec 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -71,9 +71,9 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,  }  /// Runs a CPU core while the system is powered on -void RunCpuCore(std::shared_ptr<Cpu> cpu_state) { +void RunCpuCore(Cpu& cpu_state) {      while (Core::System::GetInstance().IsPoweredOn()) { -        cpu_state->RunLoop(true); +        cpu_state.RunLoop(true);      }  }  } // Anonymous namespace @@ -95,7 +95,7 @@ struct System::Impl {          status = ResultStatus::Success;          // Update thread_to_cpu in case Core 0 is run from a different host thread -        thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; +        thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();          if (GDBStub::IsServerEnabled()) {              GDBStub::HandlePacket(); @@ -139,16 +139,16 @@ struct System::Impl {          auto main_process = Kernel::Process::Create(kernel, "main");          kernel.MakeCurrentProcess(main_process.get()); -        cpu_barrier = std::make_shared<CpuBarrier>(); +        cpu_barrier = std::make_unique<CpuBarrier>();          cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());          for (std::size_t index = 0; index < cpu_cores.size(); ++index) { -            cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); +            cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index);          }          telemetry_session = std::make_unique<Core::TelemetrySession>();          service_manager = std::make_shared<Service::SM::ServiceManager>(); -        Service::Init(service_manager, virtual_filesystem); +        Service::Init(service_manager, *virtual_filesystem);          GDBStub::Init();          renderer = VideoCore::CreateRenderer(emu_window); @@ -160,12 +160,12 @@ struct System::Impl {          // Create threads for CPU cores 1-3, and build thread_to_cpu map          // CPU core 0 is run on the main thread -        thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; +        thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();          if (Settings::values.use_multi_core) {              for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {                  cpu_core_threads[index] = -                    std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]); -                thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1]; +                    std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1])); +                thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get();              }          } @@ -245,6 +245,7 @@ struct System::Impl {          for (auto& cpu_core : cpu_cores) {              cpu_core.reset();          } +        cpu_exclusive_monitor.reset();          cpu_barrier.reset();          // Shutdown kernel and core timing @@ -282,9 +283,9 @@ struct System::Impl {      std::unique_ptr<VideoCore::RendererBase> renderer;      std::unique_ptr<Tegra::GPU> gpu_core;      std::shared_ptr<Tegra::DebugContext> debug_context; -    std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor; -    std::shared_ptr<CpuBarrier> cpu_barrier; -    std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; +    std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor; +    std::unique_ptr<CpuBarrier> cpu_barrier; +    std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;      std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;      std::size_t active_core{}; ///< Active core, only used in single thread mode @@ -298,7 +299,7 @@ struct System::Impl {      std::string status_details = "";      /// Map of guest threads to CPU cores -    std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu; +    std::map<std::thread::id, Cpu*> thread_to_cpu;      Core::PerfStats perf_stats;      Core::FrameLimiter frame_limiter; @@ -354,12 +355,15 @@ std::size_t System::CurrentCoreIndex() {  }  Kernel::Scheduler& System::CurrentScheduler() { -    return *CurrentCpuCore().Scheduler(); +    return CurrentCpuCore().Scheduler();  } -const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_index) { -    ASSERT(core_index < NUM_CPU_CORES); -    return impl->cpu_cores[core_index]->Scheduler(); +Kernel::Scheduler& System::Scheduler(std::size_t core_index) { +    return CpuCore(core_index).Scheduler(); +} + +const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const { +    return CpuCore(core_index).Scheduler();  }  Kernel::Process* System::CurrentProcess() { @@ -380,6 +384,11 @@ Cpu& System::CpuCore(std::size_t core_index) {      return *impl->cpu_cores[core_index];  } +const Cpu& System::CpuCore(std::size_t core_index) const { +    ASSERT(core_index < NUM_CPU_CORES); +    return *impl->cpu_cores[core_index]; +} +  ExclusiveMonitor& System::Monitor() {      return *impl->cpu_exclusive_monitor;  } diff --git a/src/core/core.h b/src/core/core.h index ea4d53914..173be45f8 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -156,6 +156,9 @@ public:      /// Gets a CPU interface to the CPU core with the specified index      Cpu& CpuCore(std::size_t core_index); +    /// Gets a CPU interface to the CPU core with the specified index +    const Cpu& CpuCore(std::size_t core_index) const; +      /// Gets the exclusive monitor      ExclusiveMonitor& Monitor(); @@ -172,7 +175,10 @@ public:      const VideoCore::RendererBase& Renderer() const;      /// Gets the scheduler for the CPU core with the specified index -    const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index); +    Kernel::Scheduler& Scheduler(std::size_t core_index); + +    /// Gets the scheduler for the CPU core with the specified index +    const Kernel::Scheduler& Scheduler(std::size_t core_index) const;      /// Provides a pointer to the current process      Kernel::Process* CurrentProcess(); diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index 265f8ed9c..fffda8a99 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp @@ -49,10 +49,8 @@ bool CpuBarrier::Rendezvous() {      return false;  } -Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, -         std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index) -    : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} { - +Cpu::Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index) +    : cpu_barrier{cpu_barrier}, core_index{core_index} {      if (Settings::values.use_cpu_jit) {  #ifdef ARCHITECTURE_x86_64          arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index); @@ -64,15 +62,15 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,          arm_interface = std::make_unique<ARM_Unicorn>();      } -    scheduler = std::make_shared<Kernel::Scheduler>(*arm_interface); +    scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface);  }  Cpu::~Cpu() = default; -std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) { +std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {      if (Settings::values.use_cpu_jit) {  #ifdef ARCHITECTURE_x86_64 -        return std::make_shared<DynarmicExclusiveMonitor>(num_cores); +        return std::make_unique<DynarmicExclusiveMonitor>(num_cores);  #else          return nullptr; // TODO(merry): Passthrough exclusive monitor  #endif @@ -83,7 +81,7 @@ std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_core  void Cpu::RunLoop(bool tight_loop) {      // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step -    if (!cpu_barrier->Rendezvous()) { +    if (!cpu_barrier.Rendezvous()) {          // If rendezvous failed, session has been killed          return;      } diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h index ee7e04abc..1d2bdc6cd 100644 --- a/src/core/core_cpu.h +++ b/src/core/core_cpu.h @@ -41,8 +41,7 @@ private:  class Cpu {  public: -    Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, -        std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index); +    Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index);      ~Cpu();      void RunLoop(bool tight_loop = true); @@ -59,8 +58,12 @@ public:          return *arm_interface;      } -    const std::shared_ptr<Kernel::Scheduler>& Scheduler() const { -        return scheduler; +    Kernel::Scheduler& Scheduler() { +        return *scheduler; +    } + +    const Kernel::Scheduler& Scheduler() const { +        return *scheduler;      }      bool IsMainCore() const { @@ -71,14 +74,14 @@ public:          return core_index;      } -    static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores); +    static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores);  private:      void Reschedule();      std::unique_ptr<ARM_Interface> arm_interface; -    std::shared_ptr<CpuBarrier> cpu_barrier; -    std::shared_ptr<Kernel::Scheduler> scheduler; +    CpuBarrier& cpu_barrier; +    std::unique_ptr<Kernel::Scheduler> scheduler;      std::atomic<bool> reschedule_pending = false;      std::size_t core_index; diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index a59a7e1f5..fd0786068 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -98,7 +98,7 @@ std::array<u8, 144> DecryptKeyblob(const std::array<u8, 176>& encrypted_keyblob,      return keyblob;  } -void KeyManager::DeriveGeneralPurposeKeys(u8 crypto_revision) { +void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {      const auto kek_generation_source =          GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));      const auto key_generation_source = @@ -147,31 +147,38 @@ boost::optional<Key128> DeriveSDSeed() {                                     "rb+");      if (!save_43.IsOpen())          return boost::none; +      const FileUtil::IOFile sd_private(          FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");      if (!sd_private.IsOpen())          return boost::none; -    sd_private.Seek(0, SEEK_SET);      std::array<u8, 0x10> private_seed{}; -    if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != 0x10) +    if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {          return boost::none; +    }      std::array<u8, 0x10> buffer{};      std::size_t offset = 0;      for (; offset + 0x10 < save_43.GetSize(); ++offset) { -        save_43.Seek(offset, SEEK_SET); +        if (!save_43.Seek(offset, SEEK_SET)) { +            return boost::none; +        } +          save_43.ReadBytes(buffer.data(), buffer.size()); -        if (buffer == private_seed) +        if (buffer == private_seed) {              break; +        }      } -    if (offset + 0x10 >= save_43.GetSize()) +    if (!save_43.Seek(offset + 0x10, SEEK_SET)) {          return boost::none; +    }      Key128 seed{}; -    save_43.Seek(offset + 0x10, SEEK_SET); -    save_43.ReadBytes(seed.data(), seed.size()); +    if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) { +        return boost::none; +    }      return seed;  } @@ -234,7 +241,9 @@ std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {          return {};      std::vector<u8> buffer(ticket_save.GetSize()); -    ticket_save.ReadBytes(buffer.data(), buffer.size()); +    if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) { +        return {}; +    }      std::vector<TicketRaw> out;      u32 magic{}; @@ -261,6 +270,9 @@ static std::array<u8, size> operator^(const std::array<u8, size>& lhs,  template <size_t target_size, size_t in_size>  static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) { +    // Avoids truncation overflow within the loop below. +    static_assert(target_size <= 0xFF); +      std::array<u8, in_size + 4> seed_exp{};      std::memcpy(seed_exp.data(), seed.data(), in_size); @@ -268,7 +280,7 @@ static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {      size_t i = 0;      while (out.size() < target_size) {          out.resize(out.size() + 0x20); -        seed_exp[in_size + 3] = i; +        seed_exp[in_size + 3] = static_cast<u8>(i);          mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);          ++i;      } @@ -299,10 +311,11 @@ boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,      std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));      if (cert_authority == 0)          return boost::none; -    if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) +    if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) {          LOG_INFO(Crypto,                   "Attempting to parse ticket with non-standard certificate authority {:08X}.",                   cert_authority); +    }      Key128 rights_id;      std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128)); @@ -871,9 +884,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {                                       "/system/save/80000000000000e2",                                   "rb+"); +    const auto blob2 = GetTicketblob(save2);      auto res = GetTicketblob(save1); -    const auto res2 = GetTicketblob(save2); -    std::copy(res2.begin(), res2.end(), std::back_inserter(res)); +    res.insert(res.end(), blob2.begin(), blob2.end());      for (const auto& raw : res) {          const auto pair = ParseTicket(raw, rsa_key); diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index a41abbdfc..cccb3c0ae 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -175,7 +175,7 @@ private:      void WriteKeyToFile(KeyCategory category, std::string_view keyname,                          const std::array<u8, Size>& key); -    void DeriveGeneralPurposeKeys(u8 crypto_revision); +    void DeriveGeneralPurposeKeys(std::size_t crypto_revision);      void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);      void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp index d1c04e98d..25cee1f3a 100644 --- a/src/core/crypto/partition_data_manager.cpp +++ b/src/core/crypto/partition_data_manager.cpp @@ -11,7 +11,6 @@  #include <array>  #include <cctype>  #include <cstring> -#include <boost/optional/optional.hpp>  #include <mbedtls/sha256.h>  #include "common/assert.h"  #include "common/common_funcs.h" @@ -19,7 +18,7 @@  #include "common/hex_util.h"  #include "common/logging/log.h"  #include "common/string_util.h" -#include "core/crypto/ctr_encryption_layer.h" +#include "common/swap.h"  #include "core/crypto/key_manager.h"  #include "core/crypto/partition_data_manager.h"  #include "core/crypto/xts_encryption_layer.h" @@ -302,10 +301,10 @@ FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,      return nullptr;  } -PartitionDataManager::PartitionDataManager(FileSys::VirtualDir sysdata_dir) +PartitionDataManager::PartitionDataManager(const FileSys::VirtualDir& sysdata_dir)      : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")), -      fuses(FindFileInDirWithNames(sysdata_dir, "fuse")), -      kfuses(FindFileInDirWithNames(sysdata_dir, "kfuse")), +      fuses(FindFileInDirWithNames(sysdata_dir, "fuses")), +      kfuses(FindFileInDirWithNames(sysdata_dir, "kfuses")),        package2({            FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"),            FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"), @@ -314,13 +313,14 @@ PartitionDataManager::PartitionDataManager(FileSys::VirtualDir sysdata_dir)            FindFileInDirWithNames(sysdata_dir, "BCPKG2-5-Repair-Main"),            FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"),        }), +      prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")),        secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")),        package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")),        secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{}                                                       : secure_monitor->ReadAllBytes()),        package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{} -                                                             : package1_decrypted->ReadAllBytes()), -      prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")) {} +                                                             : package1_decrypted->ReadAllBytes()) { +}  PartitionDataManager::~PartitionDataManager() = default; @@ -332,18 +332,19 @@ FileSys::VirtualFile PartitionDataManager::GetBoot0Raw() const {      return boot0;  } -std::array<u8, 176> PartitionDataManager::GetEncryptedKeyblob(u8 index) const { -    if (HasBoot0() && index < 32) +PartitionDataManager::EncryptedKeyBlob PartitionDataManager::GetEncryptedKeyblob( +    std::size_t index) const { +    if (HasBoot0() && index < NUM_ENCRYPTED_KEYBLOBS)          return GetEncryptedKeyblobs()[index];      return {};  } -std::array<std::array<u8, 176>, 32> PartitionDataManager::GetEncryptedKeyblobs() const { +PartitionDataManager::EncryptedKeyBlobs PartitionDataManager::GetEncryptedKeyblobs() const {      if (!HasBoot0())          return {}; -    std::array<std::array<u8, 176>, 32> out{}; -    for (size_t i = 0; i < 0x20; ++i) +    EncryptedKeyBlobs out{}; +    for (size_t i = 0; i < out.size(); ++i)          boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200);      return out;  } @@ -389,7 +390,7 @@ std::array<u8, 16> PartitionDataManager::GetKeyblobMACKeySource() const {      return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]);  } -std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(u8 revision) const { +std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(std::size_t revision) const {      if (keyblob_source_hashes[revision] == SHA256Hash{}) {          LOG_WARNING(Crypto,                      "No keyblob source hash for crypto revision {:02X}! Cannot derive keys...", @@ -446,7 +447,7 @@ bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {      return false;  } -void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2_keys, +void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& package2_keys,                                             Package2Type type) {      FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>(          package2[static_cast<size_t>(type)], @@ -456,43 +457,38 @@ void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20>      if (file->ReadObject(&header) != sizeof(Package2Header))          return; -    u8 revision = 0xFF; +    std::size_t revision = 0xFF;      if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) { -        for (size_t i = 0; i < package2_keys.size(); ++i) { -            if (AttemptDecrypt(package2_keys[i], header)) +        for (std::size_t i = 0; i < package2_keys.size(); ++i) { +            if (AttemptDecrypt(package2_keys[i], header)) {                  revision = i; +            }          }      }      if (header.magic != Common::MakeMagic('P', 'K', '2', '1'))          return; -    const std::vector<u8> s1_iv(header.section_ctr[1].begin(), header.section_ctr[1].end()); -      const auto a = std::make_shared<FileSys::OffsetVfsFile>(          file, header.section_size[1], header.section_size[0] + sizeof(Package2Header));      auto c = a->ReadAllBytes();      AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR); -    cipher.SetIV(s1_iv); +    cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});      cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); -    // package2_decrypted[static_cast<size_t>(type)] = s1; -      INIHeader ini;      std::memcpy(&ini, c.data(), sizeof(INIHeader));      if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))          return; -    std::map<u64, KIPHeader> kips{};      u64 offset = sizeof(INIHeader);      for (size_t i = 0; i < ini.process_count; ++i) {          KIPHeader kip;          std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));          if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))              return; -        kips.emplace(offset, kip);          const auto name =              Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size()); @@ -503,33 +499,29 @@ void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20>              continue;          } -        std::vector<u8> text(kip.sections[0].size_compressed); -        std::vector<u8> rodata(kip.sections[1].size_compressed); -        std::vector<u8> data(kip.sections[2].size_compressed); +        const u64 initial_offset = sizeof(KIPHeader) + offset; +        const auto text_begin = c.cbegin() + initial_offset; +        const auto text_end = text_begin + kip.sections[0].size_compressed; +        const std::vector<u8> text = DecompressBLZ({text_begin, text_end}); -        u64 offset_sec = sizeof(KIPHeader) + offset; -        std::memcpy(text.data(), c.data() + offset_sec, text.size()); -        offset_sec += text.size(); -        std::memcpy(rodata.data(), c.data() + offset_sec, rodata.size()); -        offset_sec += rodata.size(); -        std::memcpy(data.data(), c.data() + offset_sec, data.size()); +        const auto rodata_end = text_end + kip.sections[1].size_compressed; +        const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end}); -        offset += sizeof(KIPHeader) + kip.sections[0].size_compressed + -                  kip.sections[1].size_compressed + kip.sections[2].size_compressed; +        const auto data_end = rodata_end + kip.sections[2].size_compressed; +        const std::vector<u8> data = DecompressBLZ({rodata_end, data_end}); -        text = DecompressBLZ(text); -        rodata = DecompressBLZ(rodata); -        data = DecompressBLZ(data); +        std::vector<u8> out; +        out.reserve(text.size() + rodata.size() + data.size()); +        out.insert(out.end(), text.begin(), text.end()); +        out.insert(out.end(), rodata.begin(), rodata.end()); +        out.insert(out.end(), data.begin(), data.end()); -        std::vector<u8> out(text.size() + rodata.size() + data.size()); -        std::memcpy(out.data(), text.data(), text.size()); -        std::memcpy(out.data() + text.size(), rodata.data(), rodata.size()); -        std::memcpy(out.data() + text.size() + rodata.size(), data.data(), data.size()); +        offset += sizeof(KIPHeader) + out.size();          if (name == "FS") -            package2_fs[static_cast<size_t>(type)] = out; +            package2_fs[static_cast<size_t>(type)] = std::move(out);          else if (name == "spl") -            package2_spl[static_cast<size_t>(type)] = out; +            package2_spl[static_cast<size_t>(type)] = std::move(out);      }  } diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h index 45c7fecfa..0ad007c72 100644 --- a/src/core/crypto/partition_data_manager.h +++ b/src/core/crypto/partition_data_manager.h @@ -5,9 +5,7 @@  #pragma once  #include <vector> -#include "common/common_funcs.h"  #include "common/common_types.h" -#include "common/swap.h"  #include "core/file_sys/vfs_types.h"  namespace Core::Crypto { @@ -24,15 +22,20 @@ enum class Package2Type {  class PartitionDataManager {  public:      static const u8 MAX_KEYBLOB_SOURCE_HASH; +    static constexpr std::size_t NUM_ENCRYPTED_KEYBLOBS = 32; +    static constexpr std::size_t ENCRYPTED_KEYBLOB_SIZE = 0xB0; -    explicit PartitionDataManager(FileSys::VirtualDir sysdata_dir); +    using EncryptedKeyBlob = std::array<u8, ENCRYPTED_KEYBLOB_SIZE>; +    using EncryptedKeyBlobs = std::array<EncryptedKeyBlob, NUM_ENCRYPTED_KEYBLOBS>; + +    explicit PartitionDataManager(const FileSys::VirtualDir& sysdata_dir);      ~PartitionDataManager();      // BOOT0      bool HasBoot0() const;      FileSys::VirtualFile GetBoot0Raw() const; -    std::array<u8, 0xB0> GetEncryptedKeyblob(u8 index) const; -    std::array<std::array<u8, 0xB0>, 0x20> GetEncryptedKeyblobs() const; +    EncryptedKeyBlob GetEncryptedKeyblob(std::size_t index) const; +    EncryptedKeyBlobs GetEncryptedKeyblobs() const;      std::vector<u8> GetSecureMonitor() const;      std::array<u8, 0x10> GetPackage2KeySource() const;      std::array<u8, 0x10> GetAESKekGenerationSource() const; @@ -43,7 +46,7 @@ public:      std::vector<u8> GetPackage1Decrypted() const;      std::array<u8, 0x10> GetMasterKeySource() const;      std::array<u8, 0x10> GetKeyblobMACKeySource() const; -    std::array<u8, 0x10> GetKeyblobKeySource(u8 revision) const; +    std::array<u8, 0x10> GetKeyblobKeySource(std::size_t revision) const;      // Fuses      bool HasFuses() const; @@ -57,7 +60,8 @@ public:      // Package2      bool HasPackage2(Package2Type type = Package2Type::NormalMain) const;      FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const; -    void DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2, Package2Type type); +    void DecryptPackage2(const std::array<std::array<u8, 16>, 0x20>& package2_keys, +                         Package2Type type);      const std::vector<u8>& GetPackage2FSDecompressed(          Package2Type type = Package2Type::NormalMain) const;      std::array<u8, 0x10> GetKeyAreaKeyApplicationSource( diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 6102ef476..76a2b7e86 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp @@ -10,19 +10,19 @@ namespace FileSys {  BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)      : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)), -      sysnand_cache(std::make_shared<RegisteredCache>( +      sysnand_cache(std::make_unique<RegisteredCache>(            GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), -      usrnand_cache(std::make_shared<RegisteredCache>( +      usrnand_cache(std::make_unique<RegisteredCache>(            GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}  BISFactory::~BISFactory() = default; -std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const { -    return sysnand_cache; +RegisteredCache* BISFactory::GetSystemNANDContents() const { +    return sysnand_cache.get();  } -std::shared_ptr<RegisteredCache> BISFactory::GetUserNANDContents() const { -    return usrnand_cache; +RegisteredCache* BISFactory::GetUserNANDContents() const { +    return usrnand_cache.get();  }  VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h index c352e0925..364d309bd 100644 --- a/src/core/file_sys/bis_factory.h +++ b/src/core/file_sys/bis_factory.h @@ -20,8 +20,8 @@ public:      explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);      ~BISFactory(); -    std::shared_ptr<RegisteredCache> GetSystemNANDContents() const; -    std::shared_ptr<RegisteredCache> GetUserNANDContents() const; +    RegisteredCache* GetSystemNANDContents() const; +    RegisteredCache* GetUserNANDContents() const;      VirtualDir GetModificationLoadRoot(u64 title_id) const; @@ -29,8 +29,8 @@ private:      VirtualDir nand_root;      VirtualDir load_root; -    std::shared_ptr<RegisteredCache> sysnand_cache; -    std::shared_ptr<RegisteredCache> usrnand_cache; +    std::unique_ptr<RegisteredCache> sysnand_cache; +    std::unique_ptr<RegisteredCache> usrnand_cache;  };  } // namespace FileSys diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index aa1b3c17d..6dcec7816 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -133,7 +133,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty                       static_cast<u8>(type));      u128 out_128{};      memcpy(out_128.data(), out.data(), 16); -    LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}", +    LOG_TRACE(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}",                master_key_id, header.key_index, out_128[1], out_128[0]);      return out; diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index 5b1177a03..a012c2be9 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -17,11 +17,13 @@ const std::array<const char*, 15> LANGUAGE_NAMES = {  };  std::string LanguageEntry::GetApplicationName() const { -    return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200); +    return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), +                                                       application_name.size());  }  std::string LanguageEntry::GetDeveloperName() const { -    return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), 0x100); +    return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), +                                                       developer_name.size());  }  NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) { @@ -56,7 +58,12 @@ u64 NACP::GetTitleId() const {      return raw->title_id;  } +u64 NACP::GetDLCBaseTitleId() const { +    return raw->dlc_base_title_id; +} +  std::string NACP::GetVersionString() const { -    return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 0x10); +    return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), +                                                       raw->version_string.size());  }  } // namespace FileSys diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 43d6f0719..141f7e056 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -79,6 +79,7 @@ public:      std::string GetApplicationName(Language language = Language::Default) const;      std::string GetDeveloperName(Language language = Language::Default) const;      u64 GetTitleId() const; +    u64 GetDLCBaseTitleId() const;      std::string GetVersionString() const;  private: diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 019caebe9..0117cb0bf 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -214,8 +214,14 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t  VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type,                                       VirtualFile update_raw) const { -    LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id, -             static_cast<u8>(type)); +    const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}", +                                        title_id, static_cast<u8>(type)) +                                .c_str(); + +    if (type == ContentRecordType::Program) +        LOG_INFO(Loader, log_string); +    else +        LOG_DEBUG(Loader, log_string);      if (romfs == nullptr)          return romfs; @@ -346,7 +352,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam  }  std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { -    const auto& installed{Service::FileSystem::GetUnionContents()}; +    const auto installed{Service::FileSystem::GetUnionContents()};      const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);      if (base_control_nca == nullptr) diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index e9b040689..1febb398e 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -308,14 +308,14 @@ VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {      return GetEntryRaw(entry.title_id, entry.type);  } -std::shared_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const { +std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {      const auto raw = GetEntryRaw(title_id, type);      if (raw == nullptr)          return nullptr; -    return std::make_shared<NCA>(raw); +    return std::make_unique<NCA>(raw);  } -std::shared_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const { +std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {      return GetEntry(entry.title_id, entry.type);  } @@ -516,7 +516,7 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {                          }) != yuzu_meta.end();  } -RegisteredCacheUnion::RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches) +RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches)      : caches(std::move(caches)) {}  void RegisteredCacheUnion::Refresh() { @@ -572,14 +572,14 @@ VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const      return GetEntryRaw(entry.title_id, entry.type);  } -std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const { +std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {      const auto raw = GetEntryRaw(title_id, type);      if (raw == nullptr)          return nullptr; -    return std::make_shared<NCA>(raw); +    return std::make_unique<NCA>(raw);  } -std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const { +std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {      return GetEntry(entry.title_id, entry.type);  } diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index c0cd59fc5..5ddacba47 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -88,8 +88,8 @@ public:      VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;      VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; -    std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; -    std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; +    std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; +    std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;      std::vector<RegisteredCacheEntry> ListEntries() const;      // If a parameter is not boost::none, it will be filtered for from all entries. @@ -142,7 +142,7 @@ private:  // Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface.  class RegisteredCacheUnion {  public: -    explicit RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches); +    explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches);      void Refresh(); @@ -157,8 +157,8 @@ public:      VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;      VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; -    std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; -    std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; +    std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; +    std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;      std::vector<RegisteredCacheEntry> ListEntries() const;      // If a parameter is not boost::none, it will be filtered for from all entries. @@ -168,7 +168,7 @@ public:          boost::optional<u64> title_id = boost::none) const;  private: -    std::vector<std::shared_ptr<RegisteredCache>> caches; +    std::vector<RegisteredCache*> caches;  };  } // namespace FileSys diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 47f2ab9e0..ef1aaebbb 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -51,6 +51,13 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr                      meta.title_id);      } +    if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) { +        LOG_WARNING(Service_FS, +                    "Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is " +                    "non-zero ({:016X}{:016X})", +                    meta.user_id[1], meta.user_id[0]); +    } +      std::string save_directory =          GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); @@ -92,6 +99,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ      case SaveDataSpaceId::NandUser:          out = "/user/";          break; +    case SaveDataSpaceId::TemporaryStorage: +        out = "/temp/"; +        break;      default:          ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));      } @@ -100,10 +110,11 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ      case SaveDataType::SystemSaveData:          return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);      case SaveDataType::SaveData: +    case SaveDataType::DeviceSaveData:          return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],                             title_id);      case SaveDataType::TemporaryStorage: -        return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], +        return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],                             title_id);      default:          ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp index d66a9c9a4..bd3a57058 100644 --- a/src/core/file_sys/sdmc_factory.cpp +++ b/src/core/file_sys/sdmc_factory.cpp @@ -10,10 +10,10 @@  namespace FileSys {  SDMCFactory::SDMCFactory(VirtualDir dir_) -    : dir(std::move(dir_)), contents(std::make_shared<RegisteredCache>( +    : dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>(                                  GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),                                  [](const VirtualFile& file, const NcaID& id) { -                                    return std::make_shared<NAX>(file, id)->GetDecrypted(); +                                    return NAX{file, id}.GetDecrypted();                                  })) {}  SDMCFactory::~SDMCFactory() = default; @@ -22,8 +22,8 @@ ResultVal<VirtualDir> SDMCFactory::Open() {      return MakeResult<VirtualDir>(dir);  } -std::shared_ptr<RegisteredCache> SDMCFactory::GetSDMCContents() const { -    return contents; +RegisteredCache* SDMCFactory::GetSDMCContents() const { +    return contents.get();  }  } // namespace FileSys diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h index ea12149de..42794ba5b 100644 --- a/src/core/file_sys/sdmc_factory.h +++ b/src/core/file_sys/sdmc_factory.h @@ -19,12 +19,12 @@ public:      ~SDMCFactory();      ResultVal<VirtualDir> Open(); -    std::shared_ptr<RegisteredCache> GetSDMCContents() const; +    RegisteredCache* GetSDMCContents() const;  private:      VirtualDir dir; -    std::shared_ptr<RegisteredCache> contents; +    std::unique_ptr<RegisteredCache> contents;  };  } // namespace FileSys diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index e961ef121..bdcc889e0 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -207,7 +207,7 @@ void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {  static Kernel::Thread* FindThreadById(int id) {      for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { -        const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); +        const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();          for (auto& thread : threads) {              if (thread->GetThreadID() == static_cast<u32>(id)) {                  current_core = core; @@ -597,7 +597,7 @@ static void HandleQuery() {      } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {          std::string val = "m";          for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { -            const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); +            const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();              for (const auto& thread : threads) {                  val += fmt::format("{:x}", thread->GetThreadID());                  val += ","; @@ -612,7 +612,7 @@ static void HandleQuery() {          buffer += "l<?xml version=\"1.0\"?>";          buffer += "<threads>";          for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { -            const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); +            const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();              for (const auto& thread : threads) {                  buffer +=                      fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*", diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index ebf193930..57157beb4 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -39,7 +39,7 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address)                                             std::vector<SharedPtr<Thread>>& waiting_threads,                                             VAddr arb_addr) {          const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); -        const auto& thread_list = scheduler->GetThreadList(); +        const auto& thread_list = scheduler.GetThreadList();          for (const auto& thread : thread_list) {              if (thread->GetArbiterWaitAddress() == arb_addr) diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index c80b2c507..073dd5a7d 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -153,11 +153,11 @@ void Process::PrepareForTermination() {          }      }; -    auto& system = Core::System::GetInstance(); -    stop_threads(system.Scheduler(0)->GetThreadList()); -    stop_threads(system.Scheduler(1)->GetThreadList()); -    stop_threads(system.Scheduler(2)->GetThreadList()); -    stop_threads(system.Scheduler(3)->GetThreadList()); +    const auto& system = Core::System::GetInstance(); +    stop_threads(system.Scheduler(0).GetThreadList()); +    stop_threads(system.Scheduler(1).GetThreadList()); +    stop_threads(system.Scheduler(2).GetThreadList()); +    stop_threads(system.Scheduler(3).GetThreadList());  }  /** diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 73ec01e11..f2816943a 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -24,6 +24,7 @@ class ProgramMetadata;  namespace Kernel {  class KernelCore; +class ResourceLimit;  struct AddressMapping {      // Address and size must be page-aligned @@ -57,9 +58,23 @@ union ProcessFlags {      BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).  }; -enum class ProcessStatus { Created, Running, Exited }; - -class ResourceLimit; +/** + * Indicates the status of a Process instance. + * + * @note These match the values as used by kernel, + *       so new entries should only be added if RE + *       shows that a new value has been introduced. + */ +enum class ProcessStatus { +    Created, +    CreatedWithDebuggerAttached, +    Running, +    WaitingForDebuggerToAttach, +    DebuggerAttached, +    Exiting, +    Exited, +    DebugBreak, +};  struct CodeSet final {      struct Segment { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index e406df829..d08b84bde 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -389,6 +389,12 @@ static void Break(u32 reason, u64 info1, u64 info2) {              "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",              reason, info1, info2);          ASSERT(false); + +        Core::CurrentProcess()->PrepareForTermination(); + +        // Kill the current thread +        GetCurrentThread()->Stop(); +        Core::System::GetInstance().PrepareReschedule();      }  } @@ -442,25 +448,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)      case GetInfoType::RandomEntropy:          *result = 0;          break; -    case GetInfoType::AddressSpaceBaseAddr: -        *result = vm_manager.GetCodeRegionBaseAddress(); +    case GetInfoType::ASLRRegionBaseAddr: +        *result = vm_manager.GetASLRRegionBaseAddress();          break; -    case GetInfoType::AddressSpaceSize: { -        const u64 width = vm_manager.GetAddressSpaceWidth(); - -        switch (width) { -        case 32: -            *result = 0xFFE00000; -            break; -        case 36: -            *result = 0xFF8000000; -            break; -        case 39: -            *result = 0x7FF8000000; -            break; -        } +    case GetInfoType::ASLRRegionSize: +        *result = vm_manager.GetASLRRegionSize();          break; -    }      case GetInfoType::NewMapRegionBaseAddr:          *result = vm_manager.GetNewMapRegionBaseAddress();          break; @@ -803,7 +796,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target                                             std::vector<SharedPtr<Thread>>& waiting_threads,                                             VAddr condvar_addr) {          const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); -        const auto& thread_list = scheduler->GetThreadList(); +        const auto& thread_list = scheduler.GetThreadList();          for (const auto& thread : thread_list) {              if (thread->GetCondVarWaitAddress() == condvar_addr) @@ -1092,6 +1085,29 @@ static ResultCode ClearEvent(Handle handle) {      return RESULT_SUCCESS;  } +static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) { +    LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); + +    // This function currently only allows retrieving a process' status. +    enum class InfoType { +        Status, +    }; + +    const auto& kernel = Core::System::GetInstance().Kernel(); +    const auto process = kernel.HandleTable().Get<Process>(process_handle); +    if (!process) { +        return ERR_INVALID_HANDLE; +    } + +    const auto info_type = static_cast<InfoType>(type); +    if (info_type != InfoType::Status) { +        return ERR_INVALID_ENUM_VALUE; +    } + +    *out = static_cast<u64>(process->GetStatus()); +    return RESULT_SUCCESS; +} +  namespace {  struct FunctionDef {      using Func = void(); @@ -1227,7 +1243,7 @@ static const FunctionDef SVC_Table[] = {      {0x79, nullptr, "CreateProcess"},      {0x7A, nullptr, "StartProcess"},      {0x7B, nullptr, "TerminateProcess"}, -    {0x7C, nullptr, "GetProcessInfo"}, +    {0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"},      {0x7D, nullptr, "CreateResourceLimit"},      {0x7E, nullptr, "SetResourceLimitLimitValue"},      {0x7F, nullptr, "CallSecureMonitor"}, diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index 70148c4fe..554a5e328 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h @@ -41,8 +41,8 @@ enum class GetInfoType : u64 {      RandomEntropy = 11,      PerformanceCounter = 0xF0000002,      // 2.0.0+ -    AddressSpaceBaseAddr = 12, -    AddressSpaceSize = 13, +    ASLRRegionBaseAddr = 12, +    ASLRRegionSize = 13,      NewMapRegionBaseAddr = 14,      NewMapRegionSize = 15,      // 3.0.0+ diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index cbb80c3c4..b09753c80 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -77,6 +77,14 @@ void SvcWrap() {      FuncReturn(retval);  } +template <ResultCode func(u64*, u32, u32)> +void SvcWrap() { +    u64 param_1 = 0; +    u32 retval = func(¶m_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw; +    Core::CurrentArmInterface().SetReg(1, param_1); +    FuncReturn(retval); +} +  template <ResultCode func(u32, u64)>  void SvcWrap() {      FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 352ce1725..35ec98c1a 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -97,7 +97,7 @@ void Thread::CancelWakeupTimer() {  static boost::optional<s32> GetNextProcessorId(u64 mask) {      for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) {          if (mask & (1ULL << index)) { -            if (!Core::System::GetInstance().Scheduler(index)->GetCurrentThread()) { +            if (!Core::System::GetInstance().Scheduler(index).GetCurrentThread()) {                  // Core is enabled and not running any threads, use this one                  return index;              } @@ -147,14 +147,14 @@ void Thread::ResumeFromWait() {          new_processor_id = processor_id;      }      if (ideal_core != -1 && -        Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { +        Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {          new_processor_id = ideal_core;      }      ASSERT(*new_processor_id < 4);      // Add thread to new core's scheduler -    auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id); +    auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);      if (*new_processor_id != processor_id) {          // Remove thread from previous core's scheduler @@ -169,7 +169,7 @@ void Thread::ResumeFromWait() {      next_scheduler->ScheduleThread(this, current_priority);      // Change thread's scheduler -    scheduler = next_scheduler.get(); +    scheduler = next_scheduler;      Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();  } @@ -230,7 +230,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name      thread->name = std::move(name);      thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();      thread->owner_process = &owner_process; -    thread->scheduler = Core::System::GetInstance().Scheduler(processor_id).get(); +    thread->scheduler = &Core::System::GetInstance().Scheduler(processor_id);      thread->scheduler->AddThread(thread, priority);      thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); @@ -375,14 +375,14 @@ void Thread::ChangeCore(u32 core, u64 mask) {          new_processor_id = processor_id;      }      if (ideal_core != -1 && -        Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { +        Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {          new_processor_id = ideal_core;      }      ASSERT(*new_processor_id < 4);      // Add thread to new core's scheduler -    auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id); +    auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);      if (*new_processor_id != processor_id) {          // Remove thread from previous core's scheduler @@ -397,7 +397,7 @@ void Thread::ChangeCore(u32 core, u64 mask) {      next_scheduler->ScheduleThread(this, current_priority);      // Change thread's scheduler -    scheduler = next_scheduler.get(); +    scheduler = next_scheduler;      Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();  } diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index e412309fd..1e28ccbda 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -393,30 +393,35 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty      switch (type) {      case FileSys::ProgramAddressSpaceType::Is32Bit: +    case FileSys::ProgramAddressSpaceType::Is32BitNoMap:          address_space_width = 32;          code_region_base = 0x200000;          code_region_end = code_region_base + 0x3FE00000; -        map_region_size = 0x40000000; -        heap_region_size = 0x40000000; +        aslr_region_base = 0x200000; +        aslr_region_end = aslr_region_base + 0xFFE00000; +        if (type == FileSys::ProgramAddressSpaceType::Is32Bit) { +            map_region_size = 0x40000000; +            heap_region_size = 0x40000000; +        } else { +            map_region_size = 0; +            heap_region_size = 0x80000000; +        }          break;      case FileSys::ProgramAddressSpaceType::Is36Bit:          address_space_width = 36;          code_region_base = 0x8000000;          code_region_end = code_region_base + 0x78000000; +        aslr_region_base = 0x8000000; +        aslr_region_end = aslr_region_base + 0xFF8000000;          map_region_size = 0x180000000;          heap_region_size = 0x180000000;          break; -    case FileSys::ProgramAddressSpaceType::Is32BitNoMap: -        address_space_width = 32; -        code_region_base = 0x200000; -        code_region_end = code_region_base + 0x3FE00000; -        map_region_size = 0; -        heap_region_size = 0x80000000; -        break;      case FileSys::ProgramAddressSpaceType::Is39Bit:          address_space_width = 39;          code_region_base = 0x8000000;          code_region_end = code_region_base + 0x80000000; +        aslr_region_base = 0x8000000; +        aslr_region_end = aslr_region_base + 0x7FF8000000;          map_region_size = 0x1000000000;          heap_region_size = 0x180000000;          new_map_region_size = 0x80000000; @@ -490,6 +495,18 @@ u64 VMManager::GetAddressSpaceWidth() const {      return address_space_width;  } +VAddr VMManager::GetASLRRegionBaseAddress() const { +    return aslr_region_base; +} + +VAddr VMManager::GetASLRRegionEndAddress() const { +    return aslr_region_end; +} + +u64 VMManager::GetASLRRegionSize() const { +    return aslr_region_end - aslr_region_base; +} +  VAddr VMManager::GetCodeRegionBaseAddress() const {      return code_region_base;  } diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 015559a64..4accde6b3 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -205,6 +205,15 @@ public:      /// Gets the address space width in bits.      u64 GetAddressSpaceWidth() const; +    /// Gets the base address of the ASLR region. +    VAddr GetASLRRegionBaseAddress() const; + +    /// Gets the end address of the ASLR region. +    VAddr GetASLRRegionEndAddress() const; + +    /// Gets the size of the ASLR region +    u64 GetASLRRegionSize() const; +      /// Gets the base address of the code region.      VAddr GetCodeRegionBaseAddress() const; @@ -306,6 +315,9 @@ private:      VAddr address_space_base = 0;      VAddr address_space_end = 0; +    VAddr aslr_region_base = 0; +    VAddr aslr_region_end = 0; +      VAddr code_region_base = 0;      VAddr code_region_end = 0; diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 0ecfb5af1..518161bf7 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -7,8 +7,10 @@  #include <vector>  #include "common/logging/log.h"  #include "core/file_sys/content_archive.h" +#include "core/file_sys/control_metadata.h"  #include "core/file_sys/nca_metadata.h"  #include "core/file_sys/partition_filesystem.h" +#include "core/file_sys/patch_manager.h"  #include "core/file_sys/registered_cache.h"  #include "core/hle/ipc_helpers.h"  #include "core/hle/kernel/process.h" @@ -19,7 +21,7 @@  namespace Service::AOC {  constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; -constexpr u64 DLC_BASE_TO_AOC_ID_MASK = 0x1000; +constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000;  static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) {      return (aoc & DLC_BASE_TITLE_ID_MASK) == base; @@ -97,14 +99,24 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {      ctx.WriteBuffer(out); -    IPC::ResponseBuilder rb{ctx, 2}; +    IPC::ResponseBuilder rb{ctx, 3};      rb.Push(RESULT_SUCCESS); +    rb.Push(count);  }  void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {      IPC::ResponseBuilder rb{ctx, 4};      rb.Push(RESULT_SUCCESS); -    rb.Push(Core::System::GetInstance().CurrentProcess()->GetTitleID() | DLC_BASE_TO_AOC_ID_MASK); +    const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); +    FileSys::PatchManager pm{title_id}; + +    const auto res = pm.GetControlMetadata(); +    if (res.first == nullptr) { +        rb.Push(title_id + DLC_BASE_TO_AOC_ID); +        return; +    } + +    rb.Push(res.first->GetDLCBaseTitleId());  }  void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 439e62d27..e32a7c48e 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -319,13 +319,12 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {      return sdmc_factory->Open();  } -std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() { -    return std::make_shared<FileSys::RegisteredCacheUnion>( -        std::vector<std::shared_ptr<FileSys::RegisteredCache>>{ -            GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}); +std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() { +    return std::make_unique<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{ +        GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});  } -std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() { +FileSys::RegisteredCache* GetSystemNANDContents() {      LOG_TRACE(Service_FS, "Opening System NAND Contents");      if (bis_factory == nullptr) @@ -334,7 +333,7 @@ std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() {      return bis_factory->GetSystemNANDContents();  } -std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents() { +FileSys::RegisteredCache* GetUserNANDContents() {      LOG_TRACE(Service_FS, "Opening User NAND Contents");      if (bis_factory == nullptr) @@ -343,7 +342,7 @@ std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents() {      return bis_factory->GetUserNANDContents();  } -std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents() { +FileSys::RegisteredCache* GetSDMCContents() {      LOG_TRACE(Service_FS, "Opening SDMC Contents");      if (sdmc_factory == nullptr) @@ -361,19 +360,19 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {      return bis_factory->GetModificationLoadRoot(title_id);  } -void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) { +void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {      if (overwrite) {          bis_factory = nullptr;          save_data_factory = nullptr;          sdmc_factory = nullptr;      } -    auto nand_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), -                                             FileSys::Mode::ReadWrite); -    auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), -                                           FileSys::Mode::ReadWrite); -    auto load_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), -                                             FileSys::Mode::ReadWrite); +    auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), +                                            FileSys::Mode::ReadWrite); +    auto sd_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), +                                          FileSys::Mode::ReadWrite); +    auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), +                                            FileSys::Mode::ReadWrite);      if (bis_factory == nullptr)          bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory); @@ -383,7 +382,7 @@ void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {          sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));  } -void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs) { +void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) {      romfs_factory = nullptr;      CreateFactories(vfs, false);      std::make_shared<FSP_LDR>()->InstallAsService(service_manager); diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 53b01bb01..6ca5c5636 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -47,19 +47,19 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,                                              FileSys::SaveDataDescriptor save_struct);  ResultVal<FileSys::VirtualDir> OpenSDMC(); -std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); +std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); -std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents(); -std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents(); -std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents(); +FileSys::RegisteredCache* GetSystemNANDContents(); +FileSys::RegisteredCache* GetUserNANDContents(); +FileSys::RegisteredCache* GetSDMCContents();  FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);  // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function  // above is called. -void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite = true); +void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); -void InstallInterfaces(SM::ServiceManager& service_manager, const FileSys::VirtualFilesystem& vfs); +void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs);  // A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of  // pointers and booleans. This makes using a VfsDirectory with switch services much easier and diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index 4b2f758a8..44accecb7 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp @@ -161,7 +161,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {      };      RegisterHandlers(functions);      // Attempt to load shared font data from disk -    const auto nand = FileSystem::GetSystemNANDContents(); +    const auto* nand = FileSystem::GetSystemNANDContents();      std::size_t offset = 0;      // Rebuild shared fonts from data ncas      if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 62f049660..a225cb4cb 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -197,7 +197,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co  // Module interface  /// Initialize ServiceManager -void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) { +void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs) {      // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it      // here and pass it into the respective InstallInterfaces functions.      auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); @@ -220,7 +220,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesys      EUPLD::InstallInterfaces(*sm);      Fatal::InstallInterfaces(*sm);      FGM::InstallInterfaces(*sm); -    FileSystem::InstallInterfaces(*sm, rfs); +    FileSystem::InstallInterfaces(*sm, vfs);      Friend::InstallInterfaces(*sm);      GRC::InstallInterfaces(*sm);      HID::InstallInterfaces(*sm); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 2fc57a82e..98483ecf1 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -180,8 +180,7 @@ private:  };  /// Initialize ServiceManager -void Init(std::shared_ptr<SM::ServiceManager>& sm, -          const std::shared_ptr<FileSys::VfsFilesystem>& vfs); +void Init(std::shared_ptr<SM::ServiceManager>& sm, FileSys::VfsFilesystem& vfs);  /// Shutdown ServiceManager  void Shutdown(); diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index bbc02abcc..184537daa 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -968,6 +968,54 @@ private:          rb.PushCopyObjects(vsync_event);      } +    enum class ConvertedScaleMode : u64 { +        None = 0, // VI seems to name this as "Unknown" but lots of games pass it, assume it's no +                  // scaling/default +        Freeze = 1, +        ScaleToWindow = 2, +        Crop = 3, +        NoCrop = 4, +    }; + +    // This struct is different, currently it's 1:1 but this might change in the future. +    enum class NintendoScaleMode : u32 { +        None = 0, +        Freeze = 1, +        ScaleToWindow = 2, +        Crop = 3, +        NoCrop = 4, +    }; + +    void ConvertScalingMode(Kernel::HLERequestContext& ctx) { +        IPC::RequestParser rp{ctx}; +        auto mode = rp.PopEnum<NintendoScaleMode>(); +        LOG_DEBUG(Service_VI, "called mode={}", static_cast<u32>(mode)); + +        IPC::ResponseBuilder rb{ctx, 4}; +        rb.Push(RESULT_SUCCESS); +        switch (mode) { +        case NintendoScaleMode::None: +            rb.PushEnum(ConvertedScaleMode::None); +            break; +        case NintendoScaleMode::Freeze: +            rb.PushEnum(ConvertedScaleMode::Freeze); +            break; +        case NintendoScaleMode::ScaleToWindow: +            rb.PushEnum(ConvertedScaleMode::ScaleToWindow); +            break; +        case NintendoScaleMode::Crop: +            rb.PushEnum(ConvertedScaleMode::Crop); +            break; +        case NintendoScaleMode::NoCrop: +            rb.PushEnum(ConvertedScaleMode::NoCrop); +            break; +        default: +            UNIMPLEMENTED_MSG("Unknown scaling mode {}", static_cast<u32>(mode)); +            rb.PushEnum(ConvertedScaleMode::None); +            break; +        } +    } +      std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;  }; @@ -991,7 +1039,7 @@ IApplicationDisplayService::IApplicationDisplayService(          {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},          {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},          {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"}, -        {2102, nullptr, "ConvertScalingMode"}, +        {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},          {2450, nullptr, "GetIndirectLayerImageMap"},          {2451, nullptr, "GetIndirectLayerImageCropMap"},          {2460, nullptr, "GetIndirectLayerImageRequiredMemoryInfo"}, diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 951fd8257..8518dddcb 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -139,14 +139,22 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)      for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",                                 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {          const FileSys::VirtualFile module_file = dir->GetFile(module); -        if (module_file != nullptr) { -            const VAddr load_addr = next_load_addr; -            next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr, -                                                       std::strcmp(module, "rtld") == 0, pm); -            LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); -            // Register module with GDBStub -            GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); +        if (module_file == nullptr) { +            continue;          } + +        const VAddr load_addr = next_load_addr; +        const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; +        const auto tentative_next_load_addr = +            AppLoader_NSO::LoadModule(*module_file, load_addr, should_pass_arguments, pm); +        if (!tentative_next_load_addr) { +            return ResultStatus::ErrorLoadingNSO; +        } + +        next_load_addr = *tentative_next_load_addr; +        LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); +        // Register module with GDBStub +        GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);      }      process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()); diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 91659ec17..9cd0b0ccd 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) {      return "unknown";  } -constexpr std::array<const char*, 59> RESULT_MESSAGES{ +constexpr std::array<const char*, 60> RESULT_MESSAGES{      "The operation completed successfully.",      "The loader requested to load is already loaded.",      "The operation is not implemented.", @@ -128,6 +128,7 @@ constexpr std::array<const char*, 59> RESULT_MESSAGES{      "The RomFS could not be found.",      "The ELF file has incorrect size as determined by the header.",      "There was a general error loading the NRO into emulated memory.", +    "There was a general error loading the NSO into emulated memory.",      "There is no icon available.",      "There is no control data available.",      "The NAX file has a bad header.", diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 0e0333db5..e562b3a04 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -90,6 +90,7 @@ enum class ResultStatus : u16 {      ErrorNoRomFS,      ErrorIncorrectELFFileSize,      ErrorLoadingNRO, +    ErrorLoadingNSO,      ErrorNoIcon,      ErrorNoControl,      ErrorBadNAXHeader, diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 576fe692a..243b499f2 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -127,10 +127,10 @@ static constexpr u32 PageAlignSize(u32 size) {      return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;  } -bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { +bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {      // Read NSO header      NroHeader nro_header{}; -    if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { +    if (sizeof(NroHeader) != file.ReadObject(&nro_header)) {          return {};      }      if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { @@ -138,7 +138,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {      }      // Build program image -    std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size)); +    std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size));      if (program_image.size() != PageAlignSize(nro_header.file_size)) {          return {};      } @@ -182,7 +182,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {      Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);      // Register module with GDBStub -    GDBStub::RegisterModule(file->GetName(), load_base, load_base); +    GDBStub::RegisterModule(file.GetName(), load_base, load_base);      return true;  } @@ -195,7 +195,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {      // Load NRO      const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); -    if (!LoadNro(file, base_address)) { +    if (!LoadNro(*file, base_address)) {          return ResultStatus::ErrorLoadingNRO;      } diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 04b46119a..50ee5a78a 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -41,7 +41,7 @@ public:      bool IsRomFSUpdatable() const override;  private: -    bool LoadNro(FileSys::VirtualFile file, VAddr load_base); +    bool LoadNro(const FileSys::VfsFile& file, VAddr load_base);      std::vector<u8> icon_data;      std::unique_ptr<FileSys::NACP> nacp; diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index ba57db9bf..68efca5c0 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -93,17 +93,14 @@ static constexpr u32 PageAlignSize(u32 size) {      return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;  } -VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, -                                bool should_pass_arguments, -                                boost::optional<FileSys::PatchManager> pm) { -    if (file == nullptr) -        return {}; - -    if (file->GetSize() < sizeof(NsoHeader)) +std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAddr load_base, +                                               bool should_pass_arguments, +                                               std::optional<FileSys::PatchManager> pm) { +    if (file.GetSize() < sizeof(NsoHeader))          return {};      NsoHeader nso_header{}; -    if (sizeof(NsoHeader) != file->ReadObject(&nso_header)) +    if (sizeof(NsoHeader) != file.ReadObject(&nso_header))          return {};      if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) @@ -114,7 +111,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,      std::vector<u8> program_image;      for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {          std::vector<u8> data = -            file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset); +            file.ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);          if (nso_header.IsSegmentCompressed(i)) {              data = DecompressSegment(data, nso_header.segments[i]);          } @@ -157,7 +154,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,      program_image.resize(image_size);      // Apply patches if necessary -    if (pm != boost::none && pm->HasNSOPatch(nso_header.build_id)) { +    if (pm && pm->HasNSOPatch(nso_header.build_id)) {          std::vector<u8> pi_header(program_image.size() + 0x100);          std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));          std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); @@ -172,7 +169,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,      Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);      // Register module with GDBStub -    GDBStub::RegisterModule(file->GetName(), load_base, load_base); +    GDBStub::RegisterModule(file.GetName(), load_base, load_base);      return load_base + image_size;  } @@ -184,7 +181,9 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {      // Load module      const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); -    LoadModule(file, base_address, true); +    if (!LoadModule(*file, base_address, true)) { +        return ResultStatus::ErrorLoadingNSO; +    }      LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);      process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 70ab3b718..433306139 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -4,6 +4,7 @@  #pragma once +#include <optional>  #include "common/common_types.h"  #include "core/file_sys/patch_manager.h"  #include "core/loader/linker.h" @@ -36,8 +37,9 @@ public:          return IdentifyType(file);      } -    static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, bool should_pass_arguments, -                            boost::optional<FileSys::PatchManager> pm = boost::none); +    static std::optional<VAddr> LoadModule(const FileSys::VfsFile& file, VAddr load_base, +                                           bool should_pass_arguments, +                                           std::optional<FileSys::PatchManager> pm = {});      ResultStatus Load(Kernel::Process& process) override;  }; diff --git a/src/core/settings.h b/src/core/settings.h index 83b9a04c8..8f2da01c8 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -136,7 +136,7 @@ struct Values {      float resolution_factor;      bool use_frame_limit;      u16 frame_limit; -    bool use_accurate_framebuffers; +    bool use_accurate_gpu_emulation;      float bg_red;      float bg_green; diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 7b04792b5..0de13edd3 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -163,8 +163,8 @@ TelemetrySession::TelemetrySession() {      AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimit",               Settings::values.use_frame_limit);      AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit); -    AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateFramebuffers", -             Settings::values.use_accurate_framebuffers); +    AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation", +             Settings::values.use_accurate_gpu_emulation);      AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",               Settings::values.use_docked_mode);  } diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp index 912e785b9..597b279b9 100644 --- a/src/video_core/engines/fermi_2d.cpp +++ b/src/video_core/engines/fermi_2d.cpp @@ -62,14 +62,16 @@ void Fermi2D::HandleSurfaceCopy() {          u8* dst_buffer = Memory::GetPointer(dest_cpu);          if (!regs.src.linear && regs.dst.linear) {              // If the input is tiled and the output is linear, deswizzle the input and copy it over. -            Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel, -                                      dst_bytes_per_pixel, src_buffer, dst_buffer, true, -                                      regs.src.BlockHeight()); +            Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth, +                                      src_bytes_per_pixel, dst_bytes_per_pixel, src_buffer, +                                      dst_buffer, true, regs.src.BlockHeight(), +                                      regs.src.BlockDepth());          } else {              // If the input is linear and the output is tiled, swizzle the input and copy it over. -            Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel, -                                      dst_bytes_per_pixel, dst_buffer, src_buffer, false, -                                      regs.dst.BlockHeight()); +            Texture::CopySwizzledData(regs.src.width, regs.src.height, regs.src.depth, +                                      src_bytes_per_pixel, dst_bytes_per_pixel, dst_buffer, +                                      src_buffer, false, regs.dst.BlockHeight(), +                                      regs.dst.BlockDepth());          }      }  } diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index aa7481b8c..bf2a21bb6 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -68,12 +68,14 @@ void MaxwellDMA::HandleCopy() {      if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {          // If the input is tiled and the output is linear, deswizzle the input and copy it over. -        Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, 1, 1, src_buffer, -                                  dst_buffer, true, regs.src_params.BlockHeight()); +        Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, +                                  regs.src_params.size_z, 1, 1, src_buffer, dst_buffer, true, +                                  regs.src_params.BlockHeight(), regs.src_params.BlockDepth());      } else {          // If the input is linear and the output is tiled, swizzle the input and copy it over. -        Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, 1, 1, dst_buffer, -                                  src_buffer, false, regs.dst_params.BlockHeight()); +        Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, +                                  regs.dst_params.size_z, 1, 1, dst_buffer, src_buffer, false, +                                  regs.dst_params.BlockHeight(), regs.dst_params.BlockDepth());      }  } diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index 311ccb616..df19e02e2 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h @@ -43,6 +43,10 @@ public:              u32 BlockHeight() const {                  return 1 << block_height;              } + +            u32 BlockDepth() const { +                return 1 << block_depth; +            }          };          static_assert(sizeof(Parameters) == 24, "Parameters has wrong size"); diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 9a59b65b3..f356f9a03 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -267,7 +267,7 @@ enum class ControlCode : u64 {      GTU = 12,      NEU = 13,      GEU = 14, -    // +    T = 15,      OFF = 16,      LO = 17,      SFF = 18, diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index ca923d17d..022d4ab74 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -87,6 +87,16 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {      return gpu_addr;  } +GPUVAddr MemoryManager::GetRegionEnd(GPUVAddr region_start) const { +    for (const auto& region : mapped_regions) { +        const GPUVAddr region_end{region.gpu_addr + region.size}; +        if (region_start >= region.gpu_addr && region_start < region_end) { +            return region_end; +        } +    } +    return {}; +} +  boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) {      GPUVAddr gpu_addr = 0;      u64 free_space = 0; diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 86765e72a..caf80093f 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -26,6 +26,7 @@ public:      GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size);      GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size);      GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); +    GPUVAddr GetRegionEnd(GPUVAddr region_start) const;      boost::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr);      std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const; diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h index 083b283b0..0a3b3951e 100644 --- a/src/video_core/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache.h @@ -11,32 +11,77 @@  #include "common/common_types.h"  #include "core/core.h" +#include "core/settings.h"  #include "video_core/rasterizer_interface.h"  #include "video_core/renderer_base.h" +class RasterizerCacheObject { +public: +    /// Gets the address of the shader in guest memory, required for cache management +    virtual VAddr GetAddr() const = 0; + +    /// Gets the size of the shader in guest memory, required for cache management +    virtual std::size_t GetSizeInBytes() const = 0; + +    /// Wriets any cached resources back to memory +    virtual void Flush() = 0; + +    /// Sets whether the cached object should be considered registered +    void SetIsRegistered(bool registered) { +        is_registered = registered; +    } + +    /// Returns true if the cached object is registered +    bool IsRegistered() const { +        return is_registered; +    } + +    /// Returns true if the cached object is dirty +    bool IsDirty() const { +        return is_dirty; +    } + +    /// Returns ticks from when this cached object was last modified +    u64 GetLastModifiedTicks() const { +        return last_modified_ticks; +    } + +    /// Marks an object as recently modified, used to specify whether it is clean or dirty +    template <class T> +    void MarkAsModified(bool dirty, T& cache) { +        is_dirty = dirty; +        last_modified_ticks = cache.GetModifiedTicks(); +    } + +private: +    bool is_registered{};      ///< Whether the object is currently registered with the cache +    bool is_dirty{};           ///< Whether the object is dirty (out of sync with guest memory) +    u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing +}; +  template <class T>  class RasterizerCache : NonCopyable { +    friend class RasterizerCacheObject; +  public: +    /// Write any cached resources overlapping the specified region back to memory +    void FlushRegion(Tegra::GPUVAddr addr, size_t size) { +        const auto& objects{GetSortedObjectsFromRegion(addr, size)}; +        for (auto& object : objects) { +            FlushObject(object); +        } +    } +      /// Mark the specified region as being invalidated      void InvalidateRegion(VAddr addr, u64 size) { -        if (size == 0) -            return; - -        const ObjectInterval interval{addr, addr + size}; -        for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) { -            for (auto& cached_object : pair.second) { -                if (!cached_object) -                    continue; - -                remove_objects.emplace(cached_object); +        const auto& objects{GetSortedObjectsFromRegion(addr, size)}; +        for (auto& object : objects) { +            if (!object->IsRegistered()) { +                // Skip duplicates +                continue;              } +            Unregister(object);          } - -        for (auto& remove_object : remove_objects) { -            Unregister(remove_object); -        } - -        remove_objects.clear();      }      /// Invalidates everything in the cache @@ -62,6 +107,7 @@ protected:      /// Register an object into the cache      void Register(const T& object) { +        object->SetIsRegistered(true);          object_cache.add({GetInterval(object), ObjectSet{object}});          auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();          rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1); @@ -69,12 +115,57 @@ protected:      /// Unregisters an object from the cache      void Unregister(const T& object) { +        object->SetIsRegistered(false);          auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();          rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1); + +        // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit +        if (Settings::values.use_accurate_gpu_emulation) { +            FlushObject(object); +        } +          object_cache.subtract({GetInterval(object), ObjectSet{object}});      } +    /// Returns a ticks counter used for tracking when cached objects were last modified +    u64 GetModifiedTicks() { +        return ++modified_ticks; +    } +  private: +    /// Returns a list of cached objects from the specified memory region, ordered by access time +    std::vector<T> GetSortedObjectsFromRegion(VAddr addr, u64 size) { +        if (size == 0) { +            return {}; +        } + +        std::vector<T> objects; +        const ObjectInterval interval{addr, addr + size}; +        for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) { +            for (auto& cached_object : pair.second) { +                if (!cached_object) { +                    continue; +                } +                objects.push_back(cached_object); +            } +        } + +        std::sort(objects.begin(), objects.end(), [](const T& a, const T& b) -> bool { +            return a->GetLastModifiedTicks() < b->GetLastModifiedTicks(); +        }); + +        return objects; +    } + +    /// Flushes the specified object, updating appropriate cache state as needed +    void FlushObject(const T& object) { +        if (!object->IsDirty()) { +            return; +        } +        object->Flush(); +        object->MarkAsModified(false, *this); +    } +      using ObjectSet = std::set<T>;      using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>;      using ObjectInterval = typename ObjectCache::interval_type; @@ -84,6 +175,6 @@ private:                                            object->GetAddr() + object->GetSizeInBytes());      } -    ObjectCache object_cache; -    ObjectSet remove_objects; +    ObjectCache object_cache; ///< Cache of objects +    u64 modified_ticks{};     ///< Counter of cache state ticks, used for in-order flushing  }; diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 965976334..be29dc8be 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -15,15 +15,18 @@  namespace OpenGL { -struct CachedBufferEntry final { -    VAddr GetAddr() const { +struct CachedBufferEntry final : public RasterizerCacheObject { +    VAddr GetAddr() const override {          return addr;      } -    std::size_t GetSizeInBytes() const { +    std::size_t GetSizeInBytes() const override {          return size;      } +    // We do not have to flush this cache as things in it are never modified by us. +    void Flush() override {} +      VAddr addr;      std::size_t size;      GLintptr offset; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 84582c777..468253033 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -286,7 +286,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {              &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));          // Bind the buffer -        glBindBufferRange(GL_UNIFORM_BUFFER, stage, buffer_cache.GetHandle(), offset, sizeof(ubo)); +        glBindBufferRange(GL_UNIFORM_BUFFER, static_cast<GLuint>(stage), buffer_cache.GetHandle(), +                          offset, static_cast<GLsizeiptr>(sizeof(ubo)));          Shader shader{shader_cache.GetStageProgram(program)}; @@ -423,6 +424,13 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep              // Used when just a single color attachment is enabled, e.g. for clearing a color buffer              Surface color_surface =                  res_cache.GetColorBufferSurface(*single_color_target, preserve_contents); + +            if (color_surface) { +                // Assume that a surface will be written to if it is used as a framebuffer, even if +                // the shader doesn't actually write to it. +                color_surface->MarkAsModified(true, res_cache); +            } +              glFramebufferTexture2D(                  GL_DRAW_FRAMEBUFFER,                  GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(*single_color_target), GL_TEXTURE_2D, @@ -433,6 +441,13 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep              std::array<GLenum, Maxwell::NumRenderTargets> buffers;              for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {                  Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents); + +                if (color_surface) { +                    // Assume that a surface will be written to if it is used as a framebuffer, even +                    // if the shader doesn't actually write to it. +                    color_surface->MarkAsModified(true, res_cache); +                } +                  buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);                  glFramebufferTexture2D(                      GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), @@ -452,6 +467,10 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep      }      if (depth_surface) { +        // Assume that a surface will be written to if it is used as a framebuffer, even if +        // the shader doesn't actually write to it. +        depth_surface->MarkAsModified(true, res_cache); +          if (regs.stencil_enable) {              // Attach both depth and stencil              glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, @@ -616,7 +635,14 @@ void RasterizerOpenGL::DrawArrays() {  void RasterizerOpenGL::FlushAll() {} -void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {} +void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) { +    MICROPROFILE_SCOPE(OpenGL_CacheManagement); + +    if (Settings::values.use_accurate_gpu_emulation) { +        // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit +        res_cache.FlushRegion(addr, size); +    } +}  void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {      MICROPROFILE_SCOPE(OpenGL_CacheManagement); @@ -626,6 +652,7 @@ void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {  }  void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) { +    FlushRegion(addr, size);      InvalidateRegion(addr, size);  } diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 65a220c41..1cb77aaf2 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -34,16 +34,53 @@ struct FormatTuple {      bool compressed;  }; -static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { -    auto& gpu{Core::System::GetInstance().GPU()}; -    const auto cpu_addr{gpu.MemoryManager().GpuToCpuAddress(gpu_addr)}; -    return cpu_addr ? *cpu_addr : 0; +static bool IsPixelFormatASTC(PixelFormat format) { +    switch (format) { +    case PixelFormat::ASTC_2D_4X4: +    case PixelFormat::ASTC_2D_5X4: +    case PixelFormat::ASTC_2D_8X8: +    case PixelFormat::ASTC_2D_8X5: +        return true; +    default: +        return false; +    } +} + +static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) { +    switch (format) { +    case PixelFormat::ASTC_2D_4X4: +        return {4, 4}; +    case PixelFormat::ASTC_2D_5X4: +        return {5, 4}; +    case PixelFormat::ASTC_2D_8X8: +        return {8, 8}; +    case PixelFormat::ASTC_2D_8X5: +        return {8, 5}; +    default: +        LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format)); +        UNREACHABLE(); +    } +} + +void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { +    auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; +    const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)}; + +    addr = cpu_addr ? *cpu_addr : 0; +    gpu_addr = gpu_addr_; +    size_in_bytes = SizeInBytesRaw(); + +    if (IsPixelFormatASTC(pixel_format)) { +        // ASTC is uncompressed in software, in emulated as RGBA8 +        size_in_bytes_gl = width * height * depth * 4; +    } else { +        size_in_bytes_gl = SizeInBytesGL(); +    }  }  /*static*/ SurfaceParams SurfaceParams::CreateForTexture(      const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) {      SurfaceParams params{}; -    params.addr = TryGetCpuAddr(config.tic.Address());      params.is_tiled = config.tic.IsTiled();      params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,      params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, @@ -87,18 +124,18 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {          break;      } -    params.size_in_bytes_total = params.SizeInBytesTotal(); -    params.size_in_bytes_2d = params.SizeInBytes2D();      params.max_mip_level = config.tic.max_mip_level + 1;      params.rt = {}; +    params.InitCacheParameters(config.tic.Address()); +      return params;  }  /*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) {      const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]};      SurfaceParams params{}; -    params.addr = TryGetCpuAddr(config.Address()); +      params.is_tiled =          config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;      params.block_width = 1 << config.memory_layout.block_width; @@ -112,8 +149,6 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {      params.unaligned_height = config.height;      params.target = SurfaceTarget::Texture2D;      params.depth = 1; -    params.size_in_bytes_total = params.SizeInBytesTotal(); -    params.size_in_bytes_2d = params.SizeInBytes2D();      params.max_mip_level = 0;      // Render target specific parameters, not used for caching @@ -122,6 +157,8 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {      params.rt.layer_stride = config.layer_stride;      params.rt.base_layer = config.base_layer; +    params.InitCacheParameters(config.Address()); +      return params;  } @@ -130,7 +167,7 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {      u32 block_width, u32 block_height, u32 block_depth,      Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {      SurfaceParams params{}; -    params.addr = TryGetCpuAddr(zeta_address); +      params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;      params.block_width = 1 << std::min(block_width, 5U);      params.block_height = 1 << std::min(block_height, 5U); @@ -143,18 +180,18 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {      params.unaligned_height = zeta_height;      params.target = SurfaceTarget::Texture2D;      params.depth = 1; -    params.size_in_bytes_total = params.SizeInBytesTotal(); -    params.size_in_bytes_2d = params.SizeInBytes2D();      params.max_mip_level = 0;      params.rt = {}; +    params.InitCacheParameters(zeta_address); +      return params;  }  /*static*/ SurfaceParams SurfaceParams::CreateForFermiCopySurface(      const Tegra::Engines::Fermi2D::Regs::Surface& config) {      SurfaceParams params{}; -    params.addr = TryGetCpuAddr(config.Address()); +      params.is_tiled = !config.linear;      params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,      params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0, @@ -167,11 +204,11 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {      params.unaligned_height = config.height;      params.target = SurfaceTarget::Texture2D;      params.depth = 1; -    params.size_in_bytes_total = params.SizeInBytesTotal(); -    params.size_in_bytes_2d = params.SizeInBytes2D();      params.max_mip_level = 0;      params.rt = {}; +    params.InitCacheParameters(config.Address()); +      return params;  } @@ -231,6 +268,8 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form      {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false},              // RG32UI      {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false},              // R32UI      {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 +    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5 +    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4      // Depth formats      {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F @@ -274,28 +313,6 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType      return format;  } -static bool IsPixelFormatASTC(PixelFormat format) { -    switch (format) { -    case PixelFormat::ASTC_2D_4X4: -    case PixelFormat::ASTC_2D_8X8: -        return true; -    default: -        return false; -    } -} - -static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) { -    switch (format) { -    case PixelFormat::ASTC_2D_4X4: -        return {4, 4}; -    case PixelFormat::ASTC_2D_8X8: -        return {8, 8}; -    default: -        LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format)); -        UNREACHABLE(); -    } -} -  MathUtil::Rectangle<u32> SurfaceParams::GetRect() const {      u32 actual_height{unaligned_height};      if (IsPixelFormatASTC(pixel_format)) { @@ -323,29 +340,27 @@ static bool IsFormatBCn(PixelFormat format) {  }  template <bool morton_to_gl, PixelFormat format> -void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::size_t gl_buffer_size, -                VAddr addr) { -    constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; -    constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); +void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer, +                std::size_t gl_buffer_size, VAddr addr) { +    constexpr u32 bytes_per_pixel = SurfaceParams::GetBytesPerPixel(format); + +    // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual +    // pixel values. +    const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};      if (morton_to_gl) { -        // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual -        // pixel values. -        const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};          const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( -            addr, tile_size, bytes_per_pixel, stride, height, block_height); +            addr, tile_size, bytes_per_pixel, stride, height, depth, block_height, block_depth);          const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())};          memcpy(gl_buffer, data.data(), size_to_copy);      } else { -        // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should -        // check the configuration for this and perform more generic un/swizzle -        LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); -        VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel, -                                       Memory::GetPointer(addr), gl_buffer, morton_to_gl); +        Tegra::Texture::CopySwizzledData(stride / tile_size, height / tile_size, depth, +                                         bytes_per_pixel, bytes_per_pixel, Memory::GetPointer(addr), +                                         gl_buffer, false, block_height, block_depth);      }  } -static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), +static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),                              SurfaceParams::MaxPixelFormat>      morton_to_gl_fns = {          // clang-format off @@ -395,6 +410,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),          MortonCopy<true, PixelFormat::RG32UI>,          MortonCopy<true, PixelFormat::R32UI>,          MortonCopy<true, PixelFormat::ASTC_2D_8X8>, +        MortonCopy<true, PixelFormat::ASTC_2D_8X5>, +        MortonCopy<true, PixelFormat::ASTC_2D_5X4>,          MortonCopy<true, PixelFormat::Z32F>,          MortonCopy<true, PixelFormat::Z16>,          MortonCopy<true, PixelFormat::Z24S8>, @@ -403,7 +420,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),          // clang-format on  }; -static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), +static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr),                              SurfaceParams::MaxPixelFormat>      gl_to_morton_fns = {          // clang-format off @@ -420,17 +437,16 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),          MortonCopy<false, PixelFormat::RGBA16UI>,          MortonCopy<false, PixelFormat::R11FG11FB10F>,          MortonCopy<false, PixelFormat::RGBA32UI>, -        // TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/BC6H_UF16/BC6H_SF16/ASTC_2D_4X4 -        // formats are not supported -        nullptr, -        nullptr, -        nullptr, -        nullptr, -        nullptr, -        nullptr, -        nullptr, -        nullptr, -        nullptr, +        MortonCopy<false, PixelFormat::DXT1>, +        MortonCopy<false, PixelFormat::DXT23>, +        MortonCopy<false, PixelFormat::DXT45>, +        MortonCopy<false, PixelFormat::DXN1>, +        MortonCopy<false, PixelFormat::DXN2UNORM>, +        MortonCopy<false, PixelFormat::DXN2SNORM>, +        MortonCopy<false, PixelFormat::BC7U>, +        MortonCopy<false, PixelFormat::BC6H_UF16>, +        MortonCopy<false, PixelFormat::BC6H_SF16>, +        // TODO(Subv): Swizzling ASTC formats are not supported          nullptr,          MortonCopy<false, PixelFormat::G8R8U>,          MortonCopy<false, PixelFormat::G8R8S>, @@ -455,6 +471,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),          MortonCopy<false, PixelFormat::RG32UI>,          MortonCopy<false, PixelFormat::R32UI>,          nullptr, +        nullptr, +        nullptr,          MortonCopy<false, PixelFormat::Z32F>,          MortonCopy<false, PixelFormat::Z16>,          MortonCopy<false, PixelFormat::Z24S8>, @@ -614,22 +632,21 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,      auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);      auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type); -    std::size_t buffer_size = -        std::max(src_params.size_in_bytes_total, dst_params.size_in_bytes_total); +    std::size_t buffer_size = std::max(src_params.size_in_bytes, dst_params.size_in_bytes);      glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);      glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);      if (source_format.compressed) {          glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment, -                                    static_cast<GLsizei>(src_params.size_in_bytes_total), nullptr); +                                    static_cast<GLsizei>(src_params.size_in_bytes), nullptr);      } else {          glGetTextureImage(src_surface->Texture().handle, src_attachment, source_format.format, -                          source_format.type, static_cast<GLsizei>(src_params.size_in_bytes_total), +                          source_format.type, static_cast<GLsizei>(src_params.size_in_bytes),                            nullptr);      }      // If the new texture is bigger than the previous one, we need to fill in the rest with data      // from the CPU. -    if (src_params.size_in_bytes_total < dst_params.size_in_bytes_total) { +    if (src_params.size_in_bytes < dst_params.size_in_bytes) {          // Upload the rest of the memory.          if (dst_params.is_tiled) {              // TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest @@ -639,12 +656,12 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,              LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "                                "reinterpretation but the texture is tiled.");          } -        std::size_t remaining_size = -            dst_params.size_in_bytes_total - src_params.size_in_bytes_total; +        std::size_t remaining_size = dst_params.size_in_bytes - src_params.size_in_bytes;          std::vector<u8> data(remaining_size); -        Memory::ReadBlock(dst_params.addr + src_params.size_in_bytes_total, data.data(), -                          data.size()); -        glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes_total, remaining_size, +        std::memcpy(data.data(), Memory::GetPointer(dst_params.addr + src_params.size_in_bytes), +                    data.size()); + +        glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes, remaining_size,                          data.data());      } @@ -690,7 +707,8 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface,  }  CachedSurface::CachedSurface(const SurfaceParams& params) -    : params(params), gl_target(SurfaceTargetToGL(params.target)) { +    : params(params), gl_target(SurfaceTargetToGL(params.target)), +      cached_size_in_bytes(params.size_in_bytes) {      texture.Create();      const auto& rect{params.GetRect()}; @@ -740,9 +758,21 @@ CachedSurface::CachedSurface(const SurfaceParams& params)      VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr,                               SurfaceParams::SurfaceTargetName(params.target)); + +    // Clamp size to mapped GPU memory region +    // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000 +    // R32F render buffer. We do not yet know if this is a game bug or something else, but this +    // check is necessary to prevent flushing from overwriting unmapped memory. + +    auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; +    const u64 max_size{memory_manager.GetRegionEnd(params.gpu_addr) - params.gpu_addr}; +    if (cached_size_in_bytes > max_size) { +        LOG_ERROR(HW_GPU, "Surface size {} exceeds region size {}", params.size_in_bytes, max_size); +        cached_size_in_bytes = max_size; +    }  } -static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { +static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bool reverse) {      union S8Z24 {          BitField<0, 24, u32> z24;          BitField<24, 8, u32> s8; @@ -755,22 +785,29 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {      };      static_assert(sizeof(Z24S8) == 4, "Z24S8 is incorrect size"); -    S8Z24 input_pixel{}; -    Z24S8 output_pixel{}; -    constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)}; +    S8Z24 s8z24_pixel{}; +    Z24S8 z24s8_pixel{}; +    constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::S8Z24)};      for (std::size_t y = 0; y < height; ++y) {          for (std::size_t x = 0; x < width; ++x) {              const std::size_t offset{bpp * (y * width + x)}; -            std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24)); -            output_pixel.s8.Assign(input_pixel.s8); -            output_pixel.z24.Assign(input_pixel.z24); -            std::memcpy(&data[offset], &output_pixel, sizeof(Z24S8)); +            if (reverse) { +                std::memcpy(&z24s8_pixel, &data[offset], sizeof(Z24S8)); +                s8z24_pixel.s8.Assign(z24s8_pixel.s8); +                s8z24_pixel.z24.Assign(z24s8_pixel.z24); +                std::memcpy(&data[offset], &s8z24_pixel, sizeof(S8Z24)); +            } else { +                std::memcpy(&s8z24_pixel, &data[offset], sizeof(S8Z24)); +                z24s8_pixel.s8.Assign(s8z24_pixel.s8); +                z24s8_pixel.z24.Assign(s8z24_pixel.z24); +                std::memcpy(&data[offset], &z24s8_pixel, sizeof(Z24S8)); +            }          }      }  }  static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { -    constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)}; +    constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::G8R8U)};      for (std::size_t y = 0; y < height; ++y) {          for (std::size_t x = 0; x < width; ++x) {              const std::size_t offset{bpp * (y * width + x)}; @@ -790,7 +827,9 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma                                                 u32 width, u32 height) {      switch (pixel_format) {      case PixelFormat::ASTC_2D_4X4: -    case PixelFormat::ASTC_2D_8X8: { +    case PixelFormat::ASTC_2D_8X8: +    case PixelFormat::ASTC_2D_8X5: +    case PixelFormat::ASTC_2D_5X4: {          // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC.          u32 block_width{};          u32 block_height{}; @@ -800,7 +839,7 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma      }      case PixelFormat::S8Z24:          // Convert the S8Z24 depth format to Z24S8, as OpenGL does not support S8Z24. -        ConvertS8Z24ToZ24S8(data, width, height); +        ConvertS8Z24ToZ24S8(data, width, height, false);          break;      case PixelFormat::G8R8U: @@ -811,54 +850,54 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma      }  } +/** + * Helper function to perform software conversion (as needed) when flushing a buffer from OpenGL to + * Switch memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or + * with typical desktop GPUs. + */ +static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelFormat pixel_format, +                                                u32 width, u32 height) { +    switch (pixel_format) { +    case PixelFormat::G8R8U: +    case PixelFormat::G8R8S: +    case PixelFormat::ASTC_2D_4X4: +    case PixelFormat::ASTC_2D_8X8: { +        LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented", +                     static_cast<u32>(pixel_format)); +        UNREACHABLE(); +        break; +    } +    case PixelFormat::S8Z24: +        // Convert the Z24S8 depth format to S8Z24, as OpenGL does not support S8Z24. +        ConvertS8Z24ToZ24S8(data, width, height, true); +        break; +    } +} +  MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192));  void CachedSurface::LoadGLBuffer() { -    ASSERT(params.type != SurfaceType::Fill); - -    const u8* const texture_src_data = Memory::GetPointer(params.addr); - -    ASSERT(texture_src_data); - -    const u32 bytes_per_pixel = GetGLBytesPerPixel(params.pixel_format); -    const u32 copy_size = params.width * params.height * bytes_per_pixel; -    const std::size_t total_size = copy_size * params.depth; -      MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); +    gl_buffer.resize(params.size_in_bytes_gl);      if (params.is_tiled) { -        gl_buffer.resize(total_size); +        u32 depth = params.depth; +        u32 block_depth = params.block_depth;          ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}",                     params.block_width, static_cast<u32>(params.target)); -        ASSERT_MSG(params.block_depth == 1, "Block depth is defined as {} on texture type {}", -                   params.block_depth, static_cast<u32>(params.target)); -        // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do -        // this for 3D textures, etc. -        switch (params.target) { -        case SurfaceParams::SurfaceTarget::Texture2D: -            // Pass impl. to the fallback code below -            break; -        case SurfaceParams::SurfaceTarget::Texture2DArray: -        case SurfaceParams::SurfaceTarget::TextureCubemap: -            for (std::size_t index = 0; index < params.depth; ++index) { -                const std::size_t offset{index * copy_size}; -                morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( -                    params.width, params.block_height, params.height, gl_buffer.data() + offset, -                    copy_size, params.addr + offset); -            } -            break; -        default: -            LOG_CRITICAL(HW_GPU, "Unimplemented tiled load for target={}", -                         static_cast<u32>(params.target)); -            UNREACHABLE(); +        if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { +            // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. +            depth = 1U; +            block_depth = 1U;          }          morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( -            params.width, params.block_height, params.height, gl_buffer.data(), copy_size, -            params.addr); +            params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(), +            gl_buffer.size(), params.addr);      } else { -        const u8* const texture_src_data_end{texture_src_data + total_size}; +        const auto texture_src_data{Memory::GetPointer(params.addr)}; +        const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl};          gl_buffer.assign(texture_src_data, texture_src_data_end);      } @@ -867,7 +906,44 @@ void CachedSurface::LoadGLBuffer() {  MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));  void CachedSurface::FlushGLBuffer() { -    ASSERT_MSG(false, "Unimplemented"); +    MICROPROFILE_SCOPE(OpenGL_SurfaceFlush); + +    ASSERT_MSG(!IsPixelFormatASTC(params.pixel_format), "Unimplemented"); + +    // OpenGL temporary buffer needs to be big enough to store raw texture size +    gl_buffer.resize(GetSizeInBytes()); + +    const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); +    // Ensure no bad interactions with GL_UNPACK_ALIGNMENT +    ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0); +    glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width)); +    ASSERT(!tuple.compressed); +    glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); +    glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, gl_buffer.size(), +                      gl_buffer.data()); +    glPixelStorei(GL_PACK_ROW_LENGTH, 0); +    ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width, +                                        params.height); +    ASSERT(params.type != SurfaceType::Fill); +    const u8* const texture_src_data = Memory::GetPointer(params.addr); +    ASSERT(texture_src_data); +    if (params.is_tiled) { +        u32 depth = params.depth; +        u32 block_depth = params.block_depth; + +        ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", +                   params.block_width, static_cast<u32>(params.target)); + +        if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { +            // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. +            depth = 1U; +        } +        gl_to_morton_fns[static_cast<size_t>(params.pixel_format)]( +            params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(), +            gl_buffer.size(), GetAddr()); +    } else { +        std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes()); +    }  }  MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); @@ -877,9 +953,6 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle      MICROPROFILE_SCOPE(OpenGL_TextureUL); -    ASSERT(gl_buffer.size() == static_cast<std::size_t>(params.width) * params.height * -                                   GetGLBytesPerPixel(params.pixel_format) * params.depth); -      const auto& rect{params.GetRect()};      // Load data from memory to the surface @@ -888,7 +961,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle      std::size_t buffer_offset =          static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width +                                   static_cast<std::size_t>(x0)) * -        GetGLBytesPerPixel(params.pixel_format); +        SurfaceParams::GetBytesPerPixel(params.pixel_format);      const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);      const GLuint target_tex = texture.handle; @@ -904,7 +977,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle      cur_state.Apply();      // Ensure no bad interactions with GL_UNPACK_ALIGNMENT -    ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0); +    ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0);      glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width));      glActiveTexture(GL_TEXTURE0); @@ -914,7 +987,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle              glCompressedTexImage2D(                  SurfaceTargetToGL(params.target), 0, tuple.internal_format,                  static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0, -                static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]); +                static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);              break;          case SurfaceParams::SurfaceTarget::Texture3D:          case SurfaceParams::SurfaceTarget::Texture2DArray: @@ -922,16 +995,16 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle                  SurfaceTargetToGL(params.target), 0, tuple.internal_format,                  static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height),                  static_cast<GLsizei>(params.depth), 0, -                static_cast<GLsizei>(params.size_in_bytes_total), &gl_buffer[buffer_offset]); +                static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);              break;          case SurfaceParams::SurfaceTarget::TextureCubemap:              for (std::size_t face = 0; face < params.depth; ++face) {                  glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face),                                         0, tuple.internal_format, static_cast<GLsizei>(params.width),                                         static_cast<GLsizei>(params.height), 0, -                                       static_cast<GLsizei>(params.size_in_bytes_2d), +                                       static_cast<GLsizei>(params.SizeInBytesCubeFaceGL()),                                         &gl_buffer[buffer_offset]); -                buffer_offset += params.size_in_bytes_2d; +                buffer_offset += params.SizeInBytesCubeFace();              }              break;          default: @@ -941,7 +1014,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle              glCompressedTexImage2D(                  GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width),                  static_cast<GLsizei>(params.height), 0, -                static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]); +                static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]);          }      } else { @@ -970,7 +1043,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle                                  y0, static_cast<GLsizei>(rect.GetWidth()),                                  static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,                                  &gl_buffer[buffer_offset]); -                buffer_offset += params.size_in_bytes_2d; +                buffer_offset += params.SizeInBytesCubeFace();              }              break;          default: @@ -1032,10 +1105,7 @@ Surface RasterizerCacheOpenGL::GetColorBufferSurface(std::size_t index, bool pre  void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {      surface->LoadGLBuffer();      surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle); -} - -void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) { -    surface->FlushGLBuffer(); +    surface->MarkAsModified(false, *this);  }  Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool preserve_contents) { @@ -1104,6 +1174,14 @@ void RasterizerCacheOpenGL::FermiCopySurface(      FastCopySurface(GetSurface(src_params, true), GetSurface(dst_params, false));  } +void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface, +                                                const Surface& dst_surface) { +    const auto& src_params{src_surface->GetSurfaceParams()}; +    const auto& dst_params{dst_surface->GetSurfaceParams()}; +    FlushRegion(src_params.addr, dst_params.size_in_bytes); +    LoadSurface(dst_surface); +} +  Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,                                                 const SurfaceParams& new_params) {      // Verify surface is compatible for blitting @@ -1112,6 +1190,12 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,      // Get a new surface with the new parameters, and blit the previous surface to it      Surface new_surface{GetUncachedSurface(new_params)}; +    // With use_accurate_gpu_emulation enabled, do an accurate surface copy +    if (Settings::values.use_accurate_gpu_emulation) { +        AccurateCopySurface(old_surface, new_surface); +        return new_surface; +    } +      // For compatible surfaces, we can just do fast glCopyImageSubData based copy      if (old_params.target == new_params.target && old_params.type == new_params.type &&          old_params.depth == new_params.depth && old_params.depth == 1 && @@ -1123,11 +1207,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,      // If the format is the same, just do a framebuffer blit. This is significantly faster than      // using PBOs. The is also likely less accurate, as textures will be converted rather than -    // reinterpreted. When use_accurate_framebuffers setting is enabled, perform a more accurate +    // reinterpreted. When use_accurate_gpu_emulation setting is enabled, perform a more accurate      // surface copy, where pixels are reinterpreted as a new format (without conversion). This      // code path uses OpenGL PBOs and is quite slow. -    const bool is_blit{old_params.pixel_format == new_params.pixel_format || -                       !Settings::values.use_accurate_framebuffers}; +    const bool is_blit{old_params.pixel_format == new_params.pixel_format};      switch (new_params.target) {      case SurfaceParams::SurfaceTarget::Texture2D: diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 66d98ad4e..7c1cb72d0 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -18,6 +18,7 @@  #include "video_core/rasterizer_cache.h"  #include "video_core/renderer_opengl/gl_resource_manager.h"  #include "video_core/renderer_opengl/gl_shader_gen.h" +#include "video_core/textures/decoders.h"  #include "video_core/textures/texture.h"  namespace OpenGL { @@ -74,19 +75,21 @@ struct SurfaceParams {          RG32UI = 43,          R32UI = 44,          ASTC_2D_8X8 = 45, +        ASTC_2D_8X5 = 46, +        ASTC_2D_5X4 = 47,          MaxColorFormat,          // Depth formats -        Z32F = 46, -        Z16 = 47, +        Z32F = 48, +        Z16 = 49,          MaxDepthFormat,          // DepthStencil formats -        Z24S8 = 48, -        S8Z24 = 49, -        Z32FS8 = 50, +        Z24S8 = 50, +        S8Z24 = 51, +        Z32FS8 = 52,          MaxDepthStencilFormat, @@ -220,6 +223,8 @@ struct SurfaceParams {              1, // RG32UI              1, // R32UI              4, // ASTC_2D_8X8 +            4, // ASTC_2D_8X5 +            4, // ASTC_2D_5X4              1, // Z32F              1, // Z16              1, // Z24S8 @@ -282,6 +287,8 @@ struct SurfaceParams {              64,  // RG32UI              32,  // R32UI              16,  // ASTC_2D_8X8 +            32,  // ASTC_2D_8X5 +            32,  // ASTC_2D_5X4              32,  // Z32F              16,  // Z16              32,  // Z24S8 @@ -553,8 +560,12 @@ struct SurfaceParams {              return PixelFormat::BC6H_SF16;          case Tegra::Texture::TextureFormat::ASTC_2D_4X4:              return PixelFormat::ASTC_2D_4X4; +        case Tegra::Texture::TextureFormat::ASTC_2D_5X4: +            return PixelFormat::ASTC_2D_5X4;          case Tegra::Texture::TextureFormat::ASTC_2D_8X8:              return PixelFormat::ASTC_2D_8X8; +        case Tegra::Texture::TextureFormat::ASTC_2D_8X5: +            return PixelFormat::ASTC_2D_8X5;          case Tegra::Texture::TextureFormat::R16_G16:              switch (component_type) {              case Tegra::Texture::ComponentType::FLOAT: @@ -691,21 +702,42 @@ struct SurfaceParams {          return SurfaceType::Invalid;      } +    /// Returns the sizer in bytes of the specified pixel format +    static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) { +        if (pixel_format == SurfaceParams::PixelFormat::Invalid) { +            return 0; +        } +        return GetFormatBpp(pixel_format) / CHAR_BIT; +    } +      /// Returns the rectangle corresponding to this surface      MathUtil::Rectangle<u32> GetRect() const; -    /// Returns the size of this surface as a 2D texture in bytes, adjusted for compression -    std::size_t SizeInBytes2D() const { +    /// Returns the total size of this surface in bytes, adjusted for compression +    std::size_t SizeInBytesRaw(bool ignore_tiled = false) const {          const u32 compression_factor{GetCompressionFactor(pixel_format)}; -        ASSERT(width % compression_factor == 0); -        ASSERT(height % compression_factor == 0); -        return (width / compression_factor) * (height / compression_factor) * -               GetFormatBpp(pixel_format) / CHAR_BIT; +        const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)}; +        const size_t uncompressed_size{ +            Tegra::Texture::CalculateSize((ignore_tiled ? false : is_tiled), bytes_per_pixel, width, +                                          height, depth, block_height, block_depth)}; + +        // Divide by compression_factor^2, as height and width are factored by this +        return uncompressed_size / (compression_factor * compression_factor);      } -    /// Returns the total size of this surface in bytes, adjusted for compression -    std::size_t SizeInBytesTotal() const { -        return SizeInBytes2D() * depth; +    /// Returns the size of this surface as an OpenGL texture in bytes +    std::size_t SizeInBytesGL() const { +        return SizeInBytesRaw(true); +    } + +    /// Returns the size of this surface as a cube face in bytes +    std::size_t SizeInBytesCubeFace() const { +        return size_in_bytes / 6; +    } + +    /// Returns the size of this surface as an OpenGL cube face in bytes +    std::size_t SizeInBytesCubeFaceGL() const { +        return size_in_bytes_gl / 6;      }      /// Creates SurfaceParams from a texture configuration @@ -732,7 +764,9 @@ struct SurfaceParams {                          other.depth);      } -    VAddr addr; +    /// Initializes parameters for caching, should be called after everything has been initialized +    void InitCacheParameters(Tegra::GPUVAddr gpu_addr); +      bool is_tiled;      u32 block_width;      u32 block_height; @@ -744,11 +778,15 @@ struct SurfaceParams {      u32 height;      u32 depth;      u32 unaligned_height; -    std::size_t size_in_bytes_total; -    std::size_t size_in_bytes_2d;      SurfaceTarget target;      u32 max_mip_level; +    // Parameters used for caching +    VAddr addr; +    Tegra::GPUVAddr gpu_addr; +    std::size_t size_in_bytes; +    std::size_t size_in_bytes_gl; +      // Render target specific parameters, not used in caching      struct {          u32 index; @@ -765,7 +803,8 @@ struct SurfaceReserveKey : Common::HashableStruct<OpenGL::SurfaceParams> {      static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) {          SurfaceReserveKey res;          res.state = params; -        res.state.rt = {}; // Ignore rt config in caching +        res.state.gpu_addr = {}; // Ignore GPU vaddr in caching +        res.state.rt = {};       // Ignore rt config in caching          return res;      }  }; @@ -780,16 +819,20 @@ struct hash<SurfaceReserveKey> {  namespace OpenGL { -class CachedSurface final { +class CachedSurface final : public RasterizerCacheObject {  public:      CachedSurface(const SurfaceParams& params); -    VAddr GetAddr() const { +    VAddr GetAddr() const override {          return params.addr;      } -    std::size_t GetSizeInBytes() const { -        return params.size_in_bytes_total; +    std::size_t GetSizeInBytes() const override { +        return cached_size_in_bytes; +    } + +    void Flush() override { +        FlushGLBuffer();      }      const OGLTexture& Texture() const { @@ -800,13 +843,6 @@ public:          return gl_target;      } -    static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) { -        if (format == SurfaceParams::PixelFormat::Invalid) -            return 0; - -        return SurfaceParams::GetFormatBpp(format) / CHAR_BIT; -    } -      const SurfaceParams& GetSurfaceParams() const {          return params;      } @@ -823,6 +859,7 @@ private:      std::vector<u8> gl_buffer;      SurfaceParams params;      GLenum gl_target; +    std::size_t cached_size_in_bytes;  };  class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { @@ -839,9 +876,6 @@ public:      /// Get the color surface based on the framebuffer configuration and the specified render target      Surface GetColorBufferSurface(std::size_t index, bool preserve_contents); -    /// Flushes the surface to Switch memory -    void FlushSurface(const Surface& surface); -      /// Tries to find a framebuffer using on the provided CPU address      Surface TryFindFramebufferSurface(VAddr addr) const; @@ -865,6 +899,9 @@ private:      /// Tries to get a reserved surface for the specified parameters      Surface TryGetReservedSurface(const SurfaceParams& params); +    /// Performs a slow but accurate surface copy, flushing to RAM and reinterpreting the data +    void AccurateCopySurface(const Surface& src_surface, const Surface& dst_surface); +      /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have      /// previously been used. This is to prevent surfaces from being constantly created and      /// destroyed when used with different surface parameters. diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 7bb287f56..a210f1731 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -19,20 +19,21 @@ class CachedShader;  using Shader = std::shared_ptr<CachedShader>;  using Maxwell = Tegra::Engines::Maxwell3D::Regs; -class CachedShader final { +class CachedShader final : public RasterizerCacheObject {  public:      CachedShader(VAddr addr, Maxwell::ShaderProgram program_type); -    /// Gets the address of the shader in guest memory, required for cache management -    VAddr GetAddr() const { +    VAddr GetAddr() const override {          return addr;      } -    /// Gets the size of the shader in guest memory, required for cache management -    std::size_t GetSizeInBytes() const { +    std::size_t GetSizeInBytes() const override {          return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64);      } +    // We do not have to flush this cache as things in it are never modified by us. +    void Flush() override {} +      /// Gets the shader entries for the shader      const GLShader::ShaderEntries& GetShaderEntries() const {          return entries; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 6220323a0..55c33c3a9 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -1436,7 +1436,6 @@ private:              break;          } -          case OpCode::Type::Shift: {              std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true);              std::string op_b; @@ -1478,7 +1477,6 @@ private:              }              break;          } -          case OpCode::Type::ArithmeticIntegerImmediate: {              std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);              std::string op_b = std::to_string(instr.alu.imm20_32.Value()); @@ -2038,9 +2036,9 @@ private:                  break;              }              case OpCode::Id::TEX: { -                ASSERT_MSG(instr.tex.array == 0, "TEX arrays unimplemented");                  Tegra::Shader::TextureType texture_type{instr.tex.texture_type};                  std::string coord; +                const bool is_array = instr.tex.array != 0;                  ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),                             "NODEP is not implemented"); @@ -2055,21 +2053,59 @@ private:                  switch (num_coordinates) {                  case 1: { -                    const std::string x = regs.GetRegisterAsFloat(instr.gpr8); -                    coord = "float coords = " + x + ';'; +                    if (is_array) { +                        const std::string index = regs.GetRegisterAsInteger(instr.gpr8); +                        const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); +                        coord = "vec2 coords = vec2(" + x + ", " + index + ");"; +                    } else { +                        const std::string x = regs.GetRegisterAsFloat(instr.gpr8); +                        coord = "float coords = " + x + ';'; +                    }                      break;                  }                  case 2: { -                    const std::string x = regs.GetRegisterAsFloat(instr.gpr8); -                    const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); -                    coord = "vec2 coords = vec2(" + x + ", " + y + ");"; +                    if (is_array) { +                        const std::string index = regs.GetRegisterAsInteger(instr.gpr8); +                        const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); +                        const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); +                        coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");"; +                    } else { +                        const std::string x = regs.GetRegisterAsFloat(instr.gpr8); +                        const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); +                        coord = "vec2 coords = vec2(" + x + ", " + y + ");"; +                    }                      break;                  }                  case 3: { -                    const std::string x = regs.GetRegisterAsFloat(instr.gpr8); -                    const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); -                    const std::string z = regs.GetRegisterAsFloat(instr.gpr20); -                    coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; +                    if (depth_compare) { +                        if (is_array) { +                            const std::string index = regs.GetRegisterAsInteger(instr.gpr8); +                            const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); +                            const std::string y = regs.GetRegisterAsFloat(instr.gpr20); +                            const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); +                            coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + +                                    ");"; +                        } else { +                            const std::string x = regs.GetRegisterAsFloat(instr.gpr8); +                            const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); +                            const std::string z = regs.GetRegisterAsFloat(instr.gpr20); +                            coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; +                        } +                    } else { +                        if (is_array) { +                            const std::string index = regs.GetRegisterAsInteger(instr.gpr8); +                            const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); +                            const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); +                            const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 3); +                            coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index + +                                    ");"; +                        } else { +                            const std::string x = regs.GetRegisterAsFloat(instr.gpr8); +                            const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); +                            const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); +                            coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"; +                        } +                    }                      break;                  }                  default: @@ -2088,7 +2124,7 @@ private:                  std::string op_c;                  const std::string sampler = -                    GetSampler(instr.sampler, texture_type, false, depth_compare); +                    GetSampler(instr.sampler, texture_type, is_array, depth_compare);                  // Add an extra scope and declare the texture coords inside to prevent                  // overwriting them in case they are used as outputs of the texs instruction. @@ -2108,10 +2144,13 @@ private:                  }                  case Tegra::Shader::TextureProcessMode::LB:                  case Tegra::Shader::TextureProcessMode::LBA: { -                    if (num_coordinates <= 2) { -                        op_c = regs.GetRegisterAsFloat(instr.gpr20); +                    if (depth_compare) { +                        if (is_array) +                            op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 2); +                        else +                            op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);                      } else { -                        op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1); +                        op_c = regs.GetRegisterAsFloat(instr.gpr20);                      }                      // TODO: Figure if A suffix changes the equation at all.                      texture = "texture(" + sampler + ", coords, " + op_c + ')'; @@ -2630,14 +2669,14 @@ private:                  const std::string pred =                      GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0);                  const std::string combiner = GetPredicateCombiner(instr.csetp.op); -                const std::string controlCode = regs.GetControlCode(instr.csetp.cc); +                const std::string control_code = regs.GetControlCode(instr.csetp.cc);                  if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) {                      SetPredicate(instr.csetp.pred3, -                                 '(' + controlCode + ") " + combiner + " (" + pred + ')'); +                                 '(' + control_code + ") " + combiner + " (" + pred + ')');                  }                  if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {                      SetPredicate(instr.csetp.pred0, -                                 "!(" + controlCode + ") " + combiner + " (" + pred + ')'); +                                 "!(" + control_code + ") " + combiner + " (" + pred + ')');                  }                  break;              } diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 0d2456b56..18ab723f7 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -40,72 +40,146 @@ struct alignas(64) SwizzleTable {  constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>();  constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>(); -static void LegacySwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, -                              u8* swizzled_data, u8* unswizzled_data, bool unswizzle, -                              u32 block_height) { +/** + * This function manages ALL the GOBs(Group of Bytes) Inside a single block. + * Instead of going gob by gob, we map the coordinates inside a block and manage from + * those. Block_Width is assumed to be 1. + */ +void PreciseProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, +                         const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end, +                         const u32 y_end, const u32 z_end, const u32 tile_offset, +                         const u32 xy_block_size, const u32 layer_z, const u32 stride_x, +                         const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {      std::array<u8*, 2> data_ptrs; -    const std::size_t stride = width * bytes_per_pixel; -    const std::size_t gobs_in_x = 64; -    const std::size_t gobs_in_y = 8; -    const std::size_t gobs_size = gobs_in_x * gobs_in_y; -    const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; -    for (std::size_t y = 0; y < height; ++y) { -        const std::size_t gob_y_address = -            (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + -            (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; -        const auto& table = legacy_swizzle_table[y % gobs_in_y]; -        for (std::size_t x = 0; x < width; ++x) { -            const std::size_t gob_address = -                gob_y_address + (x * bytes_per_pixel / gobs_in_x) * gobs_size * block_height; -            const std::size_t x2 = x * bytes_per_pixel; -            const std::size_t swizzle_offset = gob_address + table[x2 % gobs_in_x]; -            const std::size_t pixel_index = (x + y * width) * out_bytes_per_pixel; - -            data_ptrs[unswizzle] = swizzled_data + swizzle_offset; -            data_ptrs[!unswizzle] = unswizzled_data + pixel_index; - -            std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel); +    u32 z_address = tile_offset; +    const u32 gob_size_x = 64; +    const u32 gob_size_y = 8; +    const u32 gob_size_z = 1; +    const u32 gob_size = gob_size_x * gob_size_y * gob_size_z; +    for (u32 z = z_start; z < z_end; z++) { +        u32 y_address = z_address; +        u32 pixel_base = layer_z * z + y_start * stride_x; +        for (u32 y = y_start; y < y_end; y++) { +            const auto& table = legacy_swizzle_table[y % gob_size_y]; +            for (u32 x = x_start; x < x_end; x++) { +                const u32 swizzle_offset{y_address + table[x * bytes_per_pixel % gob_size_x]}; +                const u32 pixel_index{x * out_bytes_per_pixel + pixel_base}; +                data_ptrs[unswizzle] = swizzled_data + swizzle_offset; +                data_ptrs[!unswizzle] = unswizzled_data + pixel_index; +                std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel); +            } +            pixel_base += stride_x; +            if ((y + 1) % gob_size_y == 0) +                y_address += gob_size;          } +        z_address += xy_block_size;      }  } -static void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, -                            u8* swizzled_data, u8* unswizzled_data, bool unswizzle, -                            u32 block_height) { +/** + * This function manages ALL the GOBs(Group of Bytes) Inside a single block. + * Instead of going gob by gob, we map the coordinates inside a block and manage from + * those. Block_Width is assumed to be 1. + */ +void FastProcessBlock(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, +                      const u32 x_start, const u32 y_start, const u32 z_start, const u32 x_end, +                      const u32 y_end, const u32 z_end, const u32 tile_offset, +                      const u32 xy_block_size, const u32 layer_z, const u32 stride_x, +                      const u32 bytes_per_pixel, const u32 out_bytes_per_pixel) {      std::array<u8*, 2> data_ptrs; -    const std::size_t stride{width * bytes_per_pixel}; -    const std::size_t gobs_in_x = 64; -    const std::size_t gobs_in_y = 8; -    const std::size_t gobs_size = gobs_in_x * gobs_in_y; -    const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; -    const std::size_t copy_size{16}; -    for (std::size_t y = 0; y < height; ++y) { -        const std::size_t initial_gob = -            (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + -            (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; -        const std::size_t pixel_base{y * width * out_bytes_per_pixel}; -        const auto& table = fast_swizzle_table[y % gobs_in_y]; -        for (std::size_t xb = 0; xb < stride; xb += copy_size) { -            const std::size_t gob_address{initial_gob + -                                          (xb / gobs_in_x) * gobs_size * block_height}; -            const std::size_t swizzle_offset{gob_address + table[(xb / 16) % 4]}; -            const std::size_t out_x = xb * out_bytes_per_pixel / bytes_per_pixel; -            const std::size_t pixel_index{out_x + pixel_base}; -            data_ptrs[unswizzle] = swizzled_data + swizzle_offset; -            data_ptrs[!unswizzle] = unswizzled_data + pixel_index; -            std::memcpy(data_ptrs[0], data_ptrs[1], copy_size); +    u32 z_address = tile_offset; +    const u32 x_startb = x_start * bytes_per_pixel; +    const u32 x_endb = x_end * bytes_per_pixel; +    const u32 copy_size = 16; +    const u32 gob_size_x = 64; +    const u32 gob_size_y = 8; +    const u32 gob_size_z = 1; +    const u32 gob_size = gob_size_x * gob_size_y * gob_size_z; +    for (u32 z = z_start; z < z_end; z++) { +        u32 y_address = z_address; +        u32 pixel_base = layer_z * z + y_start * stride_x; +        for (u32 y = y_start; y < y_end; y++) { +            const auto& table = fast_swizzle_table[y % gob_size_y]; +            for (u32 xb = x_startb; xb < x_endb; xb += copy_size) { +                const u32 swizzle_offset{y_address + table[(xb / copy_size) % 4]}; +                const u32 out_x = xb * out_bytes_per_pixel / bytes_per_pixel; +                const u32 pixel_index{out_x + pixel_base}; +                data_ptrs[unswizzle] = swizzled_data + swizzle_offset; +                data_ptrs[!unswizzle] = unswizzled_data + pixel_index; +                std::memcpy(data_ptrs[0], data_ptrs[1], copy_size); +            } +            pixel_base += stride_x; +            if ((y + 1) % gob_size_y == 0) +                y_address += gob_size; +        } +        z_address += xy_block_size; +    } +} + +/** + * This function unswizzles or swizzles a texture by mapping Linear to BlockLinear Textue. + * The body of this function takes care of splitting the swizzled texture into blocks, + * and managing the extents of it. Once all the parameters of a single block are obtained, + * the function calls 'ProcessBlock' to process that particular Block. + * + * Documentation for the memory layout and decoding can be found at: + *  https://envytools.readthedocs.io/en/latest/hw/memory/g80-surface.html#blocklinear-surfaces + */ +template <bool fast> +void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, const u32 width, +                  const u32 height, const u32 depth, const u32 bytes_per_pixel, +                  const u32 out_bytes_per_pixel, const u32 block_height, const u32 block_depth) { +    auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); }; +    const u32 stride_x = width * out_bytes_per_pixel; +    const u32 layer_z = height * stride_x; +    const u32 gob_x_bytes = 64; +    const u32 gob_elements_x = gob_x_bytes / bytes_per_pixel; +    const u32 gob_elements_y = 8; +    const u32 gob_elements_z = 1; +    const u32 block_x_elements = gob_elements_x; +    const u32 block_y_elements = gob_elements_y * block_height; +    const u32 block_z_elements = gob_elements_z * block_depth; +    const u32 blocks_on_x = div_ceil(width, block_x_elements); +    const u32 blocks_on_y = div_ceil(height, block_y_elements); +    const u32 blocks_on_z = div_ceil(depth, block_z_elements); +    const u32 blocks = blocks_on_x * blocks_on_y * blocks_on_z; +    const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z; +    const u32 xy_block_size = gob_size * block_height; +    const u32 block_size = xy_block_size * block_depth; +    u32 tile_offset = 0; +    for (u32 zb = 0; zb < blocks_on_z; zb++) { +        const u32 z_start = zb * block_z_elements; +        const u32 z_end = std::min(depth, z_start + block_z_elements); +        for (u32 yb = 0; yb < blocks_on_y; yb++) { +            const u32 y_start = yb * block_y_elements; +            const u32 y_end = std::min(height, y_start + block_y_elements); +            for (u32 xb = 0; xb < blocks_on_x; xb++) { +                const u32 x_start = xb * block_x_elements; +                const u32 x_end = std::min(width, x_start + block_x_elements); +                if (fast) { +                    FastProcessBlock(swizzled_data, unswizzled_data, unswizzle, x_start, y_start, +                                     z_start, x_end, y_end, z_end, tile_offset, xy_block_size, +                                     layer_z, stride_x, bytes_per_pixel, out_bytes_per_pixel); +                } else { +                    PreciseProcessBlock(swizzled_data, unswizzled_data, unswizzle, x_start, y_start, +                                        z_start, x_end, y_end, z_end, tile_offset, xy_block_size, +                                        layer_z, stride_x, bytes_per_pixel, out_bytes_per_pixel); +                } +                tile_offset += block_size; +            }          }      }  } -void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, -                      u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height) { +void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel, +                      u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, +                      bool unswizzle, u32 block_height, u32 block_depth) {      if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) { -        FastSwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, -                        unswizzled_data, unswizzle, block_height); +        SwizzledData<true>(swizzled_data, unswizzled_data, unswizzle, width, height, depth, +                           bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);      } else { -        LegacySwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, -                          unswizzled_data, unswizzle, block_height); +        SwizzledData<false>(swizzled_data, unswizzled_data, unswizzle, width, height, depth, +                            bytes_per_pixel, out_bytes_per_pixel, block_height, block_depth);      }  } @@ -126,7 +200,9 @@ u32 BytesPerPixel(TextureFormat format) {      case TextureFormat::R32_G32_B32:          return 12;      case TextureFormat::ASTC_2D_4X4: +    case TextureFormat::ASTC_2D_5X4:      case TextureFormat::ASTC_2D_8X8: +    case TextureFormat::ASTC_2D_8X5:      case TextureFormat::A8R8G8B8:      case TextureFormat::A2B10G10R10:      case TextureFormat::BF10GF11RF11: @@ -153,10 +229,11 @@ u32 BytesPerPixel(TextureFormat format) {  }  std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, -                                 u32 height, u32 block_height) { -    std::vector<u8> unswizzled_data(width * height * bytes_per_pixel); -    CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel, -                     Memory::GetPointer(address), unswizzled_data.data(), true, block_height); +                                 u32 height, u32 depth, u32 block_height, u32 block_depth) { +    std::vector<u8> unswizzled_data(width * height * depth * bytes_per_pixel); +    CopySwizzledData(width / tile_size, height / tile_size, depth, bytes_per_pixel, bytes_per_pixel, +                     Memory::GetPointer(address), unswizzled_data.data(), true, block_height, +                     block_depth);      return unswizzled_data;  } diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index 234d250af..aaf316947 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h @@ -14,17 +14,14 @@ namespace Tegra::Texture {   * Unswizzles a swizzled texture without changing its format.   */  std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width, -                                 u32 height, u32 block_height = TICEntry::DefaultBlockHeight); - -/** - * Unswizzles a swizzled depth texture without changing its format. - */ -std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height, -                                      u32 block_height = TICEntry::DefaultBlockHeight); +                                 u32 height, u32 depth, +                                 u32 block_height = TICEntry::DefaultBlockHeight, +                                 u32 block_depth = TICEntry::DefaultBlockHeight);  /// Copies texture data from a buffer and performs swizzling/unswizzling as necessary. -void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, -                      u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height); +void CopySwizzledData(u32 width, u32 height, u32 depth, u32 bytes_per_pixel, +                      u32 out_bytes_per_pixel, u8* swizzled_data, u8* unswizzled_data, +                      bool unswizzle, u32 block_height, u32 block_depth);  /**   * Decodes an unswizzled texture into a A8R8G8B8 texture. diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index 58d17abcb..5947bd2b9 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h @@ -141,6 +141,7 @@ static_assert(sizeof(TextureHandle) == 4, "TextureHandle has wrong size");  struct TICEntry {      static constexpr u32 DefaultBlockHeight = 16; +    static constexpr u32 DefaultBlockDepth = 1;      union {          u32 raw; diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp index 033ea1ea4..0a8f2bd9e 100644 --- a/src/web_service/telemetry_json.cpp +++ b/src/web_service/telemetry_json.cpp @@ -2,96 +2,114 @@  // Licensed under GPLv2 or any later version  // Refer to the license.txt file included. -#include <thread> -#include "common/assert.h" +#include <json.hpp>  #include "common/detached_tasks.h" +#include "common/web_result.h"  #include "web_service/telemetry_json.h"  #include "web_service/web_backend.h"  namespace WebService { -TelemetryJson::TelemetryJson(const std::string& host, const std::string& username, -                             const std::string& token) -    : host(std::move(host)), username(std::move(username)), token(std::move(token)) {} -TelemetryJson::~TelemetryJson() = default; +struct TelemetryJson::Impl { +    Impl(std::string host, std::string username, std::string token) +        : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {} -template <class T> -void TelemetryJson::Serialize(Telemetry::FieldType type, const std::string& name, T value) { -    sections[static_cast<u8>(type)][name] = value; -} +    nlohmann::json& TopSection() { +        return sections[static_cast<u8>(Telemetry::FieldType::None)]; +    } -void TelemetryJson::SerializeSection(Telemetry::FieldType type, const std::string& name) { -    TopSection()[name] = sections[static_cast<unsigned>(type)]; -} +    const nlohmann::json& TopSection() const { +        return sections[static_cast<u8>(Telemetry::FieldType::None)]; +    } + +    template <class T> +    void Serialize(Telemetry::FieldType type, const std::string& name, T value) { +        sections[static_cast<u8>(type)][name] = value; +    } + +    void SerializeSection(Telemetry::FieldType type, const std::string& name) { +        TopSection()[name] = sections[static_cast<unsigned>(type)]; +    } + +    nlohmann::json output; +    std::array<nlohmann::json, 7> sections; +    std::string host; +    std::string username; +    std::string token; +}; + +TelemetryJson::TelemetryJson(std::string host, std::string username, std::string token) +    : impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {} +TelemetryJson::~TelemetryJson() = default;  void TelemetryJson::Visit(const Telemetry::Field<bool>& field) { -    Serialize(field.GetType(), field.GetName(), field.GetValue()); +    impl->Serialize(field.GetType(), field.GetName(), field.GetValue());  }  void TelemetryJson::Visit(const Telemetry::Field<double>& field) { -    Serialize(field.GetType(), field.GetName(), field.GetValue()); +    impl->Serialize(field.GetType(), field.GetName(), field.GetValue());  }  void TelemetryJson::Visit(const Telemetry::Field<float>& field) { -    Serialize(field.GetType(), field.GetName(), field.GetValue()); +    impl->Serialize(field.GetType(), field.GetName(), field.GetValue());  }  void TelemetryJson::Visit(const Telemetry::Field<u8>& field) { -    Serialize(field.GetType(), field.GetName(), field.GetValue()); +    impl->Serialize(field.GetType(), field.GetName(), field.GetValue());  }  void TelemetryJson::Visit(const Telemetry::Field<u16>& field) { -    Serialize(field.GetType(), field.GetName(), field.GetValue()); +    impl->Serialize(field.GetType(), field.GetName(), field.GetValue());  }  void TelemetryJson::Visit(const Telemetry::Field<u32>& field) { -    Serialize(field.GetType(), field.GetName(), field.GetValue()); +    impl->Serialize(field.GetType(), field.GetName(), field.GetValue());  }  void TelemetryJson::Visit(const Telemetry::Field<u64>& field) { -    Serialize(field.GetType(), field.GetName(), field.GetValue()); +    impl->Serialize(field.GetType(), field.GetName(), field.GetValue());  }  void TelemetryJson::Visit(const Telemetry::Field<s8>& field) { -    Serialize(field.GetType(), field.GetName(), field.GetValue()); +    impl->Serialize(field.GetType(), field.GetName(), field.GetValue());  }  void TelemetryJson::Visit(const Telemetry::Field<s16>& field) { -    Serialize(field.GetType(), field.GetName(), field.GetValue()); +    impl->Serialize(field.GetType(), field.GetName(), field.GetValue());  }  void TelemetryJson::Visit(const Telemetry::Field<s32>& field) { -    Serialize(field.GetType(), field.GetName(), field.GetValue()); +    impl->Serialize(field.GetType(), field.GetName(), field.GetValue());  }  void TelemetryJson::Visit(const Telemetry::Field<s64>& field) { -    Serialize(field.GetType(), field.GetName(), field.GetValue()); +    impl->Serialize(field.GetType(), field.GetName(), field.GetValue());  }  void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) { -    Serialize(field.GetType(), field.GetName(), field.GetValue()); +    impl->Serialize(field.GetType(), field.GetName(), field.GetValue());  }  void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) { -    Serialize(field.GetType(), field.GetName(), std::string(field.GetValue())); +    impl->Serialize(field.GetType(), field.GetName(), std::string(field.GetValue()));  }  void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) { -    Serialize(field.GetType(), field.GetName(), field.GetValue().count()); +    impl->Serialize(field.GetType(), field.GetName(), field.GetValue().count());  }  void TelemetryJson::Complete() { -    SerializeSection(Telemetry::FieldType::App, "App"); -    SerializeSection(Telemetry::FieldType::Session, "Session"); -    SerializeSection(Telemetry::FieldType::Performance, "Performance"); -    SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); -    SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); -    SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); - -    auto content = TopSection().dump(); +    impl->SerializeSection(Telemetry::FieldType::App, "App"); +    impl->SerializeSection(Telemetry::FieldType::Session, "Session"); +    impl->SerializeSection(Telemetry::FieldType::Performance, "Performance"); +    impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); +    impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); +    impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); + +    auto content = impl->TopSection().dump();      // Send the telemetry async but don't handle the errors since they were written to the log      Common::DetachedTasks::AddTask( -        [host{this->host}, username{this->username}, token{this->token}, content]() { +        [host{impl->host}, username{impl->username}, token{impl->token}, content]() {              Client{host, username, token}.PostJson("/telemetry", content, true);          });  } diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h index 0fe6f9a3e..93371414a 100644 --- a/src/web_service/telemetry_json.h +++ b/src/web_service/telemetry_json.h @@ -4,11 +4,9 @@  #pragma once -#include <array> +#include <chrono>  #include <string> -#include <json.hpp>  #include "common/telemetry.h" -#include "common/web_result.h"  namespace WebService { @@ -18,8 +16,8 @@ namespace WebService {   */  class TelemetryJson : public Telemetry::VisitorInterface {  public: -    TelemetryJson(const std::string& host, const std::string& username, const std::string& token); -    ~TelemetryJson(); +    TelemetryJson(std::string host, std::string username, std::string token); +    ~TelemetryJson() override;      void Visit(const Telemetry::Field<bool>& field) override;      void Visit(const Telemetry::Field<double>& field) override; @@ -39,20 +37,8 @@ public:      void Complete() override;  private: -    nlohmann::json& TopSection() { -        return sections[static_cast<u8>(Telemetry::FieldType::None)]; -    } - -    template <class T> -    void Serialize(Telemetry::FieldType type, const std::string& name, T value); - -    void SerializeSection(Telemetry::FieldType type, const std::string& name); - -    nlohmann::json output; -    std::array<nlohmann::json, 7> sections; -    std::string host; -    std::string username; -    std::string token; +    struct Impl; +    std::unique_ptr<Impl> impl;  };  } // namespace WebService diff --git a/src/web_service/verify_login.cpp b/src/web_service/verify_login.cpp index 124aa3863..ca4b43b93 100644 --- a/src/web_service/verify_login.cpp +++ b/src/web_service/verify_login.cpp @@ -3,6 +3,7 @@  // Refer to the license.txt file included.  #include <json.hpp> +#include "common/web_result.h"  #include "web_service/verify_login.h"  #include "web_service/web_backend.h" diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index 787b0fbcb..b7737b615 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp @@ -3,9 +3,11 @@  // Refer to the license.txt file included.  #include <cstdlib> +#include <mutex>  #include <string> -#include <thread>  #include <LUrlParser.h> +#include <httplib.h> +#include "common/common_types.h"  #include "common/logging/log.h"  #include "common/web_result.h"  #include "core/settings.h" @@ -20,99 +22,132 @@ constexpr u32 HTTPS_PORT = 443;  constexpr u32 TIMEOUT_SECONDS = 30; -Client::JWTCache Client::jwt_cache{}; +struct Client::Impl { +    Impl(std::string host, std::string username, std::string token) +        : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} { +        std::lock_guard<std::mutex> lock(jwt_cache.mutex); +        if (this->username == jwt_cache.username && this->token == jwt_cache.token) { +            jwt = jwt_cache.jwt; +        } +    } + +    /// A generic function handles POST, GET and DELETE request together +    Common::WebResult GenericJson(const std::string& method, const std::string& path, +                                  const std::string& data, bool allow_anonymous) { +        if (jwt.empty()) { +            UpdateJWT(); +        } + +        if (jwt.empty() && !allow_anonymous) { +            LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); +            return Common::WebResult{Common::WebResult::Code::CredentialsMissing, +                                     "Credentials needed"}; +        } + +        auto result = GenericJson(method, path, data, jwt); +        if (result.result_string == "401") { +            // Try again with new JWT +            UpdateJWT(); +            result = GenericJson(method, path, data, jwt); +        } -Client::Client(const std::string& host, const std::string& username, const std::string& token) -    : host(host), username(username), token(token) { -    std::lock_guard<std::mutex> lock(jwt_cache.mutex); -    if (username == jwt_cache.username && token == jwt_cache.token) { -        jwt = jwt_cache.jwt; +        return result;      } -} -Common::WebResult Client::GenericJson(const std::string& method, const std::string& path, -                                      const std::string& data, const std::string& jwt, -                                      const std::string& username, const std::string& token) { -    if (cli == nullptr) { -        auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); -        int port; -        if (parsedUrl.m_Scheme == "http") { -            if (!parsedUrl.GetPort(&port)) { -                port = HTTP_PORT; -            } -            cli = -                std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port, TIMEOUT_SECONDS); -        } else if (parsedUrl.m_Scheme == "https") { -            if (!parsedUrl.GetPort(&port)) { -                port = HTTPS_PORT; +    /** +     * A generic function with explicit authentication method specified +     * JWT is used if the jwt parameter is not empty +     * username + token is used if jwt is empty but username and token are not empty +     * anonymous if all of jwt, username and token are empty +     */ +    Common::WebResult GenericJson(const std::string& method, const std::string& path, +                                  const std::string& data, const std::string& jwt = "", +                                  const std::string& username = "", const std::string& token = "") { +        if (cli == nullptr) { +            auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); +            int port; +            if (parsedUrl.m_Scheme == "http") { +                if (!parsedUrl.GetPort(&port)) { +                    port = HTTP_PORT; +                } +                cli = std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port, +                                                        TIMEOUT_SECONDS); +            } else if (parsedUrl.m_Scheme == "https") { +                if (!parsedUrl.GetPort(&port)) { +                    port = HTTPS_PORT; +                } +                cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port, +                                                           TIMEOUT_SECONDS); +            } else { +                LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme); +                return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"};              } -            cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port, -                                                       TIMEOUT_SECONDS); -        } else { -            LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme); -            return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"};          } -    } -    if (cli == nullptr) { -        LOG_ERROR(WebService, "Invalid URL {}", host + path); -        return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"}; -    } +        if (cli == nullptr) { +            LOG_ERROR(WebService, "Invalid URL {}", host + path); +            return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"}; +        } -    httplib::Headers params; -    if (!jwt.empty()) { -        params = { -            {std::string("Authorization"), fmt::format("Bearer {}", jwt)}, -        }; -    } else if (!username.empty()) { -        params = { -            {std::string("x-username"), username}, -            {std::string("x-token"), token}, +        httplib::Headers params; +        if (!jwt.empty()) { +            params = { +                {std::string("Authorization"), fmt::format("Bearer {}", jwt)}, +            }; +        } else if (!username.empty()) { +            params = { +                {std::string("x-username"), username}, +                {std::string("x-token"), token}, +            }; +        } + +        params.emplace(std::string("api-version"), +                       std::string(API_VERSION.begin(), API_VERSION.end())); +        if (method != "GET") { +            params.emplace(std::string("Content-Type"), std::string("application/json"));          }; -    } -    params.emplace(std::string("api-version"), std::string(API_VERSION.begin(), API_VERSION.end())); -    if (method != "GET") { -        params.emplace(std::string("Content-Type"), std::string("application/json")); -    }; +        httplib::Request request; +        request.method = method; +        request.path = path; +        request.headers = params; +        request.body = data; -    httplib::Request request; -    request.method = method; -    request.path = path; -    request.headers = params; -    request.body = data; +        httplib::Response response; -    httplib::Response response; +        if (!cli->send(request, response)) { +            LOG_ERROR(WebService, "{} to {} returned null", method, host + path); +            return Common::WebResult{Common::WebResult::Code::LibError, "Null response"}; +        } -    if (!cli->send(request, response)) { -        LOG_ERROR(WebService, "{} to {} returned null", method, host + path); -        return Common::WebResult{Common::WebResult::Code::LibError, "Null response"}; -    } +        if (response.status >= 400) { +            LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path, +                      response.status); +            return Common::WebResult{Common::WebResult::Code::HttpError, +                                     std::to_string(response.status)}; +        } -    if (response.status >= 400) { -        LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path, -                  response.status); -        return Common::WebResult{Common::WebResult::Code::HttpError, -                                 std::to_string(response.status)}; -    } +        auto content_type = response.headers.find("content-type"); -    auto content_type = response.headers.find("content-type"); +        if (content_type == response.headers.end()) { +            LOG_ERROR(WebService, "{} to {} returned no content", method, host + path); +            return Common::WebResult{Common::WebResult::Code::WrongContent, ""}; +        } -    if (content_type == response.headers.end()) { -        LOG_ERROR(WebService, "{} to {} returned no content", method, host + path); -        return Common::WebResult{Common::WebResult::Code::WrongContent, ""}; +        if (content_type->second.find("application/json") == std::string::npos && +            content_type->second.find("text/html; charset=utf-8") == std::string::npos) { +            LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path, +                      content_type->second); +            return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"}; +        } +        return Common::WebResult{Common::WebResult::Code::Success, "", response.body};      } -    if (content_type->second.find("application/json") == std::string::npos && -        content_type->second.find("text/html; charset=utf-8") == std::string::npos) { -        LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path, -                  content_type->second); -        return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"}; -    } -    return Common::WebResult{Common::WebResult::Code::Success, "", response.body}; -} +    // Retrieve a new JWT from given username and token +    void UpdateJWT() { +        if (username.empty() || token.empty()) { +            return; +        } -void Client::UpdateJWT() { -    if (!username.empty() && !token.empty()) {          auto result = GenericJson("POST", "/jwt/internal", "", "", username, token);          if (result.result_code != Common::WebResult::Code::Success) {              LOG_ERROR(WebService, "UpdateJWT failed"); @@ -123,27 +158,39 @@ void Client::UpdateJWT() {              jwt_cache.jwt = jwt = result.returned_data;          }      } -} -Common::WebResult Client::GenericJson(const std::string& method, const std::string& path, -                                      const std::string& data, bool allow_anonymous) { -    if (jwt.empty()) { -        UpdateJWT(); -    } +    std::string host; +    std::string username; +    std::string token; +    std::string jwt; +    std::unique_ptr<httplib::Client> cli; + +    struct JWTCache { +        std::mutex mutex; +        std::string username; +        std::string token; +        std::string jwt; +    }; +    static inline JWTCache jwt_cache; +}; -    if (jwt.empty() && !allow_anonymous) { -        LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); -        return Common::WebResult{Common::WebResult::Code::CredentialsMissing, "Credentials needed"}; -    } +Client::Client(std::string host, std::string username, std::string token) +    : impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {} -    auto result = GenericJson(method, path, data, jwt); -    if (result.result_string == "401") { -        // Try again with new JWT -        UpdateJWT(); -        result = GenericJson(method, path, data, jwt); -    } +Client::~Client() = default; + +Common::WebResult Client::PostJson(const std::string& path, const std::string& data, +                                   bool allow_anonymous) { +    return impl->GenericJson("POST", path, data, allow_anonymous); +} + +Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) { +    return impl->GenericJson("GET", path, "", allow_anonymous); +} -    return result; +Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data, +                                     bool allow_anonymous) { +    return impl->GenericJson("DELETE", path, data, allow_anonymous);  }  } // namespace WebService diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h index d75fbcc15..c637e09df 100644 --- a/src/web_service/web_backend.h +++ b/src/web_service/web_backend.h @@ -4,23 +4,19 @@  #pragma once -#include <functional> -#include <mutex> +#include <memory>  #include <string> -#include <tuple> -#include <httplib.h> -#include "common/common_types.h" -#include "common/web_result.h" -namespace httplib { -class Client; +namespace Common { +struct WebResult;  }  namespace WebService {  class Client {  public: -    Client(const std::string& host, const std::string& username, const std::string& token); +    Client(std::string host, std::string username, std::string token); +    ~Client();      /**       * Posts JSON to the specified path. @@ -30,9 +26,7 @@ public:       * @return the result of the request.       */      Common::WebResult PostJson(const std::string& path, const std::string& data, -                               bool allow_anonymous) { -        return GenericJson("POST", path, data, allow_anonymous); -    } +                               bool allow_anonymous);      /**       * Gets JSON from the specified path. @@ -40,9 +34,7 @@ public:       * @param allow_anonymous If true, allow anonymous unauthenticated requests.       * @return the result of the request.       */ -    Common::WebResult GetJson(const std::string& path, bool allow_anonymous) { -        return GenericJson("GET", path, "", allow_anonymous); -    } +    Common::WebResult GetJson(const std::string& path, bool allow_anonymous);      /**       * Deletes JSON to the specified path. @@ -52,41 +44,11 @@ public:       * @return the result of the request.       */      Common::WebResult DeleteJson(const std::string& path, const std::string& data, -                                 bool allow_anonymous) { -        return GenericJson("DELETE", path, data, allow_anonymous); -    } +                                 bool allow_anonymous);  private: -    /// A generic function handles POST, GET and DELETE request together -    Common::WebResult GenericJson(const std::string& method, const std::string& path, -                                  const std::string& data, bool allow_anonymous); - -    /** -     * A generic function with explicit authentication method specified -     * JWT is used if the jwt parameter is not empty -     * username + token is used if jwt is empty but username and token are not empty -     * anonymous if all of jwt, username and token are empty -     */ -    Common::WebResult GenericJson(const std::string& method, const std::string& path, -                                  const std::string& data, const std::string& jwt = "", -                                  const std::string& username = "", const std::string& token = ""); - -    // Retrieve a new JWT from given username and token -    void UpdateJWT(); - -    std::string host; -    std::string username; -    std::string token; -    std::string jwt; -    std::unique_ptr<httplib::Client> cli; - -    struct JWTCache { -        std::mutex mutex; -        std::string username; -        std::string token; -        std::string jwt; -    }; -    static JWTCache jwt_cache; +    struct Impl; +    std::unique_ptr<Impl> impl;  };  } // namespace WebService diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 7fec15991..71c6ebb41 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -85,8 +85,8 @@ void Config::ReadValues() {      Settings::values.resolution_factor = qt_config->value("resolution_factor", 1.0).toFloat();      Settings::values.use_frame_limit = qt_config->value("use_frame_limit", true).toBool();      Settings::values.frame_limit = qt_config->value("frame_limit", 100).toInt(); -    Settings::values.use_accurate_framebuffers = -        qt_config->value("use_accurate_framebuffers", false).toBool(); +    Settings::values.use_accurate_gpu_emulation = +        qt_config->value("use_accurate_gpu_emulation", false).toBool();      Settings::values.bg_red = qt_config->value("bg_red", 0.0).toFloat();      Settings::values.bg_green = qt_config->value("bg_green", 0.0).toFloat(); @@ -233,7 +233,7 @@ void Config::SaveValues() {      qt_config->setValue("resolution_factor", (double)Settings::values.resolution_factor);      qt_config->setValue("use_frame_limit", Settings::values.use_frame_limit);      qt_config->setValue("frame_limit", Settings::values.frame_limit); -    qt_config->setValue("use_accurate_framebuffers", Settings::values.use_accurate_framebuffers); +    qt_config->setValue("use_accurate_gpu_emulation", Settings::values.use_accurate_gpu_emulation);      // Cast to double because Qt's written float values are not human-readable      qt_config->setValue("bg_red", (double)Settings::values.bg_red); diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index cd1549462..8290b4384 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -75,7 +75,7 @@ void ConfigureGraphics::setConfiguration() {          static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));      ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);      ui->frame_limit->setValue(Settings::values.frame_limit); -    ui->use_accurate_framebuffers->setChecked(Settings::values.use_accurate_framebuffers); +    ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation);      bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,                                  Settings::values.bg_blue);      ui->bg_button->setStyleSheet( @@ -87,7 +87,7 @@ void ConfigureGraphics::applyConfiguration() {          ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));      Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();      Settings::values.frame_limit = ui->frame_limit->value(); -    Settings::values.use_accurate_framebuffers = ui->use_accurate_framebuffers->isChecked(); +    Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked();      Settings::values.bg_red = static_cast<float>(bg_color.redF());      Settings::values.bg_green = static_cast<float>(bg_color.greenF());      Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 8fc00af1b..91fcad994 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -50,9 +50,9 @@            </layout>          </item>          <item> -         <widget class="QCheckBox" name="use_accurate_framebuffers"> +         <widget class="QCheckBox" name="use_accurate_gpu_emulation">            <property name="text"> -           <string>Use accurate framebuffers (slow)</string> +           <string>Use accurate GPU emulation (slow)</string>            </property>           </widget>          </item> diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp index cbcd5dd5f..44d423da2 100644 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ b/src/yuzu/debugger/graphics/graphics_surface.cpp @@ -386,8 +386,9 @@ void GraphicsSurfaceWidget::OnUpdate() {      // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.      // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. -    auto unswizzled_data = Tegra::Texture::UnswizzleTexture( -        *address, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height); +    auto unswizzled_data = +        Tegra::Texture::UnswizzleTexture(*address, 1, Tegra::Texture::BytesPerPixel(surface_format), +                                         surface_width, surface_height, 1U);      auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,                                                        surface_width, surface_height); diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 4a09da685..7403e9ccd 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -66,10 +66,11 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList()          }      }; -    add_threads(Core::System::GetInstance().Scheduler(0)->GetThreadList()); -    add_threads(Core::System::GetInstance().Scheduler(1)->GetThreadList()); -    add_threads(Core::System::GetInstance().Scheduler(2)->GetThreadList()); -    add_threads(Core::System::GetInstance().Scheduler(3)->GetThreadList()); +    const auto& system = Core::System::GetInstance(); +    add_threads(system.Scheduler(0).GetThreadList()); +    add_threads(system.Scheduler(1).GetThreadList()); +    add_threads(system.Scheduler(2).GetThreadList()); +    add_threads(system.Scheduler(3).GetThreadList());      return item_list;  } diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 8f99a1c78..3881aba5f 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -96,7 +96,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {                                                            FileSys::ContentRecordType::Program);      for (const auto& game : installed_games) { -        const auto& file = cache->GetEntryUnparsed(game); +        const auto file = cache->GetEntryUnparsed(game);          std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);          if (!loader)              continue; @@ -107,7 +107,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {          loader->ReadProgramId(program_id);          const FileSys::PatchManager patch{program_id}; -        const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); +        const auto control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);          if (control != nullptr)              GetMetadataFromControlNCA(patch, *control, icon, name); @@ -135,9 +135,10 @@ void GameListWorker::AddInstalledTitlesToGameList() {                                                         FileSys::ContentRecordType::Control);      for (const auto& entry : control_data) { -        const auto nca = cache->GetEntry(entry); -        if (nca != nullptr) -            nca_control_map.insert_or_assign(entry.title_id, nca); +        auto nca = cache->GetEntry(entry); +        if (nca != nullptr) { +            nca_control_map.insert_or_assign(entry.title_id, std::move(nca)); +        }      }  } @@ -153,9 +154,11 @@ void GameListWorker::FillControlMap(const std::string& dir_path) {          QFileInfo file_info(physical_name.c_str());          if (!is_dir && file_info.suffix().toStdString() == "nca") {              auto nca = -                std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); -            if (nca->GetType() == FileSys::NCAContentType::Control) -                nca_control_map.insert_or_assign(nca->GetTitleId(), nca); +                std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); +            if (nca->GetType() == FileSys::NCAContentType::Control) { +                const u64 title_id = nca->GetTitleId(); +                nca_control_map.insert_or_assign(title_id, std::move(nca)); +            }          }          return true;      }; diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h index 09d20c42f..0e42d0bde 100644 --- a/src/yuzu/game_list_worker.h +++ b/src/yuzu/game_list_worker.h @@ -63,7 +63,7 @@ private:      void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);      std::shared_ptr<FileSys::VfsFilesystem> vfs; -    std::map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map; +    std::map<u64, std::unique_ptr<FileSys::NCA>> nca_control_map;      QStringList watch_list;      QString dir_path;      bool deep_scan; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index fc186dc2d..bef9df00d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -176,7 +176,7 @@ GMainWindow::GMainWindow()      OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);      // Necessary to load titles from nand in gamelist. -    Service::FileSystem::CreateFactories(vfs); +    Service::FileSystem::CreateFactories(*vfs);      game_list->LoadCompatibilityList();      game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); @@ -908,22 +908,20 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,  }  void GMainWindow::OnMenuLoadFile() { -    QString extensions; -    for (const auto& piece : game_list->supported_file_extensions) -        extensions += "*." + piece + " "; +    const QString extensions = +        QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main"); +    const QString file_filter = tr("Switch Executable (%1);;All Files (*.*)", +                                   "%1 is an identifier for the Switch executable file extensions.") +                                    .arg(extensions); +    const QString filename = QFileDialog::getOpenFileName( +        this, tr("Load File"), UISettings::values.roms_path, file_filter); -    extensions += "main "; - -    QString file_filter = tr("Switch Executable") + " (" + extensions + ")"; -    file_filter += ";;" + tr("All Files (*.*)"); - -    QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), -                                                    UISettings::values.roms_path, file_filter); -    if (!filename.isEmpty()) { -        UISettings::values.roms_path = QFileInfo(filename).path(); - -        BootGame(filename); +    if (filename.isEmpty()) { +        return;      } + +    UISettings::values.roms_path = QFileInfo(filename).path(); +    BootGame(filename);  }  void GMainWindow::OnMenuLoadFolder() { @@ -1139,7 +1137,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)          FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir                                                                        : FileUtil::UserPath::NANDDir,                                dir_path.toStdString()); -        Service::FileSystem::CreateFactories(vfs); +        Service::FileSystem::CreateFactories(*vfs);          game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);      }  } @@ -1410,7 +1408,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {          const auto function = [this, &keys, &pdm] {              keys.PopulateFromPartitionData(pdm); -            Service::FileSystem::CreateFactories(vfs); +            Service::FileSystem::CreateFactories(*vfs);              keys.DeriveETicket(pdm);          }; @@ -1430,8 +1428,12 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {              QMessageBox::warning(                  this, tr("Warning Missing Derivation Components"),                  tr("The following are missing from your configuration that may hinder key " -                   "derivation. It will be attempted but may not complete.\n\n") + -                    errors); +                   "derivation. It will be attempted but may not complete.<br><br>") + +                    errors + +                    tr("<br><br>You can get all of these and dump all of your games easily by " +                       "following <a href='https://yuzu-emu.org/help/quickstart/quickstart/'>the " +                       "quickstart guide</a>. Alternatively, you can use another method of dumping " +                       "to obtain all of your keys."));          }          QProgressDialog prog; @@ -1450,7 +1452,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {          prog.close();      } -    Service::FileSystem::CreateFactories(vfs); +    Service::FileSystem::CreateFactories(*vfs);      if (behavior == ReinitializeKeyBehavior::Warning) {          game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); @@ -1565,7 +1567,7 @@ void GMainWindow::UpdateUITheme() {      emit UpdateThemedIcons();  } -void GMainWindow::SetDiscordEnabled(bool state) { +void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {  #ifdef USE_DISCORD_PRESENCE      if (state) {          discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>(); diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 2470f4640..5e42e48b2 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -99,8 +99,8 @@ void Config::ReadValues() {      Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);      Settings::values.frame_limit =          static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); -    Settings::values.use_accurate_framebuffers = -        sdl2_config->GetBoolean("Renderer", "use_accurate_framebuffers", false); +    Settings::values.use_accurate_gpu_emulation = +        sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);      Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 0.0);      Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 0.0); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 762396e3b..a97b75f7b 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -110,9 +110,9 @@ use_frame_limit =  # 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)  frame_limit = -# Whether to use accurate framebuffers +# Whether to use accurate GPU emulation  # 0 (default): Off (fast), 1 : On (slow) -use_accurate_framebuffers = +use_accurate_gpu_emulation =  # The clear color for the renderer. What shows up on the sides of the bottom screen.  # Must be in range of 0.0-1.0. Defaults to 1.0 for all. diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 27aba95f6..c8b93b85b 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -175,7 +175,7 @@ int main(int argc, char** argv) {      Core::System& system{Core::System::GetInstance()};      system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); -    Service::FileSystem::CreateFactories(system.GetFilesystem()); +    Service::FileSystem::CreateFactories(*system.GetFilesystem());      SCOPE_EXIT({ system.Shutdown(); });  | 
