Compare commits

..

37 Commits

Author SHA1 Message Date
David Marcec
0bc323bafb Fixed HID crash when launching more than 1 game & signaled syleset change event
This should fix crashes when launching multiple games in yuzu
2018-11-02 12:35:49 +11:00
bunnei
1069eced84 Merge pull request #1615 from lioncash/input
configure_system: Contrain profile usernames to 32 characters
2018-11-01 19:10:26 -04:00
bunnei
cc1fe93297 Merge pull request #1623 from Tinob/master
Improve OpenGL state handling
2018-11-01 15:53:33 -04:00
bunnei
9afcbba8e4 Merge pull request #1527 from FernandoS27/assert-flow
Assert Control Flow Instructions using Control Codes
2018-11-01 00:34:56 -04:00
bunnei
7992dee8e9 Merge pull request #1622 from bunnei/fix-macros
maxwell_3d: Restructure macro upload to use a single macro code memory.
2018-11-01 00:14:59 -04:00
bunnei
de0ab806df maxwell_3d: Restructure macro upload to use a single macro code memory.
- Fixes an issue where macros could be skipped.
- Fixes rendering of distant objects in Super Mario Odyssey.
2018-10-31 23:29:21 -04:00
bunnei
d08457f879 Merge pull request #1604 from FearlessTobi/port-4369
Port citra-emu/citra#4369: "compatdb: Use a seperate endpoint for testcase submission"
2018-10-31 22:37:07 -04:00
bunnei
86e70cf302 Merge pull request #1528 from FernandoS27/assert-control-codes
Assert Control Codes Generation on Shader Instructions
2018-10-31 22:34:18 -04:00
bunnei
0b33d38e9b Merge pull request #1614 from ReinUsesLisp/surface-params
video_core: Move surface declarations out of gl_rasterizer_cache
2018-10-31 22:31:02 -04:00
bunnei
e7fc3d13ed Merge pull request #1626 from lioncash/table
service/usb: Update IPdSession's function table
2018-10-31 22:28:58 -04:00
Lioncash
a6830e61b8 configure_system: Contrain profile usernames to 32 characters
Previously, we would let a user enter an unbounded name and then
silently truncate away characters that went over the 32-character limit.
This is kind of bad from the UX point of view, because we're essentially
not doing what the user intended in certain scenarios.

Instead, we clamp it to 32 characters and make that visually apparent in
the dialog box to provide a name for a user.
2018-10-31 02:05:00 -04:00
Rodolfo Bogado
aca218aea0 Improve OpenGL state handling 2018-10-30 21:19:04 -03:00
Lioncash
9b9c586dff service/usb: Update IPdSession's function table
Updated based off information on SwitchBrew.
2018-10-30 15:23:49 -04:00
ReinUsesLisp
76754f5705 video_core: Move surface declarations out of gl_rasterizer_cache 2018-10-30 16:07:20 -03:00
FernandoS27
5bb80ab009 Assert Control Codes Generation 2018-10-30 13:37:55 -04:00
bunnei
da5fcbf501 Merge pull request #1624 from lioncash/boost
general: Remove unused boost inclusions where applicable
2018-10-30 13:13:39 -04:00
bunnei
c31412c433 Merge pull request #1595 from FreddyFunk/cast
configure_system: Fix compiler warning
2018-10-30 00:27:41 -04:00
Lioncash
352b56367c general: Remove unused boost inclusions where applicable
Cleans up unused includes and trims off some dependencies on externals.
2018-10-30 00:09:46 -04:00
Frederic L
7a5eda5914 global: Use std::optional instead of boost::optional (#1578)
* get rid of boost::optional

* Remove optional references

* Use std::reference_wrapper for optional references

* Fix clang format

* Fix clang format part 2

* Adressed feedback

* Fix clang format and MacOS build
2018-10-30 00:03:25 -04:00
bunnei
adf26ae668 Merge pull request #1621 from lioncash/ipc
hle_ipc: Make GetDomainMessageHeader return a regular pointer
2018-10-29 23:55:59 -04:00
Lioncash
6383653a8d hle_ipc: Add member function for querying the existence of a domain header
Gets rid of the need to call the getter and then check for null.
2018-10-29 23:28:04 -04:00
Lioncash
0cc347462d hle_ipc: Make GetDomainMessageHeader return a regular pointer
Nothing requires the shared owner ship here, so we can just return a
plain pointer.
2018-10-29 23:18:25 -04:00
bunnei
938e45eb83 Merge pull request #1611 from lioncash/const
core: Add missing const variants of getters for the System class
2018-10-29 22:48:59 -04:00
bunnei
c5a849212f Merge pull request #1580 from FernandoS27/mm-impl
Implemented Mipmaps
2018-10-29 22:34:00 -04:00
bunnei
f1cb425d92 Merge pull request #1617 from FearlessTobi/fix-stretch-delay
time_stretch: Switch to values of Citra
2018-10-29 19:09:30 -04:00
bunnei
0270906dbf Merge pull request #1613 from ReinUsesLisp/gl-utils
video_core: Move OpenGL specific utils to its renderer
2018-10-29 13:22:14 -04:00
fearlessTobi
655694253a time_stretch: Switch to values of Citra 2018-10-29 14:49:36 +01:00
bunnei
5d7167dfca Merge pull request #1610 from slashiee/dxt1-alpha
renderer_opengl: Enable alpha channel for DXT1 texture format
2018-10-28 21:29:43 -04:00
bunnei
9049aedd83 Merge pull request #1612 from Tinob/master
renderer_opengl: Correct bpp value for ASTC_2D_8X5_SRGB
2018-10-28 21:28:34 -04:00
ReinUsesLisp
80cbd81276 video_core: Move OpenGL specific utils to its renderer 2018-10-28 22:22:30 -03:00
Rodolfo Bogado
e8b565b239 renderer_opengl: Correct bpp value for ASTC_2D_8X5_SRGB 2018-10-28 20:52:57 -03:00
FernandoS27
3aa8b644a9 Assert Control Flow Instructions using Control Codes 2018-10-28 19:16:41 -04:00
Lioncash
a973a049b7 core: Make System references const where applicable 2018-10-28 17:45:29 -04:00
Lioncash
b77f571d20 core: Add missing const variants of getters for the System class
Many of the Current<Thing> getters (as well as a few others) were
missing const qualified variants, which makes it a pain to retrieve
certain things from const qualified references to System.
2018-10-28 17:44:58 -04:00
Michael
635d1e5651 Enable alpha channel for DXT1 texture format 2018-10-28 14:11:04 -07:00
Frederic Laing
f50f065c31 configure_system: Fix compiler warning 2018-10-28 21:13:09 +01:00
fearlessTobi
585b6a6a50 compatdb: Use a seperate endpoint for testcase submission 2018-10-28 13:23:02 +01:00
85 changed files with 1888 additions and 1396 deletions

View File

@@ -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);

View File

@@ -153,6 +153,7 @@ struct VisitorInterface : NonCopyable {
/// Completion method, called once all fields have been visited
virtual void Complete() = 0;
virtual bool SubmitTestcase() = 0;
};
/**
@@ -178,6 +179,9 @@ struct NullVisitor : public VisitorInterface {
void Visit(const Field<std::chrono::microseconds>& /*field*/) override {}
void Complete() override {}
bool SubmitTestcase() override {
return false;
}
};
/// Appends build-specific information to the given FieldCollection,

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);
}
@@ -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]);

View File

@@ -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

View File

@@ -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];

View File

@@ -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;

View File

@@ -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"

View File

@@ -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())

View File

@@ -65,7 +65,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
if (update != nullptr && update->GetExeFS() != nullptr &&
update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0)));
exefs = update->GetExeFS();
}
@@ -236,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) {
@@ -280,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");

View File

@@ -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;
});

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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(),

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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);

View File

@@ -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

View File

@@ -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) {

View File

@@ -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"

View File

@@ -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) {

View File

@@ -572,7 +572,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
return ERR_INVALID_HANDLE;
}
auto& system = Core::System::GetInstance();
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;

View File

@@ -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;

View File

@@ -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 {

View File

@@ -57,7 +57,8 @@ struct UUID {
};
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
using ProfileUsername = std::array<u8, 0x20>;
constexpr std::size_t profile_username_size = 32;
using ProfileUsername = std::array<u8, profile_username_size>;
using ProfileData = std::array<u8, MAX_DATA>;
using UserIDArray = std::array<UUID, MAX_USERS>;

View File

@@ -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};

View File

@@ -427,6 +427,9 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
}
Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const {
// TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
// be signalled at least once, and signaled after a new controller is connected?
styleset_changed_event->Signal();
return styleset_changed_event;
}

View File

@@ -96,6 +96,8 @@ public:
// TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
ReloadInputDevices();
}
void ActivateController(HidController controller) {

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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};

View File

@@ -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);
}

View File

@@ -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"

View File

@@ -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;

View File

@@ -184,4 +184,13 @@ TelemetrySession::~TelemetrySession() {
backend = nullptr;
}
bool TelemetrySession::SubmitTestcase() {
#ifdef ENABLE_WEB_SERVICE
field_collection.Accept(*backend);
return backend->SubmitTestcase();
#else
return false;
#endif
}
} // namespace Core

View File

@@ -31,6 +31,12 @@ public:
field_collection.AddField(type, name, std::move(value));
}
/**
* Submits a Testcase.
* @returns A bool indicating whether the submission succeeded
*/
bool SubmitTestcase();
private:
Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session
std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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)};

View File

@@ -43,15 +43,17 @@ void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
// Reset the current macro.
executing_macro = 0;
// The requested macro must have been uploaded already.
auto macro_code = uploaded_macros.find(method);
if (macro_code == uploaded_macros.end()) {
LOG_ERROR(HW_GPU, "Macro {:04X} was not uploaded", method);
// Lookup the macro offset
const u32 entry{(method - MacroRegistersStart) >> 1};
const auto& search{macro_offsets.find(entry)};
if (search == macro_offsets.end()) {
LOG_CRITICAL(HW_GPU, "macro not found for method 0x{:X}!", method);
UNREACHABLE();
return;
}
// Execute the current macro.
macro_interpreter.Execute(macro_code->second, std::move(parameters));
macro_interpreter.Execute(search->second, std::move(parameters));
}
void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
@@ -97,6 +99,10 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
ProcessMacroUpload(value);
break;
}
case MAXWELL3D_REG_INDEX(macros.bind): {
ProcessMacroBind(value);
break;
}
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
@@ -158,16 +164,20 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
}
void Maxwell3D::ProcessMacroUpload(u32 data) {
// Store the uploaded macro code to interpret them when they're called.
auto& macro = uploaded_macros[regs.macros.entry * 2 + MacroRegistersStart];
macro.push_back(data);
ASSERT_MSG(regs.macros.upload_address < macro_memory.size(),
"upload_address exceeded macro_memory size!");
macro_memory[regs.macros.upload_address++] = data;
}
void Maxwell3D::ProcessMacroBind(u32 data) {
macro_offsets[regs.macros.entry] = data;
}
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 +295,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 +308,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 +332,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 +396,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{};

View File

@@ -475,12 +475,13 @@ public:
INSERT_PADDING_WORDS(0x45);
struct {
INSERT_PADDING_WORDS(1);
u32 upload_address;
u32 data;
u32 entry;
u32 bind;
} macros;
INSERT_PADDING_WORDS(0x189);
INSERT_PADDING_WORDS(0x188);
u32 tfb_enabled;
@@ -994,12 +995,25 @@ public:
/// Returns the texture information for a specific texture in a specific shader stage.
Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const;
/// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than
/// we've seen used.
using MacroMemory = std::array<u32, 0x40000>;
/// Gets a reference to macro memory.
const MacroMemory& GetMacroMemory() const {
return macro_memory;
}
private:
void InitializeRegisterDefaults();
VideoCore::RasterizerInterface& rasterizer;
std::unordered_map<u32, std::vector<u32>> uploaded_macros;
/// Start offsets of each macro in macro_memory
std::unordered_map<u32, u32> macro_offsets;
/// Memory for macro code
MacroMemory macro_memory;
/// Macro method that is currently being executed / being fed parameters.
u32 executing_macro = 0;
@@ -1022,9 +1036,12 @@ private:
*/
void CallMacroMethod(u32 method, std::vector<u32> parameters);
/// Handles writes to the macro uploading registers.
/// Handles writes to the macro uploading register.
void ProcessMacroUpload(u32 data);
/// Handles writes to the macro bind register.
void ProcessMacroBind(u32 data);
/// Handles a write to the CLEAR_BUFFERS register.
void ProcessClearBuffers();

View File

@@ -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"
@@ -578,6 +577,10 @@ union Instruction {
BitField<55, 1, u64> saturate;
} fmul32;
union {
BitField<52, 1, u64> generates_cc;
} op_32;
union {
BitField<48, 1, u64> is_signed;
} shift;
@@ -1232,6 +1235,7 @@ union Instruction {
BitField<60, 1, u64> is_b_gpr;
BitField<59, 1, u64> is_c_gpr;
BitField<20, 24, s64> smem_imm;
BitField<0, 5, ControlCode> flow_control_code;
Attribute attribute;
Sampler sampler;
@@ -1456,7 +1460,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 +1468,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 +1663,4 @@ private:
}
};
} // namespace Tegra::Shader
} // namespace Tegra::Shader

View File

@@ -11,7 +11,7 @@ namespace Tegra {
MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {}
void MacroInterpreter::Execute(const std::vector<u32>& code, std::vector<u32> parameters) {
void MacroInterpreter::Execute(u32 offset, std::vector<u32> parameters) {
Reset();
registers[1] = parameters[0];
this->parameters = std::move(parameters);
@@ -19,7 +19,7 @@ void MacroInterpreter::Execute(const std::vector<u32>& code, std::vector<u32> pa
// Execute the code until we hit an exit condition.
bool keep_executing = true;
while (keep_executing) {
keep_executing = Step(code, false);
keep_executing = Step(offset, false);
}
// Assert the the macro used all the input parameters
@@ -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
@@ -37,17 +37,17 @@ void MacroInterpreter::Reset() {
next_parameter_index = 1;
}
bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) {
bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
u32 base_address = pc;
Opcode opcode = GetOpcode(code);
Opcode opcode = GetOpcode(offset);
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) {
@@ -108,7 +108,7 @@ bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) {
delayed_pc = base_address + opcode.GetBranchTarget();
// Execute one more instruction due to the delay slot.
return Step(code, true);
return Step(offset, true);
}
break;
}
@@ -121,17 +121,18 @@ bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) {
// Exit has a delay slot, execute the next instruction
// Note: Executing an exit during a branch delay slot will cause the instruction at the
// branch target to be executed before exiting.
Step(code, true);
Step(offset, true);
return false;
}
return true;
}
MacroInterpreter::Opcode MacroInterpreter::GetOpcode(const std::vector<u32>& code) const {
MacroInterpreter::Opcode MacroInterpreter::GetOpcode(u32 offset) const {
const auto& macro_memory{maxwell3d.GetMacroMemory()};
ASSERT((pc % sizeof(u32)) == 0);
ASSERT(pc < code.size() * sizeof(u32));
return {code[pc / sizeof(u32)]};
ASSERT((pc + offset) < macro_memory.size() * sizeof(u32));
return {macro_memory[offset + pc / sizeof(u32)]};
}
u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const {

View File

@@ -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"
@@ -21,10 +22,10 @@ public:
/**
* Executes the macro code with the specified input parameters.
* @param code The macro byte code to execute
* @param parameters The parameters of the macro
* @param offset Offset to start execution at.
* @param parameters The parameters of the macro.
*/
void Execute(const std::vector<u32>& code, std::vector<u32> parameters);
void Execute(u32 offset, std::vector<u32> parameters);
private:
enum class Operation : u32 {
@@ -109,11 +110,11 @@ private:
/**
* Executes a single macro instruction located at the current program counter. Returns whether
* the interpreter should keep running.
* @param code The macro code to execute.
* @param offset Offset to start execution at.
* @param is_delay_slot Whether the current step is being executed due to a delay slot in a
* previous instruction.
*/
bool Step(const std::vector<u32>& code, bool is_delay_slot);
bool Step(u32 offset, bool is_delay_slot);
/// Calculates the result of an ALU operation. src_a OP src_b;
u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const;
@@ -126,7 +127,7 @@ private:
bool EvaluateBranchCondition(BranchCondition cond, u32 value) const;
/// Reads an opcode at the current program counter location.
Opcode GetOpcode(const std::vector<u32>& code) const;
Opcode GetOpcode(u32 offset) const;
/// Returns the specified register's value. Register 0 is hardcoded to always return 0.
u32 GetRegister(u32 register_id) const;
@@ -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;

View File

@@ -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) ||

View File

@@ -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);

View File

@@ -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;

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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));
@@ -104,7 +104,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
}
ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported");
OpenGLState::ApplyDefaultState();
// Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
state.clip_distance[0] = true;
@@ -115,8 +115,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
state.draw.shader_program = 0;
state.Apply();
glEnable(GL_BLEND);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment);
LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!");
@@ -401,7 +399,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;
@@ -703,7 +701,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");

View File

@@ -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>
@@ -111,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.

View File

@@ -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,46 +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:
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;
}
}
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};
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();
}
}
void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) {
auto& memory_manager{Core::System::GetInstance().GPU().MemoryManager()};
const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr_)};
@@ -266,7 +232,7 @@ std::size_t SurfaceParams::InnerMemorySize(bool force_gl, bool layer_only,
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
@@ -282,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
@@ -327,7 +293,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_5X4
{GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // BGRA8
// Compressed sRGB formats
{GL_COMPRESSED_SRGB_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
{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
@@ -354,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));
@@ -391,31 +357,10 @@ MathUtil::Rectangle<u32> SurfaceParams::GetRect(u32 mip_level) const {
return {0, actual_height, MipWidth(mip_level), 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:
case PixelFormat::DXT1_SRGB:
case PixelFormat::DXT23_SRGB:
case PixelFormat::DXT45_SRGB:
case PixelFormat::BC7U_SRGB:
return true;
}
return false;
}
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.
@@ -434,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
@@ -574,7 +519,7 @@ static constexpr GLConversionArray gl_to_morton_fns = {
void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params,
std::vector<u8>& gl_buffer, u32 mip_level) {
u32 depth = params.MipDepth(mip_level);
if (params.target == SurfaceParams::SurfaceTarget::Texture2D) {
if (params.target == SurfaceTarget::Texture2D) {
// TODO(Blinkhawk): Eliminate this condition once all texture types are implemented.
depth = 1U;
}
@@ -621,13 +566,13 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
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),
@@ -636,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);
@@ -657,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),
@@ -672,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);
@@ -799,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);
@@ -850,17 +795,17 @@ 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:
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:
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:
case SurfaceTarget::Texture3D:
case SurfaceTarget::Texture2DArray:
glTexStorage3D(SurfaceTargetToGL(params.target), params.max_mip_level,
format_tuple.internal_format, rect.GetWidth(), rect.GetHeight(),
params.depth);
@@ -884,8 +829,8 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
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
@@ -915,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)};
@@ -935,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)};
@@ -1041,7 +986,7 @@ void CachedSurface::FlushGLBuffer() {
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);
@@ -1073,7 +1018,7 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
std::size_t buffer_offset =
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;
@@ -1089,35 +1034,34 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
cur_state.Apply();
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT
ASSERT(params.MipWidth(mip_map) * SurfaceParams::GetBytesPerPixel(params.pixel_format) % 4 ==
0);
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:
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 SurfaceParams::SurfaceTarget::Texture3D:
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::Texture2DArray:
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),
@@ -1142,30 +1086,30 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
} else {
switch (params.target) {
case SurfaceParams::SurfaceTarget::Texture1D:
case SurfaceTarget::Texture1D:
glTexSubImage1D(SurfaceTargetToGL(params.target), mip_map, x0,
static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type,
&gl_buffer[mip_map][buffer_offset]);
break;
case SurfaceParams::SurfaceTarget::Texture2D:
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[mip_map][buffer_offset]);
break;
case SurfaceParams::SurfaceTarget::Texture3D:
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 SurfaceParams::SurfaceTarget::Texture2DArray:
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[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), mip_map,
@@ -1340,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;
}
@@ -1354,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:
@@ -1372,7 +1315,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
}
return new_surface;
} // namespace OpenGL
}
Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const {
return TryGet(addr);

View File

@@ -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,135 +29,12 @@ class CachedSurface;
using Surface = std::shared_ptr<CachedSurface>;
using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, MathUtil::Rectangle<u32>>;
using SurfaceTarget = VideoCore::Surface::SurfaceTarget;
using SurfaceType = VideoCore::Surface::SurfaceType;
using PixelFormat = VideoCore::Surface::PixelFormat;
using ComponentType = VideoCore::Surface::ComponentType;
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,
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,
};
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;
}
}
static std::string SurfaceTargetName(SurfaceTarget target) {
switch (target) {
case SurfaceTarget::Texture1D:
@@ -177,660 +56,8 @@ 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, // 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
32, // 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)];
}
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:
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();
}
}
static 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();
}
}
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

View File

@@ -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;
};

View File

@@ -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
@@ -373,6 +373,7 @@ public:
if (sets_cc) {
const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )";
SetInternalFlag(InternalFlag::ZeroFlag, zero_condition);
LOG_WARNING(HW_GPU, "Control Codes Imcomplete.");
}
}
@@ -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 + ';');
@@ -807,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;
};
@@ -1465,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,
@@ -1473,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 (" +
@@ -1483,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);
@@ -1500,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.
@@ -1525,6 +1526,10 @@ private:
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
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:
@@ -1535,6 +1540,10 @@ private:
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1,
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: {
@@ -1588,6 +1597,10 @@ private:
'(' + condition + ") ? min(" + parameters + ") : max(" +
parameters + ')',
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:
@@ -1600,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;
@@ -1617,6 +1631,10 @@ private:
regs.GetRegisterAsFloat(instr.gpr8) + " * " +
GetImmediate32(instr),
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: {
@@ -1640,6 +1658,10 @@ private:
}
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;
}
}
@@ -1651,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()) + ')';
@@ -1660,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();
}
}
@@ -1685,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: {
@@ -1697,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();
}
}
@@ -1715,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)
@@ -1733,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();
}
}
@@ -1757,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: {
@@ -1769,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:
@@ -1793,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);
@@ -1809,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
@@ -1830,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:
@@ -1845,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:
@@ -1876,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:
@@ -1884,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:
@@ -1905,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:
@@ -1914,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);
@@ -1959,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();
}
}
@@ -1974,7 +2042,7 @@ private:
}
default: {
LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}",
opcode->GetName());
opcode->get().GetName());
UNREACHABLE();
}
}
@@ -1982,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,
@@ -2013,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 + ')';
@@ -2021,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");
}
@@ -2032,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,
@@ -2046,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:
@@ -2072,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);
@@ -2096,25 +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, "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;
@@ -2122,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),
@@ -2160,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");
@@ -2208,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: {
@@ -2246,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:
@@ -2295,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,
@@ -2949,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();
}
}
@@ -3043,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,
@@ -3102,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);
@@ -3151,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();
}
}
@@ -3239,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,
@@ -3288,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,
@@ -3317,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();
}
}
@@ -3366,15 +3461,25 @@ 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();
}
const Tegra::Shader::ControlCode cc = instr.flow_control_code;
if (cc != Tegra::Shader::ControlCode::T) {
LOG_CRITICAL(HW_GPU, "EXIT Control Code used: {}", static_cast<u32>(cc));
UNREACHABLE();
}
switch (instr.flow.cond) {
case Tegra::Shader::FlowCondition::Always:
shader.AddLine("return true;");
@@ -3404,6 +3509,11 @@ private:
// Enclose "discard" in a conditional, so that GLSL compilation does not complain
// about unexecuted instructions that may follow this.
const Tegra::Shader::ControlCode cc = instr.flow_control_code;
if (cc != Tegra::Shader::ControlCode::T) {
LOG_CRITICAL(HW_GPU, "KIL Control Code used: {}", static_cast<u32>(cc));
UNREACHABLE();
}
shader.AddLine("if (true) {");
++shader.scope;
shader.AddLine("discard;");
@@ -3461,6 +3571,11 @@ private:
case OpCode::Id::BRA: {
ASSERT_MSG(instr.bra.constant_buffer == 0,
"BRA with constant buffers are not implemented");
const Tegra::Shader::ControlCode cc = instr.flow_control_code;
if (cc != Tegra::Shader::ControlCode::T) {
LOG_CRITICAL(HW_GPU, "BRA Control Code used: {}", static_cast<u32>(cc));
UNREACHABLE();
}
const u32 target = offset + instr.bra.GetBranchTarget();
shader.AddLine("{ jmp_to = " + std::to_string(target) + "u; break; }");
break;
@@ -3501,13 +3616,21 @@ private:
}
case OpCode::Id::SYNC: {
// The SYNC opcode jumps to the address previously set by the SSY opcode
ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
const Tegra::Shader::ControlCode cc = instr.flow_control_code;
if (cc != Tegra::Shader::ControlCode::T) {
LOG_CRITICAL(HW_GPU, "SYNC Control Code used: {}", static_cast<u32>(cc));
UNREACHABLE();
}
EmitPopFromFlowStack();
break;
}
case OpCode::Id::BRK: {
// The BRK opcode jumps to the address previously set by the PBK opcode
ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
const Tegra::Shader::ControlCode cc = instr.flow_control_code;
if (cc != Tegra::Shader::ControlCode::T) {
LOG_CRITICAL(HW_GPU, "BRK Control Code used: {}", static_cast<u32>(cc));
UNREACHABLE();
}
EmitPopFromFlowStack();
break;
}
@@ -3537,6 +3660,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: {
@@ -3564,7 +3692,7 @@ private:
break;
}
default: {
LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
LOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->get().GetName());
UNREACHABLE();
}
}
@@ -3705,9 +3833,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();
@@ -3716,7 +3844,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

View File

@@ -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

View File

@@ -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;

View File

@@ -89,7 +89,18 @@ OpenGLState::OpenGLState() {
point.size = 1;
}
void OpenGLState::Apply() const {
void OpenGLState::ApplyDefaultState() {
glDisable(GL_FRAMEBUFFER_SRGB);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_PRIMITIVE_RESTART);
glDisable(GL_STENCIL_TEST);
glEnable(GL_BLEND);
glDisable(GL_COLOR_LOGIC_OP);
glDisable(GL_SCISSOR_TEST);
}
void OpenGLState::ApplySRgb() const {
// sRGB
if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) {
if (framebuffer_srgb.enabled) {
@@ -100,96 +111,122 @@ void OpenGLState::Apply() const {
glDisable(GL_FRAMEBUFFER_SRGB);
}
}
}
void OpenGLState::ApplyCulling() const {
// Culling
if (cull.enabled != cur_state.cull.enabled) {
const bool cull_changed = cull.enabled != cur_state.cull.enabled;
if (cull_changed) {
if (cull.enabled) {
glEnable(GL_CULL_FACE);
} else {
glDisable(GL_CULL_FACE);
}
}
if (cull.enabled) {
if (cull_changed || cull.mode != cur_state.cull.mode) {
glCullFace(cull.mode);
}
if (cull.mode != cur_state.cull.mode) {
glCullFace(cull.mode);
}
if (cull.front_face != cur_state.cull.front_face) {
glFrontFace(cull.front_face);
if (cull_changed || cull.front_face != cur_state.cull.front_face) {
glFrontFace(cull.front_face);
}
}
}
void OpenGLState::ApplyDepth() const {
// Depth test
if (depth.test_enabled != cur_state.depth.test_enabled) {
const bool depth_test_changed = depth.test_enabled != cur_state.depth.test_enabled;
if (depth_test_changed) {
if (depth.test_enabled) {
glEnable(GL_DEPTH_TEST);
} else {
glDisable(GL_DEPTH_TEST);
}
}
if (depth.test_func != cur_state.depth.test_func) {
if (depth.test_enabled &&
(depth_test_changed || depth.test_func != cur_state.depth.test_func)) {
glDepthFunc(depth.test_func);
}
// Depth mask
if (depth.write_mask != cur_state.depth.write_mask) {
glDepthMask(depth.write_mask);
}
// Depth range
if (depth.depth_range_near != cur_state.depth.depth_range_near ||
depth.depth_range_far != cur_state.depth.depth_range_far) {
glDepthRange(depth.depth_range_near, depth.depth_range_far);
}
}
// Primitive restart
if (primitive_restart.enabled != cur_state.primitive_restart.enabled) {
void OpenGLState::ApplyPrimitiveRestart() const {
const bool primitive_restart_changed =
primitive_restart.enabled != cur_state.primitive_restart.enabled;
if (primitive_restart_changed) {
if (primitive_restart.enabled) {
glEnable(GL_PRIMITIVE_RESTART);
} else {
glDisable(GL_PRIMITIVE_RESTART);
}
}
if (primitive_restart.index != cur_state.primitive_restart.index) {
if (primitive_restart_changed ||
(primitive_restart.enabled &&
primitive_restart.index != cur_state.primitive_restart.index)) {
glPrimitiveRestartIndex(primitive_restart.index);
}
}
// Color mask
if (color_mask.red_enabled != cur_state.color_mask.red_enabled ||
color_mask.green_enabled != cur_state.color_mask.green_enabled ||
color_mask.blue_enabled != cur_state.color_mask.blue_enabled ||
color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) {
glColorMask(color_mask.red_enabled, color_mask.green_enabled, color_mask.blue_enabled,
color_mask.alpha_enabled);
}
// Stencil test
if (stencil.test_enabled != cur_state.stencil.test_enabled) {
void OpenGLState::ApplyStencilTest() const {
const bool stencil_test_changed = stencil.test_enabled != cur_state.stencil.test_enabled;
if (stencil_test_changed) {
if (stencil.test_enabled) {
glEnable(GL_STENCIL_TEST);
} else {
glDisable(GL_STENCIL_TEST);
}
}
auto config_stencil = [](GLenum face, const auto& config, const auto& prev_config) {
if (config.test_func != prev_config.test_func || config.test_ref != prev_config.test_ref ||
config.test_mask != prev_config.test_mask) {
glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask);
}
if (config.action_depth_fail != prev_config.action_depth_fail ||
config.action_depth_pass != prev_config.action_depth_pass ||
config.action_stencil_fail != prev_config.action_stencil_fail) {
glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail,
config.action_depth_pass);
}
if (config.write_mask != prev_config.write_mask) {
glStencilMaskSeparate(face, config.write_mask);
}
};
config_stencil(GL_FRONT, stencil.front, cur_state.stencil.front);
config_stencil(GL_BACK, stencil.back, cur_state.stencil.back);
if (stencil.test_enabled) {
auto config_stencil = [stencil_test_changed](GLenum face, const auto& config,
const auto& prev_config) {
if (stencil_test_changed || config.test_func != prev_config.test_func ||
config.test_ref != prev_config.test_ref ||
config.test_mask != prev_config.test_mask) {
glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask);
}
if (stencil_test_changed || config.action_depth_fail != prev_config.action_depth_fail ||
config.action_depth_pass != prev_config.action_depth_pass ||
config.action_stencil_fail != prev_config.action_stencil_fail) {
glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail,
config.action_depth_pass);
}
if (config.write_mask != prev_config.write_mask) {
glStencilMaskSeparate(face, config.write_mask);
}
};
config_stencil(GL_FRONT, stencil.front, cur_state.stencil.front);
config_stencil(GL_BACK, stencil.back, cur_state.stencil.back);
}
}
// Blending
if (blend.enabled != cur_state.blend.enabled) {
void OpenGLState::ApplyScissorTest() const {
const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled;
if (scissor_changed) {
if (scissor.enabled) {
glEnable(GL_SCISSOR_TEST);
} else {
glDisable(GL_SCISSOR_TEST);
}
}
if (scissor_changed || scissor_changed || scissor.x != cur_state.scissor.x ||
scissor.y != cur_state.scissor.y || scissor.width != cur_state.scissor.width ||
scissor.height != cur_state.scissor.height) {
glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
}
}
void OpenGLState::ApplyBlending() const {
const bool blend_changed = blend.enabled != cur_state.blend.enabled;
if (blend_changed) {
if (blend.enabled) {
ASSERT(!logic_op.enabled);
glEnable(GL_BLEND);
@@ -197,29 +234,32 @@ void OpenGLState::Apply() const {
glDisable(GL_BLEND);
}
}
if (blend.enabled) {
if (blend_changed || blend.color.red != cur_state.blend.color.red ||
blend.color.green != cur_state.blend.color.green ||
blend.color.blue != cur_state.blend.color.blue ||
blend.color.alpha != cur_state.blend.color.alpha) {
glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha);
}
if (blend.color.red != cur_state.blend.color.red ||
blend.color.green != cur_state.blend.color.green ||
blend.color.blue != cur_state.blend.color.blue ||
blend.color.alpha != cur_state.blend.color.alpha) {
glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha);
if (blend_changed || blend.src_rgb_func != cur_state.blend.src_rgb_func ||
blend.dst_rgb_func != cur_state.blend.dst_rgb_func ||
blend.src_a_func != cur_state.blend.src_a_func ||
blend.dst_a_func != cur_state.blend.dst_a_func) {
glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func,
blend.dst_a_func);
}
if (blend_changed || blend.rgb_equation != cur_state.blend.rgb_equation ||
blend.a_equation != cur_state.blend.a_equation) {
glBlendEquationSeparate(blend.rgb_equation, blend.a_equation);
}
}
}
if (blend.src_rgb_func != cur_state.blend.src_rgb_func ||
blend.dst_rgb_func != cur_state.blend.dst_rgb_func ||
blend.src_a_func != cur_state.blend.src_a_func ||
blend.dst_a_func != cur_state.blend.dst_a_func) {
glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func,
blend.dst_a_func);
}
if (blend.rgb_equation != cur_state.blend.rgb_equation ||
blend.a_equation != cur_state.blend.a_equation) {
glBlendEquationSeparate(blend.rgb_equation, blend.a_equation);
}
// Logic Operation
if (logic_op.enabled != cur_state.logic_op.enabled) {
void OpenGLState::ApplyLogicOp() const {
const bool logic_op_changed = logic_op.enabled != cur_state.logic_op.enabled;
if (logic_op_changed) {
if (logic_op.enabled) {
ASSERT(!blend.enabled);
glEnable(GL_COLOR_LOGIC_OP);
@@ -228,11 +268,13 @@ void OpenGLState::Apply() const {
}
}
if (logic_op.operation != cur_state.logic_op.operation) {
if (logic_op.enabled &&
(logic_op_changed || logic_op.operation != cur_state.logic_op.operation)) {
glLogicOp(logic_op.operation);
}
}
// Textures
void OpenGLState::ApplyTextures() const {
for (std::size_t i = 0; i < std::size(texture_units); ++i) {
const auto& texture_unit = texture_units[i];
const auto& cur_state_texture_unit = cur_state.texture_units[i];
@@ -251,28 +293,29 @@ void OpenGLState::Apply() const {
glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data());
}
}
}
// Samplers
{
bool has_delta{};
std::size_t first{}, last{};
std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers;
for (std::size_t i = 0; i < std::size(samplers); ++i) {
samplers[i] = texture_units[i].sampler;
if (samplers[i] != cur_state.texture_units[i].sampler) {
if (!has_delta) {
first = i;
has_delta = true;
}
last = i;
void OpenGLState::ApplySamplers() const {
bool has_delta{};
std::size_t first{}, last{};
std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers;
for (std::size_t i = 0; i < std::size(samplers); ++i) {
samplers[i] = texture_units[i].sampler;
if (samplers[i] != cur_state.texture_units[i].sampler) {
if (!has_delta) {
first = i;
has_delta = true;
}
}
if (has_delta) {
glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
samplers.data());
last = i;
}
}
if (has_delta) {
glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
samplers.data());
}
}
void OpenGLState::Apply() const {
// Framebuffer
if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
@@ -305,27 +348,12 @@ void OpenGLState::Apply() const {
if (draw.program_pipeline != cur_state.draw.program_pipeline) {
glBindProgramPipeline(draw.program_pipeline);
}
// Scissor test
if (scissor.enabled != cur_state.scissor.enabled) {
if (scissor.enabled) {
glEnable(GL_SCISSOR_TEST);
} else {
glDisable(GL_SCISSOR_TEST);
}
}
if (scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y ||
scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height) {
glScissor(scissor.x, scissor.y, scissor.width, scissor.height);
}
// Viewport
if (viewport.x != cur_state.viewport.x || viewport.y != cur_state.viewport.y ||
viewport.width != cur_state.viewport.width ||
viewport.height != cur_state.viewport.height) {
glViewport(viewport.x, viewport.y, viewport.width, viewport.height);
}
// Clip distance
for (std::size_t i = 0; i < clip_distance.size(); ++i) {
if (clip_distance[i] != cur_state.clip_distance[i]) {
@@ -336,12 +364,28 @@ void OpenGLState::Apply() const {
}
}
}
// Color mask
if (color_mask.red_enabled != cur_state.color_mask.red_enabled ||
color_mask.green_enabled != cur_state.color_mask.green_enabled ||
color_mask.blue_enabled != cur_state.color_mask.blue_enabled ||
color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) {
glColorMask(color_mask.red_enabled, color_mask.green_enabled, color_mask.blue_enabled,
color_mask.alpha_enabled);
}
// Point
if (point.size != cur_state.point.size) {
glPointSize(point.size);
}
ApplyScissorTest();
ApplyStencilTest();
ApplySRgb();
ApplyCulling();
ApplyDepth();
ApplyPrimitiveRestart();
ApplyBlending();
ApplyLogicOp();
ApplyTextures();
ApplySamplers();
cur_state = *this;
}

View File

@@ -173,7 +173,8 @@ public:
}
/// Apply this state as the current OpenGL state
void Apply() const;
/// Set the initial OpenGL state
static void ApplyDefaultState();
/// Resets any references to the given resource
OpenGLState& UnbindTexture(GLuint handle);
OpenGLState& ResetSampler(GLuint handle);
@@ -188,6 +189,16 @@ private:
// Workaround for sRGB problems caused by
// QT not supporting srgb output
static bool s_rgb_used;
void ApplySRgb() const;
void ApplyCulling() const;
void ApplyDepth() const;
void ApplyPrimitiveRestart() const;
void ApplyStencilTest() const;
void ApplyScissorTest() const;
void ApplyBlending() const;
void ApplyLogicOp() const;
void ApplyTextures() const;
void ApplySamplers() const;
};
} // namespace OpenGL

View File

@@ -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.

View File

@@ -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;

View File

@@ -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

View File

@@ -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

499
src/video_core/surface.cpp Normal file
View File

@@ -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

385
src/video_core/surface.h Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -102,16 +102,27 @@ void TelemetryJson::Complete() {
impl->SerializeSection(Telemetry::FieldType::App, "App");
impl->SerializeSection(Telemetry::FieldType::Session, "Session");
impl->SerializeSection(Telemetry::FieldType::Performance, "Performance");
impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
auto content = impl->TopSection().dump();
// Send the telemetry async but don't handle the errors since they were written to the log
Common::DetachedTasks::AddTask(
[host{impl->host}, username{impl->username}, token{impl->token}, content]() {
Client{host, username, token}.PostJson("/telemetry", content, true);
});
Common::DetachedTasks::AddTask([host{impl->host}, content]() {
Client{host, "", ""}.PostJson("/telemetry", content, true);
});
}
bool TelemetryJson::SubmitTestcase() {
impl->SerializeSection(Telemetry::FieldType::App, "App");
impl->SerializeSection(Telemetry::FieldType::Session, "Session");
impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
auto content = impl->TopSection().dump();
Client client(impl->host, impl->username, impl->token);
auto value = client.PostJson("/gamedb/testcase", content, false);
return value.result_code == Common::WebResult::Code::Success;
}
} // namespace WebService

View File

@@ -35,6 +35,7 @@ public:
void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override;
void Complete() override;
bool SubmitTestcase() override;
private:
struct Impl;

View File

@@ -56,6 +56,8 @@ add_executable(yuzu
main.h
ui_settings.cpp
ui_settings.h
util/limitable_input_dialog.cpp
util/limitable_input_dialog.h
util/spinbox.cpp
util/spinbox.h
util/util.cpp

View File

@@ -5,6 +5,7 @@
#include <QButtonGroup>
#include <QMessageBox>
#include <QPushButton>
#include <QtConcurrent/qtconcurrentrun.h>
#include "common/logging/log.h"
#include "common/telemetry.h"
#include "core/core.h"
@@ -23,6 +24,8 @@ CompatDB::CompatDB(QWidget* parent)
connect(ui->radioButton_IntroMenu, &QRadioButton::clicked, this, &CompatDB::EnableNext);
connect(ui->radioButton_WontBoot, &QRadioButton::clicked, this, &CompatDB::EnableNext);
connect(button(NextButton), &QPushButton::clicked, this, &CompatDB::Submit);
connect(&testcase_watcher, &QFutureWatcher<bool>::finished, this,
&CompatDB::OnTestcaseSubmitted);
}
CompatDB::~CompatDB() = default;
@@ -48,18 +51,38 @@ void CompatDB::Submit() {
}
break;
case CompatDBPage::Final:
back();
LOG_DEBUG(Frontend, "Compatibility Rating: {}", compatibility->checkedId());
Core::Telemetry().AddField(Telemetry::FieldType::UserFeedback, "Compatibility",
compatibility->checkedId());
// older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a
// workaround
button(NextButton)->setEnabled(false);
button(NextButton)->setText(tr("Submitting"));
button(QWizard::CancelButton)->setVisible(false);
testcase_watcher.setFuture(QtConcurrent::run(
[this]() { return Core::System::GetInstance().TelemetrySession().SubmitTestcase(); }));
break;
default:
LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
}
}
void CompatDB::OnTestcaseSubmitted() {
if (!testcase_watcher.result()) {
QMessageBox::critical(this, tr("Communication error"),
tr("An error occured while sending the Testcase"));
button(NextButton)->setEnabled(true);
button(NextButton)->setText(tr("Next"));
button(QWizard::CancelButton)->setVisible(true);
} else {
next();
// older versions of QT don't support the "NoCancelButtonOnLastPage" option, this is a
// workaround
button(QWizard::CancelButton)->setVisible(false);
}
}
void CompatDB::EnableNext() {
button(NextButton)->setEnabled(true);
}

View File

@@ -5,6 +5,7 @@
#pragma once
#include <memory>
#include <QFutureWatcher>
#include <QWizard>
namespace Ui {
@@ -19,8 +20,11 @@ public:
~CompatDB();
private:
QFutureWatcher<bool> testcase_watcher;
std::unique_ptr<Ui::CompatDB> ui;
void Submit();
void OnTestcaseSubmitted();
void EnableNext();
};

View File

@@ -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) {

View File

@@ -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;

View File

@@ -6,20 +6,20 @@
#include <QFileDialog>
#include <QGraphicsItem>
#include <QGraphicsScene>
#include <QInputDialog>
#include <QHeaderView>
#include <QMessageBox>
#include <QStandardItemModel>
#include <QTreeView>
#include <QVBoxLayout>
#include "common/common_paths.h"
#include "common/logging/backend.h"
#include "common/assert.h"
#include "common/file_util.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/settings.h"
#include "ui_configure_system.h"
#include "yuzu/configuration/configure_system.h"
#include "yuzu/main.h"
#include "yuzu/util/limitable_input_dialog.h"
namespace {
constexpr std::array<int, 12> days_in_month = {{
@@ -78,11 +78,17 @@ QPixmap GetIcon(Service::Account::UUID uuid) {
if (!icon) {
icon.fill(Qt::black);
icon.loadFromData(backup_jpeg.data(), backup_jpeg.size());
icon.loadFromData(backup_jpeg.data(), static_cast<u32>(backup_jpeg.size()));
}
return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
QString GetProfileUsernameFromUser(QWidget* parent, const QString& description_text) {
return LimitableInputDialog::GetText(parent, ConfigureSystem::tr("Enter Username"),
description_text, 1,
static_cast<int>(Service::Account::profile_username_size));
}
} // Anonymous namespace
ConfigureSystem::ConfigureSystem(QWidget* parent)
@@ -173,7 +179,7 @@ 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();
@@ -244,15 +250,13 @@ void ConfigureSystem::SelectUser(const QModelIndex& index) {
}
void ConfigureSystem::AddUser() {
const auto uuid = Service::Account::UUID::Generate();
bool ok = false;
const auto username =
QInputDialog::getText(this, tr("Enter Username"), tr("Enter a username for the new user:"),
QLineEdit::Normal, QString(), &ok);
if (!ok)
GetProfileUsernameFromUser(this, tr("Enter a username for the new user:"));
if (username.isEmpty()) {
return;
}
const auto uuid = Service::Account::UUID::Generate();
profile_manager->CreateNewUser(uuid, username.toStdString());
item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)});
@@ -261,30 +265,21 @@ void ConfigureSystem::AddUser() {
void ConfigureSystem::RenameUser() {
const auto user = tree_view->currentIndex().row();
const auto uuid = profile_manager->GetUser(user);
ASSERT(uuid != std::nullopt);
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, old_username, &ok);
if (!ok)
const auto new_username = GetProfileUsernameFromUser(this, tr("Enter a new username:"));
if (new_username.isEmpty()) {
return;
std::fill(profile.username.begin(), profile.username.end(), '\0');
const auto username_std = new_username.toStdString();
if (username_std.size() > profile.username.size()) {
std::copy_n(username_std.begin(), std::min(profile.username.size(), username_std.size()),
profile.username.begin());
} else {
std::copy(username_std.begin(), username_std.end(), profile.username.begin());
}
const auto username_std = new_username.toStdString();
std::fill(profile.username.begin(), profile.username.end(), '\0');
std::copy(username_std.begin(), username_std.end(), profile.username.begin());
profile_manager->SetProfileBase(*uuid, profile);
item_model->setItem(
@@ -297,7 +292,7 @@ void ConfigureSystem::RenameUser() {
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(
@@ -324,7 +319,7 @@ void ConfigureSystem::DeleteUser() {
void ConfigureSystem::SetUserImage() {
const auto index = tree_view->currentIndex().row();
const auto uuid = profile_manager->GetUser(index);
ASSERT(uuid != std::nullopt);
ASSERT(uuid);
const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(),
tr("JPEG Images (*.jpg *.jpeg)"));

View File

@@ -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");

View File

@@ -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"

View File

@@ -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)];

View File

@@ -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;

View File

@@ -0,0 +1,59 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QDialogButtonBox>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include "yuzu/util/limitable_input_dialog.h"
LimitableInputDialog::LimitableInputDialog(QWidget* parent) : QDialog{parent} {
CreateUI();
ConnectEvents();
}
LimitableInputDialog::~LimitableInputDialog() = default;
void LimitableInputDialog::CreateUI() {
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
text_label = new QLabel(this);
text_entry = new QLineEdit(this);
buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
auto* const layout = new QVBoxLayout;
layout->addWidget(text_label);
layout->addWidget(text_entry);
layout->addWidget(buttons);
setLayout(layout);
}
void LimitableInputDialog::ConnectEvents() {
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
QString LimitableInputDialog::GetText(QWidget* parent, const QString& title, const QString& text,
int min_character_limit, int max_character_limit) {
Q_ASSERT(min_character_limit <= max_character_limit);
LimitableInputDialog dialog{parent};
dialog.setWindowTitle(title);
dialog.text_label->setText(text);
dialog.text_entry->setMaxLength(max_character_limit);
auto* const ok_button = dialog.buttons->button(QDialogButtonBox::Ok);
ok_button->setEnabled(false);
connect(dialog.text_entry, &QLineEdit::textEdited, [&](const QString& new_text) {
ok_button->setEnabled(new_text.length() >= min_character_limit);
});
if (dialog.exec() != QDialog::Accepted) {
return {};
}
return dialog.text_entry->text();
}

View File

@@ -0,0 +1,31 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <QDialog>
class QDialogButtonBox;
class QLabel;
class QLineEdit;
/// A QDialog that functions similarly to QInputDialog, however, it allows
/// restricting the minimum and total number of characters that can be entered.
class LimitableInputDialog final : public QDialog {
Q_OBJECT
public:
explicit LimitableInputDialog(QWidget* parent = nullptr);
~LimitableInputDialog() override;
static QString GetText(QWidget* parent, const QString& title, const QString& text,
int min_character_limit, int max_character_limit);
private:
void CreateUI();
void ConnectEvents();
QLabel* text_label;
QLineEdit* text_entry;
QDialogButtonBox* buttons;
};