diff options
Diffstat (limited to 'src')
74 files changed, 1107 insertions, 558 deletions
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp index 392039688..d31a1c844 100644 --- a/src/audio_core/cubeb_sink.cpp +++ b/src/audio_core/cubeb_sink.cpp @@ -121,7 +121,8 @@ CubebSink::CubebSink(std::string target_device_name) { const auto collection_end{collection.device + collection.count}; const auto device{ std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) { - return target_device_name == info.friendly_name; + return info.friendly_name != nullptr && + target_device_name == info.friendly_name; })}; if (device != collection_end) { output_device = device->devid; diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp index d72d67994..cee8b12dd 100644 --- a/src/audio_core/time_stretch.cpp +++ b/src/audio_core/time_stretch.cpp @@ -10,8 +10,7 @@ namespace AudioCore { -TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) - : m_sample_rate(sample_rate), m_channel_count(channel_count) { +TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} { m_sound_touch.setChannels(channel_count); m_sound_touch.setSampleRate(sample_rate); m_sound_touch.setPitch(1.0); diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h index decd760f1..bb2270b96 100644 --- a/src/audio_core/time_stretch.h +++ b/src/audio_core/time_stretch.h @@ -27,7 +27,6 @@ public: private: u32 m_sample_rate; - u32 m_channel_count; soundtouch::SoundTouch m_sound_touch; double m_stretch_ratio = 1.0; }; diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 9f5918851..6d5218465 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -196,6 +196,7 @@ void FileBackend::Write(const Entry& entry) { SUB(Service, NFP) \ SUB(Service, NIFM) \ SUB(Service, NIM) \ + SUB(Service, NPNS) \ SUB(Service, NS) \ SUB(Service, NVDRV) \ SUB(Service, PCIE) \ @@ -204,10 +205,12 @@ void FileBackend::Write(const Entry& entry) { SUB(Service, PM) \ SUB(Service, PREPO) \ SUB(Service, PSC) \ + SUB(Service, PSM) \ SUB(Service, SET) \ SUB(Service, SM) \ SUB(Service, SPL) \ SUB(Service, SSL) \ + SUB(Service, TCAP) \ SUB(Service, Time) \ SUB(Service, USB) \ SUB(Service, VI) \ diff --git a/src/common/logging/log.h b/src/common/logging/log.h index c9161155a..d4ec31ec3 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -83,6 +83,7 @@ enum class Class : ClassType { Service_NFP, ///< The NFP service Service_NIFM, ///< The NIFM (Network interface) service Service_NIM, ///< The NIM service + Service_NPNS, ///< The NPNS service Service_NS, ///< The NS services Service_NVDRV, ///< The NVDRV (Nvidia driver) service Service_PCIE, ///< The PCIe service @@ -96,6 +97,7 @@ enum class Class : ClassType { Service_SM, ///< The SM (Service manager) service Service_SPL, ///< The SPL service Service_SSL, ///< The SSL service + Service_TCAP, ///< The TCAP service. Service_Time, ///< The time service Service_USB, ///< The USB (Universal Serial Bus) service Service_VI, ///< The VI (Video interface) service diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index fd0786068..89ae79eb3 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -395,7 +395,7 @@ static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_ if (base.size() < begin + length) return false; return std::all_of(base.begin() + begin, base.begin() + begin + length, - [](u8 c) { return std::isdigit(c); }); + [](u8 c) { return std::isxdigit(c); }); } void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { @@ -713,7 +713,6 @@ void KeyManager::DeriveBase() { const auto sbk = GetKey(S128KeyType::SecureBoot); const auto tsec = GetKey(S128KeyType::TSEC); - const auto master_source = GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master)); for (size_t i = 0; i < revisions.size(); ++i) { if (!revisions[i]) diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index ecdd7505b..1ece55731 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -168,10 +168,6 @@ VirtualDir XCI::GetParentDirectory() const { return file->GetContainingDirectory(); } -bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - return false; -} - Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { if (partitions[static_cast<std::size_t>(part)] == nullptr) { return Loader::ResultStatus::ErrorXCIMissingPartition; diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index 48cbef666..8f62571cf 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -94,9 +94,6 @@ public: VirtualDir GetParentDirectory() const override; -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: Loader::ResultStatus AddNCAFromPartition(XCIPartition part); diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 6c356d85d..77e04704e 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -546,7 +546,4 @@ u64 NCA::GetBaseIVFCOffset() const { return ivfc_offset; } -bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - return false; -} } // namespace FileSys diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 1c903cd3f..211946686 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -100,9 +100,6 @@ public: // Returns the base ivfc offset used in BKTR patching. u64 GetBaseIVFCOffset() const; -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: bool CheckSupportedNCA(const NCAHeader& header); bool HandlePotentialHeaderDecryption(); diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index 554eae9bc..999939d5a 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp @@ -99,7 +99,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { u16 rle_size{}; if (ips->ReadObject(&rle_size, offset) != sizeof(u16)) return nullptr; - rle_size = Common::swap16(data_size); + rle_size = Common::swap16(rle_size); offset += sizeof(u16); const auto data = ips->ReadByte(offset++); diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index 5791c76ff..a5259a593 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp @@ -83,7 +83,7 @@ std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const { } std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const { - return pfs_dirs; + return {}; } std::string PartitionFilesystem::GetName() const { @@ -103,18 +103,4 @@ void PartitionFilesystem::PrintDebugInfo() const { pfs_files[i]->GetName(), pfs_files[i]->GetSize()); } } - -bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - const auto iter = std::find(pfs_files.begin(), pfs_files.end(), file); - if (iter == pfs_files.end()) - return false; - - const std::ptrdiff_t offset = std::distance(pfs_files.begin(), iter); - pfs_files[offset] = std::move(pfs_files.back()); - pfs_files.pop_back(); - - pfs_dirs.emplace_back(std::move(dir)); - - return true; -} } // namespace FileSys diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h index 739c63a7f..248fdfdeb 100644 --- a/src/core/file_sys/partition_filesystem.h +++ b/src/core/file_sys/partition_filesystem.h @@ -35,9 +35,6 @@ public: std::shared_ptr<VfsDirectory> GetParentDirectory() const override; void PrintDebugInfo() const; -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: struct Header { u32_le magic; @@ -84,7 +81,6 @@ private: std::size_t content_offset = 0; std::vector<VirtualFile> pfs_files; - std::vector<VirtualDir> pfs_dirs; }; } // namespace FileSys diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 1f4928562..cb457b987 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -61,13 +61,12 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { // Game Updates const auto update_tid = GetUpdateTitleID(title_id); const auto update = installed->GetEntry(update_tid, ContentRecordType::Program); - if (update != nullptr) { - if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && - update->GetExeFS() != nullptr) { - LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", - FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); - exefs = update->GetExeFS(); - } + + if (update != nullptr && update->GetExeFS() != nullptr && + update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { + LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", + FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); + exefs = update->GetExeFS(); } return exefs; diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index ab5dc900c..2aaba4179 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -205,10 +205,6 @@ VirtualDir NSP::GetParentDirectory() const { return file->GetContainingDirectory(); } -bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - return false; -} - void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) { exefs = pfs; diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index da3dc5e9f..338080b7e 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h @@ -55,9 +55,6 @@ public: VirtualDir GetParentDirectory() const override; -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files); void ReadNCAs(const std::vector<VirtualFile>& files); diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index bfe50da73..3824c74e0 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -472,10 +472,14 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t blo std::vector<u8> temp(std::min(block_size, src->GetSize())); for (std::size_t i = 0; i < src->GetSize(); i += block_size) { const auto read = std::min(block_size, src->GetSize() - i); - const auto block = src->Read(temp.data(), read, i); - if (dest->Write(temp.data(), read, i) != read) + if (src->Read(temp.data(), read, i) != read) { return false; + } + + if (dest->Write(temp.data(), read, i) != read) { + return false; + } } return true; diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index 7f0d520ca..09dc9f288 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h @@ -262,36 +262,8 @@ public: // item name -> type. virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const; - // Interprets the file with name file instead as a directory of type directory. - // The directory must have a constructor that takes a single argument of type - // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a - // subdirectory in one call. - template <typename Directory> - bool InterpretAsDirectory(std::string_view file) { - auto file_p = GetFile(file); - - if (file_p == nullptr) { - return false; - } - - return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p)); - } - - bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function, - const std::string& file) { - auto file_p = GetFile(file); - if (file_p == nullptr) - return false; - return ReplaceFileWithSubdirectory(file_p, function(file_p)); - } - // Returns the full path of this directory as a string, recursively virtual std::string GetFullPath() const; - -protected: - // Backend for InterpretAsDirectory. - // Removes all references to file and adds a reference to dir in the directory's implementation. - virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0; }; // A convenience partial-implementation of VfsDirectory that stubs out methods that should only work diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp index bfee01725..338e398da 100644 --- a/src/core/file_sys/vfs_layered.cpp +++ b/src/core/file_sys/vfs_layered.cpp @@ -126,7 +126,4 @@ bool LayeredVfsDirectory::Rename(std::string_view name_) { return true; } -bool LayeredVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - return false; -} } // namespace FileSys diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs_layered.h index d85310f57..8a25c3428 100644 --- a/src/core/file_sys/vfs_layered.h +++ b/src/core/file_sys/vfs_layered.h @@ -39,9 +39,6 @@ public: bool DeleteFile(std::string_view name) override; bool Rename(std::string_view name) override; -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: std::vector<VirtualDir> dirs; std::string name; diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 9defad04c..e21300a7c 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -430,7 +430,4 @@ std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() return out; } -bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - return false; -} } // namespace FileSys diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h index 5b61db90d..a0a857a31 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs_real.h @@ -100,9 +100,6 @@ public: std::string GetFullPath() const override; std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override; -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read); diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp index 389c7e003..808f31e81 100644 --- a/src/core/file_sys/vfs_vector.cpp +++ b/src/core/file_sys/vfs_vector.cpp @@ -132,11 +132,4 @@ void VectorVfsDirectory::AddFile(VirtualFile file) { void VectorVfsDirectory::AddDirectory(VirtualDir dir) { dirs.push_back(std::move(dir)); } - -bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - if (!DeleteFile(file->GetName())) - return false; - dirs.emplace_back(std::move(dir)); - return true; -} } // namespace FileSys diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h index 48a414c98..3e3f790c3 100644 --- a/src/core/file_sys/vfs_vector.h +++ b/src/core/file_sys/vfs_vector.h @@ -57,9 +57,6 @@ public: virtual void AddFile(VirtualFile file); virtual void AddDirectory(VirtualDir dir); -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: std::vector<VirtualFile> files; std::vector<VirtualDir> dirs; diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index b2b164368..eec51c64e 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp @@ -163,7 +163,4 @@ std::shared_ptr<VfsDirectory> NAX::GetParentDirectory() const { return file->GetContainingDirectory(); } -bool NAX::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { - return false; -} } // namespace FileSys diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h index 8fedd8585..7704dee90 100644 --- a/src/core/file_sys/xts_archive.h +++ b/src/core/file_sys/xts_archive.h @@ -51,9 +51,6 @@ public: std::shared_ptr<VfsDirectory> GetParentDirectory() const override; -protected: - bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; - private: Loader::ResultStatus Parse(std::string_view path); diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 419f45896..ed84197b3 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -14,11 +14,6 @@ namespace IPC { /// Size of the command buffer area, in 32-bit words. constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); -// These errors are commonly returned by invalid IPC translations, so alias them here for -// convenience. -// TODO(yuriks): These will probably go away once translation is implemented inside the kernel. -constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS; - enum class ControlCommand : u32 { ConvertSessionToDomain = 0, ConvertDomainToSession = 1, diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 885259618..ee698c8a7 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -10,11 +10,6 @@ namespace Kernel { namespace ErrCodes { enum { - // TODO(Subv): Remove these 3DS OS error codes. - SessionClosedByRemote = 26, - NoPendingSessions = 35, - InvalidBufferDescriptor = 48, - // Confirmed Switch OS error codes MaxConnectionsReached = 7, InvalidSize = 101, @@ -26,6 +21,7 @@ enum { InvalidThreadPriority = 112, InvalidProcessorId = 113, InvalidHandle = 114, + InvalidPointer = 115, InvalidCombination = 116, Timeout = 117, SynchronizationCanceled = 118, @@ -33,6 +29,7 @@ enum { InvalidEnumValue = 120, NoSuchEntry = 121, AlreadyRegistered = 122, + SessionClosed = 123, InvalidState = 125, ResourceLimitExceeded = 132, }; @@ -41,18 +38,14 @@ enum { // WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always // double check that the code matches before re-using the constant. -// TODO(bunnei): Replace -1 with correct errors for Switch OS constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); -constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1); +constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed); constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel, ErrCodes::MaxConnectionsReached); constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); -constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1); -constexpr ResultCode ERR_INVALID_COMBINATION(-1); constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel, ErrCodes::InvalidCombination); -constexpr ResultCode ERR_OUT_OF_MEMORY(-1); constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, @@ -65,14 +58,8 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::Alrea constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, ErrCodes::InvalidThreadPriority); -constexpr ResultCode ERR_INVALID_POINTER(-1); -constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1); -constexpr ResultCode ERR_NOT_AUTHORIZED(-1); -/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths. -constexpr ResultCode ERR_INVALID_HANDLE_OS(-1); +constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer); constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry); constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); -/// Returned when Accept() is called on a port with no sessions to be accepted. -constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1); } // namespace Kernel diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 073dd5a7d..420218d59 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -232,6 +232,12 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) { MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); + + // Clear instruction cache in CPU JIT + Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); + Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); } ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 3792e3e18..d6ceeb2da 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -18,7 +18,7 @@ ServerPort::~ServerPort() = default; ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { if (pending_sessions.empty()) { - return ERR_NO_PENDING_SESSIONS; + return ERR_NOT_FOUND; } auto session = std::move(pending_sessions.back()); @@ -28,7 +28,7 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { bool ServerPort::ShouldWait(Thread* thread) const { // If there are no pending sessions, we wait until a new one is added. - return pending_sessions.size() == 0; + return pending_sessions.empty(); } void ServerPort::Acquire(Thread* thread) { diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index d061e6155..a016a86b6 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -80,20 +80,19 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet( ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, MemoryPermission other_permissions) { - - MemoryPermission own_other_permissions = + const MemoryPermission own_other_permissions = target_process == owner_process ? this->permissions : this->other_permissions; // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { - return ERR_INVALID_COMBINATION; + return ERR_INVALID_MEMORY_PERMISSIONS; } // Error out if the requested permissions don't match what the creator process allows. if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", GetObjectId(), address, name); - return ERR_INVALID_COMBINATION; + return ERR_INVALID_MEMORY_PERMISSIONS; } // Error out if the provided permissions are not compatible with what the creator process needs. diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 9a783d524..a5302d924 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -594,16 +594,17 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { } const auto* const current_process = Core::CurrentProcess(); - SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); - if (!thread) { - return ERR_INVALID_HANDLE; - } // Note: The kernel uses the current process's resource limit instead of // the one from the thread owner's resource limit. const ResourceLimit& resource_limit = current_process->GetResourceLimit(); if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { - return ERR_NOT_AUTHORIZED; + return ERR_INVALID_THREAD_PRIORITY; + } + + SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); + if (!thread) { + return ERR_INVALID_HANDLE; } thread->SetPriority(priority); @@ -745,7 +746,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V auto* const current_process = Core::CurrentProcess(); const ResourceLimit& resource_limit = current_process->GetResourceLimit(); if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { - return ERR_NOT_AUTHORIZED; + return ERR_INVALID_THREAD_PRIORITY; } if (processor_id == THREADPROCESSORID_DEFAULT) { diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index e1a34eef1..1a92c8f70 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -143,6 +143,26 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); } +ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const { + // Find the first Free VMA. + const VAddr base = GetASLRRegionBaseAddress(); + const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) { + if (vma.second.type != VMAType::Free) + return false; + + const VAddr vma_end = vma.second.base + vma.second.size; + return vma_end > base && vma_end >= base + size; + }); + + if (vma_handle == vma_map.end()) { + // TODO(Subv): Find the correct error code here. + return ResultCode(-1); + } + + const VAddr target = std::max(base, vma_handle->second.base); + return MakeResult<VAddr>(target); +} + ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state, Memory::MemoryHookPointer mmio_handler) { diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 84c890224..2447cbb8f 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -158,6 +158,14 @@ public: ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); /** + * Finds the first free address that can hold a region of the desired size. + * + * @param size Size of the desired region. + * @return The found free address. + */ + ResultVal<VAddr> FindFreeRegion(u64 size) const; + + /** * Maps a memory-mapped IO region at a given address. * * @param target The guest address to start the mapping at. diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index cf065c2e0..c6437a671 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -21,8 +21,6 @@ namespace Service::Account { -constexpr u32 MAX_JPEG_IMAGE_SIZE = 0x20000; - // TODO: RE this structure struct UserData { INSERT_PADDING_WORDS(1); @@ -34,11 +32,29 @@ struct UserData { }; static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); +// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg +// used as a backup should the one on disk not exist +constexpr u32 backup_jpeg_size = 107; +constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{ + 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, + 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, + 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, + 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, + 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, + 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, + 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, +}}; + static std::string GetImagePath(UUID uuid) { return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; } +static constexpr u32 SanitizeJPEGSize(std::size_t size) { + constexpr std::size_t max_jpeg_image_size = 0x20000; + return static_cast<u32>(std::min(size, max_jpeg_image_size)); +} + class IProfile final : public ServiceFramework<IProfile> { public: explicit IProfile(UUID user_id, ProfileManager& profile_manager) @@ -86,43 +102,29 @@ private: void LoadImage(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); - // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg - // used as a backup should the one on disk not exist - constexpr u32 backup_jpeg_size = 107; - static constexpr std::array<u8, backup_jpeg_size> backup_jpeg{ - 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, - 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, - 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, - 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, - 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00, - 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, - 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, - }; IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); const FileUtil::IOFile image(GetImagePath(user_id), "rb"); - if (!image.IsOpen()) { LOG_WARNING(Service_ACC, "Failed to load user provided image! Falling back to built-in backup..."); ctx.WriteBuffer(backup_jpeg); rb.Push<u32>(backup_jpeg_size); - } else { - const auto size = std::min<u32>(image.GetSize(), MAX_JPEG_IMAGE_SIZE); - std::vector<u8> buffer(size); - image.ReadBytes(buffer.data(), buffer.size()); - - ctx.WriteBuffer(buffer.data(), buffer.size()); - rb.Push<u32>(buffer.size()); + return; } + + const u32 size = SanitizeJPEGSize(image.GetSize()); + std::vector<u8> buffer(size); + image.ReadBytes(buffer.data(), buffer.size()); + + ctx.WriteBuffer(buffer.data(), buffer.size()); + rb.Push<u32>(size); } void GetImageSize(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); - constexpr u32 backup_jpeg_size = 107; IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); @@ -133,7 +135,7 @@ private: "Failed to load user provided image! Falling back to built-in backup..."); rb.Push<u32>(backup_jpeg_size); } else { - rb.Push<u32>(std::min<u32>(image.GetSize(), MAX_JPEG_IMAGE_SIZE)); + rb.Push<u32>(SanitizeJPEGSize(image.GetSize())); } } diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 06f7d1b15..3cac1b4ff 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #include <random> -#include <boost/optional.hpp> + #include "common/file_util.h" #include "core/hle/service/acc/profile_manager.h" #include "core/settings.h" @@ -58,11 +58,11 @@ ProfileManager::~ProfileManager() { /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the /// internal management of the users profiles -boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { +std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) { if (user_count >= MAX_USERS) { - return boost::none; + return {}; } - profiles[user_count] = user; + profiles[user_count] = profile; return user_count++; } @@ -81,7 +81,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) { /// Helper function to register a user to the system ResultCode ProfileManager::AddUser(const ProfileInfo& user) { - if (AddToProfiles(user) == boost::none) { + if (!AddToProfiles(user)) { return ERROR_TOO_MANY_USERS; } return RESULT_SUCCESS; @@ -126,37 +126,40 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) return CreateNewUser(uuid, username_output); } -boost::optional<UUID> ProfileManager::GetUser(std::size_t index) const { - if (index >= MAX_USERS) - return boost::none; +std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { + if (index >= MAX_USERS) { + return {}; + } + return profiles[index].user_uuid; } /// Returns a users profile index based on their user id. -boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { +std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { if (!uuid) { - return boost::none; + return {}; } - auto iter = std::find_if(profiles.begin(), profiles.end(), - [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); + + const auto iter = std::find_if(profiles.begin(), profiles.end(), + [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); if (iter == profiles.end()) { - return boost::none; + return {}; } + return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); } /// Returns a users profile index based on their profile -boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { +std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { return GetUserIndex(user.user_uuid); } /// Returns the data structure used by the switch when GetProfileBase is called on acc:* -bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, - ProfileBase& profile) const { - if (index == boost::none || index >= MAX_USERS) { +bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const { + if (!index || index >= MAX_USERS) { return false; } - const auto& prof_info = profiles[index.get()]; + const auto& prof_info = profiles[*index]; profile.user_uuid = prof_info.user_uuid; profile.username = prof_info.username; profile.timestamp = prof_info.creation_time; @@ -165,7 +168,7 @@ bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, /// Returns the data structure used by the switch when GetProfileBase is called on acc:* bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const { - auto idx = GetUserIndex(uuid); + const auto idx = GetUserIndex(uuid); return GetProfileBase(idx, profile); } @@ -192,7 +195,7 @@ std::size_t ProfileManager::GetOpenUserCount() const { /// Checks if a user id exists in our profile manager bool ProfileManager::UserExists(UUID uuid) const { - return (GetUserIndex(uuid) != boost::none); + return GetUserIndex(uuid) != std::nullopt; } bool ProfileManager::UserExistsIndex(std::size_t index) const { @@ -203,21 +206,23 @@ bool ProfileManager::UserExistsIndex(std::size_t index) const { /// Opens a specific user void ProfileManager::OpenUser(UUID uuid) { - auto idx = GetUserIndex(uuid); - if (idx == boost::none) { + const auto idx = GetUserIndex(uuid); + if (!idx) { return; } - profiles[idx.get()].is_open = true; + + profiles[*idx].is_open = true; last_opened_user = uuid; } /// Closes a specific user void ProfileManager::CloseUser(UUID uuid) { - auto idx = GetUserIndex(uuid); - if (idx == boost::none) { + const auto idx = GetUserIndex(uuid); + if (!idx) { return; } - profiles[idx.get()].is_open = false; + + profiles[*idx].is_open = false; } /// Gets all valid user ids on the system @@ -247,10 +252,10 @@ UUID ProfileManager::GetLastOpenedUser() const { } /// Return the users profile base and the unknown arbitary data. -bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, +bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, ProfileData& data) const { if (GetProfileBase(index, profile)) { - data = profiles[index.get()].data; + data = profiles[*index].data; return true; } return false; @@ -259,7 +264,7 @@ bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, P /// Return the users profile base and the unknown arbitary data. bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const { - auto idx = GetUserIndex(uuid); + const auto idx = GetUserIndex(uuid); return GetProfileBaseAndData(idx, profile, data); } @@ -277,8 +282,8 @@ bool ProfileManager::CanSystemRegisterUser() const { } bool ProfileManager::RemoveUser(UUID uuid) { - auto index = GetUserIndex(uuid); - if (index == boost::none) { + const auto index = GetUserIndex(uuid); + if (!index) { return false; } @@ -289,8 +294,8 @@ bool ProfileManager::RemoveUser(UUID uuid) { } bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { - auto index = GetUserIndex(uuid); - if (profile_new.user_uuid == UUID(INVALID_UUID) || index == boost::none) { + const auto index = GetUserIndex(uuid); + if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) { return false; } diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 235208d56..1cd2e51b2 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -5,8 +5,8 @@ #pragma once #include <array> +#include <optional> -#include "boost/optional.hpp" #include "common/common_types.h" #include "common/swap.h" #include "core/hle/result.h" @@ -96,13 +96,13 @@ public: ResultCode AddUser(const ProfileInfo& user); ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); ResultCode CreateNewUser(UUID uuid, const std::string& username); - boost::optional<UUID> GetUser(std::size_t index) const; - boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const; - boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; - bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const; + std::optional<UUID> GetUser(std::size_t index) const; + std::optional<std::size_t> GetUserIndex(const UUID& uuid) const; + std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; + bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const; bool GetProfileBase(UUID uuid, ProfileBase& profile) const; bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; - bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, + bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, ProfileData& data) const; bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, @@ -120,16 +120,16 @@ public: bool CanSystemRegisterUser() const; bool RemoveUser(UUID uuid); - bool SetProfileBase(UUID uuid, const ProfileBase& profile); + bool SetProfileBase(UUID uuid, const ProfileBase& profile_new); private: void ParseUserSaveFile(); void WriteUserSaveFile(); + std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); + bool RemoveProfileAtIndex(std::size_t index); std::array<ProfileInfo, MAX_USERS> profiles{}; std::size_t user_count = 0; - boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); - bool RemoveProfileAtIndex(std::size_t index); UUID last_opened_user{INVALID_UUID}; }; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 4ed66d817..59aafd616 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -743,7 +743,7 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { Account::ProfileManager profile_manager{}; const auto uuid = profile_manager.GetUser(Settings::values.current_user); - ASSERT(uuid != boost::none); + ASSERT(uuid != std::nullopt); params.current_user = uuid->uuid; IPC::ResponseBuilder rb{ctx, 2, 0, 1}; diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 428069df2..54305cf05 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -24,8 +24,8 @@ namespace Service::AOC { constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000; -static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { - return (aoc & DLC_BASE_TITLE_ID_MASK) == base; +static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) { + return (title_id & DLC_BASE_TITLE_ID_MASK) == base; } static std::vector<u64> AccumulateAOCTitleIDs() { @@ -74,7 +74,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); rb.Push<u32>(static_cast<u32>( std::count_if(add_on_content.begin(), add_on_content.end(), - [¤t](u64 tid) { return (tid & DLC_BASE_TITLE_ID_MASK) == current; }))); + [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }))); } void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index c87721c39..c1c83a11d 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -273,8 +273,8 @@ public: {0, &IFileSystem::CreateFile, "CreateFile"}, {1, &IFileSystem::DeleteFile, "DeleteFile"}, {2, &IFileSystem::CreateDirectory, "CreateDirectory"}, - {3, nullptr, "DeleteDirectory"}, - {4, nullptr, "DeleteDirectoryRecursively"}, + {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"}, + {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"}, {5, &IFileSystem::RenameFile, "RenameFile"}, {6, nullptr, "RenameDirectory"}, {7, &IFileSystem::GetEntryType, "GetEntryType"}, @@ -329,6 +329,30 @@ public: rb.Push(backend.CreateDirectory(name)); } + void DeleteDirectory(Kernel::HLERequestContext& ctx) { + const IPC::RequestParser rp{ctx}; + + const auto file_buffer = ctx.ReadBuffer(); + std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called directory {}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.DeleteDirectory(name)); + } + + void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) { + const IPC::RequestParser rp{ctx}; + + const auto file_buffer = ctx.ReadBuffer(); + std::string name = Common::StringFromBuffer(file_buffer); + + LOG_DEBUG(Service_FS, "called directory {}", name); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(backend.DeleteDirectoryRecursively(name)); + } + void RenameFile(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index b06e65a77..4b4d1324f 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -108,9 +108,10 @@ void Controller_NPad::OnInit() { styleset_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); - if (!IsControllerActivated()) + if (!IsControllerActivated()) { return; - std::size_t controller{}; + } + if (style.raw == 0) { // We want to support all controllers style.handheld.Assign(1); diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index ec32faf15..d607d985e 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -3,9 +3,13 @@ // Refer to the license.txt file included. #include <memory> +#include <fmt/format.h> +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/ldr/ldr.h" #include "core/hle/service/service.h" +#include "core/loader/nro.h" namespace Service::LDR { @@ -59,16 +63,58 @@ public: explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "LoadNro"}, + {0, &RelocatableObject::LoadNro, "LoadNro"}, {1, nullptr, "UnloadNro"}, - {2, nullptr, "LoadNrr"}, + {2, &RelocatableObject::LoadNrr, "LoadNrr"}, {3, nullptr, "UnloadNrr"}, - {4, nullptr, "Initialize"}, + {4, &RelocatableObject::Initialize, "Initialize"}, }; // clang-format on RegisterHandlers(functions); } + + void LoadNrr(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_LDR, "(STUBBED) called"); + } + + void LoadNro(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + rp.Skip(2, false); + const VAddr nro_addr{rp.Pop<VAddr>()}; + const u64 nro_size{rp.Pop<u64>()}; + const VAddr bss_addr{rp.Pop<VAddr>()}; + const u64 bss_size{rp.Pop<u64>()}; + + // Read NRO data from memory + std::vector<u8> nro_data(nro_size); + Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); + + // Load NRO as new executable module + const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)}; + Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr); + + // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party. + // It is currently missing: + // - Signature checks with LoadNRR + // - Checking if a module has already been loaded + // - Using/validating BSS, etc. params (these are used from NRO header instead) + // - Error checking + // - ...Probably other things + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(addr); + LOG_WARNING(Service_LDR, "(STUBBED) called"); + } + + void Initialize(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_LDR, "(STUBBED) called"); + } }; void InstallInterfaces(SM::ServiceManager& sm) { diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 9a4eb9301..c1af878fe 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -328,13 +328,15 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { rb.PushIpcInterface<IUser>(*this); } -void Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { +bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); if (buffer.size() < sizeof(AmiiboFile)) { - return; // Failed to load file + return false; } + std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); nfc_tag_load->Signal(); + return true; } const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const { return nfc_tag_load; diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 46370dedd..5c0ae8a54 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -32,7 +32,7 @@ public: static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); void CreateUserInterface(Kernel::HLERequestContext& ctx); - void LoadAmiibo(const std::vector<u8>& buffer); + bool LoadAmiibo(const std::vector<u8>& buffer); const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const; const AmiiboFile& GetAmiiboBuffer() const; diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp index e7fb5a419..c489da071 100644 --- a/src/core/hle/service/usb/usb.cpp +++ b/src/core/hle/service/usb/usb.cpp @@ -67,15 +67,15 @@ public: explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "Unknown1"}, - {1, nullptr, "Unknown2"}, - {2, nullptr, "Unknown3"}, - {3, nullptr, "Unknown4"}, + {0, nullptr, "Open"}, + {1, nullptr, "Close"}, + {2, nullptr, "Unknown1"}, + {3, nullptr, "Populate"}, {4, nullptr, "PostBufferAsync"}, - {5, nullptr, "Unknown5"}, - {6, nullptr, "Unknown6"}, - {7, nullptr, "Unknown7"}, - {8, nullptr, "Unknown8"}, + {5, nullptr, "GetXferReport"}, + {6, nullptr, "Unknown2"}, + {7, nullptr, "Unknown3"}, + {8, nullptr, "Unknown4"}, }; // clang-format on @@ -89,15 +89,15 @@ public: // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "Unknown1"}, - {1, nullptr, "Unknown2"}, - {2, nullptr, "Unknown3"}, - {3, nullptr, "Unknown4"}, - {4, nullptr, "Unknown5"}, + {1, nullptr, "SetInterface"}, + {2, nullptr, "GetInterface"}, + {3, nullptr, "GetAlternateInterface"}, + {4, nullptr, "GetCurrentFrame"}, {5, nullptr, "CtrlXferAsync"}, - {6, nullptr, "Unknown6"}, + {6, nullptr, "Unknown2"}, {7, nullptr, "GetCtrlXferReport"}, - {8, nullptr, "Unknown7"}, - {9, nullptr, "GetClientEpSession"}, + {8, nullptr, "ResetDevice"}, + {9, nullptr, "OpenUsbEp"}, }; // clang-format on @@ -111,13 +111,14 @@ public: // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "BindClientProcess"}, - {1, nullptr, "Unknown1"}, - {2, nullptr, "Unknown2"}, - {3, nullptr, "Unknown3"}, - {4, nullptr, "Unknown4"}, - {5, nullptr, "Unknown5"}, + {1, nullptr, "QueryAllInterfaces"}, + {2, nullptr, "QueryAvailableInterfaces"}, + {3, nullptr, "QueryAcquiredInterfaces"}, + {4, nullptr, "CreateInterfaceAvailableEvent"}, + {5, nullptr, "DestroyInterfaceAvailableEvent"}, {6, nullptr, "GetInterfaceStateChangeEvent"}, - {7, nullptr, "GetClientIfSession"}, + {7, nullptr, "AcquireUsbIf"}, + {8, nullptr, "Unknown1"}, }; // clang-format on diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 243b499f2..bc8e402a8 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -127,18 +127,23 @@ static constexpr u32 PageAlignSize(u32 size) { return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; } -bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { - // Read NSO header - NroHeader nro_header{}; - if (sizeof(NroHeader) != file.ReadObject(&nro_header)) { +/*static*/ bool AppLoader_NRO::LoadNro(const std::vector<u8>& data, const std::string& name, + VAddr load_base) { + + if (data.size() < sizeof(NroHeader)) { return {}; } + + // Read NSO header + NroHeader nro_header{}; + std::memcpy(&nro_header, data.data(), sizeof(NroHeader)); if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { return {}; } // Build program image - std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size)); + std::vector<u8> program_image(PageAlignSize(nro_header.file_size)); + std::memcpy(program_image.data(), data.data(), program_image.size()); if (program_image.size() != PageAlignSize(nro_header.file_size)) { return {}; } @@ -182,11 +187,15 @@ bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& 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(name, load_base, load_base); return true; } +bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { + return AppLoader_NRO::LoadNro(file.ReadAllBytes(), file.GetName(), load_base); +} + ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { if (is_loaded) { return ResultStatus::ErrorAlreadyLoaded; diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 50ee5a78a..3e6959302 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -5,6 +5,7 @@ #pragma once #include <string> +#include <vector> #include "common/common_types.h" #include "core/loader/linker.h" #include "core/loader/loader.h" @@ -40,6 +41,8 @@ public: ResultStatus ReadTitle(std::string& title) override; bool IsRomFSUpdatable() const override; + static bool LoadNro(const std::vector<u8>& data, const std::string& name, VAddr load_base); + private: bool LoadNro(const FileSys::VfsFile& file, VAddr load_base); diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 13e57848d..080d89904 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -36,6 +36,16 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file) std::tie(nacp_file, icon_file) = FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca); + + if (nsp->IsExtractedType()) { + secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS()); + } else { + if (title_id == 0) + return; + + secondary_loader = std::make_unique<AppLoader_NCA>( + nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program)); + } } AppLoader_NSP::~AppLoader_NSP() = default; @@ -67,26 +77,19 @@ ResultStatus AppLoader_NSP::Load(Kernel::Process& process) { return ResultStatus::ErrorAlreadyLoaded; } - if (nsp->IsExtractedType()) { - secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS()); - } else { - if (title_id == 0) - return ResultStatus::ErrorNSPMissingProgramNCA; - - secondary_loader = std::make_unique<AppLoader_NCA>( - nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program)); + if (title_id == 0) + return ResultStatus::ErrorNSPMissingProgramNCA; - if (nsp->GetStatus() != ResultStatus::Success) - return nsp->GetStatus(); + if (nsp->GetStatus() != ResultStatus::Success) + return nsp->GetStatus(); - if (nsp->GetProgramStatus(title_id) != ResultStatus::Success) - return nsp->GetProgramStatus(title_id); + if (nsp->GetProgramStatus(title_id) != ResultStatus::Success) + return nsp->GetProgramStatus(title_id); - if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) { - if (!Core::Crypto::KeyManager::KeyFileExists(false)) - return ResultStatus::ErrorMissingProductionKeyFile; - return ResultStatus::ErrorNSPMissingProgramNCA; - } + if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) { + if (!Core::Crypto::KeyManager::KeyFileExists(false)) + return ResultStatus::ErrorMissingProductionKeyFile; + return ResultStatus::ErrorNSPMissingProgramNCA; } const auto result = secondary_loader->Load(process); diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 7d95816fe..c716a462b 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -74,10 +74,6 @@ double PerfStats::GetLastFrameTimeScale() { } void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { - // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher - // values increase the time needed to recover and limit framerate again after spikes. - constexpr microseconds MAX_LAG_TIME_US = 25000us; - if (!Settings::values.use_frame_limit) { return; } diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index bca014a4a..27ef865a2 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <cinttypes> +#include <cstring> #include "common/assert.h" #include "core/core.h" #include "core/core_timing.h" @@ -19,7 +20,24 @@ namespace Tegra::Engines { constexpr u32 MacroRegistersStart = 0xE00; Maxwell3D::Maxwell3D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager) - : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) {} + : memory_manager(memory_manager), rasterizer{rasterizer}, macro_interpreter(*this) { + InitializeRegisterDefaults(); +} + +void Maxwell3D::InitializeRegisterDefaults() { + // Initializes registers to their default values - what games expect them to be at boot. This is + // for certain registers that may not be explicitly set by games. + + // Reset all registers to zero + std::memset(®s, 0, sizeof(regs)); + + // Depth range near/far is not always set, but is expected to be the default 0.0f, 1.0f. This is + // needed for ARMS. + for (std::size_t viewport{}; viewport < Regs::NumViewports; ++viewport) { + regs.viewport[viewport].depth_range_near = 0.0f; + regs.viewport[viewport].depth_range_far = 1.0f; + } +} void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) { // Reset the current macro. @@ -155,7 +173,6 @@ void Maxwell3D::ProcessQueryGet() { ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, "Units other than CROP are unimplemented"); - u32 value = Memory::Read32(*address); u64 result = 0; // TODO(Subv): Support the other query variables diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 0e09a7ee5..443affc36 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -723,7 +723,11 @@ public: StencilOp stencil_back_op_zpass; ComparisonOp stencil_back_func_func; - INSERT_PADDING_WORDS(0x17); + INSERT_PADDING_WORDS(0x4); + + u32 framebuffer_srgb; + + INSERT_PADDING_WORDS(0x12); union { BitField<2, 1, u32> coord_origin; @@ -751,7 +755,14 @@ public: }; } draw; - INSERT_PADDING_WORDS(0x6B); + INSERT_PADDING_WORDS(0xA); + + struct { + u32 enabled; + u32 index; + } primitive_restart; + + INSERT_PADDING_WORDS(0x5F); struct { u32 start_addr_high; @@ -984,6 +995,8 @@ public: Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; private: + void InitializeRegisterDefaults(); + VideoCore::RasterizerInterface& rasterizer; std::unordered_map<u32, std::vector<u32>> uploaded_macros; @@ -1077,9 +1090,11 @@ ASSERT_REG_POSITION(stencil_back_op_fail, 0x566); ASSERT_REG_POSITION(stencil_back_op_zfail, 0x567); ASSERT_REG_POSITION(stencil_back_op_zpass, 0x568); ASSERT_REG_POSITION(stencil_back_func_func, 0x569); +ASSERT_REG_POSITION(framebuffer_srgb, 0x56E); ASSERT_REG_POSITION(point_coord_replace, 0x581); ASSERT_REG_POSITION(code_address, 0x582); ASSERT_REG_POSITION(draw, 0x585); +ASSERT_REG_POSITION(primitive_restart, 0x591); ASSERT_REG_POSITION(index_array, 0x5F2); ASSERT_REG_POSITION(instanced_arrays, 0x620); ASSERT_REG_POSITION(cull, 0x646); diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 6cd08d28b..141b9159b 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -79,6 +79,7 @@ union Attribute { constexpr explicit Attribute(u64 value) : value(value) {} enum class Index : u64 { + PointSize = 6, Position = 7, Attribute_0 = 8, Attribute_31 = 39, @@ -207,6 +208,16 @@ enum class UniformType : u64 { Double = 5, }; +enum class StoreType : u64 { + Unsigned8 = 0, + Signed8 = 1, + Unsigned16 = 2, + Signed16 = 3, + Bytes32 = 4, + Bytes64 = 5, + Bytes128 = 6, +}; + enum class IMinMaxExchange : u64 { None = 0, XLo = 1, @@ -747,6 +758,18 @@ union Instruction { } ld_c; union { + BitField<48, 3, StoreType> type; + } ldst_sl; + + union { + BitField<44, 2, u64> unknown; + } ld_l; + + union { + BitField<44, 2, u64> unknown; + } st_l; + + union { BitField<0, 3, u64> pred0; BitField<3, 3, u64> pred3; BitField<7, 1, u64> abs_a; @@ -1208,6 +1231,7 @@ union Instruction { BitField<61, 1, u64> is_b_imm; BitField<60, 1, u64> is_b_gpr; BitField<59, 1, u64> is_c_gpr; + BitField<20, 24, s64> smem_imm; Attribute attribute; Sampler sampler; @@ -1231,8 +1255,12 @@ public: BRA, PBK, LD_A, + LD_L, + LD_S, LD_C, ST_A, + ST_L, + ST_S, LDG, // Load from global memory STG, // Store in global memory TEX, @@ -1489,8 +1517,12 @@ private: INST("111000110100---", Id::BRK, Type::Flow, "BRK"), INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"), INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), + INST("1110111101001---", Id::LD_S, Type::Memory, "LD_S"), + INST("1110111101000---", Id::LD_L, Type::Memory, "LD_L"), INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"), INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), + INST("1110111101011---", Id::ST_S, Type::Memory, "ST_S"), + INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"), INST("1110111011010---", Id::LDG, Type::Memory, "LDG"), INST("1110111011011---", Id::STG, Type::Memory, "STG"), INST("110000----111---", Id::TEX, Type::Memory, "TEX"), diff --git a/src/video_core/engines/shader_header.h b/src/video_core/engines/shader_header.h index a885ee3cf..a0e015c4b 100644 --- a/src/video_core/engines/shader_header.h +++ b/src/video_core/engines/shader_header.h @@ -96,6 +96,11 @@ struct Header { } } ps; }; + + u64 GetLocalMemorySize() { + return (common1.shader_local_memory_low_size | + (common2.shader_local_memory_high_size << 24)); + } }; static_assert(sizeof(Header) == 0x50, "Incorrect structure size"); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index be51c5215..cb180b93c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -418,6 +418,7 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep // Bind the framebuffer surfaces state.draw.draw_framebuffer = framebuffer.handle; state.Apply(); + state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0; if (using_color_fb) { if (single_color_target) { @@ -429,6 +430,9 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep // 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); + // Workaround for and issue in nvidia drivers + // https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/ + state.framebuffer_srgb.enabled |= color_surface->GetSurfaceParams().srgb_conversion; } glFramebufferTexture2D( @@ -446,6 +450,11 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep // 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); + // Enable sRGB only for supported formats + // Workaround for and issue in nvidia drivers + // https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/ + state.framebuffer_srgb.enabled |= + color_surface->GetSurfaceParams().srgb_conversion; } buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); @@ -537,7 +546,9 @@ void RasterizerOpenGL::Clear() { ConfigureFramebuffers(use_color, use_depth || use_stencil, false, regs.clear_buffers.RT.Value()); - + // Copy the sRGB setting to the clear state to avoid problem with + // specific driver implementations + clear_state.framebuffer_srgb.enabled = state.framebuffer_srgb.enabled; clear_state.Apply(); if (use_color) { @@ -570,6 +581,8 @@ void RasterizerOpenGL::DrawArrays() { SyncBlendState(); SyncLogicOpState(); SyncCullMode(); + SyncPrimitiveRestart(); + SyncDepthRange(); SyncScissorTest(); // Alpha Testing is synced on shaders. SyncTransformFeedback(); @@ -923,12 +936,18 @@ void RasterizerOpenGL::SyncCullMode() { } } -void RasterizerOpenGL::SyncDepthScale() { - UNREACHABLE(); +void RasterizerOpenGL::SyncPrimitiveRestart() { + const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; + + state.primitive_restart.enabled = regs.primitive_restart.enabled; + state.primitive_restart.index = regs.primitive_restart.index; } -void RasterizerOpenGL::SyncDepthOffset() { - UNREACHABLE(); +void RasterizerOpenGL::SyncDepthRange() { + const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; + + state.depth.depth_range_near = regs.viewport->depth_range_near; + state.depth.depth_range_far = regs.viewport->depth_range_far; } void RasterizerOpenGL::SyncDepthTestState() { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 0e90a31f5..5020a5392 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -144,11 +144,11 @@ private: /// Syncs the cull mode to match the guest state void SyncCullMode(); - /// Syncs the depth scale to match the guest state - void SyncDepthScale(); + /// Syncs the primitve restart to match the guest state + void SyncPrimitiveRestart(); - /// Syncs the depth offset to match the guest state - void SyncDepthOffset(); + /// Syncs the depth range to match the guest state + void SyncDepthRange(); /// Syncs the depth test state to match the guest state void SyncDepthTestState(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 9c8925383..b057e2efa 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -40,6 +40,10 @@ static bool IsPixelFormatASTC(PixelFormat format) { case PixelFormat::ASTC_2D_5X4: case PixelFormat::ASTC_2D_8X8: case PixelFormat::ASTC_2D_8X5: + case PixelFormat::ASTC_2D_4X4_SRGB: + case PixelFormat::ASTC_2D_5X4_SRGB: + case PixelFormat::ASTC_2D_8X8_SRGB: + case PixelFormat::ASTC_2D_8X5_SRGB: return true; default: return false; @@ -56,6 +60,14 @@ static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) { return {8, 8}; case PixelFormat::ASTC_2D_8X5: return {8, 5}; + case PixelFormat::ASTC_2D_4X4_SRGB: + return {4, 4}; + case PixelFormat::ASTC_2D_5X4_SRGB: + return {5, 4}; + case PixelFormat::ASTC_2D_8X8_SRGB: + return {8, 8}; + case PixelFormat::ASTC_2D_8X5_SRGB: + return {8, 5}; default: LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format)); UNREACHABLE(); @@ -78,6 +90,29 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { } } +std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { + const u32 compression_factor{GetCompressionFactor(pixel_format)}; + const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)}; + u32 m_depth = (layer_only ? 1U : depth); + u32 m_width = std::max(1U, width / compression_factor); + u32 m_height = std::max(1U, height / compression_factor); + std::size_t size = Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, + m_depth, block_height, block_depth); + u32 m_block_height = block_height; + u32 m_block_depth = block_depth; + std::size_t block_size_bytes = 512 * block_height * block_depth; // 512 is GOB size + for (u32 i = 1; i < max_mip_level; i++) { + m_width = std::max(1U, m_width / 2); + m_height = std::max(1U, m_height / 2); + m_depth = std::max(1U, m_depth / 2); + m_block_height = std::max(1U, m_block_height / 2); + m_block_depth = std::max(1U, m_block_depth / 2); + size += Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, m_depth, + m_block_height, m_block_depth); + } + return is_tiled ? Common::AlignUp(size, block_size_bytes) : size; +} + /*static*/ SurfaceParams SurfaceParams::CreateForTexture( const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { SurfaceParams params{}; @@ -85,8 +120,9 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0, params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0, - params.pixel_format = - PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value()); + params.srgb_conversion = config.tic.IsSrgbConversionEnabled(); + params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), + params.srgb_conversion); params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); params.type = GetFormatType(params.pixel_format); params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); @@ -124,6 +160,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { break; } + params.is_layered = SurfaceTargetIsLayered(params.target); params.max_mip_level = config.tic.max_mip_level + 1; params.rt = {}; @@ -142,6 +179,8 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { params.block_height = 1 << config.memory_layout.block_height; params.block_depth = 1 << config.memory_layout.block_depth; params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); + params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB || + config.format == Tegra::RenderTargetFormat::RGBA8_SRGB; params.component_type = ComponentTypeFromRenderTarget(config.format); params.type = GetFormatType(params.pixel_format); params.width = config.width; @@ -150,6 +189,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { params.target = SurfaceTarget::Texture2D; params.depth = 1; params.max_mip_level = 0; + params.is_layered = false; // Render target specific parameters, not used for caching params.rt.index = static_cast<u32>(index); @@ -176,12 +216,14 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { params.pixel_format = PixelFormatFromDepthFormat(format); params.component_type = ComponentTypeFromDepthFormat(format); params.type = GetFormatType(params.pixel_format); + params.srgb_conversion = false; params.width = zeta_width; params.height = zeta_height; params.unaligned_height = zeta_height; params.target = SurfaceTarget::Texture2D; params.depth = 1; params.max_mip_level = 0; + params.is_layered = false; params.rt = {}; params.InitCacheParameters(zeta_address); @@ -198,6 +240,8 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0, params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0, params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); + params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB || + config.format == Tegra::RenderTargetFormat::RGBA8_SRGB; params.component_type = ComponentTypeFromRenderTarget(config.format); params.type = GetFormatType(params.pixel_format); params.width = config.width; @@ -263,14 +307,29 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form {GL_RG16I, GL_RG_INTEGER, GL_SHORT, ComponentType::SInt, false}, // RG16I {GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S {GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F - {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // SRGBA8 - {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U - {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S - {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 + {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, + false}, // RGBA8_SRGB + {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U + {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S + {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 + {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8 + // Compressed sRGB formats + {GL_COMPRESSED_SRGB_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, + true}, // DXT1_SRGB + {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, + true}, // DXT23_SRGB + {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, + true}, // DXT45_SRGB + {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, + ComponentType::UNorm, true}, // BC7U_SRGB + {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4_SRGB + {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8_SRGB + {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5_SRGB + {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4_SRGB // Depth formats {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F @@ -335,6 +394,10 @@ static bool IsFormatBCn(PixelFormat format) { case PixelFormat::BC7U: case PixelFormat::BC6H_UF16: case PixelFormat::BC6H_SF16: + case PixelFormat::DXT1_SRGB: + case PixelFormat::DXT23_SRGB: + case PixelFormat::DXT45_SRGB: + case PixelFormat::BC7U_SRGB: return true; } return false; @@ -361,10 +424,11 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d } } -static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), - SurfaceParams::MaxPixelFormat> - morton_to_gl_fns = { - // clang-format off +using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), + SurfaceParams::MaxPixelFormat>; + +static constexpr GLConversionArray morton_to_gl_fns = { + // clang-format off MortonCopy<true, PixelFormat::ABGR8U>, MortonCopy<true, PixelFormat::ABGR8S>, MortonCopy<true, PixelFormat::ABGR8UI>, @@ -405,7 +469,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, MortonCopy<true, PixelFormat::RG16I>, MortonCopy<true, PixelFormat::RG16S>, MortonCopy<true, PixelFormat::RGB32F>, - MortonCopy<true, PixelFormat::SRGBA8>, + MortonCopy<true, PixelFormat::RGBA8_SRGB>, MortonCopy<true, PixelFormat::RG8U>, MortonCopy<true, PixelFormat::RG8S>, MortonCopy<true, PixelFormat::RG32UI>, @@ -413,18 +477,25 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, MortonCopy<true, PixelFormat::ASTC_2D_8X8>, MortonCopy<true, PixelFormat::ASTC_2D_8X5>, MortonCopy<true, PixelFormat::ASTC_2D_5X4>, + MortonCopy<true, PixelFormat::BGRA8_SRGB>, + MortonCopy<true, PixelFormat::DXT1_SRGB>, + MortonCopy<true, PixelFormat::DXT23_SRGB>, + MortonCopy<true, PixelFormat::DXT45_SRGB>, + MortonCopy<true, PixelFormat::BC7U_SRGB>, + MortonCopy<true, PixelFormat::ASTC_2D_4X4_SRGB>, + MortonCopy<true, PixelFormat::ASTC_2D_8X8_SRGB>, + MortonCopy<true, PixelFormat::ASTC_2D_8X5_SRGB>, + MortonCopy<true, PixelFormat::ASTC_2D_5X4_SRGB>, MortonCopy<true, PixelFormat::Z32F>, MortonCopy<true, PixelFormat::Z16>, MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>, MortonCopy<true, PixelFormat::Z32FS8>, - // clang-format on + // clang-format on }; -static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), - SurfaceParams::MaxPixelFormat> - gl_to_morton_fns = { - // clang-format off +static constexpr GLConversionArray gl_to_morton_fns = { + // clang-format off MortonCopy<false, PixelFormat::ABGR8U>, MortonCopy<false, PixelFormat::ABGR8S>, MortonCopy<false, PixelFormat::ABGR8UI>, @@ -466,7 +537,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, MortonCopy<false, PixelFormat::RG16I>, MortonCopy<false, PixelFormat::RG16S>, MortonCopy<false, PixelFormat::RGB32F>, - MortonCopy<false, PixelFormat::SRGBA8>, + MortonCopy<false, PixelFormat::RGBA8_SRGB>, MortonCopy<false, PixelFormat::RG8U>, MortonCopy<false, PixelFormat::RG8S>, MortonCopy<false, PixelFormat::RG32UI>, @@ -474,14 +545,49 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, nullptr, nullptr, nullptr, + MortonCopy<false, PixelFormat::BGRA8_SRGB>, + MortonCopy<false, PixelFormat::DXT1_SRGB>, + MortonCopy<false, PixelFormat::DXT23_SRGB>, + MortonCopy<false, PixelFormat::DXT45_SRGB>, + MortonCopy<false, PixelFormat::BC7U_SRGB>, + nullptr, + nullptr, + nullptr, + nullptr, MortonCopy<false, PixelFormat::Z32F>, MortonCopy<false, PixelFormat::Z16>, MortonCopy<false, PixelFormat::Z24S8>, MortonCopy<false, PixelFormat::S8Z24>, MortonCopy<false, PixelFormat::Z32FS8>, - // clang-format on + // clang-format on }; +void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params, + std::vector<u8>& gl_buffer) { + u32 depth = params.depth; + if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { + // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. + depth = 1U; + } + if (params.is_layered) { + u64 offset = 0; + u64 offset_gl = 0; + u64 layer_size = params.LayerMemorySize(); + u64 gl_size = params.LayerSizeGL(); + for (u32 i = 0; i < depth; i++) { + functions[static_cast<std::size_t>(params.pixel_format)]( + params.width, params.block_height, params.height, params.block_depth, 1, + gl_buffer.data() + offset_gl, gl_size, params.addr + offset); + offset += layer_size; + offset_gl += gl_size; + } + } else { + functions[static_cast<std::size_t>(params.pixel_format)]( + params.width, params.block_height, params.height, params.block_depth, depth, + gl_buffer.data(), gl_buffer.size(), params.addr); + } +} + static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0, GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { @@ -495,6 +601,8 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, OpenGLState state; state.draw.read_framebuffer = read_fb_handle; state.draw.draw_framebuffer = draw_fb_handle; + // Set sRGB enabled if the destination surfaces need it + state.framebuffer_srgb.enabled = dst_params.srgb_conversion; state.Apply(); u32 buffers{}; @@ -830,7 +938,11 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma case PixelFormat::ASTC_2D_4X4: case PixelFormat::ASTC_2D_8X8: case PixelFormat::ASTC_2D_8X5: - case PixelFormat::ASTC_2D_5X4: { + case PixelFormat::ASTC_2D_5X4: + case PixelFormat::ASTC_2D_4X4_SRGB: + case PixelFormat::ASTC_2D_8X8_SRGB: + case PixelFormat::ASTC_2D_8X5_SRGB: + case PixelFormat::ASTC_2D_5X4_SRGB: { // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. u32 block_width{}; u32 block_height{}; @@ -862,7 +974,9 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm case PixelFormat::G8R8U: case PixelFormat::G8R8S: case PixelFormat::ASTC_2D_4X4: - case PixelFormat::ASTC_2D_8X8: { + case PixelFormat::ASTC_2D_8X8: + case PixelFormat::ASTC_2D_4X4_SRGB: + case PixelFormat::ASTC_2D_8X8_SRGB: { LOG_CRITICAL(HW_GPU, "Conversion of format {} after texture flushing is not implemented", static_cast<u32>(pixel_format)); UNREACHABLE(); @@ -881,21 +995,10 @@ void CachedSurface::LoadGLBuffer() { gl_buffer.resize(params.size_in_bytes_gl); 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; - block_depth = 1U; - } - - morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( - params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(), - gl_buffer.size(), params.addr); + SwizzleFunc(morton_to_gl_fns, params, gl_buffer); } else { const auto texture_src_data{Memory::GetPointer(params.addr)}; const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; @@ -920,8 +1023,8 @@ void CachedSurface::FlushGLBuffer() { 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()); + glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, + static_cast<GLsizei>(gl_buffer.size()), gl_buffer.data()); glPixelStorei(GL_PACK_ROW_LENGTH, 0); ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width, params.height); @@ -929,19 +1032,10 @@ void CachedSurface::FlushGLBuffer() { 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()); + SwizzleFunc(gl_to_morton_fns, params, gl_buffer); } else { std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes()); } @@ -1179,7 +1273,7 @@ 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); + FlushRegion(src_params.addr, dst_params.MemorySize()); LoadSurface(dst_surface); } @@ -1221,44 +1315,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, CopySurface(old_surface, new_surface, copy_pbo.handle); } break; + case SurfaceParams::SurfaceTarget::TextureCubemap: case SurfaceParams::SurfaceTarget::Texture3D: AccurateCopySurface(old_surface, new_surface); break; - case SurfaceParams::SurfaceTarget::TextureCubemap: { - if (old_params.rt.array_mode != 1) { - // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this - // yet (array rendering used as a cubemap texture). - LOG_CRITICAL(HW_GPU, "Unhandled rendertarget array_mode {}", old_params.rt.array_mode); - UNREACHABLE(); - return new_surface; - } - - // This seems to be used for render-to-cubemap texture - ASSERT_MSG(old_params.target == SurfaceParams::SurfaceTarget::Texture2D, "Unexpected"); - ASSERT_MSG(old_params.pixel_format == new_params.pixel_format, "Unexpected"); - ASSERT_MSG(old_params.rt.base_layer == 0, "Unimplemented"); - - // TODO(bunnei): Verify the below - this stride seems to be in 32-bit words, not pixels. - // Tested with Splatoon 2, Super Mario Odyssey, and Breath of the Wild. - const std::size_t byte_stride{old_params.rt.layer_stride * sizeof(u32)}; - - for (std::size_t index = 0; index < new_params.depth; ++index) { - Surface face_surface{TryGetReservedSurface(old_params)}; - ASSERT_MSG(face_surface, "Unexpected"); - - if (is_blit) { - BlitSurface(face_surface, new_surface, read_framebuffer.handle, - draw_framebuffer.handle, face_surface->GetSurfaceParams().rt.index, - new_params.rt.index, index); - } else { - CopySurface(face_surface, new_surface, copy_pbo.handle, - face_surface->GetSurfaceParams().rt.index, new_params.rt.index, index); - } - - old_params.addr += byte_stride; - } - break; - } default: LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", static_cast<u32>(new_params.target)); @@ -1266,7 +1326,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, } return new_surface; -} +} // namespace OpenGL Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const { return TryGet(addr); diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 0dd0d90a3..b4701a616 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -69,7 +69,7 @@ struct SurfaceParams { RG16I = 37, RG16S = 38, RGB32F = 39, - SRGBA8 = 40, + RGBA8_SRGB = 40, RG8U = 41, RG8S = 42, RG32UI = 43, @@ -77,19 +77,28 @@ struct SurfaceParams { ASTC_2D_8X8 = 45, ASTC_2D_8X5 = 46, ASTC_2D_5X4 = 47, + BGRA8_SRGB = 48, + DXT1_SRGB = 49, + DXT23_SRGB = 50, + DXT45_SRGB = 51, + BC7U_SRGB = 52, + ASTC_2D_4X4_SRGB = 53, + ASTC_2D_8X8_SRGB = 54, + ASTC_2D_8X5_SRGB = 55, + ASTC_2D_5X4_SRGB = 56, MaxColorFormat, // Depth formats - Z32F = 48, - Z16 = 49, + Z32F = 57, + Z16 = 58, MaxDepthFormat, // DepthStencil formats - Z24S8 = 50, - S8Z24 = 51, - Z32FS8 = 52, + Z24S8 = 59, + S8Z24 = 60, + Z32FS8 = 61, MaxDepthStencilFormat, @@ -168,6 +177,23 @@ struct SurfaceParams { } } + static bool SurfaceTargetIsLayered(SurfaceTarget target) { + switch (target) { + case SurfaceTarget::Texture1D: + case SurfaceTarget::Texture2D: + case SurfaceTarget::Texture3D: + return false; + case SurfaceTarget::Texture1DArray: + case SurfaceTarget::Texture2DArray: + case SurfaceTarget::TextureCubemap: + return true; + default: + LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target)); + UNREACHABLE(); + return false; + } + } + /** * Gets the compression factor for the specified PixelFormat. This applies to just the * "compressed width" and "compressed height", not the overall compression factor of a @@ -219,7 +245,7 @@ struct SurfaceParams { 1, // RG16I 1, // RG16S 1, // RGB32F - 1, // SRGBA8 + 1, // RGBA8_SRGB 1, // RG8U 1, // RG8S 1, // RG32UI @@ -227,6 +253,15 @@ struct SurfaceParams { 4, // ASTC_2D_8X8 4, // ASTC_2D_8X5 4, // ASTC_2D_5X4 + 1, // BGRA8_SRGB + 4, // DXT1_SRGB + 4, // DXT23_SRGB + 4, // DXT45_SRGB + 4, // BC7U_SRGB + 4, // ASTC_2D_4X4_SRGB + 4, // ASTC_2D_8X8_SRGB + 4, // ASTC_2D_8X5_SRGB + 4, // ASTC_2D_5X4_SRGB 1, // Z32F 1, // Z16 1, // Z24S8 @@ -238,6 +273,77 @@ struct SurfaceParams { return compression_factor_table[static_cast<std::size_t>(format)]; } + static constexpr u32 GetDefaultBlockHeight(PixelFormat format) { + if (format == PixelFormat::Invalid) + return 0; + constexpr std::array<u32, MaxPixelFormat> block_height_table = {{ + 1, // ABGR8U + 1, // ABGR8S + 1, // ABGR8UI + 1, // B5G6R5U + 1, // A2B10G10R10U + 1, // A1B5G5R5U + 1, // R8U + 1, // R8UI + 1, // RGBA16F + 1, // RGBA16U + 1, // RGBA16UI + 1, // R11FG11FB10F + 1, // RGBA32UI + 4, // DXT1 + 4, // DXT23 + 4, // DXT45 + 4, // DXN1 + 4, // DXN2UNORM + 4, // DXN2SNORM + 4, // BC7U + 4, // BC6H_UF16 + 4, // BC6H_SF16 + 4, // ASTC_2D_4X4 + 1, // G8R8U + 1, // G8R8S + 1, // BGRA8 + 1, // RGBA32F + 1, // RG32F + 1, // R32F + 1, // R16F + 1, // R16U + 1, // R16S + 1, // R16UI + 1, // R16I + 1, // RG16 + 1, // RG16F + 1, // RG16UI + 1, // RG16I + 1, // RG16S + 1, // RGB32F + 1, // RGBA8_SRGB + 1, // RG8U + 1, // RG8S + 1, // RG32UI + 1, // R32UI + 8, // ASTC_2D_8X8 + 5, // ASTC_2D_8X5 + 4, // ASTC_2D_5X4 + 1, // BGRA8_SRGB + 4, // DXT1_SRGB + 4, // DXT23_SRGB + 4, // DXT45_SRGB + 4, // BC7U_SRGB + 4, // ASTC_2D_4X4_SRGB + 8, // ASTC_2D_8X8_SRGB + 5, // ASTC_2D_8X5_SRGB + 4, // ASTC_2D_5X4_SRGB + 1, // Z32F + 1, // Z16 + 1, // Z24S8 + 1, // S8Z24 + 1, // Z32FS8 + }}; + ASSERT(static_cast<std::size_t>(format) < block_height_table.size()); + return block_height_table[static_cast<std::size_t>(format)]; + } + static constexpr u32 GetFormatBpp(PixelFormat format) { if (format == PixelFormat::Invalid) return 0; @@ -283,7 +389,7 @@ struct SurfaceParams { 32, // RG16I 32, // RG16S 96, // RGB32F - 32, // SRGBA8 + 32, // RGBA8_SRGB 16, // RG8U 16, // RG8S 64, // RG32UI @@ -291,6 +397,15 @@ struct SurfaceParams { 16, // ASTC_2D_8X8 32, // ASTC_2D_8X5 32, // ASTC_2D_5X4 + 32, // BGRA8_SRGB + 64, // DXT1_SRGB + 128, // DXT23_SRGB + 128, // DXT45_SRGB + 128, // BC7U + 32, // ASTC_2D_4X4_SRGB + 16, // ASTC_2D_8X8_SRGB + 32, // ASTC_2D_8X5_SRGB + 32, // ASTC_2D_5X4_SRGB 32, // Z32F 16, // Z16 32, // Z24S8 @@ -329,6 +444,7 @@ struct SurfaceParams { // TODO (Hexagon12): Converting SRGBA to RGBA is a hack and doesn't completely correct the // gamma. case Tegra::RenderTargetFormat::RGBA8_SRGB: + return PixelFormat::RGBA8_SRGB; case Tegra::RenderTargetFormat::RGBA8_UNORM: return PixelFormat::ABGR8U; case Tegra::RenderTargetFormat::RGBA8_SNORM: @@ -336,6 +452,7 @@ struct SurfaceParams { case Tegra::RenderTargetFormat::RGBA8_UINT: return PixelFormat::ABGR8UI; case Tegra::RenderTargetFormat::BGRA8_SRGB: + return PixelFormat::BGRA8_SRGB; case Tegra::RenderTargetFormat::BGRA8_UNORM: return PixelFormat::BGRA8; case Tegra::RenderTargetFormat::RGB10_A2_UNORM: @@ -399,10 +516,14 @@ struct SurfaceParams { } static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, - Tegra::Texture::ComponentType component_type) { + Tegra::Texture::ComponentType component_type, + bool is_srgb) { // TODO(Subv): Properly implement this switch (format) { case Tegra::Texture::TextureFormat::A8R8G8B8: + if (is_srgb) { + return PixelFormat::RGBA8_SRGB; + } switch (component_type) { case Tegra::Texture::ComponentType::UNORM: return PixelFormat::ABGR8U; @@ -537,11 +658,11 @@ struct SurfaceParams { case Tegra::Texture::TextureFormat::Z24S8: return PixelFormat::Z24S8; case Tegra::Texture::TextureFormat::DXT1: - return PixelFormat::DXT1; + return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1; case Tegra::Texture::TextureFormat::DXT23: - return PixelFormat::DXT23; + return is_srgb ? PixelFormat::DXT23_SRGB : PixelFormat::DXT23; case Tegra::Texture::TextureFormat::DXT45: - return PixelFormat::DXT45; + return is_srgb ? PixelFormat::DXT45_SRGB : PixelFormat::DXT45; case Tegra::Texture::TextureFormat::DXN1: return PixelFormat::DXN1; case Tegra::Texture::TextureFormat::DXN2: @@ -555,19 +676,19 @@ struct SurfaceParams { static_cast<u32>(component_type)); UNREACHABLE(); case Tegra::Texture::TextureFormat::BC7U: - return PixelFormat::BC7U; + return is_srgb ? PixelFormat::BC7U_SRGB : PixelFormat::BC7U; case Tegra::Texture::TextureFormat::BC6H_UF16: return PixelFormat::BC6H_UF16; case Tegra::Texture::TextureFormat::BC6H_SF16: return PixelFormat::BC6H_SF16; case Tegra::Texture::TextureFormat::ASTC_2D_4X4: - return PixelFormat::ASTC_2D_4X4; + return is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4; case Tegra::Texture::TextureFormat::ASTC_2D_5X4: - return PixelFormat::ASTC_2D_5X4; + return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::ASTC_2D_5X4; case Tegra::Texture::TextureFormat::ASTC_2D_8X8: - return PixelFormat::ASTC_2D_8X8; + return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8; case Tegra::Texture::TextureFormat::ASTC_2D_8X5: - return PixelFormat::ASTC_2D_8X5; + return is_srgb ? PixelFormat::ASTC_2D_8X5_SRGB : PixelFormat::ASTC_2D_8X5; case Tegra::Texture::TextureFormat::R16_G16: switch (component_type) { case Tegra::Texture::ComponentType::FLOAT: @@ -742,6 +863,25 @@ struct SurfaceParams { return size_in_bytes_gl / 6; } + /// Returns the exact size of memory occupied by the texture in VRAM, including mipmaps. + std::size_t MemorySize() const { + std::size_t size = InnerMemorySize(is_layered); + if (is_layered) + return size * depth; + return size; + } + + /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including + /// mipmaps. + std::size_t LayerMemorySize() const { + return InnerMemorySize(true); + } + + /// Returns the size of a layer of this surface in OpenGL. + std::size_t LayerSizeGL() const { + return SizeInBytesRaw(true) / depth; + } + /// Creates SurfaceParams from a texture configuration static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry); @@ -782,7 +922,8 @@ struct SurfaceParams { u32 unaligned_height; SurfaceTarget target; u32 max_mip_level; - + bool is_layered; + bool srgb_conversion; // Parameters used for caching VAddr addr; Tegra::GPUVAddr gpu_addr; @@ -797,6 +938,9 @@ struct SurfaceParams { u32 layer_stride; u32 base_layer; } rt; + +private: + std::size_t InnerMemorySize(bool layer_only = false) const; }; }; // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index fe4d1bd83..dec291a7d 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -6,6 +6,7 @@ #include <set> #include <string> #include <string_view> +#include <unordered_set> #include <boost/optional.hpp> #include <fmt/format.h> @@ -276,7 +277,8 @@ public: GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix, const Tegra::Shader::Header& header) - : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header} { + : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header}, + fixed_pipeline_output_attributes_used{}, local_memory_size{0} { BuildRegisterList(); BuildInputList(); } @@ -434,6 +436,25 @@ public: shader.AddLine(dest + " = " + src + ';'); } + std::string GetLocalMemoryAsFloat(const std::string& index) { + return "lmem[" + index + ']'; + } + + std::string GetLocalMemoryAsInteger(const std::string& index, bool is_signed = false) { + const std::string func{is_signed ? "floatToIntBits" : "floatBitsToUint"}; + return func + "(lmem[" + index + "])"; + } + + void SetLocalMemoryAsFloat(const std::string& index, const std::string& value) { + shader.AddLine("lmem[" + index + "] = " + value + ';'); + } + + void SetLocalMemoryAsInteger(const std::string& index, const std::string& value, + bool is_signed = false) { + const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; + shader.AddLine("lmem[" + index + "] = " + func + '(' + value + ");"); + } + std::string GetControlCode(const Tegra::Shader::ControlCode cc) const { switch (cc) { case Tegra::Shader::ControlCode::NEU: @@ -480,7 +501,12 @@ public: std::to_string(static_cast<u32>(attribute)) + ']' + GetSwizzle(elem) + " = " + src + ';'); } else { - shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); + if (attribute == Attribute::Index::PointSize) { + fixed_pipeline_output_attributes_used.insert(attribute); + shader.AddLine(dest + " = " + src + ';'); + } else { + shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); + } } } } @@ -524,7 +550,9 @@ public: /// Add declarations. void GenerateDeclarations(const std::string& suffix) { + GenerateVertex(); GenerateRegisters(suffix); + GenerateLocalMemory(); GenerateInternalFlags(); GenerateInputAttrs(); GenerateOutputAttrs(); @@ -570,6 +598,10 @@ public: return entry.GetName(); } + void SetLocalMemory(u64 lmem) { + local_memory_size = lmem; + } + private: /// Generates declarations for registers. void GenerateRegisters(const std::string& suffix) { @@ -580,6 +612,15 @@ private: declarations.AddNewLine(); } + /// Generates declarations for local memory. + void GenerateLocalMemory() { + if (local_memory_size > 0) { + declarations.AddLine("float lmem[" + std::to_string((local_memory_size - 1 + 4) / 4) + + "];"); + declarations.AddNewLine(); + } + } + /// Generates declarations for internal flags. void GenerateInternalFlags() { for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { @@ -683,6 +724,20 @@ private: declarations.AddNewLine(); } + void GenerateVertex() { + if (stage != Maxwell3D::Regs::ShaderStage::Vertex) + return; + declarations.AddLine("out gl_PerVertex {"); + ++declarations.scope; + declarations.AddLine("vec4 gl_Position;"); + for (auto& o : fixed_pipeline_output_attributes_used) { + if (o == Attribute::Index::PointSize) + declarations.AddLine("float gl_PointSize;"); + } + --declarations.scope; + declarations.AddLine("};"); + } + /// Generates code representing a temporary (GPR) register. std::string GetRegister(const Register& reg, unsigned elem) { if (reg == Register::ZeroIndex) { @@ -836,6 +891,8 @@ private: /// Generates code representing the declaration name of an output attribute register. std::string GetOutputAttribute(Attribute::Index attribute) { switch (attribute) { + case Attribute::Index::PointSize: + return "gl_PointSize"; case Attribute::Index::Position: return "position"; default: @@ -870,6 +927,8 @@ private: const Maxwell3D::Regs::ShaderStage& stage; const std::string& suffix; const Tegra::Shader::Header& header; + std::unordered_set<Attribute::Index> fixed_pipeline_output_attributes_used; + u64 local_memory_size; }; class GLSLGenerator { @@ -879,6 +938,8 @@ public: : subroutines(subroutines), program_code(program_code), main_offset(main_offset), stage(stage), suffix(suffix) { std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); + local_memory_size = header.GetLocalMemorySize(); + regs.SetLocalMemory(local_memory_size); Generate(suffix); } @@ -2299,6 +2360,39 @@ private: shader.AddLine("}"); break; } + case OpCode::Id::LD_L: { + // Add an extra scope and declare the index register inside to prevent + // overwriting it in case it is used as an output of the LD instruction. + shader.AddLine('{'); + ++shader.scope; + + std::string op = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + " + " + + std::to_string(instr.smem_imm.Value()) + ')'; + + shader.AddLine("uint index = (" + op + " / 4);"); + + const std::string op_a = regs.GetLocalMemoryAsFloat("index"); + + if (instr.ld_l.unknown != 1) { + LOG_CRITICAL(HW_GPU, "LD_L Unhandled mode: {}", + static_cast<unsigned>(instr.ld_l.unknown.Value())); + UNREACHABLE(); + } + + switch (instr.ldst_sl.type.Value()) { + case Tegra::Shader::StoreType::Bytes32: + regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); + break; + default: + LOG_CRITICAL(HW_GPU, "LD_L Unhandled type: {}", + static_cast<unsigned>(instr.ldst_sl.type.Value())); + UNREACHABLE(); + } + + --shader.scope; + shader.AddLine('}'); + break; + } case OpCode::Id::ST_A: { ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, "Indirect attribute loads are not supported"); @@ -2327,6 +2421,37 @@ private: break; } + case OpCode::Id::ST_L: { + // Add an extra scope and declare the index register inside to prevent + // overwriting it in case it is used as an output of the LD instruction. + shader.AddLine('{'); + ++shader.scope; + + std::string op = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + " + " + + std::to_string(instr.smem_imm.Value()) + ')'; + + shader.AddLine("uint index = (" + op + " / 4);"); + + if (instr.st_l.unknown != 0) { + LOG_CRITICAL(HW_GPU, "ST_L Unhandled mode: {}", + static_cast<unsigned>(instr.st_l.unknown.Value())); + UNREACHABLE(); + } + + switch (instr.ldst_sl.type.Value()) { + case Tegra::Shader::StoreType::Bytes32: + regs.SetLocalMemoryAsFloat("index", regs.GetRegisterAsFloat(instr.gpr0)); + break; + default: + LOG_CRITICAL(HW_GPU, "ST_L Unhandled type: {}", + static_cast<unsigned>(instr.ldst_sl.type.Value())); + UNREACHABLE(); + } + + --shader.scope; + shader.AddLine('}'); + break; + } case OpCode::Id::TEX: { Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; std::string coord; @@ -3550,6 +3675,7 @@ private: const u32 main_offset; Maxwell3D::Regs::ShaderStage stage; const std::string& suffix; + u64 local_memory_size; ShaderWriter shader; ShaderWriter declarations; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index e883ffb1d..dfb562706 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -19,9 +19,6 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) { out += Decompiler::GetCommonDeclarations(); out += R"( -out gl_PerVertex { - vec4 gl_Position; -}; layout (location = 0) out vec4 position; diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 1fe26a2a9..d8a43cc94 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -11,9 +11,10 @@ namespace OpenGL { OpenGLState OpenGLState::cur_state; - +bool OpenGLState::s_rgb_used; OpenGLState::OpenGLState() { // These all match default OpenGL values + framebuffer_srgb.enabled = false; cull.enabled = false; cull.mode = GL_BACK; cull.front_face = GL_CCW; @@ -21,6 +22,11 @@ OpenGLState::OpenGLState() { depth.test_enabled = false; depth.test_func = GL_LESS; depth.write_mask = GL_TRUE; + depth.depth_range_near = 0.0f; + depth.depth_range_far = 1.0f; + + primitive_restart.enabled = false; + primitive_restart.index = 0; color_mask.red_enabled = GL_TRUE; color_mask.green_enabled = GL_TRUE; @@ -84,6 +90,16 @@ OpenGLState::OpenGLState() { } void OpenGLState::Apply() const { + // sRGB + if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) { + if (framebuffer_srgb.enabled) { + // Track if sRGB is used + s_rgb_used = true; + glEnable(GL_FRAMEBUFFER_SRGB); + } else { + glDisable(GL_FRAMEBUFFER_SRGB); + } + } // Culling if (cull.enabled != cur_state.cull.enabled) { if (cull.enabled) { @@ -119,6 +135,24 @@ void OpenGLState::Apply() const { glDepthMask(depth.write_mask); } + // Depth range + if (depth.depth_range_near != cur_state.depth.depth_range_near || + depth.depth_range_far != cur_state.depth.depth_range_far) { + glDepthRange(depth.depth_range_near, depth.depth_range_far); + } + + // Primitive restart + if (primitive_restart.enabled != cur_state.primitive_restart.enabled) { + if (primitive_restart.enabled) { + glEnable(GL_PRIMITIVE_RESTART); + } else { + glDisable(GL_PRIMITIVE_RESTART); + } + } + if (primitive_restart.index != cur_state.primitive_restart.index) { + glPrimitiveRestartIndex(primitive_restart.index); + } + // Color mask if (color_mask.red_enabled != cur_state.color_mask.red_enabled || color_mask.green_enabled != cur_state.color_mask.green_enabled || diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index dc21a2ee3..9e2c573b5 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -36,18 +36,29 @@ constexpr TextureUnit ProcTexDiffLUT{9}; class OpenGLState { public: struct { + bool enabled; // GL_FRAMEBUFFER_SRGB + } framebuffer_srgb; + + struct { bool enabled; // GL_CULL_FACE GLenum mode; // GL_CULL_FACE_MODE GLenum front_face; // GL_FRONT_FACE } cull; struct { - bool test_enabled; // GL_DEPTH_TEST - GLenum test_func; // GL_DEPTH_FUNC - GLboolean write_mask; // GL_DEPTH_WRITEMASK + bool test_enabled; // GL_DEPTH_TEST + GLenum test_func; // GL_DEPTH_FUNC + GLboolean write_mask; // GL_DEPTH_WRITEMASK + GLfloat depth_range_near; // GL_DEPTH_RANGE + GLfloat depth_range_far; // GL_DEPTH_RANGE } depth; struct { + bool enabled; + GLuint index; + } primitive_restart; // GL_PRIMITIVE_RESTART + + struct { GLboolean red_enabled; GLboolean green_enabled; GLboolean blue_enabled; @@ -154,7 +165,12 @@ public: static OpenGLState GetCurState() { return cur_state; } - + static bool GetsRGBUsed() { + return s_rgb_used; + } + static void ClearsRGBUsed() { + s_rgb_used = false; + } /// Apply this state as the current OpenGL state void Apply() const; @@ -169,6 +185,9 @@ public: private: static OpenGLState cur_state; + // Workaround for sRGB problems caused by + // QT not supporting srgb output + static bool s_rgb_used; }; } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 96d916b07..90b68943d 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -283,7 +283,8 @@ void RendererOpenGL::CreateRasterizer() { if (rasterizer) { return; } - + // Initialize sRGB Usage + OpenGLState::ClearsRGBUsed(); rasterizer = std::make_unique<RasterizerOpenGL>(render_window, screen_info); } @@ -356,13 +357,20 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, state.texture_units[0].texture = screen_info.display_texture; state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}; + // Workaround brigthness problems in SMO by enabling sRGB in the final output + // if it has been used in the frame + // Needed because of this bug in QT + // QTBUG-50987 + state.framebuffer_srgb.enabled = OpenGLState::GetsRGBUsed(); state.Apply(); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - + // restore default state + state.framebuffer_srgb.enabled = false; state.texture_units[0].texture = 0; state.Apply(); + // Clear sRGB state for the next frame + OpenGLState::ClearsRGBUsed(); } /** diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index f1b40e7f5..550ca856c 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -142,7 +142,6 @@ void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, 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; @@ -320,13 +319,13 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth) { if (tiled) { - const u32 gobs_in_x = 64 / bytes_per_pixel; + const u32 gobs_in_x = 64; const u32 gobs_in_y = 8; const u32 gobs_in_z = 1; - const u32 aligned_width = Common::AlignUp(width, gobs_in_x); + const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x); const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height); const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth); - return aligned_width * aligned_height * aligned_depth * bytes_per_pixel; + return aligned_width * aligned_height * aligned_depth; } else { return width * height * depth * bytes_per_pixel; } diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index 5947bd2b9..d12d2ecb8 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h @@ -173,6 +173,7 @@ struct TICEntry { }; union { BitField<0, 16, u32> width_minus_1; + BitField<22, 1, u32> srgb_conversion; BitField<23, 4, TextureType> texture_type; }; union { @@ -227,6 +228,10 @@ struct TICEntry { return header_version == TICHeaderVersion::BlockLinear || header_version == TICHeaderVersion::BlockLinearColorKey; } + + bool IsSrgbConversionEnabled() const { + return srgb_conversion != 0; + } }; static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size"); diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index e8ab23326..39eef8858 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -8,7 +8,6 @@ #include "common/microprofile.h" #include "common/scm_rev.h" -#include "common/string_util.h" #include "core/core.h" #include "core/frontend/framebuffer_layout.h" #include "core/settings.h" @@ -107,9 +106,8 @@ private: GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) : QWidget(parent), child(nullptr), emu_thread(emu_thread) { - std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, - Common::g_scm_branch, Common::g_scm_desc); - setWindowTitle(QString::fromStdString(window_title)); + setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") + .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); setAttribute(Qt::WA_AcceptTouchEvents); InputCommon::Init(); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 1fe9a7edd..d4fd60a73 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -13,11 +13,16 @@ Config::Config() { // TODO: Don't hardcode the path; let the frontend decide where to put the config files. qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; FileUtil::CreateFullPath(qt_config_loc); - qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat); + qt_config = + std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); Reload(); } +Config::~Config() { + Save(); +} + const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, @@ -342,9 +347,3 @@ void Config::Reload() { void Config::Save() { SaveValues(); } - -Config::~Config() { - Save(); - - delete qt_config; -} diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index cbf745ea2..9c99c1b75 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -5,6 +5,7 @@ #pragma once #include <array> +#include <memory> #include <string> #include <QVariant> #include "core/settings.h" @@ -12,12 +13,6 @@ class QSettings; class Config { - QSettings* qt_config; - std::string qt_config_loc; - - void ReadValues(); - void SaveValues(); - public: Config(); ~Config(); @@ -27,4 +22,11 @@ public: static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; + +private: + void ReadValues(); + void SaveValues(); + + std::unique_ptr<QSettings> qt_config; + std::string qt_config_loc; }; diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 83cc49dfc..4b34c1e28 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -21,12 +21,8 @@ #include "yuzu/configuration/configure_system.h" #include "yuzu/main.h" -static std::string GetImagePath(Service::Account::UUID uuid) { - return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + - "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; -} - -static const std::array<int, 12> days_in_month = {{ +namespace { +constexpr std::array<int, 12> days_in_month = {{ 31, 29, 31, @@ -42,7 +38,7 @@ static const std::array<int, 12> days_in_month = {{ }}; // Same backup JPEG used by acc IProfile::GetImage if no jpeg found -static constexpr std::array<u8, 107> backup_jpeg{ +constexpr std::array<u8, 107> backup_jpeg{ 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, @@ -52,15 +48,52 @@ static constexpr std::array<u8, 107> backup_jpeg{ 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, }; +QString GetImagePath(Service::Account::UUID uuid) { + const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + + "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; + return QString::fromStdString(path); +} + +QString GetAccountUsername(const Service::Account::ProfileManager& manager, + Service::Account::UUID uuid) { + Service::Account::ProfileBase profile; + if (!manager.GetProfileBase(uuid, profile)) { + return {}; + } + + const auto text = Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); + return QString::fromStdString(text); +} + +QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) { + return ConfigureSystem::tr("%1\n%2", + "%1 is the profile username, %2 is the formatted UUID (e.g. " + "00112233-4455-6677-8899-AABBCCDDEEFF))") + .arg(username, QString::fromStdString(uuid.FormatSwitch())); +} + +QPixmap GetIcon(Service::Account::UUID uuid) { + QPixmap icon{GetImagePath(uuid)}; + + if (!icon) { + icon.fill(Qt::black); + icon.loadFromData(backup_jpeg.data(), backup_jpeg.size()); + } + + return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); +} +} // Anonymous namespace + ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem), profile_manager(std::make_unique<Service::Account::ProfileManager>()) { ui->setupUi(this); connect(ui->combo_birthmonth, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, - &ConfigureSystem::updateBirthdayComboBox); + &ConfigureSystem::UpdateBirthdayComboBox); connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, - &ConfigureSystem::refreshConsoleID); + &ConfigureSystem::RefreshConsoleID); layout = new QVBoxLayout; tree_view = new QTreeView; @@ -118,18 +151,6 @@ void ConfigureSystem::setConfiguration() { UpdateCurrentUser(); } -static QPixmap GetIcon(Service::Account::UUID uuid) { - const auto icon_url = QString::fromStdString(GetImagePath(uuid)); - QPixmap icon{icon_url}; - - if (!icon) { - icon.fill(Qt::black); - icon.loadFromData(backup_jpeg.data(), backup_jpeg.size()); - } - - return icon; -} - void ConfigureSystem::PopulateUserList() { const auto& profiles = profile_manager->GetAllUsers(); for (const auto& user : profiles) { @@ -141,8 +162,7 @@ void ConfigureSystem::PopulateUserList() { reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); list_items.push_back(QList<QStandardItem*>{new QStandardItem{ - GetIcon(user).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - QString::fromStdString(username + '\n' + user.FormatSwitch())}}); + GetIcon(user), FormatUserEntryText(QString::fromStdString(username), user)}}); } for (const auto& item : list_items) @@ -153,25 +173,17 @@ void ConfigureSystem::UpdateCurrentUser() { ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS); const auto& current_user = profile_manager->GetUser(Settings::values.current_user); - ASSERT(current_user != boost::none); - const auto username = GetAccountUsername(*current_user); + ASSERT(current_user != std::nullopt); + const auto username = GetAccountUsername(*profile_manager, *current_user); scene->clear(); scene->addPixmap( GetIcon(*current_user).scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - ui->current_user_username->setText(QString::fromStdString(username)); + ui->current_user_username->setText(username); } void ConfigureSystem::ReadSystemSettings() {} -std::string ConfigureSystem::GetAccountUsername(Service::Account::UUID uuid) const { - Service::Account::ProfileBase profile; - if (!profile_manager->GetProfileBase(uuid, profile)) - return ""; - return Common::StringFromFixedZeroTerminatedBuffer( - reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); -} - void ConfigureSystem::applyConfiguration() { if (!enabled) return; @@ -180,7 +192,7 @@ void ConfigureSystem::applyConfiguration() { Settings::Apply(); } -void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { +void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) { if (birthmonth_index < 0 || birthmonth_index >= 12) return; @@ -205,7 +217,7 @@ void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { ui->combo_birthday->setCurrentIndex(birthday_index); } -void ConfigureSystem::refreshConsoleID() { +void ConfigureSystem::RefreshConsoleID() { QMessageBox::StandardButton reply; QString warning_text = tr("This will replace your current virtual Switch with a new one. " "Your current virtual Switch will not be recoverable. " @@ -232,8 +244,7 @@ void ConfigureSystem::SelectUser(const QModelIndex& index) { } void ConfigureSystem::AddUser() { - Service::Account::UUID uuid; - uuid.Generate(); + const auto uuid = Service::Account::UUID::Generate(); bool ok = false; const auto username = @@ -244,25 +255,23 @@ void ConfigureSystem::AddUser() { profile_manager->CreateNewUser(uuid, username.toStdString()); - item_model->appendRow(new QStandardItem{ - GetIcon(uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - QString::fromStdString(username.toStdString() + '\n' + uuid.FormatSwitch())}); + item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)}); } void ConfigureSystem::RenameUser() { const auto user = tree_view->currentIndex().row(); const auto uuid = profile_manager->GetUser(user); - ASSERT(uuid != boost::none); - const auto username = GetAccountUsername(*uuid); + ASSERT(uuid != std::nullopt); Service::Account::ProfileBase profile; if (!profile_manager->GetProfileBase(*uuid, profile)) return; bool ok = false; + const auto old_username = GetAccountUsername(*profile_manager, *uuid); const auto new_username = QInputDialog::getText(this, tr("Enter Username"), tr("Enter a new username:"), - QLineEdit::Normal, QString::fromStdString(username), &ok); + QLineEdit::Normal, old_username, &ok); if (!ok) return; @@ -280,25 +289,20 @@ void ConfigureSystem::RenameUser() { item_model->setItem( user, 0, - new QStandardItem{ - GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - tr("%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " - "00112233-4455-6677-8899-AABBCCDDEEFF))") - .arg(QString::fromStdString(username_std), - QString::fromStdString(uuid->FormatSwitch()))}); + new QStandardItem{GetIcon(*uuid), + FormatUserEntryText(QString::fromStdString(username_std), *uuid)}); UpdateCurrentUser(); } void ConfigureSystem::DeleteUser() { const auto index = tree_view->currentIndex().row(); const auto uuid = profile_manager->GetUser(index); - ASSERT(uuid != boost::none); - const auto username = GetAccountUsername(*uuid); + ASSERT(uuid != std::nullopt); + const auto username = GetAccountUsername(*profile_manager, *uuid); - const auto confirm = - QMessageBox::question(this, tr("Confirm Delete"), - tr("You are about to delete user with name %1. Are you sure?") - .arg(QString::fromStdString(username))); + const auto confirm = QMessageBox::question( + this, tr("Confirm Delete"), + tr("You are about to delete user with name \"%1\". Are you sure?").arg(username)); if (confirm == QMessageBox::No) return; @@ -320,29 +324,48 @@ void ConfigureSystem::DeleteUser() { void ConfigureSystem::SetUserImage() { const auto index = tree_view->currentIndex().row(); const auto uuid = profile_manager->GetUser(index); - ASSERT(uuid != boost::none); - const auto username = GetAccountUsername(*uuid); + ASSERT(uuid != std::nullopt); const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(), - "JPEG Images (*.jpg *.jpeg)"); + tr("JPEG Images (*.jpg *.jpeg)")); - if (file.isEmpty()) + if (file.isEmpty()) { return; + } - FileUtil::Delete(GetImagePath(*uuid)); + const auto image_path = GetImagePath(*uuid); + if (QFile::exists(image_path) && !QFile::remove(image_path)) { + QMessageBox::warning( + this, tr("Error deleting image"), + tr("Error occurred attempting to overwrite previous image at: %1.").arg(image_path)); + return; + } + + const auto raw_path = QString::fromStdString( + FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"); + const QFileInfo raw_info{raw_path}; + if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) { + QMessageBox::warning(this, tr("Error deleting file"), + tr("Unable to delete existing file: %1.").arg(raw_path)); + return; + } - const auto raw_path = - FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; - if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) - FileUtil::Delete(raw_path); + const QString absolute_dst_path = QFileInfo{image_path}.absolutePath(); + if (!QDir{raw_path}.mkpath(absolute_dst_path)) { + QMessageBox::warning( + this, tr("Error creating user image directory"), + tr("Unable to create directory %1 for storing user images.").arg(absolute_dst_path)); + return; + } - FileUtil::CreateFullPath(GetImagePath(*uuid)); - FileUtil::Copy(file.toStdString(), GetImagePath(*uuid)); + if (!QFile::copy(file, image_path)) { + QMessageBox::warning(this, tr("Error copying user image"), + tr("Unable to copy image from %1 to %2").arg(file, image_path)); + return; + } - item_model->setItem( - index, 0, - new QStandardItem{ - GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - QString::fromStdString(username + '\n' + uuid->FormatSwitch())}); + const auto username = GetAccountUsername(*profile_manager, *uuid); + item_model->setItem(index, 0, + new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)}); UpdateCurrentUser(); } diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h index b73e0719c..07764e1f7 100644 --- a/src/yuzu/configuration/configure_system.h +++ b/src/yuzu/configuration/configure_system.h @@ -9,17 +9,16 @@ #include <QList> #include <QWidget> -namespace Service::Account { -class ProfileManager; -struct UUID; -} // namespace Service::Account - class QGraphicsScene; class QStandardItem; class QStandardItemModel; class QTreeView; class QVBoxLayout; +namespace Service::Account { +class ProfileManager; +} + namespace Ui { class ConfigureSystem; } @@ -29,28 +28,25 @@ class ConfigureSystem : public QWidget { public: explicit ConfigureSystem(QWidget* parent = nullptr); - ~ConfigureSystem(); + ~ConfigureSystem() override; void applyConfiguration(); void setConfiguration(); - void PopulateUserList(); - void UpdateCurrentUser(); +private: + void ReadSystemSettings(); -public slots: - void updateBirthdayComboBox(int birthmonth_index); - void refreshConsoleID(); + void UpdateBirthdayComboBox(int birthmonth_index); + void RefreshConsoleID(); + void PopulateUserList(); + void UpdateCurrentUser(); void SelectUser(const QModelIndex& index); void AddUser(); void RenameUser(); void DeleteUser(); void SetUserImage(); -private: - void ReadSystemSettings(); - std::string GetAccountUsername(Service::Account::UUID uuid) const; - QVBoxLayout* layout; QTreeView* tree_view; QStandardItemModel* item_model; @@ -59,11 +55,12 @@ private: std::vector<QList<QStandardItem*>> list_items; std::unique_ptr<Ui::ConfigureSystem> ui; - bool enabled; + bool enabled = false; - int birthmonth, birthday; - int language_index; - int sound_index; + int birthmonth = 0; + int birthday = 0; + int language_index = 0; + int sound_index = 0; std::unique_ptr<Service::Account::ProfileManager> profile_manager; }; diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp index b5c88f944..67ed0ba6d 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp +++ b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp @@ -2,7 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <map> #include <QLabel> #include <QMetaType> #include <QPushButton> @@ -32,21 +31,8 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const { switch (role) { case Qt::DisplayRole: { if (index.column() == 0) { - static const std::map<Tegra::DebugContext::Event, QString> map = { - {Tegra::DebugContext::Event::MaxwellCommandLoaded, tr("Maxwell command loaded")}, - {Tegra::DebugContext::Event::MaxwellCommandProcessed, - tr("Maxwell command processed")}, - {Tegra::DebugContext::Event::IncomingPrimitiveBatch, - tr("Incoming primitive batch")}, - {Tegra::DebugContext::Event::FinishedPrimitiveBatch, - tr("Finished primitive batch")}, - }; - - DEBUG_ASSERT(map.size() == - static_cast<std::size_t>(Tegra::DebugContext::Event::NumEvents)); - return (map.find(event) != map.end()) ? map.at(event) : QString(); + return DebugContextEventToString(event); } - break; } @@ -128,6 +114,23 @@ void BreakPointModel::OnResumed() { active_breakpoint = context->active_breakpoint; } +QString BreakPointModel::DebugContextEventToString(Tegra::DebugContext::Event event) { + switch (event) { + case Tegra::DebugContext::Event::MaxwellCommandLoaded: + return tr("Maxwell command loaded"); + case Tegra::DebugContext::Event::MaxwellCommandProcessed: + return tr("Maxwell command processed"); + case Tegra::DebugContext::Event::IncomingPrimitiveBatch: + return tr("Incoming primitive batch"); + case Tegra::DebugContext::Event::FinishedPrimitiveBatch: + return tr("Finished primitive batch"); + case Tegra::DebugContext::Event::NumEvents: + break; + } + + return tr("Unknown debug context event"); +} + GraphicsBreakPointsWidget::GraphicsBreakPointsWidget( std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent) : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver( diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h index 7112b87e6..fb488e38f 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h +++ b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h @@ -29,6 +29,8 @@ public: void OnResumed(); private: + static QString DebugContextEventToString(Tegra::DebugContext::Event event); + std::weak_ptr<Tegra::DebugContext> context_weak; bool at_breakpoint; Tegra::DebugContext::Event active_breakpoint; diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 67890455a..a5a4aa432 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -16,7 +16,6 @@ #include <fmt/format.h> #include "common/common_paths.h" #include "common/common_types.h" -#include "common/file_util.h" #include "common/logging/log.h" #include "core/file_sys/patch_manager.h" #include "yuzu/compatibility_list.h" @@ -217,11 +216,11 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) tree_view->setContextMenuPolicy(Qt::CustomContextMenu); item_model->insertColumns(0, COLUMN_COUNT); - item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); - item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); - item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, "Add-ons"); - item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); - item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); + item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); + item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility")); + item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons")); + item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type")); + item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size")); connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); @@ -387,9 +386,9 @@ void GameList::LoadCompatibilityList() { } void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { - if (!FileUtil::Exists(dir_path.toStdString()) || - !FileUtil::IsDirectory(dir_path.toStdString())) { - LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toLocal8Bit().data()); + const QFileInfo dir_info{dir_path}; + if (!dir_info.exists() || !dir_info.isDir()) { + LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toStdString()); search_field->setFilterResult(0, 0); return; } diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 3881aba5f..3d865a12d 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -62,19 +62,24 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, FileSys::VirtualFile update_raw; loader.ReadUpdateRaw(update_raw); for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { - if (!updatable && kv.first == "Update") + const bool is_update = kv.first == "Update"; + if (!updatable && is_update) { continue; + } + + const QString type = QString::fromStdString(kv.first); if (kv.second.empty()) { - out.append(fmt::format("{}\n", kv.first).c_str()); + out.append(QStringLiteral("%1\n").arg(type)); } else { auto ver = kv.second; // Display container name for packed updates - if (ver == "PACKED" && kv.first == "Update") + if (is_update && ver == "PACKED") { ver = Loader::GetFileTypeString(loader.GetFileType()); + } - out.append(fmt::format("{} ({})\n", kv.first, ver).c_str()); + out.append(QStringLiteral("%1 (%2)\n").arg(type, QString::fromStdString(ver))); } } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 47f494841..b5bfa6741 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -30,6 +30,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #define QT_NO_OPENGL #include <QDesktopWidget> #include <QDialogButtonBox> +#include <QFile> #include <QFileDialog> #include <QMessageBox> #include <QtConcurrent/QtConcurrent> @@ -785,7 +786,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target ASSERT(index != -1 && index < 8); const auto user_id = manager.GetUser(index); - ASSERT(user_id != boost::none); + ASSERT(user_id != std::nullopt); path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, program_id, user_id->uuid, 0); @@ -1336,20 +1337,40 @@ void GMainWindow::OnLoadAmiibo() { const QString extensions{"*.bin"}; const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter); - if (!filename.isEmpty()) { - Core::System& system{Core::System::GetInstance()}; - Service::SM::ServiceManager& sm = system.ServiceManager(); - auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); - if (nfc != nullptr) { - auto nfc_file = FileUtil::IOFile(filename.toStdString(), "rb"); - if (!nfc_file.IsOpen()) { - return; - } - std::vector<u8> amiibo_buffer(nfc_file.GetSize()); - nfc_file.ReadBytes(amiibo_buffer.data(), amiibo_buffer.size()); - nfc_file.Close(); - nfc->LoadAmiibo(amiibo_buffer); - } + + if (filename.isEmpty()) { + return; + } + + Core::System& system{Core::System::GetInstance()}; + Service::SM::ServiceManager& sm = system.ServiceManager(); + auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); + if (nfc == nullptr) { + return; + } + + QFile nfc_file{filename}; + if (!nfc_file.open(QIODevice::ReadOnly)) { + QMessageBox::warning(this, tr("Error opening Amiibo data file"), + tr("Unable to open Amiibo file \"%1\" for reading.").arg(filename)); + return; + } + + const u64 nfc_file_size = nfc_file.size(); + std::vector<u8> buffer(nfc_file_size); + const u64 read_size = nfc_file.read(reinterpret_cast<char*>(buffer.data()), nfc_file_size); + if (nfc_file_size != read_size) { + QMessageBox::warning(this, tr("Error reading Amiibo data file"), + tr("Unable to fully read Amiibo data. Expected to read %1 bytes, but " + "was only able to read %2 bytes.") + .arg(nfc_file_size) + .arg(read_size)); + return; + } + + if (!nfc->LoadAmiibo(buffer)) { + QMessageBox::warning(this, tr("Error loading Amiibo data"), + tr("Unable to load Amiibo data.")); } } |