diff options
Diffstat (limited to '')
82 files changed, 2116 insertions, 1426 deletions
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp index cee8b12dd..2fe0b3aef 100644 --- a/src/audio_core/time_stretch.cpp +++ b/src/audio_core/time_stretch.cpp @@ -32,10 +32,10 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out, // We were given actual_samples number of samples, and num_samples were requested from us. double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out); - const double max_latency = 1.0; // seconds + const double max_latency = 0.25; // seconds const double max_backlog = m_sample_rate * max_latency; const double backlog_fullness = m_sound_touch.numSamples() / max_backlog; - if (backlog_fullness > 5.0) { + if (backlog_fullness > 4.0) { // Too many samples in backlog: Don't push anymore on num_in = 0; } @@ -49,7 +49,7 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out, // This low-pass filter smoothes out variance in the calculated stretch ratio. // The time-scale determines how responsive this filter is. - constexpr double lpf_time_scale = 2.0; // seconds + constexpr double lpf_time_scale = 0.712; // seconds const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale); m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio); diff --git a/src/core/core.cpp b/src/core/core.cpp index 7cb86ed92..6d5b5a2d0 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -185,7 +185,7 @@ struct System::Impl { LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); return ResultStatus::ErrorGetLoader; } - std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode = + std::pair<std::optional<u32>, Loader::ResultStatus> system_mode = app_loader->LoadKernelSystemMode(); if (system_mode.second != Loader::ResultStatus::Success) { @@ -312,6 +312,10 @@ Cpu& System::CurrentCpuCore() { return impl->CurrentCpuCore(); } +const Cpu& System::CurrentCpuCore() const { + return impl->CurrentCpuCore(); +} + System::ResultStatus System::RunLoop(bool tight_loop) { return impl->RunLoop(tight_loop); } @@ -342,7 +346,11 @@ PerfStatsResults System::GetAndResetPerfStats() { return impl->GetAndResetPerfStats(); } -Core::TelemetrySession& System::TelemetrySession() const { +TelemetrySession& System::TelemetrySession() { + return *impl->telemetry_session; +} + +const TelemetrySession& System::TelemetrySession() const { return *impl->telemetry_session; } @@ -350,7 +358,11 @@ ARM_Interface& System::CurrentArmInterface() { return CurrentCpuCore().ArmInterface(); } -std::size_t System::CurrentCoreIndex() { +const ARM_Interface& System::CurrentArmInterface() const { + return CurrentCpuCore().ArmInterface(); +} + +std::size_t System::CurrentCoreIndex() const { return CurrentCpuCore().CoreIndex(); } @@ -358,6 +370,10 @@ Kernel::Scheduler& System::CurrentScheduler() { return CurrentCpuCore().Scheduler(); } +const Kernel::Scheduler& System::CurrentScheduler() const { + return CurrentCpuCore().Scheduler(); +} + Kernel::Scheduler& System::Scheduler(std::size_t core_index) { return CpuCore(core_index).Scheduler(); } @@ -378,6 +394,10 @@ ARM_Interface& System::ArmInterface(std::size_t core_index) { return CpuCore(core_index).ArmInterface(); } +const ARM_Interface& System::ArmInterface(std::size_t core_index) const { + return CpuCore(core_index).ArmInterface(); +} + Cpu& System::CpuCore(std::size_t core_index) { ASSERT(core_index < NUM_CPU_CORES); return *impl->cpu_cores[core_index]; @@ -392,6 +412,10 @@ ExclusiveMonitor& System::Monitor() { return *impl->cpu_exclusive_monitor; } +const ExclusiveMonitor& System::Monitor() const { + return *impl->cpu_exclusive_monitor; +} + Tegra::GPU& System::GPU() { return *impl->gpu_core; } diff --git a/src/core/core.h b/src/core/core.h index 173be45f8..cfacceb81 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -129,11 +129,11 @@ public: */ bool IsPoweredOn() const; - /** - * Returns a reference to the telemetry session for this emulation session. - * @returns Reference to the telemetry session. - */ - Core::TelemetrySession& TelemetrySession() const; + /// Gets a reference to the telemetry session for this emulation session. + Core::TelemetrySession& TelemetrySession(); + + /// Gets a reference to the telemetry session for this emulation session. + const Core::TelemetrySession& TelemetrySession() const; /// Prepare the core emulation for a reschedule void PrepareReschedule(); @@ -144,24 +144,36 @@ public: /// Gets an ARM interface to the CPU core that is currently running ARM_Interface& CurrentArmInterface(); + /// Gets an ARM interface to the CPU core that is currently running + const ARM_Interface& CurrentArmInterface() const; + /// Gets the index of the currently running CPU core - std::size_t CurrentCoreIndex(); + std::size_t CurrentCoreIndex() const; /// Gets the scheduler for the CPU core that is currently running Kernel::Scheduler& CurrentScheduler(); - /// Gets an ARM interface to the CPU core with the specified index + /// Gets the scheduler for the CPU core that is currently running + const Kernel::Scheduler& CurrentScheduler() const; + + /// Gets a reference to an ARM interface for the CPU core with the specified index ARM_Interface& ArmInterface(std::size_t core_index); + /// Gets a const reference to an ARM interface from the CPU core with the specified index + const ARM_Interface& ArmInterface(std::size_t core_index) const; + /// Gets a CPU interface to the CPU core with the specified index Cpu& CpuCore(std::size_t core_index); /// Gets a CPU interface to the CPU core with the specified index const Cpu& CpuCore(std::size_t core_index) const; - /// Gets the exclusive monitor + /// Gets a reference to the exclusive monitor ExclusiveMonitor& Monitor(); + /// Gets a constant reference to the exclusive monitor + const ExclusiveMonitor& Monitor() const; + /// Gets a mutable reference to the GPU interface Tegra::GPU& GPU(); @@ -230,6 +242,9 @@ private: /// Returns the currently running CPU core Cpu& CurrentCpuCore(); + /// Returns the currently running CPU core + const Cpu& CurrentCpuCore() const; + /** * Initialize the emulated system. * @param emu_window Reference to the host-system window used for video output and keyboard diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index fefc3c747..904afa039 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -141,28 +141,28 @@ Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) return mac_key; } -boost::optional<Key128> DeriveSDSeed() { +std::optional<Key128> DeriveSDSeed() { const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000043", "rb+"); if (!save_43.IsOpen()) - return boost::none; + return {}; const FileUtil::IOFile sd_private( FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); if (!sd_private.IsOpen()) - return boost::none; + return {}; std::array<u8, 0x10> private_seed{}; if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) { - return boost::none; + return {}; } std::array<u8, 0x10> buffer{}; std::size_t offset = 0; for (; offset + 0x10 < save_43.GetSize(); ++offset) { if (!save_43.Seek(offset, SEEK_SET)) { - return boost::none; + return {}; } save_43.ReadBytes(buffer.data(), buffer.size()); @@ -172,12 +172,12 @@ boost::optional<Key128> DeriveSDSeed() { } if (!save_43.Seek(offset + 0x10, SEEK_SET)) { - return boost::none; + return {}; } Key128 seed{}; if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) { - return boost::none; + return {}; } return seed; } @@ -291,26 +291,26 @@ static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) { } template <size_t size> -static boost::optional<u64> FindTicketOffset(const std::array<u8, size>& data) { +static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) { u64 offset = 0; for (size_t i = 0x20; i < data.size() - 0x10; ++i) { if (data[i] == 0x1) { offset = i + 1; break; } else if (data[i] != 0x0) { - return boost::none; + return {}; } } return offset; } -boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, - const RSAKeyPair<2048>& key) { +std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, + const RSAKeyPair<2048>& key) { u32 cert_authority; std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority)); if (cert_authority == 0) - return boost::none; + return {}; if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) { LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority {:08X}.", @@ -321,7 +321,7 @@ boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128)); if (rights_id == Key128{}) - return boost::none; + return {}; Key128 key_temp{}; @@ -356,17 +356,17 @@ boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size()); if (m_0 != 0) - return boost::none; + return {}; m_1 = m_1 ^ MGF1<0x20>(m_2); m_2 = m_2 ^ MGF1<0xDF>(m_1); const auto offset = FindTicketOffset(m_2); - if (offset == boost::none) - return boost::none; - ASSERT(offset.get() > 0); + if (!offset) + return {}; + ASSERT(*offset > 0); - std::memcpy(key_temp.data(), m_2.data() + offset.get(), key_temp.size()); + std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size()); return std::make_pair(rights_id, key_temp); } @@ -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) { @@ -661,8 +661,8 @@ void KeyManager::DeriveSDSeedLazy() { return; const auto res = DeriveSDSeed(); - if (res != boost::none) - SetKey(S128KeyType::SDSeed, res.get()); + if (res) + SetKey(S128KeyType::SDSeed, *res); } static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) { @@ -889,9 +889,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) { for (const auto& raw : res) { const auto pair = ParseTicket(raw, rsa_key); - if (pair == boost::none) + if (!pair) continue; - const auto& [rid, key] = pair.value(); + const auto& [rid, key] = *pair; u128 rights_id; std::memcpy(rights_id.data(), rid.data(), rid.size()); SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index cccb3c0ae..22f268c65 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -6,9 +6,10 @@ #include <array> #include <map> +#include <optional> #include <string> + #include <boost/container/flat_map.hpp> -#include <boost/optional.hpp> #include <fmt/format.h> #include "common/common_types.h" #include "core/crypto/partition_data_manager.h" @@ -191,14 +192,14 @@ Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblob, const Key128& key); -boost::optional<Key128> DeriveSDSeed(); +std::optional<Key128> DeriveSDSeed(); Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys); std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save); // Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset // 0x140-0x144 is zero) -boost::optional<std::pair<Key128, Key128>> ParseTicket( - const TicketRaw& ticket, const RSAKeyPair<2048>& eticket_extended_key); +std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, + const RSAKeyPair<2048>& eticket_extended_key); } // namespace Core::Crypto diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 77e04704e..b46fe893c 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -4,10 +4,9 @@ #include <algorithm> #include <cstring> +#include <optional> #include <utility> -#include <boost/optional.hpp> - #include "common/logging/log.h" #include "core/crypto/aes_util.h" #include "core/crypto/ctr_encryption_layer.h" @@ -306,18 +305,18 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low}); subsection_buckets.back().entries.push_back({size, {0}, 0}); - boost::optional<Core::Crypto::Key128> key = boost::none; + std::optional<Core::Crypto::Key128> key = {}; if (encrypted) { if (has_rights_id) { status = Loader::ResultStatus::Success; key = GetTitlekey(); - if (key == boost::none) { + if (!key) { status = Loader::ResultStatus::ErrorMissingTitlekey; return false; } } else { key = GetKeyAreaKey(NCASectionCryptoType::BKTR); - if (key == boost::none) { + if (!key) { status = Loader::ResultStatus::ErrorMissingKeyAreaKey; return false; } @@ -332,7 +331,7 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl auto bktr = std::make_shared<BKTR>( bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset), relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted, - encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset, + encrypted ? *key : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset, section.raw.section_ctr); // BKTR applies to entire IVFC, so make an offset version to level 6 @@ -388,11 +387,11 @@ u8 NCA::GetCryptoRevision() const { return master_key_id; } -boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const { +std::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const { const auto master_key_id = GetCryptoRevision(); if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index)) - return boost::none; + return {}; std::vector<u8> key_area(header.key_area.begin(), header.key_area.end()); Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( @@ -416,25 +415,25 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty return out; } -boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() { +std::optional<Core::Crypto::Key128> NCA::GetTitlekey() { const auto master_key_id = GetCryptoRevision(); u128 rights_id{}; memcpy(rights_id.data(), header.rights_id.data(), 16); if (rights_id == u128{}) { status = Loader::ResultStatus::ErrorInvalidRightsID; - return boost::none; + return {}; } auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); if (titlekey == Core::Crypto::Key128{}) { status = Loader::ResultStatus::ErrorMissingTitlekey; - return boost::none; + return {}; } if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) { status = Loader::ResultStatus::ErrorMissingTitlekek; - return boost::none; + return {}; } Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( @@ -458,25 +457,25 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s case NCASectionCryptoType::BKTR: LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); { - boost::optional<Core::Crypto::Key128> key = boost::none; + std::optional<Core::Crypto::Key128> key = {}; if (has_rights_id) { status = Loader::ResultStatus::Success; key = GetTitlekey(); - if (key == boost::none) { + if (!key) { if (status == Loader::ResultStatus::Success) status = Loader::ResultStatus::ErrorMissingTitlekey; return nullptr; } } else { key = GetKeyAreaKey(NCASectionCryptoType::CTR); - if (key == boost::none) { + if (!key) { status = Loader::ResultStatus::ErrorMissingKeyAreaKey; return nullptr; } } - auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>( - std::move(in), key.value(), starting_offset); + auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key, + starting_offset); std::vector<u8> iv(16); for (u8 i = 0; i < 8; ++i) iv[i] = s_header.raw.section_ctr[0x8 - i - 1]; diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 211946686..4bba55607 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -6,9 +6,10 @@ #include <array> #include <memory> +#include <optional> #include <string> #include <vector> -#include <boost/optional.hpp> + #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" @@ -111,8 +112,8 @@ private: bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry); u8 GetCryptoRevision() const; - boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; - boost::optional<Core::Crypto::Key128> GetTitlekey(); + std::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; + std::optional<Core::Crypto::Key128> GetTitlekey(); VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset); std::vector<VirtualDir> dirs; diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h index 3d377b0af..a62502193 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.h +++ b/src/core/file_sys/fsmitm_romfsbuild.h @@ -27,7 +27,6 @@ #include <map> #include <memory> #include <string> -#include <boost/detail/container_fwd.hpp> #include "common/common_types.h" #include "core/file_sys/vfs.h" diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index 999939d5a..485c4913a 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp @@ -103,12 +103,12 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { offset += sizeof(u16); const auto data = ips->ReadByte(offset++); - if (data == boost::none) + if (!data) return nullptr; if (real_offset + rle_size > in_data.size()) rle_size = static_cast<u16>(in_data.size() - real_offset); - std::memset(in_data.data() + real_offset, data.get(), rle_size); + std::memset(in_data.data() + real_offset, *data, rle_size); } else { // Standard Patch auto read = data_size; if (real_offset + read > in_data.size()) diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 1f4928562..0c1156989 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).value_or(0))); + exefs = update->GetExeFS(); } return exefs; @@ -237,7 +236,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content if (new_nca->GetStatus() == Loader::ResultStatus::Success && new_nca->GetRomFS() != nullptr) { LOG_INFO(Loader, " RomFS: Update ({}) applied successfully", - FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); + FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0))); romfs = new_nca->GetRomFS(); } } else if (update_raw != nullptr) { @@ -281,12 +280,11 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam } else { if (installed->HasEntry(update_tid, ContentRecordType::Program)) { const auto meta_ver = installed->GetEntryVersion(update_tid); - if (meta_ver == boost::none || meta_ver.get() == 0) { + if (meta_ver.value_or(0) == 0) { out.insert_or_assign("Update", ""); } else { out.insert_or_assign( - "Update", - FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements)); + "Update", FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements)); } } else if (update_raw != nullptr) { out.insert_or_assign("Update", "PACKED"); diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 29b100414..96302a241 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -159,28 +159,28 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { return file; } -static boost::optional<NcaID> CheckMapForContentRecord( +static std::optional<NcaID> CheckMapForContentRecord( const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) { if (map.find(title_id) == map.end()) - return boost::none; + return {}; const auto& cnmt = map.at(title_id); const auto iter = std::find_if(cnmt.GetContentRecords().begin(), cnmt.GetContentRecords().end(), [type](const ContentRecord& rec) { return rec.type == type; }); if (iter == cnmt.GetContentRecords().end()) - return boost::none; + return {}; - return boost::make_optional(iter->nca_id); + return std::make_optional(iter->nca_id); } -boost::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id, - ContentRecordType type) const { +std::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id, + ContentRecordType type) const { if (type == ContentRecordType::Meta && meta_id.find(title_id) != meta_id.end()) return meta_id.at(title_id); const auto res1 = CheckMapForContentRecord(yuzu_meta, title_id, type); - if (res1 != boost::none) + if (res1) return res1; return CheckMapForContentRecord(meta, title_id, type); } @@ -283,17 +283,14 @@ bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const { VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { const auto id = GetNcaIDFromMetadata(title_id, type); - if (id == boost::none) - return nullptr; - - return GetFileAtID(id.get()); + return id ? GetFileAtID(*id) : nullptr; } VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const { return GetEntryUnparsed(entry.title_id, entry.type); } -boost::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { +std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { const auto meta_iter = meta.find(title_id); if (meta_iter != meta.end()) return meta_iter->second.GetTitleVersion(); @@ -302,15 +299,12 @@ boost::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { if (yuzu_meta_iter != yuzu_meta.end()) return yuzu_meta_iter->second.GetTitleVersion(); - return boost::none; + return {}; } VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const { const auto id = GetNcaIDFromMetadata(title_id, type); - if (id == boost::none) - return nullptr; - - return parser(GetFileAtID(id.get()), id.get()); + return id ? parser(GetFileAtID(*id), *id) : nullptr; } VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const { @@ -364,8 +358,8 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntries() const { } std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter( - boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type, - boost::optional<u64> title_id) const { + std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type, + std::optional<u64> title_id) const { std::vector<RegisteredCacheEntry> out; IterateAllMetadata<RegisteredCacheEntry>( out, @@ -373,11 +367,11 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter( return RegisteredCacheEntry{c.GetTitleID(), r.type}; }, [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { - if (title_type != boost::none && title_type.get() != c.GetType()) + if (title_type && *title_type != c.GetType()) return false; - if (record_type != boost::none && record_type.get() != r.type) + if (record_type && *record_type != r.type) return false; - if (title_id != boost::none && title_id.get() != c.GetTitleID()) + if (title_id && *title_id != c.GetTitleID()) return false; return true; }); @@ -459,7 +453,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy, bool overwrite_if_exists, - boost::optional<NcaID> override_id) { + std::optional<NcaID> override_id) { const auto in = nca->GetBaseFile(); Core::Crypto::SHA256Hash hash{}; @@ -468,12 +462,12 @@ InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const Vfs // game is massive), we're going to cheat and only hash the first MB of the NCA. // Also, for XCIs the NcaID matters, so if the override id isn't none, use that. NcaID id{}; - if (override_id == boost::none) { + if (override_id) { + id = *override_id; + } else { const auto& data = in->ReadBytes(0x100000); mbedtls_sha256(data.data(), data.size(), hash.data(), 0); memcpy(id.data(), hash.data(), 16); - } else { - id = override_id.get(); } std::string path = GetRelativePathFromNcaID(id, false, true); @@ -543,14 +537,14 @@ bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const { return HasEntry(entry.title_id, entry.type); } -boost::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const { +std::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const { for (const auto& c : caches) { const auto res = c->GetEntryVersion(title_id); - if (res != boost::none) + if (res) return res; } - return boost::none; + return {}; } VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { @@ -609,8 +603,8 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const { } std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter( - boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type, - boost::optional<u64> title_id) const { + std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type, + std::optional<u64> title_id) const { std::vector<RegisteredCacheEntry> out; for (const auto& c : caches) { c->IterateAllMetadata<RegisteredCacheEntry>( @@ -619,11 +613,11 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter( return RegisteredCacheEntry{c.GetTitleID(), r.type}; }, [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { - if (title_type != boost::none && title_type.get() != c.GetType()) + if (title_type && *title_type != c.GetType()) return false; - if (record_type != boost::none && record_type.get() != r.type) + if (record_type && *record_type != r.type) return false; - if (title_id != boost::none && title_id.get() != c.GetTitleID()) + if (title_id && *title_id != c.GetTitleID()) return false; return true; }); diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 5beceffb3..6cfb16017 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -84,7 +84,7 @@ public: bool HasEntry(u64 title_id, ContentRecordType type) const; bool HasEntry(RegisteredCacheEntry entry) const; - boost::optional<u32> GetEntryVersion(u64 title_id) const; + std::optional<u32> GetEntryVersion(u64 title_id) const; VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const; @@ -96,11 +96,10 @@ public: std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; std::vector<RegisteredCacheEntry> ListEntries() const; - // If a parameter is not boost::none, it will be filtered for from all entries. + // If a parameter is not std::nullopt, it will be filtered for from all entries. std::vector<RegisteredCacheEntry> ListEntriesFilter( - boost::optional<TitleType> title_type = boost::none, - boost::optional<ContentRecordType> record_type = boost::none, - boost::optional<u64> title_id = boost::none) const; + std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, + std::optional<u64> title_id = {}) const; // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure // there is a meta NCA and all of them are accessible. @@ -125,12 +124,11 @@ private: std::vector<NcaID> AccumulateFiles() const; void ProcessFiles(const std::vector<NcaID>& ids); void AccumulateYuzuMeta(); - boost::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const; + std::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const; VirtualFile GetFileAtID(NcaID id) const; VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const; InstallResult RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy, - bool overwrite_if_exists, - boost::optional<NcaID> override_id = boost::none); + bool overwrite_if_exists, std::optional<NcaID> override_id = {}); bool RawInstallYuzuMeta(const CNMT& cnmt); VirtualDir dir; @@ -153,7 +151,7 @@ public: bool HasEntry(u64 title_id, ContentRecordType type) const; bool HasEntry(RegisteredCacheEntry entry) const; - boost::optional<u32> GetEntryVersion(u64 title_id) const; + std::optional<u32> GetEntryVersion(u64 title_id) const; VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const; @@ -165,11 +163,10 @@ public: std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; std::vector<RegisteredCacheEntry> ListEntries() const; - // If a parameter is not boost::none, it will be filtered for from all entries. + // If a parameter is not std::nullopt, it will be filtered for from all entries. std::vector<RegisteredCacheEntry> ListEntriesFilter( - boost::optional<TitleType> title_type = boost::none, - boost::optional<ContentRecordType> record_type = boost::none, - boost::optional<u64> title_id = boost::none) const; + std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, + std::optional<u64> title_id = {}) const; private: std::vector<RegisteredCache*> caches; diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index 3824c74e0..7b584de7f 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -167,13 +167,13 @@ std::string VfsFile::GetExtension() const { VfsDirectory::~VfsDirectory() = default; -boost::optional<u8> VfsFile::ReadByte(std::size_t offset) const { +std::optional<u8> VfsFile::ReadByte(std::size_t offset) const { u8 out{}; std::size_t size = Read(&out, 1, offset); if (size == 1) return out; - return boost::none; + return {}; } std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index 09dc9f288..002f99d4e 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h @@ -4,13 +4,15 @@ #pragma once +#include <functional> #include <map> #include <memory> +#include <optional> #include <string> #include <string_view> #include <type_traits> #include <vector> -#include <boost/optional.hpp> + #include "common/common_types.h" #include "core/file_sys/vfs_types.h" @@ -103,8 +105,8 @@ public: // into file. Returns number of bytes successfully written. virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0; - // Reads exactly one byte at the offset provided, returning boost::none on error. - virtual boost::optional<u8> ReadByte(std::size_t offset = 0) const; + // Reads exactly one byte at the offset provided, returning std::nullopt on error. + virtual std::optional<u8> ReadByte(std::size_t offset = 0) const; // Reads size bytes starting at offset in file into a vector. virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const; // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp index a4c6719a0..c96f88488 100644 --- a/src/core/file_sys/vfs_offset.cpp +++ b/src/core/file_sys/vfs_offset.cpp @@ -57,11 +57,11 @@ std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); } -boost::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const { +std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const { if (r_offset < size) return file->ReadByte(offset + r_offset); - return boost::none; + return {}; } std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h index 8062702a7..f7b7a3256 100644 --- a/src/core/file_sys/vfs_offset.h +++ b/src/core/file_sys/vfs_offset.h @@ -29,7 +29,7 @@ public: bool IsReadable() const override; std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; - boost::optional<u8> ReadByte(std::size_t offset) const override; + std::optional<u8> ReadByte(std::size_t offset) const override; std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override; std::vector<u8> ReadAllBytes() const override; bool WriteByte(u8 data, std::size_t offset) override; diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h index 44fab51d1..9f5a90b1b 100644 --- a/src/core/file_sys/vfs_static.h +++ b/src/core/file_sys/vfs_static.h @@ -53,10 +53,10 @@ public: return 0; } - boost::optional<u8> ReadByte(std::size_t offset) const override { + std::optional<u8> ReadByte(std::size_t offset) const override { if (offset < size) return value; - return boost::none; + return {}; } std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override { diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index a4bfe2eb0..0a7142ada 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -117,8 +117,7 @@ public: AlignWithPadding(); - const bool request_has_domain_header{context.GetDomainMessageHeader() != nullptr}; - if (context.Session()->IsDomain() && request_has_domain_header) { + if (context.Session()->IsDomain() && context.HasDomainMessageHeader()) { IPC::DomainMessageHeader domain_header{}; domain_header.num_objects = num_domain_objects; PushRaw(domain_header); diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index f01491daa..a38e34b74 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -161,8 +161,12 @@ public: return buffer_c_desciptors; } - const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const { - return domain_message_header; + const IPC::DomainMessageHeader* GetDomainMessageHeader() const { + return domain_message_header.get(); + } + + bool HasDomainMessageHeader() const { + return domain_message_header != nullptr; } /// Helper function to read a buffer using the appropriate buffer descriptor diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 4b6b32dd5..1fd4ba5d2 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -32,7 +32,7 @@ namespace Kernel { */ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) { const auto proper_handle = static_cast<Handle>(thread_handle); - auto& system = Core::System::GetInstance(); + const auto& system = Core::System::GetInstance(); // Lock the global kernel mutex when we enter the kernel HLE. std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); @@ -90,7 +90,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_ /// The timer callback event, called when a timer is fired static void TimerCallback(u64 timer_handle, int cycles_late) { const auto proper_handle = static_cast<Handle>(timer_handle); - auto& system = Core::System::GetInstance(); + const auto& system = Core::System::GetInstance(); SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle); if (timer == nullptr) { diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index dd541ffcc..0743670ad 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -6,8 +6,6 @@ #include <utility> #include <vector> -#include <boost/range/algorithm_ext/erase.hpp> - #include "common/assert.h" #include "core/core.h" #include "core/hle/kernel/errors.h" diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 148478488..8d2616c79 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -202,6 +202,16 @@ public: return is_64bit_process; } + /// Gets the total running time of the process instance in ticks. + u64 GetCPUTimeTicks() const { + return total_process_running_time_ticks; + } + + /// Updates the total running time, adding the given ticks to it. + void UpdateCPUTimeTicks(u64 ticks) { + total_process_running_time_ticks += ticks; + } + /** * Loads process-specifics configuration info with metadata provided * by an executable. @@ -305,6 +315,9 @@ private: /// specified by metadata provided to the process during loading. bool is_64bit_process = true; + /// Total running time for the process in ticks. + u64 total_process_running_time_ticks = 0; + /// Per-process handle table for storing created object handles in. HandleTable handle_table; diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 1342c597e..5a5f4cef1 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -9,6 +9,7 @@ #include "common/logging/log.h" #include "core/arm/arm_interface.h" #include "core/core.h" +#include "core/core_timing.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/scheduler.h" @@ -34,6 +35,10 @@ Thread* Scheduler::GetCurrentThread() const { return current_thread.get(); } +u64 Scheduler::GetLastContextSwitchTicks() const { + return last_context_switch_time; +} + Thread* Scheduler::PopNextReadyThread() { Thread* next = nullptr; Thread* thread = GetCurrentThread(); @@ -54,7 +59,10 @@ Thread* Scheduler::PopNextReadyThread() { } void Scheduler::SwitchContext(Thread* new_thread) { - Thread* previous_thread = GetCurrentThread(); + Thread* const previous_thread = GetCurrentThread(); + Process* const previous_process = Core::CurrentProcess(); + + UpdateLastContextSwitchTime(previous_thread, previous_process); // Save context for previous thread if (previous_thread) { @@ -78,8 +86,6 @@ void Scheduler::SwitchContext(Thread* new_thread) { // Cancel any outstanding wakeup events for this thread new_thread->CancelWakeupTimer(); - auto* const previous_process = Core::CurrentProcess(); - current_thread = new_thread; ready_queue.remove(new_thread->GetPriority(), new_thread); @@ -102,6 +108,22 @@ void Scheduler::SwitchContext(Thread* new_thread) { } } +void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { + const u64 prev_switch_ticks = last_context_switch_time; + const u64 most_recent_switch_ticks = CoreTiming::GetTicks(); + const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; + + if (thread != nullptr) { + thread->UpdateCPUTimeTicks(update_ticks); + } + + if (process != nullptr) { + process->UpdateCPUTimeTicks(update_ticks); + } + + last_context_switch_time = most_recent_switch_ticks; +} + void Scheduler::Reschedule() { std::lock_guard<std::mutex> lock(scheduler_mutex); diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 2c94641ec..c63032b7d 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -17,6 +17,8 @@ class ARM_Interface; namespace Kernel { +class Process; + class Scheduler final { public: explicit Scheduler(Core::ARM_Interface& cpu_core); @@ -31,6 +33,9 @@ public: /// Gets the current running thread Thread* GetCurrentThread() const; + /// Gets the timestamp for the last context switch in ticks. + u64 GetLastContextSwitchTicks() const; + /// Adds a new thread to the scheduler void AddThread(SharedPtr<Thread> thread, u32 priority); @@ -64,6 +69,19 @@ private: */ void SwitchContext(Thread* new_thread); + /** + * Called on every context switch to update the internal timestamp + * This also updates the running time ticks for the given thread and + * process using the following difference: + * + * ticks += most_recent_ticks - last_context_switch_ticks + * + * The internal tick timestamp for the scheduler is simply the + * most recent tick count retrieved. No special arithmetic is + * applied to it. + */ + void UpdateLastContextSwitchTime(Thread* thread, Process* process); + /// Lists all thread ids that aren't deleted/etc. std::vector<SharedPtr<Thread>> thread_list; @@ -73,6 +91,7 @@ private: SharedPtr<Thread> current_thread = nullptr; Core::ARM_Interface& cpu_core; + u64 last_context_switch_time = 0; static std::mutex scheduler_mutex; }; diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 5fc320403..80897f3a4 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -63,7 +63,7 @@ void ServerSession::Acquire(Thread* thread) { } ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { - auto& domain_message_header = context.GetDomainMessageHeader(); + auto* const domain_message_header = context.GetDomainMessageHeader(); if (domain_message_header) { // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs context.SetDomainRequestHandlers(domain_request_handlers); @@ -111,7 +111,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { ResultCode result = RESULT_SUCCESS; // If the session has been converted to a domain, handle the domain request - if (IsDomain() && context.GetDomainMessageHeader()) { + if (IsDomain() && context.HasDomainMessageHeader()) { result = HandleDomainSyncRequest(context); // If there is no domain header, the regular session handler is used } else if (hle_handler != nullptr) { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index a5302d924..c7c579aaf 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -467,6 +467,37 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, info_sub_id, handle); + enum class GetInfoType : u64 { + // 1.0.0+ + AllowedCpuIdBitmask = 0, + AllowedThreadPrioBitmask = 1, + MapRegionBaseAddr = 2, + MapRegionSize = 3, + HeapRegionBaseAddr = 4, + HeapRegionSize = 5, + TotalMemoryUsage = 6, + TotalHeapUsage = 7, + IsCurrentProcessBeingDebugged = 8, + ResourceHandleLimit = 9, + IdleTickCount = 10, + RandomEntropy = 11, + PerformanceCounter = 0xF0000002, + // 2.0.0+ + ASLRRegionBaseAddr = 12, + ASLRRegionSize = 13, + NewMapRegionBaseAddr = 14, + NewMapRegionSize = 15, + // 3.0.0+ + IsVirtualAddressMemoryEnabled = 16, + PersonalMmHeapUsage = 17, + TitleId = 18, + // 4.0.0+ + PrivilegedProcessId = 19, + // 5.0.0+ + UserExceptionContextAddr = 20, + ThreadTickCount = 0xF0000002, + }; + const auto* current_process = Core::CurrentProcess(); const auto& vm_manager = current_process->VMManager(); @@ -529,6 +560,36 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) "(STUBBED) Attempted to query user exception context address, returned 0"); *result = 0; break; + case GetInfoType::ThreadTickCount: { + constexpr u64 num_cpus = 4; + if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { + return ERR_INVALID_COMBINATION_KERNEL; + } + + const auto thread = + current_process->GetHandleTable().Get<Thread>(static_cast<Handle>(handle)); + if (!thread) { + return ERR_INVALID_HANDLE; + } + + const auto& system = Core::System::GetInstance(); + const auto& scheduler = system.CurrentScheduler(); + const auto* const current_thread = scheduler.GetCurrentThread(); + const bool same_thread = current_thread == thread; + + const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks(); + u64 out_ticks = 0; + if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { + const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); + + out_ticks = thread_ticks + (CoreTiming::GetTicks() - prev_ctx_ticks); + } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { + out_ticks = CoreTiming::GetTicks() - prev_ctx_ticks; + } + + *result = out_ticks; + break; + } default: UNIMPLEMENTED(); } diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index 554a5e328..b06aac4ec 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h @@ -24,37 +24,6 @@ struct PageInfo { u64 flags; }; -/// Values accepted by svcGetInfo -enum class GetInfoType : u64 { - // 1.0.0+ - AllowedCpuIdBitmask = 0, - AllowedThreadPrioBitmask = 1, - MapRegionBaseAddr = 2, - MapRegionSize = 3, - HeapRegionBaseAddr = 4, - HeapRegionSize = 5, - TotalMemoryUsage = 6, - TotalHeapUsage = 7, - IsCurrentProcessBeingDebugged = 8, - ResourceHandleLimit = 9, - IdleTickCount = 10, - RandomEntropy = 11, - PerformanceCounter = 0xF0000002, - // 2.0.0+ - ASLRRegionBaseAddr = 12, - ASLRRegionSize = 13, - NewMapRegionBaseAddr = 14, - NewMapRegionSize = 15, - // 3.0.0+ - IsVirtualAddressMemoryEnabled = 16, - PersonalMmHeapUsage = 17, - TitleId = 18, - // 4.0.0+ - PrivilegedProcessId = 19, - // 5.0.0+ - UserExceptionContextAddr = 20, -}; - void CallSVC(u32 immediate); } // namespace Kernel diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 59bc9e0af..dd5cd9ced 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -4,9 +4,9 @@ #include <algorithm> #include <cinttypes> +#include <optional> #include <vector> -#include <boost/optional.hpp> #include <boost/range/algorithm_ext/erase.hpp> #include "common/assert.h" @@ -94,7 +94,7 @@ void Thread::CancelWakeupTimer() { CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle); } -static boost::optional<s32> GetNextProcessorId(u64 mask) { +static std::optional<s32> GetNextProcessorId(u64 mask) { for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) { if (mask & (1ULL << index)) { if (!Core::System::GetInstance().Scheduler(index).GetCurrentThread()) { @@ -142,7 +142,7 @@ void Thread::ResumeFromWait() { status = ThreadStatus::Ready; - boost::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask); + std::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask); if (!new_processor_id) { new_processor_id = processor_id; } @@ -369,7 +369,7 @@ void Thread::ChangeCore(u32 core, u64 mask) { return; } - boost::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)}; + std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)}; if (!new_processor_id) { new_processor_id = processor_id; diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index f4d7bd235..4a6e11239 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -258,6 +258,14 @@ public: return last_running_ticks; } + u64 GetTotalCPUTimeTicks() const { + return total_cpu_time_ticks; + } + + void UpdateCPUTimeTicks(u64 ticks) { + total_cpu_time_ticks += ticks; + } + s32 GetProcessorID() const { return processor_id; } @@ -378,7 +386,8 @@ private: u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application u32 current_priority = 0; ///< Current thread priority, can be temporarily changed - u64 last_running_ticks = 0; ///< CPU tick when thread was last running + u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks. + u64 last_running_ticks = 0; ///< CPU tick when thread was last running s32 processor_id = 0; diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 3cac1b4ff..c08394e4c 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -195,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) != std::nullopt; + return GetUserIndex(uuid).has_value(); } bool ProfileManager::UserExistsIndex(std::size_t index) const { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 59aafd616..ac3ff9f20 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 != std::nullopt); + ASSERT(uuid); params.current_user = uuid->uuid; IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 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/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index fd98d541d..630ebbfc7 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -31,7 +31,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) buffer_wait_event->Signal(); } -boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { +std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { // Only consider free buffers. Buffers become free once again after they've been Acquired // and Released by the compositor, see the NVFlinger::Compose method. @@ -44,7 +44,7 @@ boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { }); if (itr == queue.end()) { - return boost::none; + return {}; } itr->status = Buffer::Status::Dequeued; @@ -70,12 +70,12 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, itr->crop_rect = crop_rect; } -boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() { +std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) { return buffer.status == Buffer::Status::Queued; }); if (itr == queue.end()) - return boost::none; + return {}; itr->status = Buffer::Status::Acquired; return *itr; } diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index 50b767732..2fe81a560 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -4,8 +4,9 @@ #pragma once +#include <optional> #include <vector> -#include <boost/optional.hpp> + #include "common/common_funcs.h" #include "common/math_util.h" #include "common/swap.h" @@ -73,11 +74,11 @@ public: }; void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer); - boost::optional<u32> DequeueBuffer(u32 width, u32 height); + std::optional<u32> DequeueBuffer(u32 width, u32 height); const IGBPBuffer& RequestBuffer(u32 slot) const; void QueueBuffer(u32 slot, BufferTransformFlags transform, const MathUtil::Rectangle<int>& crop_rect); - boost::optional<const Buffer&> AcquireBuffer(); + std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); void ReleaseBuffer(u32 slot); u32 Query(QueryType type); diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index d47b6f659..214e6d1b3 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #include <algorithm> -#include <boost/optional.hpp> +#include <optional> #include "common/alignment.h" #include "common/assert.h" @@ -134,7 +134,7 @@ void NVFlinger::Compose() { MicroProfileFlip(); - if (buffer == boost::none) { + if (!buffer) { auto& system_instance = Core::System::GetInstance(); // There was no queued buffer to draw, render previous frame @@ -143,7 +143,7 @@ void NVFlinger::Compose() { continue; } - auto& igbp_buffer = buffer->igbp_buffer; + auto& igbp_buffer = buffer->get().igbp_buffer; // Now send the buffer to the GPU for drawing. // TODO(Subv): Support more than just disp0. The display device selection is probably based @@ -152,10 +152,10 @@ void NVFlinger::Compose() { ASSERT(nvdisp); nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format, - igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform, - buffer->crop_rect); + igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, + buffer->get().transform, buffer->get().crop_rect); - buffer_queue->ReleaseBuffer(buffer->slot); + buffer_queue->ReleaseBuffer(buffer->get().slot); } } diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp index c489da071..f0a831d45 100644 --- a/src/core/hle/service/usb/usb.cpp +++ b/src/core/hle/service/usb/usb.cpp @@ -132,11 +132,11 @@ public: // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "BindNoticeEvent"}, - {1, nullptr, "Unknown1"}, + {1, nullptr, "UnbindNoticeEvent"}, {2, nullptr, "GetStatus"}, {3, nullptr, "GetNotice"}, - {4, nullptr, "Unknown2"}, - {5, nullptr, "Unknown3"}, + {4, nullptr, "EnablePowerRequestNotice"}, + {5, nullptr, "DisablePowerRequestNotice"}, {6, nullptr, "ReplyPowerRequest"}, }; // clang-format on diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 184537daa..d764b2406 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -6,9 +6,10 @@ #include <array> #include <cstring> #include <memory> +#include <optional> #include <type_traits> #include <utility> -#include <boost/optional.hpp> + #include "common/alignment.h" #include "common/assert.h" #include "common/common_funcs.h" @@ -506,9 +507,9 @@ private: IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; const u32 width{request.data.width}; const u32 height{request.data.height}; - boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); + std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); - if (slot != boost::none) { + if (slot) { // Buffer is available IGBPDequeueBufferResponseParcel response{*slot}; ctx.WriteBuffer(response.Serialize()); @@ -520,7 +521,7 @@ private: Kernel::ThreadWakeupReason reason) { // Repeat TransactParcel DequeueBuffer when a buffer is available auto buffer_queue = nv_flinger->GetBufferQueue(id); - boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); + std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); IGBPDequeueBufferResponseParcel response{*slot}; ctx.WriteBuffer(response.Serialize()); IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index e562b3a04..7686634bf 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -6,10 +6,11 @@ #include <iosfwd> #include <memory> +#include <optional> #include <string> #include <utility> #include <vector> -#include <boost/optional.hpp> + #include "common/common_types.h" #include "core/file_sys/vfs.h" @@ -145,7 +146,7 @@ public: * information. * @returns A pair with the optional system mode, and and the status. */ - virtual std::pair<boost::optional<u32>, ResultStatus> LoadKernelSystemMode() { + virtual std::pair<std::optional<u32>, ResultStatus> LoadKernelSystemMode() { // 96MB allocated to the application. return std::make_pair(2, ResultStatus::Success); } 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/memory.cpp b/src/core/memory.cpp index 014298ed6..70abd856a 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -4,9 +4,9 @@ #include <algorithm> #include <cstring> +#include <optional> #include <utility> -#include <boost/optional.hpp> #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" diff --git a/src/core/memory_hook.h b/src/core/memory_hook.h index 0269c7ff1..940777107 100644 --- a/src/core/memory_hook.h +++ b/src/core/memory_hook.h @@ -5,7 +5,8 @@ #pragma once #include <memory> -#include <boost/optional.hpp> +#include <optional> + #include "common/common_types.h" namespace Memory { @@ -18,19 +19,19 @@ namespace Memory { * * A hook may be mapped to multiple regions of memory. * - * If a boost::none or false is returned from a function, the read/write request is passed through + * If a std::nullopt or false is returned from a function, the read/write request is passed through * to the underlying memory region. */ class MemoryHook { public: virtual ~MemoryHook(); - virtual boost::optional<bool> IsValidAddress(VAddr addr) = 0; + virtual std::optional<bool> IsValidAddress(VAddr addr) = 0; - virtual boost::optional<u8> Read8(VAddr addr) = 0; - virtual boost::optional<u16> Read16(VAddr addr) = 0; - virtual boost::optional<u32> Read32(VAddr addr) = 0; - virtual boost::optional<u64> Read64(VAddr addr) = 0; + virtual std::optional<u8> Read8(VAddr addr) = 0; + virtual std::optional<u16> Read16(VAddr addr) = 0; + virtual std::optional<u32> Read32(VAddr addr) = 0; + virtual std::optional<u64> Read64(VAddr addr) = 0; virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0; diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index 37e15bad0..9b8a44fa1 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp @@ -64,11 +64,11 @@ void TestEnvironment::ClearWriteRecords() { TestEnvironment::TestMemory::~TestMemory() {} -boost::optional<bool> TestEnvironment::TestMemory::IsValidAddress(VAddr addr) { +std::optional<bool> TestEnvironment::TestMemory::IsValidAddress(VAddr addr) { return true; } -boost::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) { +std::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) { const auto iter = data.find(addr); if (iter == data.end()) { @@ -79,15 +79,15 @@ boost::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) { return iter->second; } -boost::optional<u16> TestEnvironment::TestMemory::Read16(VAddr addr) { +std::optional<u16> TestEnvironment::TestMemory::Read16(VAddr addr) { return *Read8(addr) | static_cast<u16>(*Read8(addr + 1)) << 8; } -boost::optional<u32> TestEnvironment::TestMemory::Read32(VAddr addr) { +std::optional<u32> TestEnvironment::TestMemory::Read32(VAddr addr) { return *Read16(addr) | static_cast<u32>(*Read16(addr + 2)) << 16; } -boost::optional<u64> TestEnvironment::TestMemory::Read64(VAddr addr) { +std::optional<u64> TestEnvironment::TestMemory::Read64(VAddr addr) { return *Read32(addr) | static_cast<u64>(*Read32(addr + 4)) << 32; } diff --git a/src/tests/core/arm/arm_test_common.h b/src/tests/core/arm/arm_test_common.h index 5de8dab4e..0b7539601 100644 --- a/src/tests/core/arm/arm_test_common.h +++ b/src/tests/core/arm/arm_test_common.h @@ -64,12 +64,12 @@ private: ~TestMemory() override; - boost::optional<bool> IsValidAddress(VAddr addr) override; + std::optional<bool> IsValidAddress(VAddr addr) override; - boost::optional<u8> Read8(VAddr addr) override; - boost::optional<u16> Read16(VAddr addr) override; - boost::optional<u32> Read32(VAddr addr) override; - boost::optional<u64> Read64(VAddr addr) override; + std::optional<u8> Read8(VAddr addr) override; + std::optional<u16> Read16(VAddr addr) override; + std::optional<u32> Read32(VAddr addr) override; + std::optional<u64> Read64(VAddr addr) override; bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) override; diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 09ecc5bad..ddb1a1d69 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -51,6 +51,10 @@ add_library(video_core STATIC renderer_opengl/maxwell_to_gl.h renderer_opengl/renderer_opengl.cpp renderer_opengl/renderer_opengl.h + renderer_opengl/utils.cpp + renderer_opengl/utils.h + surface.cpp + surface.h textures/astc.cpp textures/astc.h textures/decoders.cpp diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index f1aa6091b..28e8c13aa 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -81,7 +81,7 @@ void GPU::ProcessCommandLists(const std::vector<CommandListHeader>& commands) { for (auto entry : commands) { Tegra::GPUVAddr address = entry.Address(); u32 size = entry.sz; - const boost::optional<VAddr> head_address = memory_manager->GpuToCpuAddress(address); + const std::optional<VAddr> head_address = memory_manager->GpuToCpuAddress(address); VAddr current_addr = *head_address; while (current_addr < *head_address + size * sizeof(CommandHeader)) { const CommandHeader header = {Memory::Read32(current_addr)}; diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 27ef865a2..7357d20d1 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -167,7 +167,7 @@ void Maxwell3D::ProcessQueryGet() { GPUVAddr sequence_address = regs.query.QueryAddress(); // Since the sequence address is given as a GPU VAddr, we have to convert it to an application // VAddr before writing. - boost::optional<VAddr> address = memory_manager.GpuToCpuAddress(sequence_address); + std::optional<VAddr> address = memory_manager.GpuToCpuAddress(sequence_address); // TODO(Subv): Support the other query units. ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, @@ -285,7 +285,7 @@ void Maxwell3D::ProcessCBData(u32 value) { // Don't allow writing past the end of the buffer. ASSERT(regs.const_buffer.cb_pos + sizeof(u32) <= regs.const_buffer.cb_size); - boost::optional<VAddr> address = + std::optional<VAddr> address = memory_manager.GpuToCpuAddress(buffer_address + regs.const_buffer.cb_pos); Memory::Write32(*address, value); @@ -298,7 +298,7 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { GPUVAddr tic_base_address = regs.tic.TICAddress(); GPUVAddr tic_address_gpu = tic_base_address + tic_index * sizeof(Texture::TICEntry); - boost::optional<VAddr> tic_address_cpu = memory_manager.GpuToCpuAddress(tic_address_gpu); + std::optional<VAddr> tic_address_cpu = memory_manager.GpuToCpuAddress(tic_address_gpu); Texture::TICEntry tic_entry; Memory::ReadBlock(*tic_address_cpu, &tic_entry, sizeof(Texture::TICEntry)); @@ -322,7 +322,7 @@ Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const { GPUVAddr tsc_base_address = regs.tsc.TSCAddress(); GPUVAddr tsc_address_gpu = tsc_base_address + tsc_index * sizeof(Texture::TSCEntry); - boost::optional<VAddr> tsc_address_cpu = memory_manager.GpuToCpuAddress(tsc_address_gpu); + std::optional<VAddr> tsc_address_cpu = memory_manager.GpuToCpuAddress(tsc_address_gpu); Texture::TSCEntry tsc_entry; Memory::ReadBlock(*tsc_address_cpu, &tsc_entry, sizeof(Texture::TSCEntry)); @@ -386,7 +386,7 @@ Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage, ASSERT(tex_info_address < tex_info_buffer.address + tex_info_buffer.size); - boost::optional<VAddr> tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address); + std::optional<VAddr> tex_address_cpu = memory_manager.GpuToCpuAddress(tex_info_address); Texture::TextureHandle tex_handle{Memory::Read32(*tex_address_cpu)}; Texture::FullTextureInfo tex_info{}; diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index d6978162a..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; @@ -1086,6 +1090,7 @@ 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); diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 141b9159b..27c011e6f 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -5,12 +5,11 @@ #pragma once #include <bitset> +#include <optional> #include <string> #include <tuple> #include <vector> -#include <boost/optional.hpp> - #include "common/assert.h" #include "common/bit_field.h" #include "common/common_types.h" @@ -579,6 +578,10 @@ union Instruction { } fmul32; union { + BitField<52, 1, u64> generates_cc; + } op_32; + + union { BitField<48, 1, u64> is_signed; } shift; @@ -1456,7 +1459,7 @@ public: Type type; }; - static boost::optional<const Matcher&> Decode(Instruction instr) { + static std::optional<std::reference_wrapper<const Matcher>> Decode(Instruction instr) { static const auto table{GetDecodeTable()}; const auto matches_instruction = [instr](const auto& matcher) { @@ -1464,7 +1467,8 @@ public: }; auto iter = std::find_if(table.begin(), table.end(), matches_instruction); - return iter != table.end() ? boost::optional<const Matcher&>(*iter) : boost::none; + return iter != table.end() ? std::optional<std::reference_wrapper<const Matcher>>(*iter) + : std::nullopt; } private: @@ -1658,4 +1662,4 @@ private: } }; -} // namespace Tegra::Shader
\ No newline at end of file +} // namespace Tegra::Shader diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp index 377bd66ab..f6af132fb 100644 --- a/src/video_core/macro_interpreter.cpp +++ b/src/video_core/macro_interpreter.cpp @@ -29,7 +29,7 @@ void MacroInterpreter::Execute(const std::vector<u32>& code, std::vector<u32> pa void MacroInterpreter::Reset() { registers = {}; pc = 0; - delayed_pc = boost::none; + delayed_pc = {}; method_address.raw = 0; parameters.clear(); // The next parameter index starts at 1, because $r1 already has the value of the first @@ -44,10 +44,10 @@ bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) { pc += 4; // Update the program counter if we were delayed - if (delayed_pc != boost::none) { + if (delayed_pc) { ASSERT(is_delay_slot); pc = *delayed_pc; - delayed_pc = boost::none; + delayed_pc = {}; } switch (opcode.operation) { diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro_interpreter.h index cee0baaf3..773684bde 100644 --- a/src/video_core/macro_interpreter.h +++ b/src/video_core/macro_interpreter.h @@ -5,8 +5,9 @@ #pragma once #include <array> +#include <optional> #include <vector> -#include <boost/optional.hpp> + #include "common/bit_field.h" #include "common/common_types.h" @@ -149,7 +150,7 @@ private: Engines::Maxwell3D& maxwell3d; u32 pc; ///< Current program counter - boost::optional<u32> + std::optional<u32> delayed_pc; ///< Program counter to execute at after the delay slot is executed. static constexpr std::size_t NumMacroRegisters = 8; diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 022d4ab74..90a8e825d 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -9,7 +9,7 @@ namespace Tegra { GPUVAddr MemoryManager::AllocateSpace(u64 size, u64 align) { - boost::optional<GPUVAddr> gpu_addr = FindFreeBlock(size, align); + std::optional<GPUVAddr> gpu_addr = FindFreeBlock(size, align); ASSERT(gpu_addr); for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { @@ -34,7 +34,7 @@ GPUVAddr MemoryManager::AllocateSpace(GPUVAddr gpu_addr, u64 size, u64 align) { } GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) { - boost::optional<GPUVAddr> gpu_addr = FindFreeBlock(size, PAGE_SIZE); + std::optional<GPUVAddr> gpu_addr = FindFreeBlock(size, PAGE_SIZE); ASSERT(gpu_addr); for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { @@ -97,7 +97,7 @@ GPUVAddr MemoryManager::GetRegionEnd(GPUVAddr region_start) const { return {}; } -boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) { +std::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) { GPUVAddr gpu_addr = 0; u64 free_space = 0; align = (align + PAGE_MASK) & ~PAGE_MASK; @@ -118,7 +118,7 @@ boost::optional<GPUVAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) { return {}; } -boost::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) { +std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) { VAddr base_addr = PageSlot(gpu_addr); if (base_addr == static_cast<u64>(PageStatus::Allocated) || diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index caf80093f..b1255fd56 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -6,10 +6,9 @@ #include <array> #include <memory> +#include <optional> #include <vector> -#include <boost/optional.hpp> - #include "common/common_types.h" namespace Tegra { @@ -27,7 +26,7 @@ public: GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size); GPUVAddr UnmapBuffer(GPUVAddr gpu_addr, u64 size); GPUVAddr GetRegionEnd(GPUVAddr region_start) const; - boost::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr); + std::optional<VAddr> GpuToCpuAddress(GPUVAddr gpu_addr); std::vector<GPUVAddr> CpuToGpuAddress(VAddr cpu_addr) const; static constexpr u64 PAGE_BITS = 16; @@ -35,7 +34,7 @@ public: static constexpr u64 PAGE_MASK = PAGE_SIZE - 1; private: - boost::optional<GPUVAddr> FindFreeBlock(u64 size, u64 align = 1); + std::optional<GPUVAddr> FindFreeBlock(u64 size, u64 align = 1); bool IsPageMapped(GPUVAddr gpu_addr); VAddr& PageSlot(GPUVAddr gpu_addr); diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 2cd0738ff..669e26e15 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -6,7 +6,8 @@ #include <atomic> #include <memory> -#include <boost/optional.hpp> +#include <optional> + #include "common/common_types.h" #include "video_core/gpu.h" #include "video_core/rasterizer_interface.h" @@ -28,7 +29,8 @@ public: virtual ~RendererBase(); /// Swap buffers (render frame) - virtual void SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) = 0; + virtual void SwapBuffers( + std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) = 0; /// Initialize the renderer virtual bool Init() = 0; diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index c142095c5..41a54b3e7 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -17,7 +17,7 @@ OGLBufferCache::OGLBufferCache(std::size_t size) : stream_buffer(GL_ARRAY_BUFFER GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, std::size_t alignment, bool cache) { auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); - const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; + const std::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; // Cache management is a big overhead, so only cache entries with a given size. // TODO: Figure out which size is the best for given games. diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp index ee1d9601b..741f14bc3 100644 --- a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp +++ b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp @@ -45,7 +45,7 @@ GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size); auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); - const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; + const std::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; const u8* source{Memory::GetPointer(*cpu_addr)}; for (u32 primitive = 0; primitive < count / 4; ++primitive) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index cd4216c4e..75e31c6de 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -30,8 +30,8 @@ namespace OpenGL { using Maxwell = Tegra::Engines::Maxwell3D::Regs; -using PixelFormat = SurfaceParams::PixelFormat; -using SurfaceType = SurfaceParams::SurfaceType; +using PixelFormat = VideoCore::Surface::PixelFormat; +using SurfaceType = VideoCore::Surface::SurfaceType; MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192)); MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192)); @@ -401,7 +401,7 @@ void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb, bool preserve_contents, - boost::optional<std::size_t> single_color_target) { + std::optional<std::size_t> single_color_target) { MICROPROFILE_SCOPE(OpenGL_Framebuffer); const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; @@ -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) { @@ -692,7 +703,8 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, // Verify that the cached surface is the same size and format as the requested framebuffer const auto& params{surface->GetSurfaceParams()}; - const auto& pixel_format{SurfaceParams::PixelFormatFromGPUPixelFormat(config.pixel_format)}; + const auto& pixel_format{ + VideoCore::Surface::PixelFormatFromGPUPixelFormat(config.pixel_format)}; ASSERT_MSG(params.width == config.width, "Framebuffer width is different"); ASSERT_MSG(params.height == config.height, "Framebuffer height is different"); ASSERT_MSG(params.pixel_format == pixel_format, "Framebuffer pixel_format is different"); @@ -720,11 +732,15 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr if (mag_filter != config.mag_filter) { mag_filter = config.mag_filter; - glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, MaxwellToGL::TextureFilterMode(mag_filter)); + glSamplerParameteri( + s, GL_TEXTURE_MAG_FILTER, + MaxwellToGL::TextureFilterMode(mag_filter, Tegra::Texture::TextureMipmapFilter::None)); } - if (min_filter != config.min_filter) { + if (min_filter != config.min_filter || mip_filter != config.mip_filter) { min_filter = config.min_filter; - glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, MaxwellToGL::TextureFilterMode(min_filter)); + mip_filter = config.mip_filter; + glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, + MaxwellToGL::TextureFilterMode(min_filter, mip_filter)); } if (wrap_u != config.wrap_u) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 5020a5392..47097c569 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -8,12 +8,12 @@ #include <cstddef> #include <map> #include <memory> +#include <optional> #include <tuple> #include <utility> #include <vector> #include <boost/icl/interval_map.hpp> -#include <boost/optional.hpp> #include <boost/range/iterator_range.hpp> #include <glad/glad.h> @@ -93,6 +93,7 @@ private: private: Tegra::Texture::TextureFilter mag_filter; Tegra::Texture::TextureFilter min_filter; + Tegra::Texture::TextureMipmapFilter mip_filter; Tegra::Texture::WrapMode wrap_u; Tegra::Texture::WrapMode wrap_v; Tegra::Texture::WrapMode wrap_p; @@ -110,7 +111,7 @@ private: */ void ConfigureFramebuffers(bool use_color_fb = true, bool using_depth_fb = true, bool preserve_contents = true, - boost::optional<std::size_t> single_color_target = {}); + std::optional<std::size_t> single_color_target = {}); /* * Configures the current constbuffers to use for the draw command. diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 591ec7998..f194a7687 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -16,15 +16,21 @@ #include "core/settings.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_opengl/gl_rasterizer_cache.h" +#include "video_core/renderer_opengl/utils.h" +#include "video_core/surface.h" #include "video_core/textures/astc.h" #include "video_core/textures/decoders.h" #include "video_core/utils.h" namespace OpenGL { -using SurfaceType = SurfaceParams::SurfaceType; -using PixelFormat = SurfaceParams::PixelFormat; -using ComponentType = SurfaceParams::ComponentType; +using VideoCore::Surface::ComponentTypeFromDepthFormat; +using VideoCore::Surface::ComponentTypeFromRenderTarget; +using VideoCore::Surface::ComponentTypeFromTexture; +using VideoCore::Surface::PixelFormatFromDepthFormat; +using VideoCore::Surface::PixelFormatFromRenderTargetFormat; +using VideoCore::Surface::PixelFormatFromTextureFormat; +using VideoCore::Surface::SurfaceTargetFromTextureType; struct FormatTuple { GLint internal_format; @@ -34,34 +40,6 @@ struct FormatTuple { bool compressed; }; -static bool IsPixelFormatASTC(PixelFormat format) { - switch (format) { - case PixelFormat::ASTC_2D_4X4: - case PixelFormat::ASTC_2D_5X4: - case PixelFormat::ASTC_2D_8X8: - case PixelFormat::ASTC_2D_8X5: - return true; - default: - return false; - } -} - -static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) { - switch (format) { - case PixelFormat::ASTC_2D_4X4: - return {4, 4}; - case PixelFormat::ASTC_2D_5X4: - return {5, 4}; - case PixelFormat::ASTC_2D_8X8: - return {8, 8}; - case PixelFormat::ASTC_2D_8X5: - return {8, 5}; - default: - LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format)); - UNREACHABLE(); - } -} - void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()}; const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)}; @@ -78,27 +56,36 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { } } -std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { +std::size_t SurfaceParams::InnerMipmapMemorySize(u32 mip_level, bool force_gl, bool layer_only, + bool uncompressed) 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); + u32 m_width = MipWidth(mip_level); + u32 m_height = MipHeight(mip_level); + m_width = uncompressed ? m_width + : std::max(1U, (m_width + compression_factor - 1) / compression_factor); + m_height = uncompressed + ? m_height + : std::max(1U, (m_height + compression_factor - 1) / compression_factor); + m_depth = std::max(1U, m_depth >> mip_level); + u32 m_block_height = MipBlockHeight(mip_level); + u32 m_block_depth = MipBlockDepth(mip_level); + return Tegra::Texture::CalculateSize(force_gl ? false : is_tiled, bytes_per_pixel, m_width, + m_height, m_depth, m_block_height, m_block_depth); +} + +std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only, + bool uncompressed) const { + std::size_t block_size_bytes = Tegra::Texture::GetGOBSize() * block_height * block_depth; + std::size_t size = 0; + for (u32 i = 0; i < max_mip_level; i++) { + size += InnerMipmapMemorySize(i, force_gl, layer_only, uncompressed); + } + if (!force_gl && is_tiled) { + size = Common::AlignUp(size, block_size_bytes); } - return is_tiled ? Common::AlignUp(size, block_size_bytes) : size; + return size; } /*static*/ SurfaceParams SurfaceParams::CreateForTexture( @@ -108,8 +95,9 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { 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)); @@ -166,6 +154,8 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { 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; @@ -173,7 +163,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { params.unaligned_height = config.height; params.target = SurfaceTarget::Texture2D; params.depth = 1; - params.max_mip_level = 0; + params.max_mip_level = 1; params.is_layered = false; // Render target specific parameters, not used for caching @@ -201,12 +191,13 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { 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.max_mip_level = 1; params.is_layered = false; params.rt = {}; @@ -224,6 +215,8 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { 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; @@ -231,7 +224,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { params.unaligned_height = config.height; params.target = SurfaceTarget::Texture2D; params.depth = 1; - params.max_mip_level = 0; + params.max_mip_level = 1; params.rt = {}; params.InitCacheParameters(config.Address()); @@ -239,7 +232,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { return params; } -static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{ +static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format_tuples = {{ {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U {GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // ABGR8UI @@ -255,7 +248,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float, false}, // R11FG11FB10F {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RGBA32UI - {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, + {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXT1 {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXT23 @@ -289,14 +282,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_ALPHA_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 @@ -312,19 +320,19 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form ComponentType::Float, false}, // Z32FS8 }}; -static GLenum SurfaceTargetToGL(SurfaceParams::SurfaceTarget target) { +static GLenum SurfaceTargetToGL(SurfaceTarget target) { switch (target) { - case SurfaceParams::SurfaceTarget::Texture1D: + case SurfaceTarget::Texture1D: return GL_TEXTURE_1D; - case SurfaceParams::SurfaceTarget::Texture2D: + case SurfaceTarget::Texture2D: return GL_TEXTURE_2D; - case SurfaceParams::SurfaceTarget::Texture3D: + case SurfaceTarget::Texture3D: return GL_TEXTURE_3D; - case SurfaceParams::SurfaceTarget::Texture1DArray: + case SurfaceTarget::Texture1DArray: return GL_TEXTURE_1D_ARRAY; - case SurfaceParams::SurfaceTarget::Texture2DArray: + case SurfaceTarget::Texture2DArray: return GL_TEXTURE_2D_ARRAY; - case SurfaceParams::SurfaceTarget::TextureCubemap: + case SurfaceTarget::TextureCubemap: return GL_TEXTURE_CUBE_MAP; } LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target)); @@ -340,36 +348,19 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType return format; } -MathUtil::Rectangle<u32> SurfaceParams::GetRect() const { - u32 actual_height{unaligned_height}; +MathUtil::Rectangle<u32> SurfaceParams::GetRect(u32 mip_level) const { + u32 actual_height{std::max(1U, unaligned_height >> mip_level)}; if (IsPixelFormatASTC(pixel_format)) { // ASTC formats must stop at the ATSC block size boundary actual_height = Common::AlignDown(actual_height, GetASTCBlockSize(pixel_format).second); } - return {0, actual_height, width, 0}; -} - -/// Returns true if the specified PixelFormat is a BCn format, e.g. DXT or DXN -static bool IsFormatBCn(PixelFormat format) { - switch (format) { - case PixelFormat::DXT1: - case PixelFormat::DXT23: - case PixelFormat::DXT45: - case PixelFormat::DXN1: - case PixelFormat::DXN2SNORM: - case PixelFormat::DXN2UNORM: - case PixelFormat::BC7U: - case PixelFormat::BC6H_UF16: - case PixelFormat::BC6H_SF16: - return true; - } - return false; + return {0, actual_height, MipWidth(mip_level), 0}; } template <bool morton_to_gl, PixelFormat format> void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 depth, u8* gl_buffer, std::size_t gl_buffer_size, VAddr addr) { - constexpr u32 bytes_per_pixel = SurfaceParams::GetBytesPerPixel(format); + constexpr u32 bytes_per_pixel = GetBytesPerPixel(format); // With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual // pixel values. @@ -388,7 +379,7 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d } using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), - SurfaceParams::MaxPixelFormat>; + VideoCore::Surface::MaxPixelFormat>; static constexpr GLConversionArray morton_to_gl_fns = { // clang-format off @@ -432,7 +423,7 @@ static constexpr GLConversionArray morton_to_gl_fns = { 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>, @@ -440,6 +431,15 @@ static constexpr GLConversionArray morton_to_gl_fns = { 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>, @@ -491,7 +491,7 @@ static constexpr GLConversionArray gl_to_morton_fns = { 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>, @@ -499,6 +499,15 @@ static constexpr GLConversionArray gl_to_morton_fns = { 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>, @@ -508,28 +517,31 @@ static constexpr GLConversionArray gl_to_morton_fns = { }; void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params, - std::vector<u8>& gl_buffer) { - u32 depth = params.depth; - if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { + std::vector<u8>& gl_buffer, u32 mip_level) { + u32 depth = params.MipDepth(mip_level); + if (params.target == SurfaceTarget::Texture2D) { // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. depth = 1U; } if (params.is_layered) { - u64 offset = 0; + u64 offset = params.GetMipmapLevelOffset(mip_level); u64 offset_gl = 0; u64 layer_size = params.LayerMemorySize(); - u64 gl_size = params.LayerSizeGL(); - for (u32 i = 0; i < depth; i++) { + u64 gl_size = params.LayerSizeGL(mip_level); + for (u32 i = 0; i < params.depth; i++) { functions[static_cast<std::size_t>(params.pixel_format)]( - params.width, params.block_height, params.height, params.block_depth, 1, + params.MipWidth(mip_level), params.MipBlockHeight(mip_level), + params.MipHeight(mip_level), params.MipBlockDepth(mip_level), 1, gl_buffer.data() + offset_gl, gl_size, params.addr + offset); offset += layer_size; offset_gl += gl_size; } } else { + u64 offset = params.GetMipmapLevelOffset(mip_level); 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); + params.MipWidth(mip_level), params.MipBlockHeight(mip_level), + params.MipHeight(mip_level), params.MipBlockDepth(mip_level), depth, gl_buffer.data(), + gl_buffer.size(), params.addr + offset); } } @@ -546,19 +558,21 @@ 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{}; if (src_params.type == SurfaceType::ColorTexture) { switch (src_params.target) { - case SurfaceParams::SurfaceTarget::Texture2D: + case SurfaceTarget::Texture2D: glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, GL_TEXTURE_2D, src_surface->Texture().handle, 0); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); break; - case SurfaceParams::SurfaceTarget::TextureCubemap: + case SurfaceTarget::TextureCubemap: glFramebufferTexture2D( GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), @@ -567,12 +581,12 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0); break; - case SurfaceParams::SurfaceTarget::Texture2DArray: + case SurfaceTarget::Texture2DArray: glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, src_surface->Texture().handle, 0, 0); glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0); break; - case SurfaceParams::SurfaceTarget::Texture3D: + case SurfaceTarget::Texture3D: glFramebufferTexture3D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment, SurfaceTargetToGL(src_params.target), src_surface->Texture().handle, 0, 0); @@ -588,13 +602,13 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, } switch (dst_params.target) { - case SurfaceParams::SurfaceTarget::Texture2D: + case SurfaceTarget::Texture2D: glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, GL_TEXTURE_2D, dst_surface->Texture().handle, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); break; - case SurfaceParams::SurfaceTarget::TextureCubemap: + case SurfaceTarget::TextureCubemap: glFramebufferTexture2D( GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), @@ -603,13 +617,13 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0); break; - case SurfaceParams::SurfaceTarget::Texture2DArray: + case SurfaceTarget::Texture2DArray: glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, dst_surface->Texture().handle, 0, 0); glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0); break; - case SurfaceParams::SurfaceTarget::Texture3D: + case SurfaceTarget::Texture3D: glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment, SurfaceTargetToGL(dst_params.target), dst_surface->Texture().handle, 0, 0); @@ -730,21 +744,21 @@ static void CopySurface(const Surface& src_surface, const Surface& dst_surface, UNREACHABLE(); } else { switch (dst_params.target) { - case SurfaceParams::SurfaceTarget::Texture1D: + case SurfaceTarget::Texture1D: glTextureSubImage1D(dst_surface->Texture().handle, 0, 0, width, dest_format.format, dest_format.type, nullptr); break; - case SurfaceParams::SurfaceTarget::Texture2D: + case SurfaceTarget::Texture2D: glTextureSubImage2D(dst_surface->Texture().handle, 0, 0, 0, width, height, dest_format.format, dest_format.type, nullptr); break; - case SurfaceParams::SurfaceTarget::Texture3D: - case SurfaceParams::SurfaceTarget::Texture2DArray: + case SurfaceTarget::Texture3D: + case SurfaceTarget::Texture2DArray: glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 0, width, height, static_cast<GLsizei>(dst_params.depth), dest_format.format, dest_format.type, nullptr); break; - case SurfaceParams::SurfaceTarget::TextureCubemap: + case SurfaceTarget::TextureCubemap: glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, static_cast<GLint>(cubemap_face), width, height, 1, dest_format.format, dest_format.type, nullptr); @@ -781,35 +795,42 @@ CachedSurface::CachedSurface(const SurfaceParams& params) if (!format_tuple.compressed) { // Only pre-create the texture for non-compressed textures. switch (params.target) { - case SurfaceParams::SurfaceTarget::Texture1D: - glTexStorage1D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, - rect.GetWidth()); + case SurfaceTarget::Texture1D: + glTexStorage1D(SurfaceTargetToGL(params.target), params.max_mip_level, + format_tuple.internal_format, rect.GetWidth()); break; - case SurfaceParams::SurfaceTarget::Texture2D: - case SurfaceParams::SurfaceTarget::TextureCubemap: - glTexStorage2D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, - rect.GetWidth(), rect.GetHeight()); + case SurfaceTarget::Texture2D: + case SurfaceTarget::TextureCubemap: + glTexStorage2D(SurfaceTargetToGL(params.target), params.max_mip_level, + format_tuple.internal_format, rect.GetWidth(), rect.GetHeight()); break; - case SurfaceParams::SurfaceTarget::Texture3D: - case SurfaceParams::SurfaceTarget::Texture2DArray: - glTexStorage3D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format, - rect.GetWidth(), rect.GetHeight(), params.depth); + case SurfaceTarget::Texture3D: + case SurfaceTarget::Texture2DArray: + glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level, + format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(), + params.depth); break; default: LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", static_cast<u32>(params.target)); UNREACHABLE(); - glTexStorage2D(GL_TEXTURE_2D, 1, format_tuple.internal_format, rect.GetWidth(), - rect.GetHeight()); + glTexStorage2D(GL_TEXTURE_2D, params.max_mip_level, format_tuple.internal_format, + rect.GetWidth(), rect.GetHeight()); } } glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAX_LEVEL, + params.max_mip_level - 1); + if (params.max_mip_level == 1) { + glTexParameterf(SurfaceTargetToGL(params.target), GL_TEXTURE_LOD_BIAS, 1000.0); + } - VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, - SurfaceParams::SurfaceTargetName(params.target)); + LabelGLObject(GL_TEXTURE, texture.handle, params.addr, + SurfaceParams::SurfaceTargetName(params.target)); // Clamp size to mapped GPU memory region // TODO(bunnei): Super Mario Odyssey maps a 0x40000 byte region and then uses it for a 0x80000 @@ -839,7 +860,7 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bo S8Z24 s8z24_pixel{}; Z24S8 z24s8_pixel{}; - constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::S8Z24)}; + constexpr auto bpp{GetBytesPerPixel(PixelFormat::S8Z24)}; for (std::size_t y = 0; y < height; ++y) { for (std::size_t x = 0; x < width; ++x) { const std::size_t offset{bpp * (y * width + x)}; @@ -859,7 +880,7 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height, bo } static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { - constexpr auto bpp{SurfaceParams::GetBytesPerPixel(PixelFormat::G8R8U)}; + constexpr auto bpp{GetBytesPerPixel(PixelFormat::G8R8U)}; for (std::size_t y = 0; y < height; ++y) { for (std::size_t x = 0; x < width; ++x) { const std::size_t offset{bpp * (y * width + x)}; @@ -881,7 +902,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{}; @@ -913,7 +938,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(); @@ -929,20 +956,22 @@ static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& data, PixelForm MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); void CachedSurface::LoadGLBuffer() { MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); - - gl_buffer.resize(params.size_in_bytes_gl); + gl_buffer.resize(params.max_mip_level); + for (u32 i = 0; i < params.max_mip_level; i++) + gl_buffer[i].resize(params.GetMipmapSizeGL(i)); if (params.is_tiled) { ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", params.block_width, static_cast<u32>(params.target)); - - SwizzleFunc(morton_to_gl_fns, params, gl_buffer); + for (u32 i = 0; i < params.max_mip_level; i++) + SwizzleFunc(morton_to_gl_fns, params, gl_buffer[i], i); } else { const auto texture_src_data{Memory::GetPointer(params.addr)}; const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; - gl_buffer.assign(texture_src_data, texture_src_data_end); + gl_buffer[0].assign(texture_src_data, texture_src_data_end); } - - ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer, params.pixel_format, params.width, params.height); + for (u32 i = 0; i < params.max_mip_level; i++) + ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer[i], params.pixel_format, params.MipWidth(i), + params.MipHeight(i)); } MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); @@ -952,18 +981,19 @@ void CachedSurface::FlushGLBuffer() { ASSERT_MSG(!IsPixelFormatASTC(params.pixel_format), "Unimplemented"); // OpenGL temporary buffer needs to be big enough to store raw texture size - gl_buffer.resize(GetSizeInBytes()); + gl_buffer.resize(1); + gl_buffer[0].resize(GetSizeInBytes()); const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); // Ensure no bad interactions with GL_UNPACK_ALIGNMENT - ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0); + ASSERT(params.width * GetBytesPerPixel(params.pixel_format) % 4 == 0); glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width)); ASSERT(!tuple.compressed); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); - glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, gl_buffer.size(), - gl_buffer.data()); + glGetTextureImage(texture.handle, 0, tuple.format, tuple.type, + static_cast<GLsizei>(gl_buffer[0].size()), gl_buffer[0].data()); glPixelStorei(GL_PACK_ROW_LENGTH, 0); - ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width, + ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer[0], params.pixel_format, params.width, params.height); ASSERT(params.type != SurfaceType::Fill); const u8* const texture_src_data = Memory::GetPointer(params.addr); @@ -972,28 +1002,23 @@ void CachedSurface::FlushGLBuffer() { ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", params.block_width, static_cast<u32>(params.target)); - SwizzleFunc(gl_to_morton_fns, params, gl_buffer); + SwizzleFunc(gl_to_morton_fns, params, gl_buffer[0], 0); } else { - std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes()); + std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer[0].data(), GetSizeInBytes()); } } -MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); -void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) { - if (params.type == SurfaceType::Fill) - return; - - MICROPROFILE_SCOPE(OpenGL_TextureUL); - - const auto& rect{params.GetRect()}; +void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, + GLuint draw_fb_handle) { + const auto& rect{params.GetRect(mip_map)}; // Load data from memory to the surface const GLint x0 = static_cast<GLint>(rect.left); const GLint y0 = static_cast<GLint>(rect.bottom); std::size_t buffer_offset = - static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width + + static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.MipWidth(mip_map) + static_cast<std::size_t>(x0)) * - SurfaceParams::GetBytesPerPixel(params.pixel_format); + GetBytesPerPixel(params.pixel_format); const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); const GLuint target_tex = texture.handle; @@ -1009,88 +1034,116 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle cur_state.Apply(); // Ensure no bad interactions with GL_UNPACK_ALIGNMENT - ASSERT(params.width * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 == 0); - glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.width)); + ASSERT(params.MipWidth(mip_map) * GetBytesPerPixel(params.pixel_format) % 4 == 0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.MipWidth(mip_map))); + GLsizei image_size = static_cast<GLsizei>(params.GetMipmapSizeGL(mip_map, false)); glActiveTexture(GL_TEXTURE0); if (tuple.compressed) { switch (params.target) { - case SurfaceParams::SurfaceTarget::Texture2D: - glCompressedTexImage2D( - SurfaceTargetToGL(params.target), 0, tuple.internal_format, - static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0, - static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]); + case SurfaceTarget::Texture2D: + glCompressedTexImage2D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format, + static_cast<GLsizei>(params.MipWidth(mip_map)), + static_cast<GLsizei>(params.MipHeight(mip_map)), 0, image_size, + &gl_buffer[mip_map][buffer_offset]); + break; + case SurfaceTarget::Texture3D: + glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format, + static_cast<GLsizei>(params.MipWidth(mip_map)), + static_cast<GLsizei>(params.MipHeight(mip_map)), + static_cast<GLsizei>(params.MipDepth(mip_map)), 0, image_size, + &gl_buffer[mip_map][buffer_offset]); break; - case SurfaceParams::SurfaceTarget::Texture3D: - case SurfaceParams::SurfaceTarget::Texture2DArray: - glCompressedTexImage3D( - SurfaceTargetToGL(params.target), 0, tuple.internal_format, - static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), - static_cast<GLsizei>(params.depth), 0, - static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]); + case SurfaceTarget::Texture2DArray: + glCompressedTexImage3D(SurfaceTargetToGL(params.target), mip_map, tuple.internal_format, + static_cast<GLsizei>(params.MipWidth(mip_map)), + static_cast<GLsizei>(params.MipHeight(mip_map)), + static_cast<GLsizei>(params.depth), 0, image_size, + &gl_buffer[mip_map][buffer_offset]); break; - case SurfaceParams::SurfaceTarget::TextureCubemap: + case SurfaceTarget::TextureCubemap: { + GLsizei layer_size = static_cast<GLsizei>(params.LayerSizeGL(mip_map)); for (std::size_t face = 0; face < params.depth; ++face) { glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), - 0, tuple.internal_format, static_cast<GLsizei>(params.width), - static_cast<GLsizei>(params.height), 0, - static_cast<GLsizei>(params.SizeInBytesCubeFaceGL()), - &gl_buffer[buffer_offset]); - buffer_offset += params.SizeInBytesCubeFace(); + mip_map, tuple.internal_format, + static_cast<GLsizei>(params.MipWidth(mip_map)), + static_cast<GLsizei>(params.MipHeight(mip_map)), 0, + layer_size, &gl_buffer[mip_map][buffer_offset]); + buffer_offset += layer_size; } break; + } default: LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", static_cast<u32>(params.target)); UNREACHABLE(); - glCompressedTexImage2D( - GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), - static_cast<GLsizei>(params.height), 0, - static_cast<GLsizei>(params.size_in_bytes_gl), &gl_buffer[buffer_offset]); + glCompressedTexImage2D(GL_TEXTURE_2D, mip_map, tuple.internal_format, + static_cast<GLsizei>(params.MipWidth(mip_map)), + static_cast<GLsizei>(params.MipHeight(mip_map)), 0, + static_cast<GLsizei>(params.size_in_bytes_gl), + &gl_buffer[mip_map][buffer_offset]); } } else { switch (params.target) { - case SurfaceParams::SurfaceTarget::Texture1D: - glTexSubImage1D(SurfaceTargetToGL(params.target), 0, x0, + case SurfaceTarget::Texture1D: + glTexSubImage1D(SurfaceTargetToGL(params.target), mip_map, x0, static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type, - &gl_buffer[buffer_offset]); + &gl_buffer[mip_map][buffer_offset]); break; - case SurfaceParams::SurfaceTarget::Texture2D: - glTexSubImage2D(SurfaceTargetToGL(params.target), 0, x0, y0, + case SurfaceTarget::Texture2D: + glTexSubImage2D(SurfaceTargetToGL(params.target), mip_map, x0, y0, static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, - &gl_buffer[buffer_offset]); + &gl_buffer[mip_map][buffer_offset]); break; - case SurfaceParams::SurfaceTarget::Texture3D: - case SurfaceParams::SurfaceTarget::Texture2DArray: - glTexSubImage3D(SurfaceTargetToGL(params.target), 0, x0, y0, 0, + case SurfaceTarget::Texture3D: + glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0, + static_cast<GLsizei>(rect.GetWidth()), + static_cast<GLsizei>(rect.GetHeight()), params.MipDepth(mip_map), + tuple.format, tuple.type, &gl_buffer[mip_map][buffer_offset]); + break; + case SurfaceTarget::Texture2DArray: + glTexSubImage3D(SurfaceTargetToGL(params.target), mip_map, x0, y0, 0, static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format, - tuple.type, &gl_buffer[buffer_offset]); + tuple.type, &gl_buffer[mip_map][buffer_offset]); break; - case SurfaceParams::SurfaceTarget::TextureCubemap: + case SurfaceTarget::TextureCubemap: { + std::size_t start = buffer_offset; for (std::size_t face = 0; face < params.depth; ++face) { - glTexSubImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), 0, x0, - y0, static_cast<GLsizei>(rect.GetWidth()), + glTexSubImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), mip_map, + x0, y0, static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, - &gl_buffer[buffer_offset]); - buffer_offset += params.SizeInBytesCubeFace(); + &gl_buffer[mip_map][buffer_offset]); + buffer_offset += params.LayerSizeGL(mip_map); } break; + } default: LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", static_cast<u32>(params.target)); UNREACHABLE(); - glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()), + glTexSubImage2D(GL_TEXTURE_2D, mip_map, x0, y0, static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, - &gl_buffer[buffer_offset]); + &gl_buffer[mip_map][buffer_offset]); } } glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); } +MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); +void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) { + if (params.type == SurfaceType::Fill) + return; + + MICROPROFILE_SCOPE(OpenGL_TextureUL); + + for (u32 i = 0; i < params.max_mip_level; i++) + UploadGLMipmapTexture(i, read_fb_handle, draw_fb_handle); +} + RasterizerCacheOpenGL::RasterizerCacheOpenGL() { read_framebuffer.Create(); draw_framebuffer.Create(); @@ -1231,8 +1284,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, // For compatible surfaces, we can just do fast glCopyImageSubData based copy if (old_params.target == new_params.target && old_params.type == new_params.type && old_params.depth == new_params.depth && old_params.depth == 1 && - SurfaceParams::GetFormatBpp(old_params.pixel_format) == - SurfaceParams::GetFormatBpp(new_params.pixel_format)) { + GetFormatBpp(old_params.pixel_format) == GetFormatBpp(new_params.pixel_format)) { FastCopySurface(old_surface, new_surface); return new_surface; } @@ -1245,15 +1297,15 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, const bool is_blit{old_params.pixel_format == new_params.pixel_format}; switch (new_params.target) { - case SurfaceParams::SurfaceTarget::Texture2D: + case SurfaceTarget::Texture2D: if (is_blit) { BlitSurface(old_surface, new_surface, read_framebuffer.handle, draw_framebuffer.handle); } else { CopySurface(old_surface, new_surface, copy_pbo.handle); } break; - case SurfaceParams::SurfaceTarget::TextureCubemap: - case SurfaceParams::SurfaceTarget::Texture3D: + case SurfaceTarget::TextureCubemap: + case SurfaceTarget::Texture3D: AccurateCopySurface(old_surface, new_surface); break; default: @@ -1263,7 +1315,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 181acfc68..f255f4419 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -7,6 +7,7 @@ #include <array> #include <map> #include <memory> +#include <string> #include <vector> #include "common/alignment.h" @@ -18,6 +19,7 @@ #include "video_core/rasterizer_cache.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_shader_gen.h" +#include "video_core/surface.h" #include "video_core/textures/decoders.h" #include "video_core/textures/texture.h" @@ -27,126 +29,12 @@ class CachedSurface; using Surface = std::shared_ptr<CachedSurface>; using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>; -struct SurfaceParams { - enum class PixelFormat { - ABGR8U = 0, - ABGR8S = 1, - ABGR8UI = 2, - B5G6R5U = 3, - A2B10G10R10U = 4, - A1B5G5R5U = 5, - R8U = 6, - R8UI = 7, - RGBA16F = 8, - RGBA16U = 9, - RGBA16UI = 10, - R11FG11FB10F = 11, - RGBA32UI = 12, - DXT1 = 13, - DXT23 = 14, - DXT45 = 15, - DXN1 = 16, // This is also known as BC4 - DXN2UNORM = 17, - DXN2SNORM = 18, - BC7U = 19, - BC6H_UF16 = 20, - BC6H_SF16 = 21, - ASTC_2D_4X4 = 22, - G8R8U = 23, - G8R8S = 24, - BGRA8 = 25, - RGBA32F = 26, - RG32F = 27, - R32F = 28, - R16F = 29, - R16U = 30, - R16S = 31, - R16UI = 32, - R16I = 33, - RG16 = 34, - RG16F = 35, - RG16UI = 36, - RG16I = 37, - RG16S = 38, - RGB32F = 39, - SRGBA8 = 40, - RG8U = 41, - RG8S = 42, - RG32UI = 43, - R32UI = 44, - ASTC_2D_8X8 = 45, - ASTC_2D_8X5 = 46, - ASTC_2D_5X4 = 47, - - MaxColorFormat, - - // Depth formats - Z32F = 48, - Z16 = 49, - - MaxDepthFormat, - - // DepthStencil formats - Z24S8 = 50, - S8Z24 = 51, - Z32FS8 = 52, - - MaxDepthStencilFormat, - - Max = MaxDepthStencilFormat, - Invalid = 255, - }; - - static constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max); - - enum class ComponentType { - Invalid = 0, - SNorm = 1, - UNorm = 2, - SInt = 3, - UInt = 4, - Float = 5, - }; - - enum class SurfaceType { - ColorTexture = 0, - Depth = 1, - DepthStencil = 2, - Fill = 3, - Invalid = 4, - }; - - enum class SurfaceTarget { - Texture1D, - Texture2D, - Texture3D, - Texture1DArray, - Texture2DArray, - TextureCubemap, - }; - - static SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type) { - switch (texture_type) { - case Tegra::Texture::TextureType::Texture1D: - return SurfaceTarget::Texture1D; - case Tegra::Texture::TextureType::Texture2D: - case Tegra::Texture::TextureType::Texture2DNoMipmap: - return SurfaceTarget::Texture2D; - case Tegra::Texture::TextureType::Texture3D: - return SurfaceTarget::Texture3D; - case Tegra::Texture::TextureType::TextureCubemap: - return SurfaceTarget::TextureCubemap; - case Tegra::Texture::TextureType::Texture1DArray: - return SurfaceTarget::Texture1DArray; - case Tegra::Texture::TextureType::Texture2DArray: - return SurfaceTarget::Texture2DArray; - default: - LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", static_cast<u32>(texture_type)); - UNREACHABLE(); - return SurfaceTarget::Texture2D; - } - } +using SurfaceTarget = VideoCore::Surface::SurfaceTarget; +using SurfaceType = VideoCore::Surface::SurfaceType; +using PixelFormat = VideoCore::Surface::PixelFormat; +using ComponentType = VideoCore::Surface::ComponentType; +struct SurfaceParams { static std::string SurfaceTargetName(SurfaceTarget target) { switch (target) { case SurfaceTarget::Texture1D: @@ -168,631 +56,12 @@ 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 - * compressed image. This is used for maintaining proper surface sizes for compressed - * texture formats. - */ - static constexpr u32 GetCompressionFactor(PixelFormat format) { - if (format == PixelFormat::Invalid) - return 0; - - constexpr std::array<u32, MaxPixelFormat> compression_factor_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, // SRGBA8 - 1, // RG8U - 1, // RG8S - 1, // RG32UI - 1, // R32UI - 4, // ASTC_2D_8X8 - 4, // ASTC_2D_8X5 - 4, // ASTC_2D_5X4 - 1, // Z32F - 1, // Z16 - 1, // Z24S8 - 1, // S8Z24 - 1, // Z32FS8 - }}; - - ASSERT(static_cast<std::size_t>(format) < compression_factor_table.size()); - 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, // SRGBA8 - 1, // RG8U - 1, // RG8S - 1, // RG32UI - 1, // R32UI - 8, // ASTC_2D_8X8 - 5, // ASTC_2D_8X5 - 4, // ASTC_2D_5X4 - 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; - - constexpr std::array<u32, MaxPixelFormat> bpp_table = {{ - 32, // ABGR8U - 32, // ABGR8S - 32, // ABGR8UI - 16, // B5G6R5U - 32, // A2B10G10R10U - 16, // A1B5G5R5U - 8, // R8U - 8, // R8UI - 64, // RGBA16F - 64, // RGBA16U - 64, // RGBA16UI - 32, // R11FG11FB10F - 128, // RGBA32UI - 64, // DXT1 - 128, // DXT23 - 128, // DXT45 - 64, // DXN1 - 128, // DXN2UNORM - 128, // DXN2SNORM - 128, // BC7U - 128, // BC6H_UF16 - 128, // BC6H_SF16 - 32, // ASTC_2D_4X4 - 16, // G8R8U - 16, // G8R8S - 32, // BGRA8 - 128, // RGBA32F - 64, // RG32F - 32, // R32F - 16, // R16F - 16, // R16U - 16, // R16S - 16, // R16UI - 16, // R16I - 32, // RG16 - 32, // RG16F - 32, // RG16UI - 32, // RG16I - 32, // RG16S - 96, // RGB32F - 32, // SRGBA8 - 16, // RG8U - 16, // RG8S - 64, // RG32UI - 32, // R32UI - 16, // ASTC_2D_8X8 - 32, // ASTC_2D_8X5 - 32, // ASTC_2D_5X4 - 32, // Z32F - 16, // Z16 - 32, // Z24S8 - 32, // S8Z24 - 64, // Z32FS8 - }}; - - ASSERT(static_cast<std::size_t>(format) < bpp_table.size()); - return bpp_table[static_cast<std::size_t>(format)]; - } - u32 GetFormatBpp() const { - return GetFormatBpp(pixel_format); - } - - static PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) { - switch (format) { - case Tegra::DepthFormat::S8_Z24_UNORM: - return PixelFormat::S8Z24; - case Tegra::DepthFormat::Z24_S8_UNORM: - return PixelFormat::Z24S8; - case Tegra::DepthFormat::Z32_FLOAT: - return PixelFormat::Z32F; - case Tegra::DepthFormat::Z16_UNORM: - return PixelFormat::Z16; - case Tegra::DepthFormat::Z32_S8_X24_FLOAT: - return PixelFormat::Z32FS8; - default: - LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); - UNREACHABLE(); - } - } - - static PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) { - switch (format) { - // TODO (Hexagon12): Converting SRGBA to RGBA is a hack and doesn't completely correct the - // gamma. - case Tegra::RenderTargetFormat::RGBA8_SRGB: - case Tegra::RenderTargetFormat::RGBA8_UNORM: - return PixelFormat::ABGR8U; - case Tegra::RenderTargetFormat::RGBA8_SNORM: - return PixelFormat::ABGR8S; - case Tegra::RenderTargetFormat::RGBA8_UINT: - return PixelFormat::ABGR8UI; - case Tegra::RenderTargetFormat::BGRA8_SRGB: - case Tegra::RenderTargetFormat::BGRA8_UNORM: - return PixelFormat::BGRA8; - case Tegra::RenderTargetFormat::RGB10_A2_UNORM: - return PixelFormat::A2B10G10R10U; - case Tegra::RenderTargetFormat::RGBA16_FLOAT: - return PixelFormat::RGBA16F; - case Tegra::RenderTargetFormat::RGBA16_UNORM: - return PixelFormat::RGBA16U; - case Tegra::RenderTargetFormat::RGBA16_UINT: - return PixelFormat::RGBA16UI; - case Tegra::RenderTargetFormat::RGBA32_FLOAT: - return PixelFormat::RGBA32F; - case Tegra::RenderTargetFormat::RG32_FLOAT: - return PixelFormat::RG32F; - case Tegra::RenderTargetFormat::R11G11B10_FLOAT: - return PixelFormat::R11FG11FB10F; - case Tegra::RenderTargetFormat::B5G6R5_UNORM: - return PixelFormat::B5G6R5U; - case Tegra::RenderTargetFormat::BGR5A1_UNORM: - return PixelFormat::A1B5G5R5U; - case Tegra::RenderTargetFormat::RGBA32_UINT: - return PixelFormat::RGBA32UI; - case Tegra::RenderTargetFormat::R8_UNORM: - return PixelFormat::R8U; - case Tegra::RenderTargetFormat::R8_UINT: - return PixelFormat::R8UI; - case Tegra::RenderTargetFormat::RG16_FLOAT: - return PixelFormat::RG16F; - case Tegra::RenderTargetFormat::RG16_UINT: - return PixelFormat::RG16UI; - case Tegra::RenderTargetFormat::RG16_SINT: - return PixelFormat::RG16I; - case Tegra::RenderTargetFormat::RG16_UNORM: - return PixelFormat::RG16; - case Tegra::RenderTargetFormat::RG16_SNORM: - return PixelFormat::RG16S; - case Tegra::RenderTargetFormat::RG8_UNORM: - return PixelFormat::RG8U; - case Tegra::RenderTargetFormat::RG8_SNORM: - return PixelFormat::RG8S; - case Tegra::RenderTargetFormat::R16_FLOAT: - return PixelFormat::R16F; - case Tegra::RenderTargetFormat::R16_UNORM: - return PixelFormat::R16U; - case Tegra::RenderTargetFormat::R16_SNORM: - return PixelFormat::R16S; - case Tegra::RenderTargetFormat::R16_UINT: - return PixelFormat::R16UI; - case Tegra::RenderTargetFormat::R16_SINT: - return PixelFormat::R16I; - case Tegra::RenderTargetFormat::R32_FLOAT: - return PixelFormat::R32F; - case Tegra::RenderTargetFormat::R32_UINT: - return PixelFormat::R32UI; - case Tegra::RenderTargetFormat::RG32_UINT: - return PixelFormat::RG32UI; - default: - LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); - UNREACHABLE(); - } - } - - static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, - Tegra::Texture::ComponentType component_type) { - // TODO(Subv): Properly implement this - switch (format) { - case Tegra::Texture::TextureFormat::A8R8G8B8: - switch (component_type) { - case Tegra::Texture::ComponentType::UNORM: - return PixelFormat::ABGR8U; - case Tegra::Texture::ComponentType::SNORM: - return PixelFormat::ABGR8S; - case Tegra::Texture::ComponentType::UINT: - return PixelFormat::ABGR8UI; - } - LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", - static_cast<u32>(component_type)); - UNREACHABLE(); - case Tegra::Texture::TextureFormat::B5G6R5: - switch (component_type) { - case Tegra::Texture::ComponentType::UNORM: - return PixelFormat::B5G6R5U; - } - LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", - static_cast<u32>(component_type)); - UNREACHABLE(); - case Tegra::Texture::TextureFormat::A2B10G10R10: - switch (component_type) { - case Tegra::Texture::ComponentType::UNORM: - return PixelFormat::A2B10G10R10U; - } - LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", - static_cast<u32>(component_type)); - UNREACHABLE(); - case Tegra::Texture::TextureFormat::A1B5G5R5: - switch (component_type) { - case Tegra::Texture::ComponentType::UNORM: - return PixelFormat::A1B5G5R5U; - } - LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", - static_cast<u32>(component_type)); - UNREACHABLE(); - case Tegra::Texture::TextureFormat::R8: - switch (component_type) { - case Tegra::Texture::ComponentType::UNORM: - return PixelFormat::R8U; - case Tegra::Texture::ComponentType::UINT: - return PixelFormat::R8UI; - } - LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", - static_cast<u32>(component_type)); - UNREACHABLE(); - case Tegra::Texture::TextureFormat::G8R8: - switch (component_type) { - case Tegra::Texture::ComponentType::UNORM: - return PixelFormat::G8R8U; - case Tegra::Texture::ComponentType::SNORM: - return PixelFormat::G8R8S; - } - LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", - static_cast<u32>(component_type)); - UNREACHABLE(); - case Tegra::Texture::TextureFormat::R16_G16_B16_A16: - switch (component_type) { - case Tegra::Texture::ComponentType::UNORM: - return PixelFormat::RGBA16U; - case Tegra::Texture::ComponentType::FLOAT: - return PixelFormat::RGBA16F; - } - LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", - static_cast<u32>(component_type)); - UNREACHABLE(); - case Tegra::Texture::TextureFormat::BF10GF11RF11: - switch (component_type) { - case Tegra::Texture::ComponentType::FLOAT: - return PixelFormat::R11FG11FB10F; - } - LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", - static_cast<u32>(component_type)); - UNREACHABLE(); - case Tegra::Texture::TextureFormat::R32_G32_B32_A32: - switch (component_type) { - case Tegra::Texture::ComponentType::FLOAT: - return PixelFormat::RGBA32F; - case Tegra::Texture::ComponentType::UINT: - return PixelFormat::RGBA32UI; - } - LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", - static_cast<u32>(component_type)); - UNREACHABLE(); - case Tegra::Texture::TextureFormat::R32_G32: - switch (component_type) { - case Tegra::Texture::ComponentType::FLOAT: - return PixelFormat::RG32F; - case Tegra::Texture::ComponentType::UINT: - return PixelFormat::RG32UI; - } - LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", - static_cast<u32>(component_type)); - UNREACHABLE(); - case Tegra::Texture::TextureFormat::R32_G32_B32: - switch (component_type) { - case Tegra::Texture::ComponentType::FLOAT: - return PixelFormat::RGB32F; - } - LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", - static_cast<u32>(component_type)); - UNREACHABLE(); - case Tegra::Texture::TextureFormat::R16: - switch (component_type) { - case Tegra::Texture::ComponentType::FLOAT: - return PixelFormat::R16F; - case Tegra::Texture::ComponentType::UNORM: - return PixelFormat::R16U; - case Tegra::Texture::ComponentType::SNORM: - return PixelFormat::R16S; - case Tegra::Texture::ComponentType::UINT: - return PixelFormat::R16UI; - case Tegra::Texture::ComponentType::SINT: - return PixelFormat::R16I; - } - LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", - static_cast<u32>(component_type)); - UNREACHABLE(); - case Tegra::Texture::TextureFormat::R32: - switch (component_type) { - case Tegra::Texture::ComponentType::FLOAT: - return PixelFormat::R32F; - case Tegra::Texture::ComponentType::UINT: - return PixelFormat::R32UI; - } - LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", - static_cast<u32>(component_type)); - UNREACHABLE(); - case Tegra::Texture::TextureFormat::ZF32: - return PixelFormat::Z32F; - case Tegra::Texture::TextureFormat::Z16: - return PixelFormat::Z16; - case Tegra::Texture::TextureFormat::Z24S8: - return PixelFormat::Z24S8; - case Tegra::Texture::TextureFormat::DXT1: - return PixelFormat::DXT1; - case Tegra::Texture::TextureFormat::DXT23: - return PixelFormat::DXT23; - case Tegra::Texture::TextureFormat::DXT45: - return PixelFormat::DXT45; - case Tegra::Texture::TextureFormat::DXN1: - return PixelFormat::DXN1; - case Tegra::Texture::TextureFormat::DXN2: - switch (component_type) { - case Tegra::Texture::ComponentType::UNORM: - return PixelFormat::DXN2UNORM; - case Tegra::Texture::ComponentType::SNORM: - return PixelFormat::DXN2SNORM; - } - LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", - static_cast<u32>(component_type)); - UNREACHABLE(); - case Tegra::Texture::TextureFormat::BC7U: - return 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; - case Tegra::Texture::TextureFormat::ASTC_2D_5X4: - return PixelFormat::ASTC_2D_5X4; - case Tegra::Texture::TextureFormat::ASTC_2D_8X8: - return PixelFormat::ASTC_2D_8X8; - case Tegra::Texture::TextureFormat::ASTC_2D_8X5: - return PixelFormat::ASTC_2D_8X5; - case Tegra::Texture::TextureFormat::R16_G16: - switch (component_type) { - case Tegra::Texture::ComponentType::FLOAT: - return PixelFormat::RG16F; - case Tegra::Texture::ComponentType::UNORM: - return PixelFormat::RG16; - case Tegra::Texture::ComponentType::SNORM: - return PixelFormat::RG16S; - case Tegra::Texture::ComponentType::UINT: - return PixelFormat::RG16UI; - case Tegra::Texture::ComponentType::SINT: - return PixelFormat::RG16I; - } - LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", - static_cast<u32>(component_type)); - UNREACHABLE(); - default: - LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", - static_cast<u32>(format), static_cast<u32>(component_type)); - UNREACHABLE(); - } - } - - static ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) { - // TODO(Subv): Implement more component types - switch (type) { - case Tegra::Texture::ComponentType::UNORM: - return ComponentType::UNorm; - case Tegra::Texture::ComponentType::FLOAT: - return ComponentType::Float; - case Tegra::Texture::ComponentType::SNORM: - return ComponentType::SNorm; - case Tegra::Texture::ComponentType::UINT: - return ComponentType::UInt; - case Tegra::Texture::ComponentType::SINT: - return ComponentType::SInt; - default: - LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type)); - UNREACHABLE(); - } - } - - static ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) { - // TODO(Subv): Implement more render targets - switch (format) { - case Tegra::RenderTargetFormat::RGBA8_UNORM: - case Tegra::RenderTargetFormat::RGBA8_SRGB: - case Tegra::RenderTargetFormat::BGRA8_UNORM: - case Tegra::RenderTargetFormat::BGRA8_SRGB: - case Tegra::RenderTargetFormat::RGB10_A2_UNORM: - case Tegra::RenderTargetFormat::R8_UNORM: - case Tegra::RenderTargetFormat::RG16_UNORM: - case Tegra::RenderTargetFormat::R16_UNORM: - case Tegra::RenderTargetFormat::B5G6R5_UNORM: - case Tegra::RenderTargetFormat::BGR5A1_UNORM: - case Tegra::RenderTargetFormat::RG8_UNORM: - case Tegra::RenderTargetFormat::RGBA16_UNORM: - return ComponentType::UNorm; - case Tegra::RenderTargetFormat::RGBA8_SNORM: - case Tegra::RenderTargetFormat::RG16_SNORM: - case Tegra::RenderTargetFormat::R16_SNORM: - case Tegra::RenderTargetFormat::RG8_SNORM: - return ComponentType::SNorm; - case Tegra::RenderTargetFormat::RGBA16_FLOAT: - case Tegra::RenderTargetFormat::R11G11B10_FLOAT: - case Tegra::RenderTargetFormat::RGBA32_FLOAT: - case Tegra::RenderTargetFormat::RG32_FLOAT: - case Tegra::RenderTargetFormat::RG16_FLOAT: - case Tegra::RenderTargetFormat::R16_FLOAT: - case Tegra::RenderTargetFormat::R32_FLOAT: - return ComponentType::Float; - case Tegra::RenderTargetFormat::RGBA32_UINT: - case Tegra::RenderTargetFormat::RGBA16_UINT: - case Tegra::RenderTargetFormat::RG16_UINT: - case Tegra::RenderTargetFormat::R8_UINT: - case Tegra::RenderTargetFormat::R16_UINT: - case Tegra::RenderTargetFormat::RG32_UINT: - case Tegra::RenderTargetFormat::R32_UINT: - case Tegra::RenderTargetFormat::RGBA8_UINT: - return ComponentType::UInt; - case Tegra::RenderTargetFormat::RG16_SINT: - case Tegra::RenderTargetFormat::R16_SINT: - return ComponentType::SInt; - default: - LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); - UNREACHABLE(); - } - } - - static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) { - switch (format) { - case Tegra::FramebufferConfig::PixelFormat::ABGR8: - return PixelFormat::ABGR8U; - default: - LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); - UNREACHABLE(); - } - } - - static ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) { - switch (format) { - case Tegra::DepthFormat::Z16_UNORM: - case Tegra::DepthFormat::S8_Z24_UNORM: - case Tegra::DepthFormat::Z24_S8_UNORM: - return ComponentType::UNorm; - case Tegra::DepthFormat::Z32_FLOAT: - case Tegra::DepthFormat::Z32_S8_X24_FLOAT: - return ComponentType::Float; - default: - LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); - UNREACHABLE(); - } - } - - static SurfaceType GetFormatType(PixelFormat pixel_format) { - if (static_cast<std::size_t>(pixel_format) < - static_cast<std::size_t>(PixelFormat::MaxColorFormat)) { - return SurfaceType::ColorTexture; - } - - if (static_cast<std::size_t>(pixel_format) < - static_cast<std::size_t>(PixelFormat::MaxDepthFormat)) { - return SurfaceType::Depth; - } - - if (static_cast<std::size_t>(pixel_format) < - static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) { - return SurfaceType::DepthStencil; - } - - // TODO(Subv): Implement the other formats - ASSERT(false); - - return SurfaceType::Invalid; - } - - /// Returns the sizer in bytes of the specified pixel format - static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) { - if (pixel_format == SurfaceParams::PixelFormat::Invalid) { - return 0; - } - return GetFormatBpp(pixel_format) / CHAR_BIT; + return VideoCore::Surface::GetFormatBpp(pixel_format); } /// Returns the rectangle corresponding to this surface - MathUtil::Rectangle<u32> GetRect() const; + MathUtil::Rectangle<u32> GetRect(u32 mip_level = 0) const; /// Returns the total size of this surface in bytes, adjusted for compression std::size_t SizeInBytesRaw(bool ignore_tiled = false) const { @@ -823,7 +92,7 @@ struct SurfaceParams { /// 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); + std::size_t size = InnerMemorySize(false, is_layered); if (is_layered) return size * depth; return size; @@ -832,12 +101,78 @@ struct SurfaceParams { /// 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); + return InnerMemorySize(false, true); } /// Returns the size of a layer of this surface in OpenGL. - std::size_t LayerSizeGL() const { - return SizeInBytesRaw(true) / depth; + std::size_t LayerSizeGL(u32 mip_level) const { + return InnerMipmapMemorySize(mip_level, true, is_layered, false); + } + + std::size_t GetMipmapSizeGL(u32 mip_level, bool ignore_compressed = true) const { + std::size_t size = InnerMipmapMemorySize(mip_level, true, is_layered, ignore_compressed); + if (is_layered) + return size * depth; + return size; + } + + std::size_t GetMipmapLevelOffset(u32 mip_level) const { + std::size_t offset = 0; + for (u32 i = 0; i < mip_level; i++) + offset += InnerMipmapMemorySize(i, false, is_layered); + return offset; + } + + std::size_t GetMipmapLevelOffsetGL(u32 mip_level) const { + std::size_t offset = 0; + for (u32 i = 0; i < mip_level; i++) + offset += InnerMipmapMemorySize(i, true, is_layered); + return offset; + } + + u32 MipWidth(u32 mip_level) const { + return std::max(1U, width >> mip_level); + } + + u32 MipHeight(u32 mip_level) const { + return std::max(1U, height >> mip_level); + } + + u32 MipDepth(u32 mip_level) const { + return std::max(1U, depth >> mip_level); + } + + // Auto block resizing algorithm from: + // https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_miptree.c + u32 MipBlockHeight(u32 mip_level) const { + if (mip_level == 0) + return block_height; + u32 alt_height = MipHeight(mip_level); + u32 h = GetDefaultBlockHeight(pixel_format); + u32 blocks_in_y = (alt_height + h - 1) / h; + u32 bh = 16; + while (bh > 1 && blocks_in_y <= bh * 4) { + bh >>= 1; + } + return bh; + } + + u32 MipBlockDepth(u32 mip_level) const { + if (mip_level == 0) + return block_depth; + if (is_layered) + return 1; + u32 depth = MipDepth(mip_level); + u32 bd = 32; + while (bd > 1 && depth * 2 <= bd) { + bd >>= 1; + } + if (bd == 32) { + u32 bh = MipBlockHeight(mip_level); + if (bh >= 4) + return 16; + } + return bd; } /// Creates SurfaceParams from a texture configuration @@ -881,7 +216,7 @@ struct SurfaceParams { SurfaceTarget target; u32 max_mip_level; bool is_layered; - + bool srgb_conversion; // Parameters used for caching VAddr addr; Tegra::GPUVAddr gpu_addr; @@ -898,7 +233,10 @@ struct SurfaceParams { } rt; private: - std::size_t InnerMemorySize(bool layer_only = false) const; + std::size_t InnerMipmapMemorySize(u32 mip_level, bool force_gl = false, bool layer_only = false, + bool uncompressed = false) const; + std::size_t InnerMemorySize(bool force_gl = false, bool layer_only = false, + bool uncompressed = false) const; }; }; // namespace OpenGL @@ -960,8 +298,10 @@ public: void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); private: + void UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, GLuint draw_fb_handle); + OGLTexture texture; - std::vector<u8> gl_buffer; + std::vector<std::vector<u8>> gl_buffer; SurfaceParams params; GLenum gl_target; std::size_t cached_size_in_bytes; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 1a03a677f..9522fd344 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -8,6 +8,7 @@ #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_opengl/gl_shader_cache.h" #include "video_core/renderer_opengl/gl_shader_manager.h" +#include "video_core/renderer_opengl/utils.h" #include "video_core/utils.h" namespace OpenGL { @@ -89,7 +90,7 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type) shader.Create(program_result.first.c_str(), gl_type); program.Create(true, shader.handle); SetShaderUniformBlockBindings(program.handle); - VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr); + LabelGLObject(GL_PROGRAM, program.handle, addr); } else { // Store shader's code to lazily build it on draw geometry_programs.code = program_result.first; @@ -130,7 +131,7 @@ GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program, shader.Create(source.c_str(), GL_GEOMETRY_SHADER); target_program.Create(true, shader.handle); SetShaderUniformBlockBindings(target_program.handle); - VideoCore::LabelGLObject(GL_PROGRAM, target_program.handle, addr, debug_name); + LabelGLObject(GL_PROGRAM, target_program.handle, addr, debug_name); return target_program.handle; }; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index dec291a7d..601c41f31 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -3,12 +3,12 @@ // Refer to the license.txt file included. #include <map> +#include <optional> #include <set> #include <string> #include <string_view> #include <unordered_set> -#include <boost/optional.hpp> #include <fmt/format.h> #include "common/assert.h" @@ -144,7 +144,7 @@ private: for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { const Instruction instr = {program_code[offset]}; if (const auto opcode = OpCode::Decode(instr)) { - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::EXIT: { // The EXIT instruction can be predicated, which means that the shader can // conditionally end on this instruction. We have to consider the case where the @@ -341,10 +341,10 @@ public: */ void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value, u64 dest_num_components, u64 value_num_components, - bool is_saturated = false, u64 dest_elem = 0) { + bool is_saturated = false, u64 dest_elem = 0, bool precise = false) { SetRegister(reg, elem, is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value, - dest_num_components, value_num_components, dest_elem); + dest_num_components, value_num_components, dest_elem, precise); } /** @@ -368,11 +368,12 @@ public: const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')', - dest_num_components, value_num_components, dest_elem); + dest_num_components, value_num_components, dest_elem, false); if (sets_cc) { const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); + LOG_WARNING(HW_GPU, "Control Codes Imcomplete."); } } @@ -416,7 +417,7 @@ public: } }(); - SetRegister(reg, elem, result, dest_num_components, value_num_components, dest_elem); + SetRegister(reg, elem, result, dest_num_components, value_num_components, dest_elem, false); } /** @@ -430,7 +431,7 @@ public: */ void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute, const Tegra::Shader::IpaMode& input_mode, - boost::optional<Register> vertex = {}) { + std::optional<Register> vertex = {}) { const std::string dest = GetRegisterAsFloat(reg); const std::string src = GetInputAttribute(attribute, input_mode, vertex) + GetSwizzle(elem); shader.AddLine(dest + " = " + src + ';'); @@ -757,7 +758,8 @@ private: * @param dest_elem Optional, the destination element to use for the operation. */ void SetRegister(const Register& reg, u64 elem, const std::string& value, - u64 dest_num_components, u64 value_num_components, u64 dest_elem) { + u64 dest_num_components, u64 value_num_components, u64 dest_elem, + bool precise) { if (reg == Register::ZeroIndex) { LOG_CRITICAL(HW_GPU, "Cannot set Register::ZeroIndex"); UNREACHABLE(); @@ -774,7 +776,18 @@ private: src += GetSwizzle(elem); } - shader.AddLine(dest + " = " + src + ';'); + if (precise && stage != Maxwell3D::Regs::ShaderStage::Fragment) { + shader.AddLine('{'); + ++shader.scope; + // This avoids optimizations of constant propagation and keeps the code as the original + // Sadly using the precise keyword causes "linking" errors on fragment shaders. + shader.AddLine("precise float tmp = " + src + ';'); + shader.AddLine(dest + " = tmp;"); + --shader.scope; + shader.AddLine('}'); + } else { + shader.AddLine(dest + " = " + src + ';'); + } } /// Build the GLSL register list. @@ -795,10 +808,10 @@ private: /// Generates code representing an input attribute register. std::string GetInputAttribute(Attribute::Index attribute, const Tegra::Shader::IpaMode& input_mode, - boost::optional<Register> vertex = {}) { + std::optional<Register> vertex = {}) { auto GeometryPass = [&](const std::string& name) { if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) { - return "gs_" + name + '[' + GetRegisterAsInteger(vertex.value(), 0, false) + ']'; + return "gs_" + name + '[' + GetRegisterAsInteger(*vertex, 0, false) + ']'; } return name; }; @@ -1453,7 +1466,7 @@ private: } shader.AddLine( - fmt::format("// {}: {} (0x{:016x})", offset, opcode->GetName(), instr.value)); + fmt::format("// {}: {} (0x{:016x})", offset, opcode->get().GetName(), instr.value)); using Tegra::Shader::Pred; ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, @@ -1461,7 +1474,7 @@ private: // Some instructions (like SSY) don't have a predicate field, they are always // unconditionally executed. - bool can_be_predicated = OpCode::IsPredicatedInstruction(opcode->GetId()); + bool can_be_predicated = OpCode::IsPredicatedInstruction(opcode->get().GetId()); if (can_be_predicated && instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) { shader.AddLine("if (" + @@ -1471,7 +1484,7 @@ private: ++shader.scope; } - switch (opcode->GetType()) { + switch (opcode->get().GetType()) { case OpCode::Type::Arithmetic: { std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); @@ -1488,7 +1501,7 @@ private: } } - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::MOV_C: case OpCode::Id::MOV_R: { // MOV does not have neither 'abs' nor 'neg' bits. @@ -1510,8 +1523,13 @@ private: ASSERT_MSG(instr.fmul.cc == 0, "FMUL cc is not implemented"); op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); + regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, - instr.alu.saturate_d); + instr.alu.saturate_d, 0, true); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "FMUL Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::FADD_C: @@ -1519,8 +1537,13 @@ private: case OpCode::Id::FADD_IMM: { op_a = GetOperandAbsNeg(op_a, instr.alu.abs_a, instr.alu.negate_a); op_b = GetOperandAbsNeg(op_b, instr.alu.abs_b, instr.alu.negate_b); + regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, - instr.alu.saturate_d); + instr.alu.saturate_d, 0, true); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "FADD Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::MUFU: { @@ -1528,31 +1551,31 @@ private: switch (instr.sub_op) { case SubOp::Cos: regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1, - instr.alu.saturate_d); + instr.alu.saturate_d, 0, true); break; case SubOp::Sin: regs.SetRegisterToFloat(instr.gpr0, 0, "sin(" + op_a + ')', 1, 1, - instr.alu.saturate_d); + instr.alu.saturate_d, 0, true); break; case SubOp::Ex2: regs.SetRegisterToFloat(instr.gpr0, 0, "exp2(" + op_a + ')', 1, 1, - instr.alu.saturate_d); + instr.alu.saturate_d, 0, true); break; case SubOp::Lg2: regs.SetRegisterToFloat(instr.gpr0, 0, "log2(" + op_a + ')', 1, 1, - instr.alu.saturate_d); + instr.alu.saturate_d, 0, true); break; case SubOp::Rcp: regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1, - instr.alu.saturate_d); + instr.alu.saturate_d, 0, true); break; case SubOp::Rsq: regs.SetRegisterToFloat(instr.gpr0, 0, "inversesqrt(" + op_a + ')', 1, 1, - instr.alu.saturate_d); + instr.alu.saturate_d, 0, true); break; case SubOp::Sqrt: regs.SetRegisterToFloat(instr.gpr0, 0, "sqrt(" + op_a + ')', 1, 1, - instr.alu.saturate_d); + instr.alu.saturate_d, 0, true); break; default: LOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}", @@ -1573,7 +1596,11 @@ private: regs.SetRegisterToFloat(instr.gpr0, 0, '(' + condition + ") ? min(" + parameters + ") : max(" + parameters + ')', - 1, 1); + 1, 1, false, 0, true); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "FMNMX Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::RRO_C: @@ -1586,14 +1613,15 @@ private: break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", + opcode->get().GetName()); UNREACHABLE(); } } break; } case OpCode::Type::ArithmeticImmediate: { - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::MOV32_IMM: { regs.SetRegisterToFloat(instr.gpr0, 0, GetImmediate32(instr), 1, 1); break; @@ -1602,7 +1630,11 @@ private: regs.SetRegisterToFloat(instr.gpr0, 0, regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), - 1, 1, instr.fmul32.saturate); + 1, 1, instr.fmul32.saturate, 0, true); + if (instr.op_32.generates_cc) { + LOG_CRITICAL(HW_GPU, "FMUL32 Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::FADD32I: { @@ -1625,7 +1657,11 @@ private: op_b = "-(" + op_b + ')'; } - regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1); + regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, false, 0, true); + if (instr.op_32.generates_cc) { + LOG_CRITICAL(HW_GPU, "FADD32 Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } } @@ -1637,7 +1673,7 @@ private: std::string op_a = instr.bfe.negate_a ? "-" : ""; op_a += regs.GetRegisterAsInteger(instr.gpr8); - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::BFE_IMM: { std::string inner_shift = '(' + op_a + " << " + std::to_string(instr.bfe.GetLeftShiftValue()) + ')'; @@ -1646,10 +1682,14 @@ private: std::to_string(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position) + ')'; regs.SetRegisterToInteger(instr.gpr0, true, 0, outer_shift, 1, 1); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "BFE Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled BFE instruction: {}", opcode->get().GetName()); UNREACHABLE(); } } @@ -1671,7 +1711,7 @@ private: } } - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::SHR_C: case OpCode::Id::SHR_R: case OpCode::Id::SHR_IMM: { @@ -1683,15 +1723,23 @@ private: // Cast to int is superfluous for arithmetic shift, it's only for a logical shift regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')', 1, 1); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "SHR Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::SHL_C: case OpCode::Id::SHL_R: case OpCode::Id::SHL_IMM: regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " << " + op_b, 1, 1); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "SHL Generates an unhandled Control Code"); + UNREACHABLE(); + } break; default: { - LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled shift instruction: {}", opcode->get().GetName()); UNREACHABLE(); } } @@ -1701,13 +1749,17 @@ private: std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); std::string op_b = std::to_string(instr.alu.imm20_32.Value()); - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::IADD32I: if (instr.iadd32i.negate_a) op_a = "-(" + op_a + ')'; regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, instr.iadd32i.saturate != 0); + if (instr.op_32.generates_cc) { + LOG_CRITICAL(HW_GPU, "IADD32 Generates an unhandled Control Code"); + UNREACHABLE(); + } break; case OpCode::Id::LOP32I: { if (instr.alu.lop32i.invert_a) @@ -1719,11 +1771,15 @@ private: WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b, Tegra::Shader::PredicateResultMode::None, Tegra::Shader::Pred::UnusedIndex); + if (instr.op_32.generates_cc) { + LOG_CRITICAL(HW_GPU, "LOP32I Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } default: { LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticIntegerImmediate instruction: {}", - opcode->GetName()); + opcode->get().GetName()); UNREACHABLE(); } } @@ -1743,7 +1799,7 @@ private: } } - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::IADD_C: case OpCode::Id::IADD_R: case OpCode::Id::IADD_IMM: { @@ -1755,6 +1811,10 @@ private: regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1, instr.alu.saturate_d); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "IADD Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::IADD3_C: @@ -1779,7 +1839,7 @@ private: } }; - if (opcode->GetId() == OpCode::Id::IADD3_R) { + if (opcode->get().GetId() == OpCode::Id::IADD3_R) { apply_height(instr.iadd3.height_a, op_a); apply_height(instr.iadd3.height_b, op_b); apply_height(instr.iadd3.height_c, op_c); @@ -1795,7 +1855,7 @@ private: op_c = "-(" + op_c + ')'; std::string result; - if (opcode->GetId() == OpCode::Id::IADD3_R) { + if (opcode->get().GetId() == OpCode::Id::IADD3_R) { switch (instr.iadd3.mode) { case Tegra::Shader::IAdd3Mode::RightShift: // TODO(tech4me): According to @@ -1816,6 +1876,11 @@ private: } regs.SetRegisterToInteger(instr.gpr0, true, 0, result, 1, 1); + + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "IADD3 Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::ISCADD_C: @@ -1831,6 +1896,10 @@ private: regs.SetRegisterToInteger(instr.gpr0, true, 0, "((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "ISCADD Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::POPC_C: @@ -1862,6 +1931,10 @@ private: WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b, instr.alu.lop.pred_result_mode, instr.alu.lop.pred48); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "LOP Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::LOP3_C: @@ -1870,13 +1943,17 @@ private: const std::string op_c = regs.GetRegisterAsInteger(instr.gpr39); std::string lut; - if (opcode->GetId() == OpCode::Id::LOP3_R) { + if (opcode->get().GetId() == OpCode::Id::LOP3_R) { lut = '(' + std::to_string(instr.alu.lop3.GetImmLut28()) + ')'; } else { lut = '(' + std::to_string(instr.alu.lop3.GetImmLut48()) + ')'; } WriteLop3Instruction(instr.gpr0, op_a, op_b, op_c, lut); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "LOP3 Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::IMNMX_C: @@ -1891,6 +1968,10 @@ private: '(' + condition + ") ? min(" + parameters + ") : max(" + parameters + ')', 1, 1); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "IMNMX Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::LEA_R2: @@ -1900,7 +1981,7 @@ private: case OpCode::Id::LEA_HI: { std::string op_c; - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::LEA_R2: { op_a = regs.GetRegisterAsInteger(instr.gpr20); op_b = regs.GetRegisterAsInteger(instr.gpr39); @@ -1945,7 +2026,8 @@ private: op_b = regs.GetRegisterAsInteger(instr.gpr8); op_a = std::to_string(instr.lea.imm.entry_a); op_c = std::to_string(instr.lea.imm.entry_b); - LOG_CRITICAL(HW_GPU, "Unhandled LEA subinstruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled LEA subinstruction: {}", + opcode->get().GetName()); UNREACHABLE(); } } @@ -1960,7 +2042,7 @@ private: } default: { LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", - opcode->GetName()); + opcode->get().GetName()); UNREACHABLE(); } } @@ -1968,20 +2050,21 @@ private: break; } case OpCode::Type::ArithmeticHalf: { - if (opcode->GetId() == OpCode::Id::HADD2_C || opcode->GetId() == OpCode::Id::HADD2_R) { + if (opcode->get().GetId() == OpCode::Id::HADD2_C || + opcode->get().GetId() == OpCode::Id::HADD2_R) { ASSERT_MSG(instr.alu_half.ftz == 0, "Unimplemented"); } const bool negate_a = - opcode->GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; + opcode->get().GetId() != OpCode::Id::HMUL2_R && instr.alu_half.negate_a != 0; const bool negate_b = - opcode->GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0; + opcode->get().GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0; const std::string op_a = GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.alu_half.type_a, instr.alu_half.abs_a != 0, negate_a); std::string op_b; - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::HADD2_C: case OpCode::Id::HMUL2_C: op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, @@ -1999,7 +2082,7 @@ private: op_b = GetHalfFloat(op_b, instr.alu_half.type_b, instr.alu_half.abs_b != 0, negate_b); const std::string result = [&]() { - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::HADD2_C: case OpCode::Id::HADD2_R: return '(' + op_a + " + " + op_b + ')'; @@ -2007,7 +2090,8 @@ private: case OpCode::Id::HMUL2_R: return '(' + op_a + " * " + op_b + ')'; default: - LOG_CRITICAL(HW_GPU, "Unhandled half float instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled half float instruction: {}", + opcode->get().GetName()); UNREACHABLE(); return std::string("0"); } @@ -2018,7 +2102,7 @@ private: break; } case OpCode::Type::ArithmeticHalfImmediate: { - if (opcode->GetId() == OpCode::Id::HADD2_IMM) { + if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) { ASSERT_MSG(instr.alu_half_imm.ftz == 0, "Unimplemented"); } else { ASSERT_MSG(instr.alu_half_imm.precision == Tegra::Shader::HalfPrecision::None, @@ -2032,7 +2116,7 @@ private: const std::string op_b = UnpackHalfImmediate(instr, true); const std::string result = [&]() { - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::HADD2_IMM: return op_a + " + " + op_b; case OpCode::Id::HMUL2_IMM: @@ -2058,7 +2142,7 @@ private: ASSERT_MSG(instr.ffma.tab5980_1 == 0, "FFMA tab5980_1({}) not implemented", instr.ffma.tab5980_1.Value()); - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::FFMA_CR: { op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, GLSLRegister::Type::Float); @@ -2082,24 +2166,29 @@ private: break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->get().GetName()); UNREACHABLE(); } } - regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + " + " + op_c, 1, 1, - instr.alu.saturate_d); + regs.SetRegisterToFloat(instr.gpr0, 0, "fma(" + op_a + ", " + op_b + ", " + op_c + ')', + 1, 1, instr.alu.saturate_d, 0, true); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "FFMA Generates an unhandled Control Code"); + UNREACHABLE(); + } + break; } case OpCode::Type::Hfma2: { - if (opcode->GetId() == OpCode::Id::HFMA2_RR) { + if (opcode->get().GetId() == OpCode::Id::HFMA2_RR) { ASSERT_MSG(instr.hfma2.rr.precision == Tegra::Shader::HalfPrecision::None, "Unimplemented"); } else { ASSERT_MSG(instr.hfma2.precision == Tegra::Shader::HalfPrecision::None, "Unimplemented"); } - const bool saturate = opcode->GetId() == OpCode::Id::HFMA2_RR + const bool saturate = opcode->get().GetId() == OpCode::Id::HFMA2_RR ? instr.hfma2.rr.saturate != 0 : instr.hfma2.saturate != 0; @@ -2107,7 +2196,7 @@ private: GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr8, 0, false), instr.hfma2.type_a); std::string op_b, op_c; - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::HFMA2_CR: op_b = GetHalfFloat(regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, GLSLRegister::Type::UnsignedInteger), @@ -2145,7 +2234,7 @@ private: break; } case OpCode::Type::Conversion: { - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::I2I_R: { ASSERT_MSG(!instr.conversion.selector, "Unimplemented"); @@ -2193,6 +2282,11 @@ private: } regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1); + + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "I2F Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::F2F_R: { @@ -2231,6 +2325,11 @@ private: } regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d); + + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "F2F Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } case OpCode::Id::F2I_R: @@ -2280,17 +2379,22 @@ private: regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, 1, false, 0, instr.conversion.dest_size); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "F2I Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled conversion instruction: {}", + opcode->get().GetName()); UNREACHABLE(); } } break; } case OpCode::Type::Memory: { - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::LD_A: { // Note: Shouldn't this be interp mode flat? As in no interpolation made. ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, @@ -2934,7 +3038,7 @@ private: break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->get().GetName()); UNREACHABLE(); } } @@ -3028,7 +3132,7 @@ private: instr.hsetp2.abs_a, instr.hsetp2.negate_a); const std::string op_b = [&]() { - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::HSETP2_R: return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false), instr.hsetp2.type_b, instr.hsetp2.abs_a, @@ -3087,10 +3191,15 @@ private: regs.SetRegisterToFloat(instr.gpr0, 0, value, 1, 1); } + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "PSET Generates an unhandled Control Code"); + UNREACHABLE(); + } + break; } case OpCode::Type::PredicateSetPredicate: { - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::PSETP: { const std::string op_a = GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0); @@ -3136,7 +3245,8 @@ private: break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}", + opcode->get().GetName()); UNREACHABLE(); } } @@ -3224,7 +3334,7 @@ private: instr.hset2.abs_a != 0, instr.hset2.negate_a != 0); const std::string op_b = [&]() { - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::HSET2_R: return GetHalfFloat(regs.GetRegisterAsInteger(instr.gpr20, 0, false), instr.hset2.type_b, instr.hset2.abs_b != 0, @@ -3273,7 +3383,7 @@ private: const bool is_signed{instr.xmad.sign_a == 1}; bool is_merge{}; - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::XMAD_CR: { is_merge = instr.xmad.merge_56; op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset, @@ -3302,7 +3412,7 @@ private: break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled XMAD instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled XMAD instruction: {}", opcode->get().GetName()); UNREACHABLE(); } } @@ -3351,10 +3461,14 @@ private: } regs.SetRegisterToInteger(instr.gpr0, is_signed, 0, sum, 1, 1); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "XMAD Generates an unhandled Control Code"); + UNREACHABLE(); + } break; } default: { - switch (opcode->GetId()) { + switch (opcode->get().GetId()) { case OpCode::Id::EXIT: { if (stage == Maxwell3D::Regs::ShaderStage::Fragment) { EmitFragmentOutputsWrite(); @@ -3522,6 +3636,11 @@ private: regs.SetRegisterToInteger(instr.gpr0, result_signed, 1, result, 1, 1, instr.vmad.saturate == 1, 0, Register::Size::Word, instr.vmad.cc); + if (instr.generates_cc) { + LOG_CRITICAL(HW_GPU, "VMAD Generates an unhandled Control Code"); + UNREACHABLE(); + } + break; } case OpCode::Id::VSETP: { @@ -3549,7 +3668,7 @@ private: break; } default: { - LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); + LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->get().GetName()); UNREACHABLE(); } } @@ -3690,9 +3809,9 @@ std::string GetCommonDeclarations() { RasterizerOpenGL::MaxConstbufferSize / sizeof(GLvec4)); } -boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, - Maxwell3D::Regs::ShaderStage stage, - const std::string& suffix) { +std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, + Maxwell3D::Regs::ShaderStage stage, + const std::string& suffix) { try { const auto subroutines = ControlFlowAnalyzer(program_code, main_offset, suffix).GetSubroutines(); @@ -3701,7 +3820,7 @@ boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, } catch (const DecompileFail& exception) { LOG_ERROR(HW_GPU, "Shader decompilation failed: {}", exception.what()); } - return boost::none; + return {}; } } // namespace OpenGL::GLShader::Decompiler diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h index b20cc4bfa..d01a4a7ee 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.h +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h @@ -6,8 +6,8 @@ #include <array> #include <functional> +#include <optional> #include <string> -#include <boost/optional.hpp> #include "common/common_types.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_opengl/gl_shader_gen.h" @@ -18,8 +18,8 @@ using Tegra::Engines::Maxwell3D; std::string GetCommonDeclarations(); -boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, - Maxwell3D::Regs::ShaderStage stage, - const std::string& suffix); +std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u32 main_offset, + Maxwell3D::Regs::ShaderStage stage, + const std::string& suffix); } // namespace OpenGL::GLShader::Decompiler diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index dfb562706..9d17edd63 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -37,7 +37,7 @@ layout(std140) uniform vs_config { ProgramResult program = Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET, Maxwell3D::Regs::ShaderStage::Vertex, "vertex") - .get_value_or({}); + .value_or(ProgramResult()); out += program.first; @@ -45,7 +45,7 @@ layout(std140) uniform vs_config { ProgramResult program_b = Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET, Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b") - .get_value_or({}); + .value_or(ProgramResult()); out += program_b.first; } @@ -90,7 +90,7 @@ ProgramResult GenerateGeometryShader(const ShaderSetup& setup) { ProgramResult program = Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET, Maxwell3D::Regs::ShaderStage::Geometry, "geometry") - .get_value_or({}); + .value_or(ProgramResult()); out += R"( out gl_PerVertex { vec4 gl_Position; @@ -124,7 +124,7 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup) { ProgramResult program = Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET, Maxwell3D::Regs::ShaderStage::Fragment, "fragment") - .get_value_or({}); + .value_or(ProgramResult()); out += R"( layout(location = 0) out vec4 FragColor0; layout(location = 1) out vec4 FragColor1; diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index f9d41ca24..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; @@ -89,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) { diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 4334b0d35..9e2c573b5 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -36,6 +36,10 @@ 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 @@ -161,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; @@ -176,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/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index 0f6dcab2b..87d511c38 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h @@ -135,12 +135,29 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) { return {}; } -inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode) { +inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode, + Tegra::Texture::TextureMipmapFilter mip_filter_mode) { switch (filter_mode) { - case Tegra::Texture::TextureFilter::Linear: - return GL_LINEAR; - case Tegra::Texture::TextureFilter::Nearest: - return GL_NEAREST; + case Tegra::Texture::TextureFilter::Linear: { + switch (mip_filter_mode) { + case Tegra::Texture::TextureMipmapFilter::None: + return GL_LINEAR; + case Tegra::Texture::TextureMipmapFilter::Nearest: + return GL_NEAREST_MIPMAP_LINEAR; + case Tegra::Texture::TextureMipmapFilter::Linear: + return GL_LINEAR_MIPMAP_LINEAR; + } + } + case Tegra::Texture::TextureFilter::Nearest: { + switch (mip_filter_mode) { + case Tegra::Texture::TextureMipmapFilter::None: + return GL_NEAREST; + case Tegra::Texture::TextureMipmapFilter::Nearest: + return GL_NEAREST_MIPMAP_NEAREST; + case Tegra::Texture::TextureMipmapFilter::Linear: + return GL_LINEAR_MIPMAP_NEAREST; + } + } } LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode={}", static_cast<u32>(filter_mode)); diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 96d916b07..ea38da932 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -115,7 +115,8 @@ RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& window) RendererOpenGL::~RendererOpenGL() = default; /// Swap buffers (render frame) -void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) { +void RendererOpenGL::SwapBuffers( + std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) { ScopeAcquireGLContext acquire_context{render_window}; Core::System::GetInstance().GetPerfStats().EndSystemFrame(); @@ -124,11 +125,11 @@ void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig& OpenGLState prev_state = OpenGLState::GetCurState(); state.Apply(); - if (framebuffer != boost::none) { + if (framebuffer) { // If framebuffer is provided, reload it from memory to a texture - if (screen_info.texture.width != (GLsizei)framebuffer->width || - screen_info.texture.height != (GLsizei)framebuffer->height || - screen_info.texture.pixel_format != framebuffer->pixel_format) { + if (screen_info.texture.width != (GLsizei)framebuffer->get().width || + screen_info.texture.height != (GLsizei)framebuffer->get().height || + screen_info.texture.pixel_format != framebuffer->get().pixel_format) { // Reallocate texture if the framebuffer size has changed. // This is expected to not happen very often and hence should not be a // performance problem. @@ -283,7 +284,8 @@ void RendererOpenGL::CreateRasterizer() { if (rasterizer) { return; } - + // Initialize sRGB Usage + OpenGLState::ClearsRGBUsed(); rasterizer = std::make_unique<RasterizerOpenGL>(render_window, screen_info); } @@ -356,13 +358,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/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 961467a62..c0868c0e4 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -51,7 +51,8 @@ public: ~RendererOpenGL() override; /// Swap buffers (render frame) - void SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) override; + void SwapBuffers( + std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) override; /// Initialize the renderer bool Init() override; diff --git a/src/video_core/renderer_opengl/utils.cpp b/src/video_core/renderer_opengl/utils.cpp new file mode 100644 index 000000000..d84634cb3 --- /dev/null +++ b/src/video_core/renderer_opengl/utils.cpp @@ -0,0 +1,38 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <string> +#include <fmt/format.h> +#include <glad/glad.h> +#include "common/common_types.h" +#include "video_core/renderer_opengl/utils.h" + +namespace OpenGL { + +void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info) { + if (!GLAD_GL_KHR_debug) { + return; // We don't need to throw an error as this is just for debugging + } + const std::string nice_addr = fmt::format("0x{:016x}", addr); + std::string object_label; + + if (extra_info.empty()) { + switch (identifier) { + case GL_TEXTURE: + object_label = "Texture@" + nice_addr; + break; + case GL_PROGRAM: + object_label = "Shader@" + nice_addr; + break; + default: + object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr); + break; + } + } else { + object_label = extra_info + '@' + nice_addr; + } + glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str())); +} + +} // namespace OpenGL
\ No newline at end of file diff --git a/src/video_core/renderer_opengl/utils.h b/src/video_core/renderer_opengl/utils.h new file mode 100644 index 000000000..1fcb6fc11 --- /dev/null +++ b/src/video_core/renderer_opengl/utils.h @@ -0,0 +1,15 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <glad/glad.h> +#include "common/common_types.h" + +namespace OpenGL { + +void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, std::string extra_info = ""); + +} // namespace OpenGL
\ No newline at end of file diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp new file mode 100644 index 000000000..d9a97e30b --- /dev/null +++ b/src/video_core/surface.cpp @@ -0,0 +1,499 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/common_types.h" +#include "common/math_util.h" +#include "video_core/surface.h" + +namespace VideoCore::Surface { + +SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type) { + switch (texture_type) { + case Tegra::Texture::TextureType::Texture1D: + return SurfaceTarget::Texture1D; + case Tegra::Texture::TextureType::Texture2D: + case Tegra::Texture::TextureType::Texture2DNoMipmap: + return SurfaceTarget::Texture2D; + case Tegra::Texture::TextureType::Texture3D: + return SurfaceTarget::Texture3D; + case Tegra::Texture::TextureType::TextureCubemap: + return SurfaceTarget::TextureCubemap; + case Tegra::Texture::TextureType::Texture1DArray: + return SurfaceTarget::Texture1DArray; + case Tegra::Texture::TextureType::Texture2DArray: + return SurfaceTarget::Texture2DArray; + default: + LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", static_cast<u32>(texture_type)); + UNREACHABLE(); + return SurfaceTarget::Texture2D; + } +} + +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; + } +} + +PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format) { + switch (format) { + case Tegra::DepthFormat::S8_Z24_UNORM: + return PixelFormat::S8Z24; + case Tegra::DepthFormat::Z24_S8_UNORM: + return PixelFormat::Z24S8; + case Tegra::DepthFormat::Z32_FLOAT: + return PixelFormat::Z32F; + case Tegra::DepthFormat::Z16_UNORM: + return PixelFormat::Z16; + case Tegra::DepthFormat::Z32_S8_X24_FLOAT: + return PixelFormat::Z32FS8; + default: + LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); + UNREACHABLE(); + } +} + +PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) { + switch (format) { + // 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: + return PixelFormat::ABGR8S; + 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: + return PixelFormat::A2B10G10R10U; + case Tegra::RenderTargetFormat::RGBA16_FLOAT: + return PixelFormat::RGBA16F; + case Tegra::RenderTargetFormat::RGBA16_UNORM: + return PixelFormat::RGBA16U; + case Tegra::RenderTargetFormat::RGBA16_UINT: + return PixelFormat::RGBA16UI; + case Tegra::RenderTargetFormat::RGBA32_FLOAT: + return PixelFormat::RGBA32F; + case Tegra::RenderTargetFormat::RG32_FLOAT: + return PixelFormat::RG32F; + case Tegra::RenderTargetFormat::R11G11B10_FLOAT: + return PixelFormat::R11FG11FB10F; + case Tegra::RenderTargetFormat::B5G6R5_UNORM: + return PixelFormat::B5G6R5U; + case Tegra::RenderTargetFormat::BGR5A1_UNORM: + return PixelFormat::A1B5G5R5U; + case Tegra::RenderTargetFormat::RGBA32_UINT: + return PixelFormat::RGBA32UI; + case Tegra::RenderTargetFormat::R8_UNORM: + return PixelFormat::R8U; + case Tegra::RenderTargetFormat::R8_UINT: + return PixelFormat::R8UI; + case Tegra::RenderTargetFormat::RG16_FLOAT: + return PixelFormat::RG16F; + case Tegra::RenderTargetFormat::RG16_UINT: + return PixelFormat::RG16UI; + case Tegra::RenderTargetFormat::RG16_SINT: + return PixelFormat::RG16I; + case Tegra::RenderTargetFormat::RG16_UNORM: + return PixelFormat::RG16; + case Tegra::RenderTargetFormat::RG16_SNORM: + return PixelFormat::RG16S; + case Tegra::RenderTargetFormat::RG8_UNORM: + return PixelFormat::RG8U; + case Tegra::RenderTargetFormat::RG8_SNORM: + return PixelFormat::RG8S; + case Tegra::RenderTargetFormat::R16_FLOAT: + return PixelFormat::R16F; + case Tegra::RenderTargetFormat::R16_UNORM: + return PixelFormat::R16U; + case Tegra::RenderTargetFormat::R16_SNORM: + return PixelFormat::R16S; + case Tegra::RenderTargetFormat::R16_UINT: + return PixelFormat::R16UI; + case Tegra::RenderTargetFormat::R16_SINT: + return PixelFormat::R16I; + case Tegra::RenderTargetFormat::R32_FLOAT: + return PixelFormat::R32F; + case Tegra::RenderTargetFormat::R32_UINT: + return PixelFormat::R32UI; + case Tegra::RenderTargetFormat::RG32_UINT: + return PixelFormat::RG32UI; + default: + LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); + UNREACHABLE(); + } +} + +PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, + 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; + case Tegra::Texture::ComponentType::SNORM: + return PixelFormat::ABGR8S; + case Tegra::Texture::ComponentType::UINT: + return PixelFormat::ABGR8UI; + } + LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); + UNREACHABLE(); + case Tegra::Texture::TextureFormat::B5G6R5: + switch (component_type) { + case Tegra::Texture::ComponentType::UNORM: + return PixelFormat::B5G6R5U; + } + LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); + UNREACHABLE(); + case Tegra::Texture::TextureFormat::A2B10G10R10: + switch (component_type) { + case Tegra::Texture::ComponentType::UNORM: + return PixelFormat::A2B10G10R10U; + } + LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); + UNREACHABLE(); + case Tegra::Texture::TextureFormat::A1B5G5R5: + switch (component_type) { + case Tegra::Texture::ComponentType::UNORM: + return PixelFormat::A1B5G5R5U; + } + LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); + UNREACHABLE(); + case Tegra::Texture::TextureFormat::R8: + switch (component_type) { + case Tegra::Texture::ComponentType::UNORM: + return PixelFormat::R8U; + case Tegra::Texture::ComponentType::UINT: + return PixelFormat::R8UI; + } + LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); + UNREACHABLE(); + case Tegra::Texture::TextureFormat::G8R8: + switch (component_type) { + case Tegra::Texture::ComponentType::UNORM: + return PixelFormat::G8R8U; + case Tegra::Texture::ComponentType::SNORM: + return PixelFormat::G8R8S; + } + LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); + UNREACHABLE(); + case Tegra::Texture::TextureFormat::R16_G16_B16_A16: + switch (component_type) { + case Tegra::Texture::ComponentType::UNORM: + return PixelFormat::RGBA16U; + case Tegra::Texture::ComponentType::FLOAT: + return PixelFormat::RGBA16F; + } + LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); + UNREACHABLE(); + case Tegra::Texture::TextureFormat::BF10GF11RF11: + switch (component_type) { + case Tegra::Texture::ComponentType::FLOAT: + return PixelFormat::R11FG11FB10F; + } + LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); + UNREACHABLE(); + case Tegra::Texture::TextureFormat::R32_G32_B32_A32: + switch (component_type) { + case Tegra::Texture::ComponentType::FLOAT: + return PixelFormat::RGBA32F; + case Tegra::Texture::ComponentType::UINT: + return PixelFormat::RGBA32UI; + } + LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); + UNREACHABLE(); + case Tegra::Texture::TextureFormat::R32_G32: + switch (component_type) { + case Tegra::Texture::ComponentType::FLOAT: + return PixelFormat::RG32F; + case Tegra::Texture::ComponentType::UINT: + return PixelFormat::RG32UI; + } + LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); + UNREACHABLE(); + case Tegra::Texture::TextureFormat::R32_G32_B32: + switch (component_type) { + case Tegra::Texture::ComponentType::FLOAT: + return PixelFormat::RGB32F; + } + LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); + UNREACHABLE(); + case Tegra::Texture::TextureFormat::R16: + switch (component_type) { + case Tegra::Texture::ComponentType::FLOAT: + return PixelFormat::R16F; + case Tegra::Texture::ComponentType::UNORM: + return PixelFormat::R16U; + case Tegra::Texture::ComponentType::SNORM: + return PixelFormat::R16S; + case Tegra::Texture::ComponentType::UINT: + return PixelFormat::R16UI; + case Tegra::Texture::ComponentType::SINT: + return PixelFormat::R16I; + } + LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); + UNREACHABLE(); + case Tegra::Texture::TextureFormat::R32: + switch (component_type) { + case Tegra::Texture::ComponentType::FLOAT: + return PixelFormat::R32F; + case Tegra::Texture::ComponentType::UINT: + return PixelFormat::R32UI; + } + LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); + UNREACHABLE(); + case Tegra::Texture::TextureFormat::ZF32: + return PixelFormat::Z32F; + case Tegra::Texture::TextureFormat::Z16: + return PixelFormat::Z16; + case Tegra::Texture::TextureFormat::Z24S8: + return PixelFormat::Z24S8; + case Tegra::Texture::TextureFormat::DXT1: + return is_srgb ? PixelFormat::DXT1_SRGB : PixelFormat::DXT1; + case Tegra::Texture::TextureFormat::DXT23: + return is_srgb ? PixelFormat::DXT23_SRGB : PixelFormat::DXT23; + case Tegra::Texture::TextureFormat::DXT45: + return is_srgb ? PixelFormat::DXT45_SRGB : PixelFormat::DXT45; + case Tegra::Texture::TextureFormat::DXN1: + return PixelFormat::DXN1; + case Tegra::Texture::TextureFormat::DXN2: + switch (component_type) { + case Tegra::Texture::ComponentType::UNORM: + return PixelFormat::DXN2UNORM; + case Tegra::Texture::ComponentType::SNORM: + return PixelFormat::DXN2SNORM; + } + LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); + UNREACHABLE(); + case Tegra::Texture::TextureFormat::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 is_srgb ? PixelFormat::ASTC_2D_4X4_SRGB : PixelFormat::ASTC_2D_4X4; + case Tegra::Texture::TextureFormat::ASTC_2D_5X4: + return is_srgb ? PixelFormat::ASTC_2D_5X4_SRGB : PixelFormat::ASTC_2D_5X4; + case Tegra::Texture::TextureFormat::ASTC_2D_8X8: + return is_srgb ? PixelFormat::ASTC_2D_8X8_SRGB : PixelFormat::ASTC_2D_8X8; + case Tegra::Texture::TextureFormat::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: + return PixelFormat::RG16F; + case Tegra::Texture::ComponentType::UNORM: + return PixelFormat::RG16; + case Tegra::Texture::ComponentType::SNORM: + return PixelFormat::RG16S; + case Tegra::Texture::ComponentType::UINT: + return PixelFormat::RG16UI; + case Tegra::Texture::ComponentType::SINT: + return PixelFormat::RG16I; + } + LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}", static_cast<u32>(component_type)); + UNREACHABLE(); + default: + LOG_CRITICAL(HW_GPU, "Unimplemented format={}, component_type={}", static_cast<u32>(format), + static_cast<u32>(component_type)); + UNREACHABLE(); + } +} + +ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) { + // TODO(Subv): Implement more component types + switch (type) { + case Tegra::Texture::ComponentType::UNORM: + return ComponentType::UNorm; + case Tegra::Texture::ComponentType::FLOAT: + return ComponentType::Float; + case Tegra::Texture::ComponentType::SNORM: + return ComponentType::SNorm; + case Tegra::Texture::ComponentType::UINT: + return ComponentType::UInt; + case Tegra::Texture::ComponentType::SINT: + return ComponentType::SInt; + default: + LOG_CRITICAL(HW_GPU, "Unimplemented component type={}", static_cast<u32>(type)); + UNREACHABLE(); + } +} + +ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) { + // TODO(Subv): Implement more render targets + switch (format) { + case Tegra::RenderTargetFormat::RGBA8_UNORM: + case Tegra::RenderTargetFormat::RGBA8_SRGB: + case Tegra::RenderTargetFormat::BGRA8_UNORM: + case Tegra::RenderTargetFormat::BGRA8_SRGB: + case Tegra::RenderTargetFormat::RGB10_A2_UNORM: + case Tegra::RenderTargetFormat::R8_UNORM: + case Tegra::RenderTargetFormat::RG16_UNORM: + case Tegra::RenderTargetFormat::R16_UNORM: + case Tegra::RenderTargetFormat::B5G6R5_UNORM: + case Tegra::RenderTargetFormat::BGR5A1_UNORM: + case Tegra::RenderTargetFormat::RG8_UNORM: + case Tegra::RenderTargetFormat::RGBA16_UNORM: + return ComponentType::UNorm; + case Tegra::RenderTargetFormat::RGBA8_SNORM: + case Tegra::RenderTargetFormat::RG16_SNORM: + case Tegra::RenderTargetFormat::R16_SNORM: + case Tegra::RenderTargetFormat::RG8_SNORM: + return ComponentType::SNorm; + case Tegra::RenderTargetFormat::RGBA16_FLOAT: + case Tegra::RenderTargetFormat::R11G11B10_FLOAT: + case Tegra::RenderTargetFormat::RGBA32_FLOAT: + case Tegra::RenderTargetFormat::RG32_FLOAT: + case Tegra::RenderTargetFormat::RG16_FLOAT: + case Tegra::RenderTargetFormat::R16_FLOAT: + case Tegra::RenderTargetFormat::R32_FLOAT: + return ComponentType::Float; + case Tegra::RenderTargetFormat::RGBA32_UINT: + case Tegra::RenderTargetFormat::RGBA16_UINT: + case Tegra::RenderTargetFormat::RG16_UINT: + case Tegra::RenderTargetFormat::R8_UINT: + case Tegra::RenderTargetFormat::R16_UINT: + case Tegra::RenderTargetFormat::RG32_UINT: + case Tegra::RenderTargetFormat::R32_UINT: + case Tegra::RenderTargetFormat::RGBA8_UINT: + return ComponentType::UInt; + case Tegra::RenderTargetFormat::RG16_SINT: + case Tegra::RenderTargetFormat::R16_SINT: + return ComponentType::SInt; + default: + LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); + UNREACHABLE(); + } +} + +PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) { + switch (format) { + case Tegra::FramebufferConfig::PixelFormat::ABGR8: + return PixelFormat::ABGR8U; + default: + LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); + UNREACHABLE(); + } +} + +ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format) { + switch (format) { + case Tegra::DepthFormat::Z16_UNORM: + case Tegra::DepthFormat::S8_Z24_UNORM: + case Tegra::DepthFormat::Z24_S8_UNORM: + return ComponentType::UNorm; + case Tegra::DepthFormat::Z32_FLOAT: + case Tegra::DepthFormat::Z32_S8_X24_FLOAT: + return ComponentType::Float; + default: + LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); + UNREACHABLE(); + } +} + +SurfaceType GetFormatType(PixelFormat pixel_format) { + if (static_cast<std::size_t>(pixel_format) < + static_cast<std::size_t>(PixelFormat::MaxColorFormat)) { + return SurfaceType::ColorTexture; + } + + if (static_cast<std::size_t>(pixel_format) < + static_cast<std::size_t>(PixelFormat::MaxDepthFormat)) { + return SurfaceType::Depth; + } + + if (static_cast<std::size_t>(pixel_format) < + static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) { + return SurfaceType::DepthStencil; + } + + // TODO(Subv): Implement the other formats + ASSERT(false); + + return SurfaceType::Invalid; +} + +bool IsPixelFormatASTC(PixelFormat format) { + switch (format) { + case PixelFormat::ASTC_2D_4X4: + case PixelFormat::ASTC_2D_5X4: + case PixelFormat::ASTC_2D_8X8: + case PixelFormat::ASTC_2D_8X5: + 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; + } +} + +std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) { + switch (format) { + case PixelFormat::ASTC_2D_4X4: + return {4, 4}; + case PixelFormat::ASTC_2D_5X4: + return {5, 4}; + case PixelFormat::ASTC_2D_8X8: + return {8, 8}; + case PixelFormat::ASTC_2D_8X5: + return {8, 5}; + 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(); + } +} + +bool IsFormatBCn(PixelFormat format) { + switch (format) { + case PixelFormat::DXT1: + case PixelFormat::DXT23: + case PixelFormat::DXT45: + case PixelFormat::DXN1: + case PixelFormat::DXN2SNORM: + case PixelFormat::DXN2UNORM: + 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; +} + +} // namespace VideoCore::Surface diff --git a/src/video_core/surface.h b/src/video_core/surface.h new file mode 100644 index 000000000..3232e437f --- /dev/null +++ b/src/video_core/surface.h @@ -0,0 +1,385 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <climits> +#include <utility> +#include "common/assert.h" +#include "common/common_types.h" +#include "common/logging/log.h" +#include "video_core/gpu.h" +#include "video_core/textures/texture.h" + +namespace VideoCore::Surface { + +enum class PixelFormat { + ABGR8U = 0, + ABGR8S = 1, + ABGR8UI = 2, + B5G6R5U = 3, + A2B10G10R10U = 4, + A1B5G5R5U = 5, + R8U = 6, + R8UI = 7, + RGBA16F = 8, + RGBA16U = 9, + RGBA16UI = 10, + R11FG11FB10F = 11, + RGBA32UI = 12, + DXT1 = 13, + DXT23 = 14, + DXT45 = 15, + DXN1 = 16, // This is also known as BC4 + DXN2UNORM = 17, + DXN2SNORM = 18, + BC7U = 19, + BC6H_UF16 = 20, + BC6H_SF16 = 21, + ASTC_2D_4X4 = 22, + G8R8U = 23, + G8R8S = 24, + BGRA8 = 25, + RGBA32F = 26, + RG32F = 27, + R32F = 28, + R16F = 29, + R16U = 30, + R16S = 31, + R16UI = 32, + R16I = 33, + RG16 = 34, + RG16F = 35, + RG16UI = 36, + RG16I = 37, + RG16S = 38, + RGB32F = 39, + RGBA8_SRGB = 40, + RG8U = 41, + RG8S = 42, + RG32UI = 43, + R32UI = 44, + 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 = 57, + Z16 = 58, + + MaxDepthFormat, + + // DepthStencil formats + Z24S8 = 59, + S8Z24 = 60, + Z32FS8 = 61, + + MaxDepthStencilFormat, + + Max = MaxDepthStencilFormat, + Invalid = 255, +}; + +static constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max); + +enum class ComponentType { + Invalid = 0, + SNorm = 1, + UNorm = 2, + SInt = 3, + UInt = 4, + Float = 5, +}; + +enum class SurfaceType { + ColorTexture = 0, + Depth = 1, + DepthStencil = 2, + Fill = 3, + Invalid = 4, +}; + +enum class SurfaceTarget { + Texture1D, + Texture2D, + Texture3D, + Texture1DArray, + Texture2DArray, + TextureCubemap, +}; + +/** + * 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 + * compressed image. This is used for maintaining proper surface sizes for compressed + * texture formats. + */ +static constexpr u32 GetCompressionFactor(PixelFormat format) { + if (format == PixelFormat::Invalid) + return 0; + + constexpr std::array<u32, MaxPixelFormat> compression_factor_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 + 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 + 1, // S8Z24 + 1, // Z32FS8 + }}; + + ASSERT(static_cast<std::size_t>(format) < compression_factor_table.size()); + 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; + + constexpr std::array<u32, MaxPixelFormat> bpp_table = {{ + 32, // ABGR8U + 32, // ABGR8S + 32, // ABGR8UI + 16, // B5G6R5U + 32, // A2B10G10R10U + 16, // A1B5G5R5U + 8, // R8U + 8, // R8UI + 64, // RGBA16F + 64, // RGBA16U + 64, // RGBA16UI + 32, // R11FG11FB10F + 128, // RGBA32UI + 64, // DXT1 + 128, // DXT23 + 128, // DXT45 + 64, // DXN1 + 128, // DXN2UNORM + 128, // DXN2SNORM + 128, // BC7U + 128, // BC6H_UF16 + 128, // BC6H_SF16 + 32, // ASTC_2D_4X4 + 16, // G8R8U + 16, // G8R8S + 32, // BGRA8 + 128, // RGBA32F + 64, // RG32F + 32, // R32F + 16, // R16F + 16, // R16U + 16, // R16S + 16, // R16UI + 16, // R16I + 32, // RG16 + 32, // RG16F + 32, // RG16UI + 32, // RG16I + 32, // RG16S + 96, // RGB32F + 32, // RGBA8_SRGB + 16, // RG8U + 16, // RG8S + 64, // RG32UI + 32, // R32UI + 16, // ASTC_2D_8X8 + 16, // 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 + 16, // ASTC_2D_8X5_SRGB + 32, // ASTC_2D_5X4_SRGB + 32, // Z32F + 16, // Z16 + 32, // Z24S8 + 32, // S8Z24 + 64, // Z32FS8 + }}; + + ASSERT(static_cast<std::size_t>(format) < bpp_table.size()); + return bpp_table[static_cast<std::size_t>(format)]; +} + +/// Returns the sizer in bytes of the specified pixel format +static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) { + if (pixel_format == PixelFormat::Invalid) { + return 0; + } + return GetFormatBpp(pixel_format) / CHAR_BIT; +} + +SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type); + +bool SurfaceTargetIsLayered(SurfaceTarget target); + +PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format); + +PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format); + +PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format, + Tegra::Texture::ComponentType component_type, + bool is_srgb); + +ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type); + +ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format); + +PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format); + +ComponentType ComponentTypeFromDepthFormat(Tegra::DepthFormat format); + +SurfaceType GetFormatType(PixelFormat pixel_format); + +bool IsPixelFormatASTC(PixelFormat format); + +std::pair<u32, u32> GetASTCBlockSize(PixelFormat format); + +/// Returns true if the specified PixelFormat is a BCn format, e.g. DXT or DXN +bool IsFormatBCn(PixelFormat format); + +} // namespace VideoCore::Surface diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index 4726f54a5..b390219e4 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h @@ -10,6 +10,12 @@ namespace Tegra::Texture { +// GOBSize constant. Calculated by 64 bytes in x multiplied by 8 y coords, represents +// an small rect of (64/bytes_per_pixel)X8. +inline std::size_t GetGOBSize() { + return 512; +} + /** * Unswizzles a swizzled texture without changing its format. */ 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/video_core/utils.h b/src/video_core/utils.h index 237cc1307..e0a14d48f 100644 --- a/src/video_core/utils.h +++ b/src/video_core/utils.h @@ -161,30 +161,4 @@ static inline void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixe } } -static void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, - std::string extra_info = "") { - if (!GLAD_GL_KHR_debug) { - return; // We don't need to throw an error as this is just for debugging - } - const std::string nice_addr = fmt::format("0x{:016x}", addr); - std::string object_label; - - if (extra_info.empty()) { - switch (identifier) { - case GL_TEXTURE: - object_label = "Texture@" + nice_addr; - break; - case GL_PROGRAM: - object_label = "Shader@" + nice_addr; - break; - default: - object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr); - break; - } - } else { - object_label = extra_info + '@' + nice_addr; - } - glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str())); -} - } // namespace VideoCore diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 94789c064..42a7beac6 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -322,7 +322,7 @@ void ConfigureInput::setPollingResult(const Common::ParamPackage& params, bool a } updateButtonLabels(); - input_setter = boost::none; + input_setter = {}; } void ConfigureInput::keyPressEvent(QKeyEvent* event) { diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index d1198db81..32c7183f9 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -7,11 +7,13 @@ #include <array> #include <functional> #include <memory> +#include <optional> #include <string> #include <unordered_map> + #include <QKeyEvent> #include <QWidget> -#include <boost/optional.hpp> + #include "common/param_package.h" #include "core/settings.h" #include "input_common/main.h" @@ -41,7 +43,7 @@ private: std::unique_ptr<QTimer> poll_timer; /// This will be the the setting function when an input is awaiting configuration. - boost::optional<std::function<void(const Common::ParamPackage&)>> input_setter; + std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param; diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 20ffb0a9a..1b8aa7de2 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -48,20 +48,40 @@ constexpr std::array<u8, 107> backup_jpeg{ 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, }; -std::string GetImagePath(Service::Account::UUID uuid) { - return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + - "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; +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); } -std::string GetAccountUsername(const Service::Account::ProfileManager& manager, - Service::Account::UUID uuid) { +QString GetAccountUsername(const Service::Account::ProfileManager& manager, + Service::Account::UUID uuid) { Service::Account::ProfileBase profile; if (!manager.GetProfileBase(uuid, profile)) { - return ""; + return {}; } - return Common::StringFromFixedZeroTerminatedBuffer( + 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(), static_cast<u32>(backup_jpeg.size())); + } + + return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } } // Anonymous namespace @@ -131,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) { @@ -154,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) @@ -166,13 +173,13 @@ 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 != std::nullopt); + ASSERT(current_user); 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() {} @@ -248,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 != std::nullopt); - const auto username = GetAccountUsername(*profile_manager, *uuid); + ASSERT(uuid); 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; @@ -284,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 != std::nullopt); + ASSERT(uuid); 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; @@ -324,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 != std::nullopt); - const auto username = GetAccountUsername(*profile_manager, *uuid); + ASSERT(uuid); const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(), 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 = - FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; - if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) - FileUtil::Delete(raw_path); + 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; + } - FileUtil::CreateFullPath(GetImagePath(*uuid)); - FileUtil::Copy(file.toStdString(), GetImagePath(*uuid)); + 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; + } - item_model->setItem( - index, 0, - new QStandardItem{ - GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), - QString::fromStdString(username + '\n' + uuid->FormatSwitch())}); + 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; + } + + 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/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp index 44d423da2..0adbab27d 100644 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ b/src/yuzu/debugger/graphics/graphics_surface.cpp @@ -382,7 +382,7 @@ void GraphicsSurfaceWidget::OnUpdate() { // TODO: Implement a good way to visualize alpha components! QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32); - boost::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); + std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); // TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles. // Needs to be fixed if we plan to use this feature more, otherwise we may remove it. @@ -444,7 +444,7 @@ void GraphicsSurfaceWidget::SaveSurface() { pixmap->save(&file, "PNG"); } else if (selectedFilter == bin_filter) { auto& gpu = Core::System::GetInstance().GPU(); - boost::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); + std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address); const u8* buffer = Memory::GetPointer(*address); ASSERT_MSG(buffer != nullptr, "Memory not accessible"); diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h index defbf734f..331f89885 100644 --- a/src/yuzu/debugger/wait_tree.h +++ b/src/yuzu/debugger/wait_tree.h @@ -11,7 +11,6 @@ #include <QAbstractItemModel> #include <QDockWidget> #include <QTreeView> -#include <boost/container/flat_set.hpp> #include "common/common_types.h" #include "core/hle/kernel/object.h" diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index b5bfa6741..c5a56cbfd 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -786,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 != std::nullopt); + ASSERT(user_id); path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, program_id, user_id->uuid, 0); @@ -1560,7 +1560,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { } } -boost::optional<u64> GMainWindow::SelectRomFSDumpTarget( +std::optional<u64> GMainWindow::SelectRomFSDumpTarget( const FileSys::RegisteredCacheUnion& installed, u64 program_id) { const auto dlc_entries = installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); @@ -1587,7 +1587,7 @@ boost::optional<u64> GMainWindow::SelectRomFSDumpTarget( this, tr("Select RomFS Dump Target"), tr("Please select which RomFS you would like to dump."), list, 0, false, &ok); if (!ok) { - return boost::none; + return {}; } return romfs_tids[list.indexOf(res)]; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 7c7c223e1..af637d89e 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -5,12 +5,12 @@ #pragma once #include <memory> +#include <optional> #include <unordered_map> #include <QMainWindow> #include <QTimer> -#include <boost/optional.hpp> #include "common/common_types.h" #include "core/core.h" #include "ui_main.h" @@ -178,8 +178,7 @@ private slots: void OnReinitializeKeys(ReinitializeKeyBehavior behavior); private: - boost::optional<u64> SelectRomFSDumpTarget(const FileSys::RegisteredCacheUnion&, - u64 program_id); + std::optional<u64> SelectRomFSDumpTarget(const FileSys::RegisteredCacheUnion&, u64 program_id); void UpdateStatusBar(); Ui::MainWindow ui; |