Compare commits
46 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c68aa65226 | ||
|
|
a921d22545 | ||
|
|
ee07041b3a | ||
|
|
9c977d2215 | ||
|
|
f2c7b5dcd6 | ||
|
|
d37da52cb3 | ||
|
|
969326bd58 | ||
|
|
224071a652 | ||
|
|
2dad1204e8 | ||
|
|
249341d08f | ||
|
|
261a4f0311 | ||
|
|
ca4bf671ce | ||
|
|
28e90fa0e0 | ||
|
|
0a93b45b6a | ||
|
|
403dfd68fc | ||
|
|
c519354506 | ||
|
|
0b668d5ff3 | ||
|
|
bc286c169f | ||
|
|
670a2c1f80 | ||
|
|
88ffa422d4 | ||
|
|
0471976b48 | ||
|
|
c1ad973881 | ||
|
|
305a05f820 | ||
|
|
d64303d185 | ||
|
|
b8b9f41b6b | ||
|
|
dfcde52f39 | ||
|
|
4f0818144e | ||
|
|
20c2928c2b | ||
|
|
f380496728 | ||
|
|
2e80e7480d | ||
|
|
8eb97706b8 | ||
|
|
aaf671a309 | ||
|
|
be53097577 | ||
|
|
0a003efde4 | ||
|
|
2e8620c877 | ||
|
|
6b0bc48a42 | ||
|
|
a5b65df9cf | ||
|
|
57626fda7b | ||
|
|
6313d54cef | ||
|
|
7e6a73963e | ||
|
|
2156cb3cbe | ||
|
|
8069fbd37f | ||
|
|
ec3bef7b4c | ||
|
|
0e1510ac29 | ||
|
|
b8c43b6080 | ||
|
|
3a67876252 |
@@ -88,7 +88,7 @@ System::ResultStatus System::SingleStep() {
|
||||
return RunLoop(false);
|
||||
}
|
||||
|
||||
System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) {
|
||||
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
|
||||
app_loader = Loader::GetLoader(virtual_filesystem->OpenFile(filepath, FileSys::Mode::Read));
|
||||
|
||||
if (!app_loader) {
|
||||
@@ -102,18 +102,8 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file
|
||||
LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
|
||||
static_cast<int>(system_mode.second));
|
||||
|
||||
switch (system_mode.second) {
|
||||
case Loader::ResultStatus::ErrorMissingKeys:
|
||||
return ResultStatus::ErrorLoader_ErrorMissingKeys;
|
||||
case Loader::ResultStatus::ErrorDecrypting:
|
||||
return ResultStatus::ErrorLoader_ErrorDecrypting;
|
||||
case Loader::ResultStatus::ErrorInvalidFormat:
|
||||
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
|
||||
case Loader::ResultStatus::ErrorUnsupportedArch:
|
||||
return ResultStatus::ErrorUnsupportedArch;
|
||||
default:
|
||||
if (system_mode.second != Loader::ResultStatus::Success)
|
||||
return ResultStatus::ErrorSystemMode;
|
||||
}
|
||||
}
|
||||
|
||||
ResultStatus init_result{Init(emu_window)};
|
||||
@@ -129,17 +119,9 @@ System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& file
|
||||
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
|
||||
System::Shutdown();
|
||||
|
||||
switch (load_result) {
|
||||
case Loader::ResultStatus::ErrorMissingKeys:
|
||||
return ResultStatus::ErrorLoader_ErrorMissingKeys;
|
||||
case Loader::ResultStatus::ErrorDecrypting:
|
||||
return ResultStatus::ErrorLoader_ErrorDecrypting;
|
||||
case Loader::ResultStatus::ErrorInvalidFormat:
|
||||
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
|
||||
case Loader::ResultStatus::ErrorUnsupportedArch:
|
||||
return ResultStatus::ErrorUnsupportedArch;
|
||||
default:
|
||||
return ResultStatus::ErrorLoader;
|
||||
if (load_result != Loader::ResultStatus::Success) {
|
||||
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
|
||||
static_cast<u32>(load_result));
|
||||
}
|
||||
}
|
||||
status = ResultStatus::Success;
|
||||
@@ -169,7 +151,7 @@ Cpu& System::CpuCore(size_t core_index) {
|
||||
return *cpu_cores[core_index];
|
||||
}
|
||||
|
||||
System::ResultStatus System::Init(EmuWindow& emu_window) {
|
||||
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
|
||||
LOG_DEBUG(HW_Memory, "initialized OK");
|
||||
|
||||
CoreTiming::Init();
|
||||
|
||||
@@ -22,9 +22,12 @@
|
||||
#include "video_core/debug_utils/debug_utils.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
class EmuWindow;
|
||||
class ARM_Interface;
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
}
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
@@ -49,21 +52,15 @@ public:
|
||||
|
||||
/// Enumeration representing the return values of the System Initialize and Load process.
|
||||
enum class ResultStatus : u32 {
|
||||
Success, ///< Succeeded
|
||||
ErrorNotInitialized, ///< Error trying to use core prior to initialization
|
||||
ErrorGetLoader, ///< Error finding the correct application loader
|
||||
ErrorSystemMode, ///< Error determining the system mode
|
||||
ErrorLoader, ///< Error loading the specified application
|
||||
ErrorLoader_ErrorMissingKeys, ///< Error because the key/keys needed to run could not be
|
||||
///< found.
|
||||
ErrorLoader_ErrorDecrypting, ///< Error loading the specified application due to encryption
|
||||
ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an
|
||||
/// invalid format
|
||||
ErrorSystemFiles, ///< Error in finding system files
|
||||
ErrorSharedFont, ///< Error in finding shared font
|
||||
ErrorVideoCore, ///< Error in the video core
|
||||
ErrorUnsupportedArch, ///< Unsupported Architecture (32-Bit ROMs)
|
||||
ErrorUnknown ///< Any other error
|
||||
Success, ///< Succeeded
|
||||
ErrorNotInitialized, ///< Error trying to use core prior to initialization
|
||||
ErrorGetLoader, ///< Error finding the correct application loader
|
||||
ErrorSystemMode, ///< Error determining the system mode
|
||||
ErrorSystemFiles, ///< Error in finding system files
|
||||
ErrorSharedFont, ///< Error in finding shared font
|
||||
ErrorVideoCore, ///< Error in the video core
|
||||
ErrorUnknown, ///< Any other error
|
||||
ErrorLoader, ///< The base for loader errors (too many to repeat)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -105,7 +102,7 @@ public:
|
||||
* @param filepath String path to the executable application to load on the host file system.
|
||||
* @returns ResultStatus code, indicating if the operation succeeded.
|
||||
*/
|
||||
ResultStatus Load(EmuWindow& emu_window, const std::string& filepath);
|
||||
ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath);
|
||||
|
||||
/**
|
||||
* Indicates if the emulated system is powered on (all subsystems initialized and able to run an
|
||||
@@ -233,7 +230,7 @@ private:
|
||||
* input.
|
||||
* @return ResultStatus code, indicating if the operation succeeded.
|
||||
*/
|
||||
ResultStatus Init(EmuWindow& emu_window);
|
||||
ResultStatus Init(Frontend::EmuWindow& emu_window);
|
||||
|
||||
/// RealVfsFilesystem instance
|
||||
FileSys::VirtualFilesystem virtual_filesystem;
|
||||
|
||||
@@ -12,14 +12,16 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", "logo"};
|
||||
|
||||
XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
|
||||
if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
|
||||
status = Loader::ResultStatus::ErrorInvalidFormat;
|
||||
status = Loader::ResultStatus::ErrorBadXCIHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) {
|
||||
status = Loader::ResultStatus::ErrorInvalidFormat;
|
||||
status = Loader::ResultStatus::ErrorBadXCIHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -31,9 +33,6 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
|
||||
return;
|
||||
}
|
||||
|
||||
static constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure",
|
||||
"logo"};
|
||||
|
||||
for (XCIPartition partition :
|
||||
{XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
|
||||
auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]);
|
||||
@@ -130,15 +129,21 @@ bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
|
||||
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
|
||||
if (partitions[static_cast<size_t>(part)] == nullptr) {
|
||||
return Loader::ResultStatus::ErrorInvalidFormat;
|
||||
return Loader::ResultStatus::ErrorXCIMissingPartition;
|
||||
}
|
||||
|
||||
for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) {
|
||||
if (file->GetExtension() != "nca")
|
||||
continue;
|
||||
auto nca = std::make_shared<NCA>(file);
|
||||
if (nca->GetStatus() == Loader::ResultStatus::Success)
|
||||
if (nca->GetStatus() == Loader::ResultStatus::Success) {
|
||||
ncas.push_back(std::move(nca));
|
||||
} else {
|
||||
const u16 error_id = static_cast<u16>(nca->GetStatus());
|
||||
LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})",
|
||||
partition_names[static_cast<size_t>(part)], nca->GetName(), error_id,
|
||||
Loader::GetMessageForResultStatus(nca->GetStatus()));
|
||||
}
|
||||
}
|
||||
|
||||
return Loader::ResultStatus::Success;
|
||||
|
||||
@@ -113,17 +113,27 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
|
||||
return out;
|
||||
}
|
||||
|
||||
boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() const {
|
||||
boost::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{})
|
||||
if (rights_id == u128{}) {
|
||||
status = Loader::ResultStatus::ErrorInvalidRightsID;
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
|
||||
if (titlekey == Core::Crypto::Key128{})
|
||||
if (titlekey == Core::Crypto::Key128{}) {
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekey;
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekek;
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
|
||||
keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id), Core::Crypto::Mode::ECB);
|
||||
cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), Core::Crypto::Op::Decrypt);
|
||||
@@ -131,7 +141,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() const {
|
||||
return titlekey;
|
||||
}
|
||||
|
||||
VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) const {
|
||||
VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) {
|
||||
if (!encrypted)
|
||||
return in;
|
||||
|
||||
@@ -143,15 +153,22 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
|
||||
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
|
||||
{
|
||||
boost::optional<Core::Crypto::Key128> key = boost::none;
|
||||
if (std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
|
||||
[](char c) { return c == 0; }) == header.rights_id.end()) {
|
||||
key = GetKeyAreaKey(NCASectionCryptoType::CTR);
|
||||
} else {
|
||||
if (has_rights_id) {
|
||||
status = Loader::ResultStatus::Success;
|
||||
key = GetTitlekey();
|
||||
if (key == boost::none) {
|
||||
if (status == Loader::ResultStatus::Success)
|
||||
status = Loader::ResultStatus::ErrorMissingTitlekey;
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
key = GetKeyAreaKey(NCASectionCryptoType::CTR);
|
||||
if (key == boost::none) {
|
||||
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (key == boost::none)
|
||||
return nullptr;
|
||||
auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(
|
||||
std::move(in), key.value(), starting_offset);
|
||||
std::vector<u8> iv(16);
|
||||
@@ -170,16 +187,31 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
|
||||
}
|
||||
|
||||
NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
|
||||
status = Loader::ResultStatus::Success;
|
||||
|
||||
if (file == nullptr) {
|
||||
status = Loader::ResultStatus::ErrorInvalidFormat;
|
||||
status = Loader::ResultStatus::ErrorNullFile;
|
||||
return;
|
||||
}
|
||||
if (sizeof(NCAHeader) != file->ReadObject(&header))
|
||||
|
||||
if (sizeof(NCAHeader) != file->ReadObject(&header)) {
|
||||
LOG_ERROR(Loader, "File reader errored out during header read.");
|
||||
status = Loader::ResultStatus::ErrorBadNCAHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
encrypted = false;
|
||||
|
||||
if (!IsValidNCA(header)) {
|
||||
if (header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
|
||||
status = Loader::ResultStatus::ErrorNCA2;
|
||||
return;
|
||||
}
|
||||
if (header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
|
||||
status = Loader::ResultStatus::ErrorNCA0;
|
||||
return;
|
||||
}
|
||||
|
||||
NCAHeader dec_header{};
|
||||
Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
|
||||
keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
|
||||
@@ -189,14 +221,26 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
|
||||
header = dec_header;
|
||||
encrypted = true;
|
||||
} else {
|
||||
if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
|
||||
status = Loader::ResultStatus::ErrorNCA2;
|
||||
return;
|
||||
}
|
||||
if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
|
||||
status = Loader::ResultStatus::ErrorNCA0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!keys.HasKey(Core::Crypto::S256KeyType::Header))
|
||||
status = Loader::ResultStatus::ErrorMissingKeys;
|
||||
status = Loader::ResultStatus::ErrorMissingHeaderKey;
|
||||
else
|
||||
status = Loader::ResultStatus::ErrorDecrypting;
|
||||
status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
|
||||
[](char c) { return c == '\0'; }) != header.rights_id.end();
|
||||
|
||||
const std::ptrdiff_t number_sections =
|
||||
std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
|
||||
[](NCASectionTableEntry entry) { return entry.media_offset > 0; });
|
||||
@@ -229,7 +273,12 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
|
||||
files.push_back(std::move(dec));
|
||||
romfs = files.back();
|
||||
} else {
|
||||
status = Loader::ResultStatus::ErrorMissingKeys;
|
||||
if (status != Loader::ResultStatus::Success)
|
||||
return;
|
||||
if (has_rights_id)
|
||||
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
|
||||
else
|
||||
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
|
||||
return;
|
||||
}
|
||||
} else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
|
||||
@@ -249,7 +298,12 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
|
||||
exefs = dirs.back();
|
||||
}
|
||||
} else {
|
||||
status = Loader::ResultStatus::ErrorMissingKeys;
|
||||
if (status != Loader::ResultStatus::Success)
|
||||
return;
|
||||
if (has_rights_id)
|
||||
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
|
||||
else
|
||||
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,8 +98,8 @@ protected:
|
||||
private:
|
||||
u8 GetCryptoRevision() const;
|
||||
boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
|
||||
boost::optional<Core::Crypto::Key128> GetTitlekey() const;
|
||||
VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const;
|
||||
boost::optional<Core::Crypto::Key128> GetTitlekey();
|
||||
VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset);
|
||||
|
||||
std::vector<VirtualDir> dirs;
|
||||
std::vector<VirtualFile> files;
|
||||
@@ -109,6 +109,7 @@ private:
|
||||
VirtualFile file;
|
||||
|
||||
NCAHeader header{};
|
||||
bool has_rights_id{};
|
||||
|
||||
Loader::ResultStatus status{};
|
||||
|
||||
|
||||
@@ -24,19 +24,19 @@ bool PartitionFilesystem::Header::HasValidMagicValue() const {
|
||||
PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
|
||||
// At least be as large as the header
|
||||
if (file->GetSize() < sizeof(Header)) {
|
||||
status = Loader::ResultStatus::Error;
|
||||
status = Loader::ResultStatus::ErrorBadPFSHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
// For cartridges, HFSs can get very large, so we need to calculate the size up to
|
||||
// the actual content itself instead of just blindly reading in the entire file.
|
||||
if (sizeof(Header) != file->ReadObject(&pfs_header)) {
|
||||
status = Loader::ResultStatus::Error;
|
||||
status = Loader::ResultStatus::ErrorBadPFSHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pfs_header.HasValidMagicValue()) {
|
||||
status = Loader::ResultStatus::ErrorInvalidFormat;
|
||||
status = Loader::ResultStatus::ErrorBadPFSHeader;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
|
||||
const size_t total_size = file_data.size();
|
||||
|
||||
if (total_size != metadata_size) {
|
||||
status = Loader::ResultStatus::Error;
|
||||
status = Loader::ResultStatus::ErrorIncorrectPFSFileSize;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,26 +12,26 @@ namespace FileSys {
|
||||
Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
|
||||
size_t total_size = static_cast<size_t>(file->GetSize());
|
||||
if (total_size < sizeof(Header))
|
||||
return Loader::ResultStatus::Error;
|
||||
return Loader::ResultStatus::ErrorBadNPDMHeader;
|
||||
|
||||
// TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable.
|
||||
std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header));
|
||||
if (sizeof(Header) != npdm_header_data.size())
|
||||
return Loader::ResultStatus::Error;
|
||||
return Loader::ResultStatus::ErrorBadNPDMHeader;
|
||||
std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header));
|
||||
|
||||
std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset);
|
||||
if (sizeof(AcidHeader) != acid_header_data.size())
|
||||
return Loader::ResultStatus::Error;
|
||||
return Loader::ResultStatus::ErrorBadACIDHeader;
|
||||
std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader));
|
||||
|
||||
if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset))
|
||||
return Loader::ResultStatus::Error;
|
||||
return Loader::ResultStatus::ErrorBadACIHeader;
|
||||
|
||||
if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset))
|
||||
return Loader::ResultStatus::Error;
|
||||
return Loader::ResultStatus::ErrorBadFileAccessControl;
|
||||
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
|
||||
return Loader::ResultStatus::Error;
|
||||
return Loader::ResultStatus::ErrorBadFileAccessHeader;
|
||||
|
||||
return Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
class EmuWindow::TouchState : public Input::Factory<Input::TouchDevice>,
|
||||
public std::enable_shared_from_this<TouchState> {
|
||||
public:
|
||||
@@ -108,3 +110,5 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
|
||||
void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) {
|
||||
NotifyFramebufferLayoutChanged(Layout::DefaultFrameLayout(width, height));
|
||||
}
|
||||
|
||||
} // namespace Core::Frontend
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
/**
|
||||
* Abstraction class used to provide an interface between emulation code and the frontend
|
||||
* (e.g. SDL, QGLWidget, GLFW, etc...).
|
||||
@@ -166,3 +168,5 @@ private:
|
||||
*/
|
||||
std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y);
|
||||
};
|
||||
|
||||
} // namespace Core::Frontend
|
||||
|
||||
@@ -71,6 +71,14 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
|
||||
const u32 object_id{context.GetDomainMessageHeader()->object_id};
|
||||
switch (domain_message_header->command) {
|
||||
case IPC::DomainMessageHeader::CommandType::SendMessage:
|
||||
if (object_id > domain_request_handlers.size()) {
|
||||
LOG_CRITICAL(IPC,
|
||||
"object_id {} is too big! This probably means a recent service call "
|
||||
"to {} needed to return a new interface!",
|
||||
object_id, name);
|
||||
UNREACHABLE();
|
||||
return RESULT_SUCCESS; // Ignore error if asserts are off
|
||||
}
|
||||
return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
|
||||
|
||||
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
|
||||
|
||||
@@ -9,10 +9,103 @@
|
||||
|
||||
namespace Service::Friend {
|
||||
|
||||
class IFriendService final : public ServiceFramework<IFriendService> {
|
||||
public:
|
||||
IFriendService() : ServiceFramework("IFriendService") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "GetCompletionEvent"},
|
||||
{1, nullptr, "Cancel"},
|
||||
{10100, nullptr, "GetFriendListIds"},
|
||||
{10101, nullptr, "GetFriendList"},
|
||||
{10102, nullptr, "UpdateFriendInfo"},
|
||||
{10110, nullptr, "GetFriendProfileImage"},
|
||||
{10200, nullptr, "SendFriendRequestForApplication"},
|
||||
{10211, nullptr, "AddFacedFriendRequestForApplication"},
|
||||
{10400, nullptr, "GetBlockedUserListIds"},
|
||||
{10500, nullptr, "GetProfileList"},
|
||||
{10600, nullptr, "DeclareOpenOnlinePlaySession"},
|
||||
{10601, &IFriendService::DeclareCloseOnlinePlaySession,
|
||||
"DeclareCloseOnlinePlaySession"},
|
||||
{10610, nullptr, "UpdateUserPresence"},
|
||||
{10700, nullptr, "GetPlayHistoryRegistrationKey"},
|
||||
{10701, nullptr, "GetPlayHistoryRegistrationKeyWithNetworkServiceAccountId"},
|
||||
{10702, nullptr, "AddPlayHistory"},
|
||||
{11000, nullptr, "GetProfileImageUrl"},
|
||||
{20100, nullptr, "GetFriendCount"},
|
||||
{20101, nullptr, "GetNewlyFriendCount"},
|
||||
{20102, nullptr, "GetFriendDetailedInfo"},
|
||||
{20103, nullptr, "SyncFriendList"},
|
||||
{20104, nullptr, "RequestSyncFriendList"},
|
||||
{20110, nullptr, "LoadFriendSetting"},
|
||||
{20200, nullptr, "GetReceivedFriendRequestCount"},
|
||||
{20201, nullptr, "GetFriendRequestList"},
|
||||
{20300, nullptr, "GetFriendCandidateList"},
|
||||
{20301, nullptr, "GetNintendoNetworkIdInfo"},
|
||||
{20302, nullptr, "GetSnsAccountLinkage"},
|
||||
{20303, nullptr, "GetSnsAccountProfile"},
|
||||
{20304, nullptr, "GetSnsAccountFriendList"},
|
||||
{20400, nullptr, "GetBlockedUserList"},
|
||||
{20401, nullptr, "SyncBlockedUserList"},
|
||||
{20500, nullptr, "GetProfileExtraList"},
|
||||
{20501, nullptr, "GetRelationship"},
|
||||
{20600, nullptr, "GetUserPresenceView"},
|
||||
{20700, nullptr, "GetPlayHistoryList"},
|
||||
{20701, nullptr, "GetPlayHistoryStatistics"},
|
||||
{20800, nullptr, "LoadUserSetting"},
|
||||
{20801, nullptr, "SyncUserSetting"},
|
||||
{20900, nullptr, "RequestListSummaryOverlayNotification"},
|
||||
{21000, nullptr, "GetExternalApplicationCatalog"},
|
||||
{30100, nullptr, "DropFriendNewlyFlags"},
|
||||
{30101, nullptr, "DeleteFriend"},
|
||||
{30110, nullptr, "DropFriendNewlyFlag"},
|
||||
{30120, nullptr, "ChangeFriendFavoriteFlag"},
|
||||
{30121, nullptr, "ChangeFriendOnlineNotificationFlag"},
|
||||
{30200, nullptr, "SendFriendRequest"},
|
||||
{30201, nullptr, "SendFriendRequestWithApplicationInfo"},
|
||||
{30202, nullptr, "CancelFriendRequest"},
|
||||
{30203, nullptr, "AcceptFriendRequest"},
|
||||
{30204, nullptr, "RejectFriendRequest"},
|
||||
{30205, nullptr, "ReadFriendRequest"},
|
||||
{30210, nullptr, "GetFacedFriendRequestRegistrationKey"},
|
||||
{30211, nullptr, "AddFacedFriendRequest"},
|
||||
{30212, nullptr, "CancelFacedFriendRequest"},
|
||||
{30213, nullptr, "GetFacedFriendRequestProfileImage"},
|
||||
{30214, nullptr, "GetFacedFriendRequestProfileImageFromPath"},
|
||||
{30215, nullptr, "SendFriendRequestWithExternalApplicationCatalogId"},
|
||||
{30216, nullptr, "ResendFacedFriendRequest"},
|
||||
{30217, nullptr, "SendFriendRequestWithNintendoNetworkIdInfo"},
|
||||
{30300, nullptr, "GetSnsAccountLinkPageUrl"},
|
||||
{30301, nullptr, "UnlinkSnsAccount"},
|
||||
{30400, nullptr, "BlockUser"},
|
||||
{30401, nullptr, "BlockUserWithApplicationInfo"},
|
||||
{30402, nullptr, "UnblockUser"},
|
||||
{30500, nullptr, "GetProfileExtraFromFriendCode"},
|
||||
{30700, nullptr, "DeletePlayHistory"},
|
||||
{30810, nullptr, "ChangePresencePermission"},
|
||||
{30811, nullptr, "ChangeFriendRequestReception"},
|
||||
{30812, nullptr, "ChangePlayLogPermission"},
|
||||
{30820, nullptr, "IssueFriendCode"},
|
||||
{30830, nullptr, "ClearPlayLog"},
|
||||
{49900, nullptr, "DeleteNetworkServiceAccountCache"},
|
||||
};
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void DeclareCloseOnlinePlaySession(Kernel::HLERequestContext& ctx) {
|
||||
// Stub used by Splatoon 2
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
};
|
||||
|
||||
void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
LOG_WARNING(Service_Friend, "(STUBBED) called");
|
||||
rb.PushIpcInterface<IFriendService>();
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
|
||||
@@ -83,13 +83,13 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
|
||||
|
||||
if (dir == nullptr) {
|
||||
if (file == nullptr)
|
||||
return ResultStatus::ErrorInvalidFormat;
|
||||
return ResultStatus::ErrorNullFile;
|
||||
dir = file->GetContainingDirectory();
|
||||
}
|
||||
|
||||
const FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
|
||||
if (npdm == nullptr)
|
||||
return ResultStatus::ErrorInvalidFormat;
|
||||
return ResultStatus::ErrorMissingNPDM;
|
||||
|
||||
ResultStatus result = metadata.Load(npdm);
|
||||
if (result != ResultStatus::Success) {
|
||||
@@ -99,7 +99,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
|
||||
|
||||
const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
|
||||
if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) {
|
||||
return ResultStatus::ErrorUnsupportedArch;
|
||||
return ResultStatus::Error32BitISA;
|
||||
}
|
||||
|
||||
// Load NSO modules
|
||||
@@ -143,28 +143,28 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
if (romfs == nullptr)
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
return ResultStatus::ErrorNoRomFS;
|
||||
dir = romfs;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buffer) {
|
||||
if (icon_data.empty())
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
return ResultStatus::ErrorNoIcon;
|
||||
buffer = icon_data;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) {
|
||||
if (name.empty())
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
return ResultStatus::ErrorNoControl;
|
||||
out_program_id = title_id;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title) {
|
||||
if (name.empty())
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
return ResultStatus::ErrorNoControl;
|
||||
title = name;
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
|
||||
std::vector<u8> buffer = file->ReadAllBytes();
|
||||
if (buffer.size() != file->GetSize())
|
||||
return ResultStatus::Error;
|
||||
return ResultStatus::ErrorIncorrectELFFileSize;
|
||||
|
||||
ElfReader elf_reader(&buffer[0]);
|
||||
SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR);
|
||||
|
||||
@@ -86,6 +86,55 @@ std::string GetFileTypeString(FileType type) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
constexpr std::array<const char*, 36> RESULT_MESSAGES{
|
||||
"The operation completed successfully.",
|
||||
"The loader requested to load is already loaded.",
|
||||
"The operation is not implemented.",
|
||||
"The loader is not initialized properly.",
|
||||
"The NPDM file has a bad header.",
|
||||
"The NPDM has a bad ACID header.",
|
||||
"The NPDM has a bad ACI header,",
|
||||
"The NPDM file has a bad file access control.",
|
||||
"The NPDM has a bad file access header.",
|
||||
"The PFS/HFS partition has a bad header.",
|
||||
"The PFS/HFS partition has incorrect size as determined by the header.",
|
||||
"The NCA file has a bad header.",
|
||||
"The general keyfile could not be found.",
|
||||
"The NCA Header key could not be found.",
|
||||
"The NCA Header key is incorrect or the header is invalid.",
|
||||
"Support for NCA2-type NCAs is not implemented.",
|
||||
"Support for NCA0-type NCAs is not implemented.",
|
||||
"The titlekey for this Rights ID could not be found.",
|
||||
"The titlekek for this crypto revision could not be found.",
|
||||
"The Rights ID in the header is invalid.",
|
||||
"The key area key for this application type and crypto revision could not be found.",
|
||||
"The key area key is incorrect or the section header is invalid.",
|
||||
"The titlekey and/or titlekek is incorrect or the section header is invalid.",
|
||||
"The XCI file is missing a Program-type NCA.",
|
||||
"The NCA file is not an application.",
|
||||
"The ExeFS partition could not be found.",
|
||||
"The XCI file has a bad header.",
|
||||
"The XCI file is missing a partition.",
|
||||
"The file could not be found or does not exist.",
|
||||
"The game is missing a program metadata file (main.npdm).",
|
||||
"The game uses the currently-unimplemented 32-bit architecture.",
|
||||
"The RomFS could not be found.",
|
||||
"The ELF file has incorrect size as determined by the header.",
|
||||
"There was a general error loading the NRO into emulated memory.",
|
||||
"There is no icon available.",
|
||||
"There is no control data available.",
|
||||
};
|
||||
|
||||
std::string GetMessageForResultStatus(ResultStatus status) {
|
||||
return GetMessageForResultStatus(static_cast<size_t>(status));
|
||||
}
|
||||
|
||||
std::string GetMessageForResultStatus(u16 status) {
|
||||
if (status >= 36)
|
||||
return "";
|
||||
return RESULT_MESSAGES[status];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a loader for a file with a specific type
|
||||
* @param file The file to load
|
||||
|
||||
@@ -58,18 +58,46 @@ std::string GetFileTypeString(FileType type);
|
||||
/// Return type for functions in Loader namespace
|
||||
enum class ResultStatus {
|
||||
Success,
|
||||
Error,
|
||||
ErrorInvalidFormat,
|
||||
ErrorNotImplemented,
|
||||
ErrorNotLoaded,
|
||||
ErrorNotUsed,
|
||||
ErrorAlreadyLoaded,
|
||||
ErrorMemoryAllocationFailed,
|
||||
ErrorMissingKeys,
|
||||
ErrorDecrypting,
|
||||
ErrorUnsupportedArch,
|
||||
ErrorNotImplemented,
|
||||
ErrorNotInitialized,
|
||||
ErrorBadNPDMHeader,
|
||||
ErrorBadACIDHeader,
|
||||
ErrorBadACIHeader,
|
||||
ErrorBadFileAccessControl,
|
||||
ErrorBadFileAccessHeader,
|
||||
ErrorBadPFSHeader,
|
||||
ErrorIncorrectPFSFileSize,
|
||||
ErrorBadNCAHeader,
|
||||
ErrorMissingProductionKeyFile,
|
||||
ErrorMissingHeaderKey,
|
||||
ErrorIncorrectHeaderKey,
|
||||
ErrorNCA2,
|
||||
ErrorNCA0,
|
||||
ErrorMissingTitlekey,
|
||||
ErrorMissingTitlekek,
|
||||
ErrorInvalidRightsID,
|
||||
ErrorMissingKeyAreaKey,
|
||||
ErrorIncorrectKeyAreaKey,
|
||||
ErrorIncorrectTitlekeyOrTitlekek,
|
||||
ErrorXCIMissingProgramNCA,
|
||||
ErrorNCANotProgram,
|
||||
ErrorNoExeFS,
|
||||
ErrorBadXCIHeader,
|
||||
ErrorXCIMissingPartition,
|
||||
ErrorNullFile,
|
||||
ErrorMissingNPDM,
|
||||
Error32BitISA,
|
||||
ErrorNoRomFS,
|
||||
ErrorIncorrectELFFileSize,
|
||||
ErrorLoadingNRO,
|
||||
ErrorNoIcon,
|
||||
ErrorNoControl,
|
||||
};
|
||||
|
||||
std::string GetMessageForResultStatus(ResultStatus status);
|
||||
std::string GetMessageForResultStatus(u16 status);
|
||||
|
||||
/// Interface for loading an application
|
||||
class AppLoader : NonCopyable {
|
||||
public:
|
||||
|
||||
@@ -46,12 +46,12 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
}
|
||||
|
||||
if (nca->GetType() != FileSys::NCAContentType::Program)
|
||||
return ResultStatus::ErrorInvalidFormat;
|
||||
return ResultStatus::ErrorNCANotProgram;
|
||||
|
||||
const auto exefs = nca->GetExeFS();
|
||||
|
||||
if (exefs == nullptr)
|
||||
return ResultStatus::ErrorInvalidFormat;
|
||||
return ResultStatus::ErrorNoExeFS;
|
||||
|
||||
directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs);
|
||||
|
||||
@@ -69,16 +69,16 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
if (nca == nullptr)
|
||||
return ResultStatus::ErrorNotLoaded;
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
if (nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0)
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
return ResultStatus::ErrorNoRomFS;
|
||||
dir = nca->GetRomFS();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) {
|
||||
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success)
|
||||
return ResultStatus::ErrorInvalidFormat;
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
out_program_id = nca->GetTitleId();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR};
|
||||
|
||||
if (!LoadNro(file, base_addr)) {
|
||||
return ResultStatus::ErrorInvalidFormat;
|
||||
return ResultStatus::ErrorLoadingNRO;
|
||||
}
|
||||
|
||||
process->svc_access_mask.set();
|
||||
@@ -197,7 +197,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {
|
||||
if (icon_data.empty()) {
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
return ResultStatus::ErrorNoIcon;
|
||||
}
|
||||
|
||||
buffer = icon_data;
|
||||
@@ -206,7 +206,7 @@ ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadProgramId(u64& out_program_id) {
|
||||
if (nacp == nullptr) {
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
out_program_id = nacp->GetTitleId();
|
||||
@@ -215,7 +215,7 @@ ResultStatus AppLoader_NRO::ReadProgramId(u64& out_program_id) {
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
if (romfs == nullptr) {
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
return ResultStatus::ErrorNoRomFS;
|
||||
}
|
||||
|
||||
dir = romfs;
|
||||
@@ -224,7 +224,7 @@ ResultStatus AppLoader_NRO::ReadRomFS(FileSys::VirtualFile& dir) {
|
||||
|
||||
ResultStatus AppLoader_NRO::ReadTitle(std::string& title) {
|
||||
if (nacp == nullptr) {
|
||||
return ResultStatus::ErrorNotUsed;
|
||||
return ResultStatus::ErrorNoControl;
|
||||
}
|
||||
|
||||
title = nacp->GetApplicationName();
|
||||
|
||||
@@ -66,10 +66,13 @@ ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) {
|
||||
return ResultStatus::ErrorAlreadyLoaded;
|
||||
}
|
||||
|
||||
if (xci->GetStatus() != ResultStatus::Success)
|
||||
return xci->GetStatus();
|
||||
|
||||
if (xci->GetNCAFileByType(FileSys::NCAContentType::Program) == nullptr) {
|
||||
if (!Core::Crypto::KeyManager::KeyFileExists(false))
|
||||
return ResultStatus::ErrorMissingKeys;
|
||||
return ResultStatus::ErrorDecrypting;
|
||||
return ResultStatus::ErrorMissingProductionKeyFile;
|
||||
return ResultStatus::ErrorXCIMissingProgramNCA;
|
||||
}
|
||||
|
||||
auto result = nca_loader->Load(process);
|
||||
@@ -91,14 +94,14 @@ ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) {
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) {
|
||||
if (icon_file == nullptr)
|
||||
return ResultStatus::ErrorInvalidFormat;
|
||||
return ResultStatus::ErrorNoControl;
|
||||
buffer = icon_file->ReadAllBytes();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_XCI::ReadTitle(std::string& title) {
|
||||
if (nacp_file == nullptr)
|
||||
return ResultStatus::ErrorInvalidFormat;
|
||||
return ResultStatus::ErrorNoControl;
|
||||
title = nacp_file->GetApplicationName();
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
|
||||
@@ -7,22 +7,18 @@
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Settings {
|
||||
|
||||
Values values = {};
|
||||
|
||||
void Apply() {
|
||||
|
||||
GDBStub::SetServerPort(values.gdbstub_port);
|
||||
GDBStub::ToggleServer(values.use_gdbstub);
|
||||
|
||||
VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit;
|
||||
|
||||
auto& system_instance = Core::System::GetInstance();
|
||||
if (system_instance.IsPoweredOn()) {
|
||||
system_instance.Renderer().UpdateCurrentFramebufferLayout();
|
||||
system_instance.Renderer().RefreshBaseSettings();
|
||||
}
|
||||
|
||||
Service::HID::ReloadInputDevices();
|
||||
|
||||
@@ -30,8 +30,7 @@ union CommandHeader {
|
||||
|
||||
BitField<29, 3, SubmissionMode> mode;
|
||||
};
|
||||
static_assert(std::is_standard_layout<CommandHeader>::value == true,
|
||||
"CommandHeader does not use standard layout");
|
||||
static_assert(std::is_standard_layout_v<CommandHeader>, "CommandHeader is not standard layout");
|
||||
static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!");
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -93,6 +93,7 @@ public:
|
||||
|
||||
struct VertexAttribute {
|
||||
enum class Size : u32 {
|
||||
Invalid = 0x0,
|
||||
Size_32_32_32_32 = 0x01,
|
||||
Size_32_32_32 = 0x02,
|
||||
Size_16_16_16_16 = 0x03,
|
||||
@@ -257,6 +258,10 @@ public:
|
||||
bool IsNormalized() const {
|
||||
return (type == Type::SignedNorm) || (type == Type::UnsignedNorm);
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
return size != Size::Invalid;
|
||||
}
|
||||
};
|
||||
|
||||
enum class PrimitiveTopology : u32 {
|
||||
@@ -352,6 +357,27 @@ public:
|
||||
OneMinusConstantColor = 0x62,
|
||||
ConstantAlpha = 0x63,
|
||||
OneMinusConstantAlpha = 0x64,
|
||||
|
||||
// These values are used by Nouveau and some games.
|
||||
ZeroGL = 0x4000,
|
||||
OneGL = 0x4001,
|
||||
SourceColorGL = 0x4300,
|
||||
OneMinusSourceColorGL = 0x4301,
|
||||
SourceAlphaGL = 0x4302,
|
||||
OneMinusSourceAlphaGL = 0x4303,
|
||||
DestAlphaGL = 0x4304,
|
||||
OneMinusDestAlphaGL = 0x4305,
|
||||
DestColorGL = 0x4306,
|
||||
OneMinusDestColorGL = 0x4307,
|
||||
SourceAlphaSaturateGL = 0x4308,
|
||||
ConstantColorGL = 0xc001,
|
||||
OneMinusConstantColorGL = 0xc002,
|
||||
ConstantAlphaGL = 0xc003,
|
||||
OneMinusConstantAlphaGL = 0xc004,
|
||||
Source1ColorGL = 0xc900,
|
||||
OneMinusSource1ColorGL = 0xc901,
|
||||
Source1AlphaGL = 0xc902,
|
||||
OneMinusSource1AlphaGL = 0xc903,
|
||||
};
|
||||
|
||||
u32 separate_alpha;
|
||||
|
||||
@@ -74,12 +74,11 @@ union Attribute {
|
||||
enum class Index : u64 {
|
||||
Position = 7,
|
||||
Attribute_0 = 8,
|
||||
Attribute_31 = 39,
|
||||
// This attribute contains a tuple of (~, ~, InstanceId, VertexId) when inside a vertex
|
||||
// shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
|
||||
// shader.
|
||||
TessCoordInstanceIDVertexID = 47,
|
||||
// TODO(bunnei): Figure out what this is used for. Super Mario Odyssey uses this.
|
||||
Unknown_63 = 63,
|
||||
};
|
||||
|
||||
union {
|
||||
@@ -477,8 +476,7 @@ union Instruction {
|
||||
u64 value;
|
||||
};
|
||||
static_assert(sizeof(Instruction) == 0x8, "Incorrect structure size");
|
||||
static_assert(std::is_standard_layout<Instruction>::value,
|
||||
"Structure does not have standard layout");
|
||||
static_assert(std::is_standard_layout_v<Instruction>, "Instruction is not standard layout");
|
||||
|
||||
class OpCode {
|
||||
public:
|
||||
@@ -598,6 +596,13 @@ public:
|
||||
Unknown,
|
||||
};
|
||||
|
||||
/// Returns whether an opcode has an execution predicate field or not (ie, whether it can be
|
||||
/// conditionally executed).
|
||||
static bool IsPredicatedInstruction(Id opcode) {
|
||||
// TODO(Subv): Add the rest of unpredicated instructions.
|
||||
return opcode != Id::SSY;
|
||||
}
|
||||
|
||||
class Matcher {
|
||||
public:
|
||||
Matcher(const char* const name, u16 mask, u16 expected, OpCode::Id id, OpCode::Type type)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
@@ -11,6 +12,15 @@
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::ABGR8:
|
||||
return 4;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
|
||||
memory_manager = std::make_unique<MemoryManager>();
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
|
||||
@@ -34,19 +44,54 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
|
||||
|
||||
switch (format) {
|
||||
case RenderTargetFormat::RGBA32_FLOAT:
|
||||
case RenderTargetFormat::RGBA32_UINT:
|
||||
return 16;
|
||||
case RenderTargetFormat::RGBA16_FLOAT:
|
||||
case RenderTargetFormat::RG32_FLOAT:
|
||||
return 8;
|
||||
case RenderTargetFormat::RGBA8_UNORM:
|
||||
case RenderTargetFormat::RGBA8_SNORM:
|
||||
case RenderTargetFormat::RGBA8_SRGB:
|
||||
case RenderTargetFormat::RGB10_A2_UNORM:
|
||||
case RenderTargetFormat::BGRA8_UNORM:
|
||||
case RenderTargetFormat::RG16_UNORM:
|
||||
case RenderTargetFormat::RG16_SNORM:
|
||||
case RenderTargetFormat::RG16_UINT:
|
||||
case RenderTargetFormat::RG16_SINT:
|
||||
case RenderTargetFormat::RG16_FLOAT:
|
||||
case RenderTargetFormat::R32_FLOAT:
|
||||
case RenderTargetFormat::R11G11B10_FLOAT:
|
||||
return 4;
|
||||
case RenderTargetFormat::R16_UNORM:
|
||||
case RenderTargetFormat::R16_SNORM:
|
||||
case RenderTargetFormat::R16_UINT:
|
||||
case RenderTargetFormat::R16_SINT:
|
||||
case RenderTargetFormat::R16_FLOAT:
|
||||
case RenderTargetFormat::RG8_SNORM:
|
||||
return 2;
|
||||
case RenderTargetFormat::R8_UNORM:
|
||||
case RenderTargetFormat::R8_UINT:
|
||||
return 1;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented render target format {}", static_cast<u32>(format));
|
||||
}
|
||||
}
|
||||
|
||||
u32 DepthFormatBytesPerPixel(DepthFormat format) {
|
||||
switch (format) {
|
||||
case DepthFormat::Z32_S8_X24_FLOAT:
|
||||
return 8;
|
||||
case DepthFormat::Z32_FLOAT:
|
||||
case DepthFormat::S8_Z24_UNORM:
|
||||
case DepthFormat::Z24_X8_UNORM:
|
||||
case DepthFormat::Z24_S8_UNORM:
|
||||
case DepthFormat::Z24_C8_UNORM:
|
||||
return 4;
|
||||
case DepthFormat::Z16_UNORM:
|
||||
return 2;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented Depth format {}", static_cast<u32>(format));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
@@ -27,6 +26,7 @@ enum class RenderTargetFormat : u32 {
|
||||
RGB10_A2_UNORM = 0xD1,
|
||||
RGBA8_UNORM = 0xD5,
|
||||
RGBA8_SRGB = 0xD6,
|
||||
RGBA8_SNORM = 0xD7,
|
||||
RG16_UNORM = 0xDA,
|
||||
RG16_SNORM = 0xDB,
|
||||
RG16_SINT = 0xDC,
|
||||
@@ -35,8 +35,14 @@ enum class RenderTargetFormat : u32 {
|
||||
R11G11B10_FLOAT = 0xE0,
|
||||
R32_FLOAT = 0xE5,
|
||||
B5G6R5_UNORM = 0xE8,
|
||||
RG8_SNORM = 0xEB,
|
||||
R16_UNORM = 0xEE,
|
||||
R16_SNORM = 0xEF,
|
||||
R16_SINT = 0xF0,
|
||||
R16_UINT = 0xF1,
|
||||
R16_FLOAT = 0xF2,
|
||||
R8_UNORM = 0xF3,
|
||||
R8_UINT = 0xF6,
|
||||
};
|
||||
|
||||
enum class DepthFormat : u32 {
|
||||
@@ -52,6 +58,9 @@ enum class DepthFormat : u32 {
|
||||
/// Returns the number of bytes per pixel of each rendertarget format.
|
||||
u32 RenderTargetBytesPerPixel(RenderTargetFormat format);
|
||||
|
||||
/// Returns the number of bytes per pixel of each depth format.
|
||||
u32 DepthFormatBytesPerPixel(DepthFormat format);
|
||||
|
||||
class DebugContext;
|
||||
|
||||
/**
|
||||
@@ -65,14 +74,7 @@ struct FramebufferConfig {
|
||||
/**
|
||||
* Returns the number of bytes per pixel.
|
||||
*/
|
||||
static u32 BytesPerPixel(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::ABGR8:
|
||||
return 4;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
static u32 BytesPerPixel(PixelFormat format);
|
||||
|
||||
VAddr address;
|
||||
u32 offset;
|
||||
|
||||
@@ -4,18 +4,23 @@
|
||||
|
||||
#include <memory>
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
RendererBase::RendererBase(EmuWindow& window) : render_window{window} {}
|
||||
RendererBase::RendererBase(Core::Frontend::EmuWindow& window) : render_window{window} {
|
||||
RefreshBaseSettings();
|
||||
}
|
||||
|
||||
RendererBase::~RendererBase() = default;
|
||||
|
||||
void RendererBase::UpdateCurrentFramebufferLayout() {
|
||||
const Layout::FramebufferLayout& layout = render_window.GetFramebufferLayout();
|
||||
void RendererBase::RefreshBaseSettings() {
|
||||
RefreshRasterizerSetting();
|
||||
UpdateCurrentFramebufferLayout();
|
||||
|
||||
render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
|
||||
renderer_settings.use_framelimiter = Settings::values.toggle_framelimit;
|
||||
}
|
||||
|
||||
void RendererBase::RefreshRasterizerSetting() {
|
||||
@@ -24,4 +29,10 @@ void RendererBase::RefreshRasterizerSetting() {
|
||||
}
|
||||
}
|
||||
|
||||
void RendererBase::UpdateCurrentFramebufferLayout() {
|
||||
const Layout::FramebufferLayout& layout = render_window.GetFramebufferLayout();
|
||||
|
||||
render_window.UpdateCurrentFramebufferLayout(layout.width, layout.height);
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -4,23 +4,26 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <boost/optional.hpp>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
}
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
struct RendererSettings {
|
||||
std::atomic_bool use_framelimiter{false};
|
||||
};
|
||||
|
||||
class RendererBase : NonCopyable {
|
||||
public:
|
||||
/// Used to reference a framebuffer
|
||||
enum kFramebuffer { kFramebuffer_VirtualXFB = 0, kFramebuffer_EFB, kFramebuffer_Texture };
|
||||
|
||||
explicit RendererBase(EmuWindow& window);
|
||||
explicit RendererBase(Core::Frontend::EmuWindow& window);
|
||||
virtual ~RendererBase();
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
@@ -32,9 +35,6 @@ public:
|
||||
/// Shutdown the renderer
|
||||
virtual void ShutDown() = 0;
|
||||
|
||||
/// Updates the framebuffer layout of the contained render window handle.
|
||||
void UpdateCurrentFramebufferLayout();
|
||||
|
||||
// Getter/setter functions:
|
||||
// ------------------------
|
||||
|
||||
@@ -54,13 +54,23 @@ public:
|
||||
return *rasterizer;
|
||||
}
|
||||
|
||||
void RefreshRasterizerSetting();
|
||||
/// Refreshes the settings common to all renderers
|
||||
void RefreshBaseSettings();
|
||||
|
||||
protected:
|
||||
EmuWindow& render_window; ///< Reference to the render window handle.
|
||||
/// Refreshes settings specific to the rasterizer.
|
||||
void RefreshRasterizerSetting();
|
||||
|
||||
Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
|
||||
std::unique_ptr<RasterizerInterface> rasterizer;
|
||||
f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer
|
||||
int m_current_frame = 0; ///< Current frame, should be set by the renderer
|
||||
|
||||
RendererSettings renderer_settings;
|
||||
|
||||
private:
|
||||
/// Updates the framebuffer layout of the contained render window handle.
|
||||
void UpdateCurrentFramebufferLayout();
|
||||
};
|
||||
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -36,7 +36,7 @@ MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
|
||||
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255));
|
||||
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
|
||||
|
||||
RasterizerOpenGL::RasterizerOpenGL(EmuWindow& window) : emu_window{window} {
|
||||
RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window) : emu_window{window} {
|
||||
// Create sampler objects
|
||||
for (size_t i = 0; i < texture_samplers.size(); ++i) {
|
||||
texture_samplers[i].Create();
|
||||
@@ -161,11 +161,16 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
|
||||
// assume every shader uses them all.
|
||||
for (unsigned index = 0; index < 16; ++index) {
|
||||
auto& attrib = regs.vertex_attrib_format[index];
|
||||
|
||||
// Ignore invalid attributes.
|
||||
if (!attrib.IsValid())
|
||||
continue;
|
||||
|
||||
auto& buffer = regs.vertex_array[attrib.buffer];
|
||||
LOG_TRACE(HW_GPU, "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}",
|
||||
index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(),
|
||||
attrib.offset.Value(), attrib.IsNormalized());
|
||||
|
||||
auto& buffer = regs.vertex_array[attrib.buffer];
|
||||
ASSERT(buffer.IsEnabled());
|
||||
|
||||
glEnableVertexAttribArray(index);
|
||||
@@ -211,7 +216,7 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
|
||||
|
||||
// Next available bindpoints to use when uploading the const buffers and textures to the GLSL
|
||||
// shaders. The constbuffer bindpoint starts after the shader stage configuration bind points.
|
||||
u32 current_constbuffer_bindpoint = uniform_buffers.size();
|
||||
u32 current_constbuffer_bindpoint = static_cast<u32>(uniform_buffers.size());
|
||||
u32 current_texture_bindpoint = 0;
|
||||
|
||||
for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
||||
@@ -332,8 +337,6 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_c
|
||||
// TODO(bunnei): Implement this
|
||||
const bool has_stencil = false;
|
||||
|
||||
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
|
||||
|
||||
const bool write_color_fb =
|
||||
state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE ||
|
||||
state.color_mask.blue_enabled == GL_TRUE || state.color_mask.alpha_enabled == GL_TRUE;
|
||||
@@ -346,9 +349,10 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_c
|
||||
Surface depth_surface;
|
||||
MathUtil::Rectangle<u32> surfaces_rect;
|
||||
std::tie(color_surface, depth_surface, surfaces_rect) =
|
||||
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect);
|
||||
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb);
|
||||
|
||||
MathUtil::Rectangle<u32> draw_rect{
|
||||
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
|
||||
const MathUtil::Rectangle<u32> draw_rect{
|
||||
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left,
|
||||
surfaces_rect.left, surfaces_rect.right)), // Left
|
||||
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top,
|
||||
@@ -812,9 +816,7 @@ void RasterizerOpenGL::SyncClipCoef() {
|
||||
void RasterizerOpenGL::SyncCullMode() {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
// TODO(bunnei): Enable the below once more things work - until then, this may hide regressions
|
||||
// state.cull.enabled = regs.cull.enabled != 0;
|
||||
state.cull.enabled = false;
|
||||
state.cull.enabled = regs.cull.enabled != 0;
|
||||
|
||||
if (state.cull.enabled) {
|
||||
state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face);
|
||||
|
||||
@@ -21,12 +21,15 @@
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
||||
|
||||
class EmuWindow;
|
||||
struct ScreenInfo;
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
}
|
||||
|
||||
class RasterizerOpenGL : public VideoCore::RasterizerInterface {
|
||||
public:
|
||||
explicit RasterizerOpenGL(EmuWindow& renderer);
|
||||
explicit RasterizerOpenGL(Core::Frontend::EmuWindow& renderer);
|
||||
~RasterizerOpenGL() override;
|
||||
|
||||
void DrawArrays() override;
|
||||
@@ -145,7 +148,7 @@ private:
|
||||
|
||||
RasterizerCacheOpenGL res_cache;
|
||||
|
||||
EmuWindow& emu_window;
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
|
||||
std::unique_ptr<GLShader::ProgramManager> shader_program_manager;
|
||||
OGLVertexArray sw_vao;
|
||||
|
||||
@@ -92,12 +92,14 @@ struct FormatTuple {
|
||||
}
|
||||
|
||||
static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8
|
||||
{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_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, ComponentType::UNorm, false}, // B5G6R5
|
||||
{GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, ComponentType::UNorm,
|
||||
false}, // A2B10G10R10
|
||||
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, ComponentType::UNorm, false}, // A1B5G5R5
|
||||
{GL_R8, GL_RED, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // R8
|
||||
{GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, ComponentType::UInt, false}, // R8UI
|
||||
{GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBA16F
|
||||
{GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, ComponentType::Float,
|
||||
false}, // R11FG11FB10F
|
||||
@@ -122,6 +124,9 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
|
||||
{GL_R32F, GL_RED, GL_FLOAT, ComponentType::Float, false}, // R32F
|
||||
{GL_R16F, GL_RED, GL_HALF_FLOAT, ComponentType::Float, false}, // R16F
|
||||
{GL_R16, GL_RED, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // R16UNORM
|
||||
{GL_R16_SNORM, GL_RED, GL_SHORT, ComponentType::SNorm, false}, // R16S
|
||||
{GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // R16UI
|
||||
{GL_R16I, GL_RED_INTEGER, GL_SHORT, ComponentType::SInt, false}, // R16I
|
||||
{GL_RG16, GL_RG, GL_UNSIGNED_SHORT, ComponentType::UNorm, false}, // RG16
|
||||
{GL_RG16F, GL_RG, GL_HALF_FLOAT, ComponentType::Float, false}, // RG16F
|
||||
{GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, ComponentType::UInt, false}, // RG16UI
|
||||
@@ -129,6 +134,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
|
||||
{GL_RG16_SNORM, GL_RG, GL_SHORT, ComponentType::SNorm, false}, // RG16S
|
||||
{GL_RGB32F, GL_RGB, GL_FLOAT, ComponentType::Float, false}, // RGB32F
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // SRGBA8
|
||||
{GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S
|
||||
|
||||
// DepthStencil formats
|
||||
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, ComponentType::UNorm,
|
||||
@@ -183,6 +189,21 @@ MathUtil::Rectangle<u32> SurfaceParams::GetRect() const {
|
||||
return {0, actual_height, width, 0};
|
||||
}
|
||||
|
||||
/// Returns true if the specified PixelFormat is a BCn format, e.g. DXT or DXN
|
||||
static bool IsFormatBCn(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::DXT1:
|
||||
case PixelFormat::DXT23:
|
||||
case PixelFormat::DXT45:
|
||||
case PixelFormat::DXN1:
|
||||
case PixelFormat::DXN2SNORM:
|
||||
case PixelFormat::DXN2UNORM:
|
||||
case PixelFormat::BC7U:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <bool morton_to_gl, PixelFormat format>
|
||||
void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_buffer,
|
||||
Tegra::GPUVAddr addr) {
|
||||
@@ -191,16 +212,12 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_bu
|
||||
const auto& gpu = Core::System::GetInstance().GPU();
|
||||
|
||||
if (morton_to_gl) {
|
||||
std::vector<u8> data;
|
||||
if (SurfaceParams::GetFormatType(format) == SurfaceType::ColorTexture) {
|
||||
data = Tegra::Texture::UnswizzleTexture(
|
||||
*gpu.memory_manager->GpuToCpuAddress(addr),
|
||||
SurfaceParams::TextureFormatFromPixelFormat(format), stride, height, block_height);
|
||||
} else {
|
||||
data = Tegra::Texture::UnswizzleDepthTexture(
|
||||
*gpu.memory_manager->GpuToCpuAddress(addr),
|
||||
SurfaceParams::DepthFormatFromPixelFormat(format), stride, height, block_height);
|
||||
}
|
||||
// With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
|
||||
// pixel values.
|
||||
const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
|
||||
const std::vector<u8> data =
|
||||
Tegra::Texture::UnswizzleTexture(*gpu.memory_manager->GpuToCpuAddress(addr), tile_size,
|
||||
bytes_per_pixel, stride, height, block_height);
|
||||
const size_t size_to_copy{std::min(gl_buffer.size(), data.size())};
|
||||
gl_buffer.assign(data.begin(), data.begin() + size_to_copy);
|
||||
} else {
|
||||
@@ -217,9 +234,10 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_bu
|
||||
static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr),
|
||||
SurfaceParams::MaxPixelFormat>
|
||||
morton_to_gl_fns = {
|
||||
MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>,
|
||||
MortonCopy<true, PixelFormat::A2B10G10R10>, MortonCopy<true, PixelFormat::A1B5G5R5>,
|
||||
MortonCopy<true, PixelFormat::R8>, MortonCopy<true, PixelFormat::RGBA16F>,
|
||||
MortonCopy<true, PixelFormat::ABGR8U>, MortonCopy<true, PixelFormat::ABGR8S>,
|
||||
MortonCopy<true, PixelFormat::B5G6R5>, MortonCopy<true, PixelFormat::A2B10G10R10>,
|
||||
MortonCopy<true, PixelFormat::A1B5G5R5>, MortonCopy<true, PixelFormat::R8>,
|
||||
MortonCopy<true, PixelFormat::R8UI>, MortonCopy<true, PixelFormat::RGBA16F>,
|
||||
MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::RGBA32UI>,
|
||||
MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>,
|
||||
MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>,
|
||||
@@ -228,10 +246,12 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPU
|
||||
MortonCopy<true, PixelFormat::G8R8>, MortonCopy<true, PixelFormat::BGRA8>,
|
||||
MortonCopy<true, PixelFormat::RGBA32F>, MortonCopy<true, PixelFormat::RG32F>,
|
||||
MortonCopy<true, PixelFormat::R32F>, MortonCopy<true, PixelFormat::R16F>,
|
||||
MortonCopy<true, PixelFormat::R16UNORM>, MortonCopy<true, PixelFormat::RG16>,
|
||||
MortonCopy<true, PixelFormat::RG16F>, MortonCopy<true, PixelFormat::RG16UI>,
|
||||
MortonCopy<true, PixelFormat::RG16I>, MortonCopy<true, PixelFormat::RG16S>,
|
||||
MortonCopy<true, PixelFormat::RGB32F>, MortonCopy<true, PixelFormat::SRGBA8>,
|
||||
MortonCopy<true, PixelFormat::R16UNORM>, MortonCopy<true, PixelFormat::R16S>,
|
||||
MortonCopy<true, PixelFormat::R16UI>, MortonCopy<true, PixelFormat::R16I>,
|
||||
MortonCopy<true, PixelFormat::RG16>, MortonCopy<true, PixelFormat::RG16F>,
|
||||
MortonCopy<true, PixelFormat::RG16UI>, MortonCopy<true, PixelFormat::RG16I>,
|
||||
MortonCopy<true, PixelFormat::RG16S>, MortonCopy<true, PixelFormat::RGB32F>,
|
||||
MortonCopy<true, PixelFormat::SRGBA8>, MortonCopy<true, PixelFormat::RG8S>,
|
||||
MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>,
|
||||
MortonCopy<true, PixelFormat::Z32F>, MortonCopy<true, PixelFormat::Z16>,
|
||||
MortonCopy<true, PixelFormat::Z32FS8>,
|
||||
@@ -240,11 +260,13 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPU
|
||||
static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr),
|
||||
SurfaceParams::MaxPixelFormat>
|
||||
gl_to_morton_fns = {
|
||||
MortonCopy<false, PixelFormat::ABGR8>,
|
||||
MortonCopy<false, PixelFormat::ABGR8U>,
|
||||
MortonCopy<false, PixelFormat::ABGR8S>,
|
||||
MortonCopy<false, PixelFormat::B5G6R5>,
|
||||
MortonCopy<false, PixelFormat::A2B10G10R10>,
|
||||
MortonCopy<false, PixelFormat::A1B5G5R5>,
|
||||
MortonCopy<false, PixelFormat::R8>,
|
||||
MortonCopy<false, PixelFormat::R8UI>,
|
||||
MortonCopy<false, PixelFormat::RGBA16F>,
|
||||
MortonCopy<false, PixelFormat::R11FG11FB10F>,
|
||||
MortonCopy<false, PixelFormat::RGBA32UI>,
|
||||
@@ -265,6 +287,9 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPU
|
||||
MortonCopy<false, PixelFormat::R32F>,
|
||||
MortonCopy<false, PixelFormat::R16F>,
|
||||
MortonCopy<false, PixelFormat::R16UNORM>,
|
||||
MortonCopy<false, PixelFormat::R16S>,
|
||||
MortonCopy<false, PixelFormat::R16UI>,
|
||||
MortonCopy<false, PixelFormat::R16I>,
|
||||
MortonCopy<false, PixelFormat::RG16>,
|
||||
MortonCopy<false, PixelFormat::RG16F>,
|
||||
MortonCopy<false, PixelFormat::RG16UI>,
|
||||
@@ -272,6 +297,7 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPU
|
||||
MortonCopy<false, PixelFormat::RG16S>,
|
||||
MortonCopy<false, PixelFormat::RGB32F>,
|
||||
MortonCopy<false, PixelFormat::SRGBA8>,
|
||||
MortonCopy<false, PixelFormat::RG8S>,
|
||||
MortonCopy<false, PixelFormat::Z24S8>,
|
||||
MortonCopy<false, PixelFormat::S8Z24>,
|
||||
MortonCopy<false, PixelFormat::Z32F>,
|
||||
@@ -611,8 +637,8 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
|
||||
return GetSurface(SurfaceParams::CreateForTexture(config));
|
||||
}
|
||||
|
||||
SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
|
||||
bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle<s32>& viewport) {
|
||||
SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool using_color_fb,
|
||||
bool using_depth_fb) {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
// TODO(bunnei): This is hard corded to use just the first render buffer
|
||||
|
||||
@@ -23,45 +23,51 @@ using PageMap = boost::icl::interval_map<u64, int>;
|
||||
|
||||
struct SurfaceParams {
|
||||
enum class PixelFormat {
|
||||
ABGR8 = 0,
|
||||
B5G6R5 = 1,
|
||||
A2B10G10R10 = 2,
|
||||
A1B5G5R5 = 3,
|
||||
R8 = 4,
|
||||
RGBA16F = 5,
|
||||
R11FG11FB10F = 6,
|
||||
RGBA32UI = 7,
|
||||
DXT1 = 8,
|
||||
DXT23 = 9,
|
||||
DXT45 = 10,
|
||||
DXN1 = 11, // This is also known as BC4
|
||||
DXN2UNORM = 12,
|
||||
DXN2SNORM = 13,
|
||||
BC7U = 14,
|
||||
ASTC_2D_4X4 = 15,
|
||||
G8R8 = 16,
|
||||
BGRA8 = 17,
|
||||
RGBA32F = 18,
|
||||
RG32F = 19,
|
||||
R32F = 20,
|
||||
R16F = 21,
|
||||
R16UNORM = 22,
|
||||
RG16 = 23,
|
||||
RG16F = 24,
|
||||
RG16UI = 25,
|
||||
RG16I = 26,
|
||||
RG16S = 27,
|
||||
RGB32F = 28,
|
||||
SRGBA8 = 29,
|
||||
ABGR8U = 0,
|
||||
ABGR8S = 1,
|
||||
B5G6R5 = 2,
|
||||
A2B10G10R10 = 3,
|
||||
A1B5G5R5 = 4,
|
||||
R8 = 5,
|
||||
R8UI = 6,
|
||||
RGBA16F = 7,
|
||||
R11FG11FB10F = 8,
|
||||
RGBA32UI = 9,
|
||||
DXT1 = 10,
|
||||
DXT23 = 11,
|
||||
DXT45 = 12,
|
||||
DXN1 = 13, // This is also known as BC4
|
||||
DXN2UNORM = 14,
|
||||
DXN2SNORM = 15,
|
||||
BC7U = 16,
|
||||
ASTC_2D_4X4 = 17,
|
||||
G8R8 = 18,
|
||||
BGRA8 = 19,
|
||||
RGBA32F = 20,
|
||||
RG32F = 21,
|
||||
R32F = 22,
|
||||
R16F = 23,
|
||||
R16UNORM = 24,
|
||||
R16S = 25,
|
||||
R16UI = 26,
|
||||
R16I = 27,
|
||||
RG16 = 28,
|
||||
RG16F = 29,
|
||||
RG16UI = 30,
|
||||
RG16I = 31,
|
||||
RG16S = 32,
|
||||
RGB32F = 33,
|
||||
SRGBA8 = 34,
|
||||
RG8S = 35,
|
||||
|
||||
MaxColorFormat,
|
||||
|
||||
// DepthStencil formats
|
||||
Z24S8 = 30,
|
||||
S8Z24 = 31,
|
||||
Z32F = 32,
|
||||
Z16 = 33,
|
||||
Z32FS8 = 34,
|
||||
Z24S8 = 36,
|
||||
S8Z24 = 37,
|
||||
Z32F = 38,
|
||||
Z16 = 39,
|
||||
Z32FS8 = 40,
|
||||
|
||||
MaxDepthStencilFormat,
|
||||
|
||||
@@ -99,11 +105,13 @@ struct SurfaceParams {
|
||||
return 0;
|
||||
|
||||
constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{
|
||||
1, // ABGR8
|
||||
1, // ABGR8U
|
||||
1, // ABGR8S
|
||||
1, // B5G6R5
|
||||
1, // A2B10G10R10
|
||||
1, // A1B5G5R5
|
||||
1, // R8
|
||||
1, // R8UI
|
||||
1, // RGBA16F
|
||||
1, // R11FG11FB10F
|
||||
1, // RGBA32UI
|
||||
@@ -122,6 +130,9 @@ struct SurfaceParams {
|
||||
1, // R32F
|
||||
1, // R16F
|
||||
1, // R16UNORM
|
||||
1, // R16S
|
||||
1, // R16UI
|
||||
1, // R16I
|
||||
1, // RG16
|
||||
1, // RG16F
|
||||
1, // RG16UI
|
||||
@@ -129,6 +140,7 @@ struct SurfaceParams {
|
||||
1, // RG16S
|
||||
1, // RGB32F
|
||||
1, // SRGBA8
|
||||
1, // RG8S
|
||||
1, // Z24S8
|
||||
1, // S8Z24
|
||||
1, // Z32F
|
||||
@@ -145,11 +157,13 @@ struct SurfaceParams {
|
||||
return 0;
|
||||
|
||||
constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
|
||||
32, // ABGR8
|
||||
32, // ABGR8U
|
||||
32, // ABGR8S
|
||||
16, // B5G6R5
|
||||
32, // A2B10G10R10
|
||||
16, // A1B5G5R5
|
||||
8, // R8
|
||||
8, // R8UI
|
||||
64, // RGBA16F
|
||||
32, // R11FG11FB10F
|
||||
128, // RGBA32UI
|
||||
@@ -168,6 +182,9 @@ struct SurfaceParams {
|
||||
32, // R32F
|
||||
16, // R16F
|
||||
16, // R16UNORM
|
||||
16, // R16S
|
||||
16, // R16UI
|
||||
16, // R16I
|
||||
32, // RG16
|
||||
32, // RG16F
|
||||
32, // RG16UI
|
||||
@@ -175,6 +192,7 @@ struct SurfaceParams {
|
||||
32, // RG16S
|
||||
96, // RGB32F
|
||||
32, // SRGBA8
|
||||
16, // RG8S
|
||||
32, // Z24S8
|
||||
32, // S8Z24
|
||||
32, // Z32F
|
||||
@@ -214,7 +232,9 @@ struct SurfaceParams {
|
||||
// gamma.
|
||||
case Tegra::RenderTargetFormat::RGBA8_SRGB:
|
||||
case Tegra::RenderTargetFormat::RGBA8_UNORM:
|
||||
return PixelFormat::ABGR8;
|
||||
return PixelFormat::ABGR8U;
|
||||
case Tegra::RenderTargetFormat::RGBA8_SNORM:
|
||||
return PixelFormat::ABGR8S;
|
||||
case Tegra::RenderTargetFormat::BGRA8_UNORM:
|
||||
return PixelFormat::BGRA8;
|
||||
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
|
||||
@@ -233,6 +253,8 @@ struct SurfaceParams {
|
||||
return PixelFormat::RGBA32UI;
|
||||
case Tegra::RenderTargetFormat::R8_UNORM:
|
||||
return PixelFormat::R8;
|
||||
case Tegra::RenderTargetFormat::R8_UINT:
|
||||
return PixelFormat::R8UI;
|
||||
case Tegra::RenderTargetFormat::RG16_FLOAT:
|
||||
return PixelFormat::RG16F;
|
||||
case Tegra::RenderTargetFormat::RG16_UINT:
|
||||
@@ -243,8 +265,18 @@ struct SurfaceParams {
|
||||
return PixelFormat::RG16;
|
||||
case Tegra::RenderTargetFormat::RG16_SNORM:
|
||||
return PixelFormat::RG16S;
|
||||
case Tegra::RenderTargetFormat::RG8_SNORM:
|
||||
return PixelFormat::RG8S;
|
||||
case Tegra::RenderTargetFormat::R16_FLOAT:
|
||||
return PixelFormat::R16F;
|
||||
case Tegra::RenderTargetFormat::R16_UNORM:
|
||||
return PixelFormat::R16UNORM;
|
||||
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;
|
||||
default:
|
||||
@@ -258,7 +290,15 @@ struct SurfaceParams {
|
||||
// TODO(Subv): Properly implement this
|
||||
switch (format) {
|
||||
case Tegra::Texture::TextureFormat::A8R8G8B8:
|
||||
return PixelFormat::ABGR8;
|
||||
switch (component_type) {
|
||||
case Tegra::Texture::ComponentType::UNORM:
|
||||
return PixelFormat::ABGR8U;
|
||||
case Tegra::Texture::ComponentType::SNORM:
|
||||
return PixelFormat::ABGR8S;
|
||||
}
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented component_type={}",
|
||||
static_cast<u32>(component_type));
|
||||
UNREACHABLE();
|
||||
case Tegra::Texture::TextureFormat::B5G6R5:
|
||||
return PixelFormat::B5G6R5;
|
||||
case Tegra::Texture::TextureFormat::A2B10G10R10:
|
||||
@@ -266,7 +306,15 @@ struct SurfaceParams {
|
||||
case Tegra::Texture::TextureFormat::A1B5G5R5:
|
||||
return PixelFormat::A1B5G5R5;
|
||||
case Tegra::Texture::TextureFormat::R8:
|
||||
return PixelFormat::R8;
|
||||
switch (component_type) {
|
||||
case Tegra::Texture::ComponentType::UNORM:
|
||||
return PixelFormat::R8;
|
||||
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:
|
||||
return PixelFormat::G8R8;
|
||||
case Tegra::Texture::TextureFormat::R16_G16_B16_A16:
|
||||
@@ -293,6 +341,12 @@ struct SurfaceParams {
|
||||
return PixelFormat::R16F;
|
||||
case Tegra::Texture::ComponentType::UNORM:
|
||||
return PixelFormat::R16UNORM;
|
||||
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));
|
||||
@@ -348,92 +402,6 @@ struct SurfaceParams {
|
||||
}
|
||||
}
|
||||
|
||||
static Tegra::Texture::TextureFormat TextureFormatFromPixelFormat(PixelFormat format) {
|
||||
// TODO(Subv): Properly implement this
|
||||
switch (format) {
|
||||
case PixelFormat::ABGR8:
|
||||
case PixelFormat::SRGBA8:
|
||||
return Tegra::Texture::TextureFormat::A8R8G8B8;
|
||||
case PixelFormat::B5G6R5:
|
||||
return Tegra::Texture::TextureFormat::B5G6R5;
|
||||
case PixelFormat::A2B10G10R10:
|
||||
return Tegra::Texture::TextureFormat::A2B10G10R10;
|
||||
case PixelFormat::A1B5G5R5:
|
||||
return Tegra::Texture::TextureFormat::A1B5G5R5;
|
||||
case PixelFormat::R8:
|
||||
return Tegra::Texture::TextureFormat::R8;
|
||||
case PixelFormat::G8R8:
|
||||
return Tegra::Texture::TextureFormat::G8R8;
|
||||
case PixelFormat::RGBA16F:
|
||||
return Tegra::Texture::TextureFormat::R16_G16_B16_A16;
|
||||
case PixelFormat::R11FG11FB10F:
|
||||
return Tegra::Texture::TextureFormat::BF10GF11RF11;
|
||||
case PixelFormat::RGBA32UI:
|
||||
return Tegra::Texture::TextureFormat::R32_G32_B32_A32;
|
||||
case PixelFormat::DXT1:
|
||||
return Tegra::Texture::TextureFormat::DXT1;
|
||||
case PixelFormat::DXT23:
|
||||
return Tegra::Texture::TextureFormat::DXT23;
|
||||
case PixelFormat::DXT45:
|
||||
return Tegra::Texture::TextureFormat::DXT45;
|
||||
case PixelFormat::DXN1:
|
||||
return Tegra::Texture::TextureFormat::DXN1;
|
||||
case PixelFormat::DXN2UNORM:
|
||||
case PixelFormat::DXN2SNORM:
|
||||
return Tegra::Texture::TextureFormat::DXN2;
|
||||
case PixelFormat::BC7U:
|
||||
return Tegra::Texture::TextureFormat::BC7U;
|
||||
case PixelFormat::ASTC_2D_4X4:
|
||||
return Tegra::Texture::TextureFormat::ASTC_2D_4X4;
|
||||
case PixelFormat::BGRA8:
|
||||
// TODO(bunnei): This is fine for unswizzling (since we just need the right component
|
||||
// sizes), but could be a bug if we used this function in different ways.
|
||||
return Tegra::Texture::TextureFormat::A8R8G8B8;
|
||||
case PixelFormat::RGBA32F:
|
||||
return Tegra::Texture::TextureFormat::R32_G32_B32_A32;
|
||||
case PixelFormat::RGB32F:
|
||||
return Tegra::Texture::TextureFormat::R32_G32_B32;
|
||||
case PixelFormat::RG32F:
|
||||
return Tegra::Texture::TextureFormat::R32_G32;
|
||||
case PixelFormat::R32F:
|
||||
return Tegra::Texture::TextureFormat::R32;
|
||||
case PixelFormat::R16F:
|
||||
case PixelFormat::R16UNORM:
|
||||
return Tegra::Texture::TextureFormat::R16;
|
||||
case PixelFormat::Z32F:
|
||||
return Tegra::Texture::TextureFormat::ZF32;
|
||||
case PixelFormat::Z24S8:
|
||||
return Tegra::Texture::TextureFormat::Z24S8;
|
||||
case PixelFormat::RG16F:
|
||||
case PixelFormat::RG16:
|
||||
case PixelFormat::RG16UI:
|
||||
case PixelFormat::RG16I:
|
||||
case PixelFormat::RG16S:
|
||||
return Tegra::Texture::TextureFormat::R16_G16;
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
static Tegra::DepthFormat DepthFormatFromPixelFormat(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::S8Z24:
|
||||
return Tegra::DepthFormat::S8_Z24_UNORM;
|
||||
case PixelFormat::Z24S8:
|
||||
return Tegra::DepthFormat::Z24_S8_UNORM;
|
||||
case PixelFormat::Z32F:
|
||||
return Tegra::DepthFormat::Z32_FLOAT;
|
||||
case PixelFormat::Z16:
|
||||
return Tegra::DepthFormat::Z16_UNORM;
|
||||
case PixelFormat::Z32FS8:
|
||||
return Tegra::DepthFormat::Z32_S8_X24_FLOAT;
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
static ComponentType ComponentTypeFromTexture(Tegra::Texture::ComponentType type) {
|
||||
// TODO(Subv): Implement more component types
|
||||
switch (type) {
|
||||
@@ -462,9 +430,13 @@ struct SurfaceParams {
|
||||
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:
|
||||
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:
|
||||
@@ -476,8 +448,11 @@ struct SurfaceParams {
|
||||
return ComponentType::Float;
|
||||
case Tegra::RenderTargetFormat::RGBA32_UINT:
|
||||
case Tegra::RenderTargetFormat::RG16_UINT:
|
||||
case Tegra::RenderTargetFormat::R8_UINT:
|
||||
case Tegra::RenderTargetFormat::R16_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));
|
||||
@@ -488,7 +463,7 @@ struct SurfaceParams {
|
||||
static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
|
||||
switch (format) {
|
||||
case Tegra::FramebufferConfig::PixelFormat::ABGR8:
|
||||
return PixelFormat::ABGR8;
|
||||
return PixelFormat::ABGR8U;
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
|
||||
UNREACHABLE();
|
||||
@@ -634,8 +609,7 @@ public:
|
||||
Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config);
|
||||
|
||||
/// Get the color and depth surfaces based on the framebuffer configuration
|
||||
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
|
||||
const MathUtil::Rectangle<s32>& viewport);
|
||||
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb);
|
||||
|
||||
/// Flushes the surface to Switch memory
|
||||
void FlushSurface(const Surface& surface);
|
||||
|
||||
@@ -141,6 +141,15 @@ private:
|
||||
ExitMethod jmp = Scan(target, end, labels);
|
||||
return exit_method = ParallelExit(no_jmp, jmp);
|
||||
}
|
||||
case OpCode::Id::SSY: {
|
||||
// The SSY instruction uses a similar encoding as the BRA instruction.
|
||||
ASSERT_MSG(instr.bra.constant_buffer == 0,
|
||||
"Constant buffer SSY is not supported");
|
||||
u32 target = offset + instr.bra.GetBranchTarget();
|
||||
labels.insert(target);
|
||||
// Continue scanning for an exit method.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -349,7 +358,12 @@ public:
|
||||
void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) {
|
||||
std::string dest = GetOutputAttribute(attribute) + GetSwizzle(elem);
|
||||
std::string src = GetRegisterAsFloat(reg);
|
||||
shader.AddLine(dest + " = " + src + ';');
|
||||
|
||||
if (!dest.empty()) {
|
||||
// Can happen with unknown/unimplemented output attributes, in which case we ignore the
|
||||
// instruction for now.
|
||||
shader.AddLine(dest + " = " + src + ';');
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates code representing a uniform (C buffer) register, interpreted as the input type.
|
||||
@@ -525,20 +539,16 @@ private:
|
||||
// shader.
|
||||
ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex);
|
||||
return "vec4(0, 0, uintBitsToFloat(gl_InstanceID), uintBitsToFloat(gl_VertexID))";
|
||||
case Attribute::Index::Unknown_63:
|
||||
// TODO(bunnei): Figure out what this is used for. Super Mario Odyssey uses this.
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled input attribute Unknown_63");
|
||||
UNREACHABLE();
|
||||
break;
|
||||
default:
|
||||
const u32 index{static_cast<u32>(attribute) -
|
||||
static_cast<u32>(Attribute::Index::Attribute_0)};
|
||||
if (attribute >= Attribute::Index::Attribute_0) {
|
||||
if (attribute >= Attribute::Index::Attribute_0 &&
|
||||
attribute <= Attribute::Index::Attribute_31) {
|
||||
declr_input_attribute.insert(attribute);
|
||||
return "input_attribute_" + std::to_string(index);
|
||||
}
|
||||
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", index);
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute));
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -560,6 +570,7 @@ private:
|
||||
|
||||
LOG_CRITICAL(HW_GPU, "Unhandled output attribute: {}", index);
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -828,7 +839,11 @@ private:
|
||||
ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute,
|
||||
"NeverExecute predicate not implemented");
|
||||
|
||||
if (instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) {
|
||||
// Some instructions (like SSY) don't have a predicate field, they are always
|
||||
// unconditionally executed.
|
||||
bool can_be_predicated = OpCode::IsPredicatedInstruction(opcode->GetId());
|
||||
|
||||
if (can_be_predicated && instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) {
|
||||
shader.AddLine("if (" +
|
||||
GetPredicateCondition(instr.pred.pred_index, instr.negate_pred != 0) +
|
||||
')');
|
||||
@@ -1652,7 +1667,15 @@ private:
|
||||
}
|
||||
case OpCode::Id::KIL: {
|
||||
ASSERT(instr.flow.cond == Tegra::Shader::FlowCondition::Always);
|
||||
|
||||
// Enclose "discard" in a conditional, so that GLSL compilation does not complain
|
||||
// about unexecuted instructions that may follow this.
|
||||
shader.AddLine("if (true) {");
|
||||
++shader.scope;
|
||||
shader.AddLine("discard;");
|
||||
--shader.scope;
|
||||
shader.AddLine("}");
|
||||
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::BRA: {
|
||||
@@ -1668,16 +1691,25 @@ private:
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::SSY: {
|
||||
// The SSY opcode tells the GPU where to re-converge divergent execution paths, we
|
||||
// can ignore this when generating GLSL code.
|
||||
// The SSY opcode tells the GPU where to re-converge divergent execution paths, it
|
||||
// sets the target of the jump that the SYNC instruction will make. The SSY opcode
|
||||
// has a similar structure to the BRA opcode.
|
||||
ASSERT_MSG(instr.bra.constant_buffer == 0, "Constant buffer SSY is not supported");
|
||||
|
||||
u32 target = offset + instr.bra.GetBranchTarget();
|
||||
shader.AddLine("ssy_target = " + std::to_string(target) + "u;");
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::SYNC:
|
||||
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);
|
||||
shader.AddLine("{ jmp_to = ssy_target; break; }");
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::DEPBAR: {
|
||||
// TODO(Subv): Find out if we actually have to care about these instructions or if
|
||||
// TODO(Subv): Find out if we actually have to care about this instruction or if
|
||||
// the GLSL compiler takes care of that for us.
|
||||
LOG_WARNING(HW_GPU, "DEPBAR/SYNC instruction is stubbed");
|
||||
LOG_WARNING(HW_GPU, "DEPBAR instruction is stubbed");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -1691,7 +1723,7 @@ private:
|
||||
}
|
||||
|
||||
// Close the predicate condition scope.
|
||||
if (instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) {
|
||||
if (can_be_predicated && instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) {
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
}
|
||||
@@ -1742,6 +1774,7 @@ private:
|
||||
} else {
|
||||
labels.insert(subroutine.begin);
|
||||
shader.AddLine("uint jmp_to = " + std::to_string(subroutine.begin) + "u;");
|
||||
shader.AddLine("uint ssy_target = 0u;");
|
||||
shader.AddLine("while (true) {");
|
||||
++shader.scope;
|
||||
|
||||
@@ -1757,7 +1790,7 @@ private:
|
||||
u32 compile_end = CompileRange(label, next_label);
|
||||
if (compile_end > next_label && compile_end != PROGRAM_END) {
|
||||
// This happens only when there is a label inside a IF/LOOP block
|
||||
shader.AddLine("{ jmp_to = " + std::to_string(compile_end) + "u; break; }");
|
||||
shader.AddLine(" jmp_to = " + std::to_string(compile_end) + "u; break; }");
|
||||
labels.emplace(compile_end);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,9 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
|
||||
case Maxwell::VertexAttribute::Type::SignedNorm: {
|
||||
|
||||
switch (attrib.size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
return GL_INT;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
return GL_BYTE;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
@@ -153,42 +156,61 @@ inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
|
||||
inline GLenum BlendFunc(Maxwell::Blend::Factor factor) {
|
||||
switch (factor) {
|
||||
case Maxwell::Blend::Factor::Zero:
|
||||
case Maxwell::Blend::Factor::ZeroGL:
|
||||
return GL_ZERO;
|
||||
case Maxwell::Blend::Factor::One:
|
||||
case Maxwell::Blend::Factor::OneGL:
|
||||
return GL_ONE;
|
||||
case Maxwell::Blend::Factor::SourceColor:
|
||||
case Maxwell::Blend::Factor::SourceColorGL:
|
||||
return GL_SRC_COLOR;
|
||||
case Maxwell::Blend::Factor::OneMinusSourceColor:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceColorGL:
|
||||
return GL_ONE_MINUS_SRC_COLOR;
|
||||
case Maxwell::Blend::Factor::SourceAlpha:
|
||||
case Maxwell::Blend::Factor::SourceAlphaGL:
|
||||
return GL_SRC_ALPHA;
|
||||
case Maxwell::Blend::Factor::OneMinusSourceAlpha:
|
||||
case Maxwell::Blend::Factor::OneMinusSourceAlphaGL:
|
||||
return GL_ONE_MINUS_SRC_ALPHA;
|
||||
case Maxwell::Blend::Factor::DestAlpha:
|
||||
case Maxwell::Blend::Factor::DestAlphaGL:
|
||||
return GL_DST_ALPHA;
|
||||
case Maxwell::Blend::Factor::OneMinusDestAlpha:
|
||||
case Maxwell::Blend::Factor::OneMinusDestAlphaGL:
|
||||
return GL_ONE_MINUS_DST_ALPHA;
|
||||
case Maxwell::Blend::Factor::DestColor:
|
||||
case Maxwell::Blend::Factor::DestColorGL:
|
||||
return GL_DST_COLOR;
|
||||
case Maxwell::Blend::Factor::OneMinusDestColor:
|
||||
case Maxwell::Blend::Factor::OneMinusDestColorGL:
|
||||
return GL_ONE_MINUS_DST_COLOR;
|
||||
case Maxwell::Blend::Factor::SourceAlphaSaturate:
|
||||
case Maxwell::Blend::Factor::SourceAlphaSaturateGL:
|
||||
return GL_SRC_ALPHA_SATURATE;
|
||||
case Maxwell::Blend::Factor::Source1Color:
|
||||
case Maxwell::Blend::Factor::Source1ColorGL:
|
||||
return GL_SRC1_COLOR;
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Color:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1ColorGL:
|
||||
return GL_ONE_MINUS_SRC1_COLOR;
|
||||
case Maxwell::Blend::Factor::Source1Alpha:
|
||||
case Maxwell::Blend::Factor::Source1AlphaGL:
|
||||
return GL_SRC1_ALPHA;
|
||||
case Maxwell::Blend::Factor::OneMinusSource1Alpha:
|
||||
case Maxwell::Blend::Factor::OneMinusSource1AlphaGL:
|
||||
return GL_ONE_MINUS_SRC1_ALPHA;
|
||||
case Maxwell::Blend::Factor::ConstantColor:
|
||||
case Maxwell::Blend::Factor::ConstantColorGL:
|
||||
return GL_CONSTANT_COLOR;
|
||||
case Maxwell::Blend::Factor::OneMinusConstantColor:
|
||||
case Maxwell::Blend::Factor::OneMinusConstantColorGL:
|
||||
return GL_ONE_MINUS_CONSTANT_COLOR;
|
||||
case Maxwell::Blend::Factor::ConstantAlpha:
|
||||
case Maxwell::Blend::Factor::ConstantAlphaGL:
|
||||
return GL_CONSTANT_ALPHA;
|
||||
case Maxwell::Blend::Factor::OneMinusConstantAlpha:
|
||||
case Maxwell::Blend::Factor::OneMinusConstantAlphaGL:
|
||||
return GL_ONE_MINUS_CONSTANT_ALPHA;
|
||||
}
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented blend factor={}", static_cast<u32>(factor));
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "core/tracer/recorder.h"
|
||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||
#include "video_core/utils.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
static const char vertex_shader[] = R"(
|
||||
#version 150 core
|
||||
@@ -92,7 +91,8 @@ static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, cons
|
||||
return matrix;
|
||||
}
|
||||
|
||||
ScopeAcquireGLContext::ScopeAcquireGLContext(EmuWindow& emu_window_) : emu_window{emu_window_} {
|
||||
ScopeAcquireGLContext::ScopeAcquireGLContext(Core::Frontend::EmuWindow& emu_window_)
|
||||
: emu_window{emu_window_} {
|
||||
if (Settings::values.use_multi_core) {
|
||||
emu_window.MakeCurrent();
|
||||
}
|
||||
@@ -103,7 +103,9 @@ ScopeAcquireGLContext::~ScopeAcquireGLContext() {
|
||||
}
|
||||
}
|
||||
|
||||
RendererOpenGL::RendererOpenGL(EmuWindow& window) : VideoCore::RendererBase{window} {}
|
||||
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& window)
|
||||
: VideoCore::RendererBase{window} {}
|
||||
|
||||
RendererOpenGL::~RendererOpenGL() = default;
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
}
|
||||
|
||||
/// Structure used for storing information about the textures for the Switch screen
|
||||
struct TextureInfo {
|
||||
@@ -34,16 +36,16 @@ struct ScreenInfo {
|
||||
/// Helper class to acquire/release OpenGL context within a given scope
|
||||
class ScopeAcquireGLContext : NonCopyable {
|
||||
public:
|
||||
explicit ScopeAcquireGLContext(EmuWindow& window);
|
||||
explicit ScopeAcquireGLContext(Core::Frontend::EmuWindow& window);
|
||||
~ScopeAcquireGLContext();
|
||||
|
||||
private:
|
||||
EmuWindow& emu_window;
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
};
|
||||
|
||||
class RendererOpenGL : public VideoCore::RendererBase {
|
||||
public:
|
||||
explicit RendererOpenGL(EmuWindow& window);
|
||||
explicit RendererOpenGL(Core::Frontend::EmuWindow& window);
|
||||
~RendererOpenGL() override;
|
||||
|
||||
/// Swap buffers (render frame)
|
||||
|
||||
@@ -86,88 +86,11 @@ u32 BytesPerPixel(TextureFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
static u32 DepthBytesPerPixel(DepthFormat format) {
|
||||
switch (format) {
|
||||
case DepthFormat::Z16_UNORM:
|
||||
return 2;
|
||||
case DepthFormat::S8_Z24_UNORM:
|
||||
case DepthFormat::Z24_S8_UNORM:
|
||||
case DepthFormat::Z32_FLOAT:
|
||||
return 4;
|
||||
case DepthFormat::Z32_S8_X24_FLOAT:
|
||||
return 8;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Format not implemented");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height,
|
||||
u32 block_height) {
|
||||
u8* data = Memory::GetPointer(address);
|
||||
u32 bytes_per_pixel = BytesPerPixel(format);
|
||||
|
||||
std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
|
||||
u32 height, u32 block_height) {
|
||||
std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
|
||||
|
||||
switch (format) {
|
||||
case TextureFormat::DXT1:
|
||||
case TextureFormat::DXT23:
|
||||
case TextureFormat::DXT45:
|
||||
case TextureFormat::DXN1:
|
||||
case TextureFormat::DXN2:
|
||||
case TextureFormat::BC7U:
|
||||
// In the DXT and DXN formats, each 4x4 tile is swizzled instead of just individual pixel
|
||||
// values.
|
||||
CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data,
|
||||
unswizzled_data.data(), true, block_height);
|
||||
break;
|
||||
case TextureFormat::A8R8G8B8:
|
||||
case TextureFormat::A2B10G10R10:
|
||||
case TextureFormat::A1B5G5R5:
|
||||
case TextureFormat::B5G6R5:
|
||||
case TextureFormat::R8:
|
||||
case TextureFormat::G8R8:
|
||||
case TextureFormat::R16_G16_B16_A16:
|
||||
case TextureFormat::R32_G32_B32_A32:
|
||||
case TextureFormat::R32_G32:
|
||||
case TextureFormat::R32:
|
||||
case TextureFormat::R16:
|
||||
case TextureFormat::R16_G16:
|
||||
case TextureFormat::BF10GF11RF11:
|
||||
case TextureFormat::ASTC_2D_4X4:
|
||||
case TextureFormat::R32_G32_B32:
|
||||
CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
|
||||
unswizzled_data.data(), true, block_height);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Format not implemented");
|
||||
break;
|
||||
}
|
||||
|
||||
return unswizzled_data;
|
||||
}
|
||||
|
||||
std::vector<u8> UnswizzleDepthTexture(VAddr address, DepthFormat format, u32 width, u32 height,
|
||||
u32 block_height) {
|
||||
u8* data = Memory::GetPointer(address);
|
||||
u32 bytes_per_pixel = DepthBytesPerPixel(format);
|
||||
|
||||
std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
|
||||
|
||||
switch (format) {
|
||||
case DepthFormat::Z16_UNORM:
|
||||
case DepthFormat::S8_Z24_UNORM:
|
||||
case DepthFormat::Z24_S8_UNORM:
|
||||
case DepthFormat::Z32_FLOAT:
|
||||
case DepthFormat::Z32_S8_X24_FLOAT:
|
||||
CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
|
||||
unswizzled_data.data(), true, block_height);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Format not implemented");
|
||||
break;
|
||||
}
|
||||
|
||||
CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel,
|
||||
Memory::GetPointer(address), unswizzled_data.data(), true, block_height);
|
||||
return unswizzled_data;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ namespace Tegra::Texture {
|
||||
/**
|
||||
* Unswizzles a swizzled texture without changing its format.
|
||||
*/
|
||||
std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height,
|
||||
u32 block_height = TICEntry::DefaultBlockHeight);
|
||||
std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
|
||||
u32 height, u32 block_height = TICEntry::DefaultBlockHeight);
|
||||
|
||||
/**
|
||||
* Unswizzles a swizzled depth texture without changing its format.
|
||||
|
||||
@@ -9,9 +9,7 @@
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
std::atomic<bool> g_toggle_framelimit_enabled;
|
||||
|
||||
std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window) {
|
||||
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window) {
|
||||
return std::make_unique<RendererOpenGL>(emu_window);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,27 +4,22 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
}
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
class RendererBase;
|
||||
|
||||
enum class Renderer { Software, OpenGL };
|
||||
|
||||
// TODO: Wrap these in a user settings struct along with any other graphics settings (often set from
|
||||
// qt ui)
|
||||
extern std::atomic<bool> g_toggle_framelimit_enabled;
|
||||
|
||||
/**
|
||||
* Creates a renderer instance.
|
||||
*
|
||||
* @note The returned renderer instance is simply allocated. Its Init()
|
||||
* function still needs to be called to fully complete its setup.
|
||||
*/
|
||||
std::unique_ptr<RendererBase> CreateRenderer(EmuWindow& emu_window);
|
||||
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window);
|
||||
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -101,7 +101,7 @@ signals:
|
||||
void ErrorThrown(Core::System::ResultStatus, std::string);
|
||||
};
|
||||
|
||||
class GRenderWindow : public QWidget, public EmuWindow {
|
||||
class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
@@ -383,8 +383,10 @@ void GraphicsSurfaceWidget::OnUpdate() {
|
||||
QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
|
||||
boost::optional<VAddr> address = gpu.memory_manager->GpuToCpuAddress(surface_address);
|
||||
|
||||
auto unswizzled_data =
|
||||
Tegra::Texture::UnswizzleTexture(*address, surface_format, surface_width, surface_height);
|
||||
// TODO(bunnei): Will not work with BCn formats that swizzle 4x4 tiles.
|
||||
// Needs to be fixed if we plan to use this feature more, otherwise we may remove it.
|
||||
auto unswizzled_data = Tegra::Texture::UnswizzleTexture(
|
||||
*address, 1, Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height);
|
||||
|
||||
auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
|
||||
surface_width, surface_height);
|
||||
|
||||
@@ -453,10 +453,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
|
||||
std::string name = " ";
|
||||
const auto res3 = loader->ReadTitle(name);
|
||||
|
||||
if ((res1 == Loader::ResultStatus::ErrorNotUsed ||
|
||||
res1 == Loader::ResultStatus::ErrorNotImplemented) &&
|
||||
(res3 == Loader::ResultStatus::ErrorNotUsed ||
|
||||
res3 == Loader::ResultStatus::ErrorNotImplemented) &&
|
||||
if (res1 != Loader::ResultStatus::Success && res3 != Loader::ResultStatus::Success &&
|
||||
res2 == Loader::ResultStatus::Success) {
|
||||
// Use from metadata pool.
|
||||
if (nca_control_map.find(program_id) != nca_control_map.end()) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
#include <QImage>
|
||||
@@ -39,7 +40,6 @@ public:
|
||||
* If this class receives valid SMDH data, it will also display game icons and titles.
|
||||
*/
|
||||
class GameListItemPath : public GameListItem {
|
||||
|
||||
public:
|
||||
static const int FullPathRole = Qt::UserRole + 1;
|
||||
static const int TitleRole = Qt::UserRole + 2;
|
||||
@@ -48,18 +48,18 @@ public:
|
||||
|
||||
GameListItemPath() = default;
|
||||
GameListItemPath(const QString& game_path, const std::vector<u8>& picture_data,
|
||||
const QString& game_name, const QString& game_type, u64 program_id)
|
||||
: GameListItem() {
|
||||
const QString& game_name, const QString& game_type, u64 program_id) {
|
||||
setData(game_path, FullPathRole);
|
||||
setData(game_name, TitleRole);
|
||||
setData(qulonglong(program_id), ProgramIdRole);
|
||||
setData(game_type, FileTypeRole);
|
||||
|
||||
QPixmap picture;
|
||||
u32 size = UISettings::values.icon_size;
|
||||
if (!picture.loadFromData(picture_data.data(), picture_data.size()))
|
||||
picture = GetDefaultIcon(size);
|
||||
const u32 size = UISettings::values.icon_size;
|
||||
|
||||
QPixmap picture;
|
||||
if (!picture.loadFromData(picture_data.data(), static_cast<u32>(picture_data.size()))) {
|
||||
picture = GetDefaultIcon(size);
|
||||
}
|
||||
picture = picture.scaled(size, size);
|
||||
|
||||
setData(picture, Qt::DecorationRole);
|
||||
@@ -70,17 +70,16 @@ public:
|
||||
std::string filename;
|
||||
Common::SplitPath(data(FullPathRole).toString().toStdString(), nullptr, &filename,
|
||||
nullptr);
|
||||
QString title = data(TitleRole).toString();
|
||||
|
||||
std::vector<QString> row_data{
|
||||
const std::array<QString, 4> row_data{{
|
||||
QString::fromStdString(filename),
|
||||
data(FileTypeRole).toString(),
|
||||
QString::fromStdString(fmt::format("0x{:016X}", data(ProgramIdRole).toULongLong())),
|
||||
data(TitleRole).toString(),
|
||||
};
|
||||
}};
|
||||
|
||||
auto row1 = row_data.at(UISettings::values.row_1_text_id);
|
||||
auto row2 = row_data.at(UISettings::values.row_2_text_id);
|
||||
const auto& row1 = row_data.at(UISettings::values.row_1_text_id);
|
||||
const auto& row2 = row_data.at(UISettings::values.row_2_text_id);
|
||||
|
||||
if (row1.isEmpty() || row1 == row2)
|
||||
return row2;
|
||||
@@ -88,9 +87,9 @@ public:
|
||||
return row1;
|
||||
|
||||
return row1 + "\n " + row2;
|
||||
} else {
|
||||
return GameListItem::data(role);
|
||||
}
|
||||
|
||||
return GameListItem::data(role);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -424,67 +424,11 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
QMessageBox::critical(this, tr("Error while loading ROM!"),
|
||||
tr("The ROM format is not supported."));
|
||||
break;
|
||||
case Core::System::ResultStatus::ErrorUnsupportedArch:
|
||||
LOG_CRITICAL(Frontend, "Unsupported architecture detected!", filename.toStdString());
|
||||
QMessageBox::critical(this, tr("Error while loading ROM!"),
|
||||
tr("The ROM uses currently unusable 32-bit architecture"));
|
||||
break;
|
||||
case Core::System::ResultStatus::ErrorSystemMode:
|
||||
LOG_CRITICAL(Frontend, "Failed to load ROM!");
|
||||
QMessageBox::critical(this, tr("Error while loading ROM!"),
|
||||
tr("Could not determine the system mode."));
|
||||
break;
|
||||
|
||||
case Core::System::ResultStatus::ErrorLoader_ErrorMissingKeys: {
|
||||
const auto reg_found = Core::Crypto::KeyManager::KeyFileExists(false);
|
||||
const auto title_found = Core::Crypto::KeyManager::KeyFileExists(true);
|
||||
|
||||
std::string file_text;
|
||||
|
||||
if (!reg_found && !title_found) {
|
||||
file_text = "A proper key file (prod.keys, dev.keys, or title.keys) could not be "
|
||||
"found. You will need to dump your keys from your switch to continue.";
|
||||
} else if (reg_found && title_found) {
|
||||
file_text =
|
||||
"Both key files were found in your config directory, but the correct key could"
|
||||
"not be found. You may be missing a titlekey or general key, depending on "
|
||||
"the game.";
|
||||
} else if (reg_found) {
|
||||
file_text =
|
||||
"The regular keys file (prod.keys/dev.keys) was found in your config, but the "
|
||||
"titlekeys file (title.keys) was not. You are either missing the correct "
|
||||
"titlekey or missing a general key required to decrypt the game.";
|
||||
} else {
|
||||
file_text = "The title keys file (title.keys) was found in your config, but "
|
||||
"the regular keys file (prod.keys/dev.keys) was not. Unfortunately, "
|
||||
"having the titlekey is not enough, you need additional general keys "
|
||||
"to properly decrypt the game. You should double-check to make sure "
|
||||
"your keys are correct.";
|
||||
}
|
||||
|
||||
QMessageBox::critical(
|
||||
this, tr("Error while loading ROM!"),
|
||||
tr(("The game you are trying to load is encrypted and the required keys to load "
|
||||
"the game could not be found in your configuration. " +
|
||||
file_text + " Please refer to the yuzu wiki for help.")
|
||||
.c_str()));
|
||||
break;
|
||||
}
|
||||
case Core::System::ResultStatus::ErrorLoader_ErrorDecrypting: {
|
||||
QMessageBox::critical(
|
||||
this, tr("Error while loading ROM!"),
|
||||
tr("There was a general error while decrypting the game. This means that the keys "
|
||||
"necessary were found, but were either incorrect, the game itself was not a "
|
||||
"valid game or the game uses an unhandled cryptographic scheme. Please double "
|
||||
"check that you have the correct "
|
||||
"keys."));
|
||||
break;
|
||||
}
|
||||
case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
|
||||
QMessageBox::critical(this, tr("Error while loading ROM!"),
|
||||
tr("The ROM format is not supported."));
|
||||
break;
|
||||
|
||||
case Core::System::ResultStatus::ErrorVideoCore:
|
||||
QMessageBox::critical(
|
||||
this, tr("An error occurred initializing the video core."),
|
||||
@@ -499,9 +443,23 @@ bool GMainWindow::LoadROM(const QString& filename) {
|
||||
break;
|
||||
|
||||
default:
|
||||
QMessageBox::critical(
|
||||
this, tr("Error while loading ROM!"),
|
||||
tr("An unknown error occurred. Please see the log for more details."));
|
||||
if (static_cast<u32>(result) >
|
||||
static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) {
|
||||
LOG_CRITICAL(Frontend, "Failed to load ROM!");
|
||||
const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);
|
||||
const u16 error_id = static_cast<u16>(result) - loader_id;
|
||||
QMessageBox::critical(
|
||||
this, tr("Error while loading ROM!"),
|
||||
QString::fromStdString(fmt::format(
|
||||
"While attempting to load the ROM requested, an error occured. Please "
|
||||
"refer to the yuzu wiki for more information or the yuzu discord for "
|
||||
"additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
|
||||
loader_id, error_id, Loader::GetMessageForResultStatus(error_id))));
|
||||
} else {
|
||||
QMessageBox::critical(
|
||||
this, tr("Error while loading ROM!"),
|
||||
tr("An unknown error occurred. Please see the log for more details."));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
class EmuWindow_SDL2 : public EmuWindow {
|
||||
class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
|
||||
public:
|
||||
explicit EmuWindow_SDL2(bool fullscreen);
|
||||
~EmuWindow_SDL2();
|
||||
|
||||
@@ -174,19 +174,6 @@ int main(int argc, char** argv) {
|
||||
case Core::System::ResultStatus::ErrorLoader:
|
||||
LOG_CRITICAL(Frontend, "Failed to load ROM!");
|
||||
return -1;
|
||||
case Core::System::ResultStatus::ErrorLoader_ErrorMissingKeys:
|
||||
LOG_CRITICAL(Frontend, "The game you are trying to load is encrypted and the keys required "
|
||||
"could not be found. Please refer to the yuzu wiki for help");
|
||||
return -1;
|
||||
case Core::System::ResultStatus::ErrorLoader_ErrorDecrypting:
|
||||
LOG_CRITICAL(Frontend, "The game you are trying to load is encrypted and there was a "
|
||||
"general error while decrypting. This could mean that the keys are "
|
||||
"incorrect, game is invalid or game uses an unsupported method of "
|
||||
"crypto. Please double-check your keys");
|
||||
return -1;
|
||||
case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
|
||||
LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported.");
|
||||
return -1;
|
||||
case Core::System::ResultStatus::ErrorNotInitialized:
|
||||
LOG_CRITICAL(Frontend, "CPUCore not initialized");
|
||||
return -1;
|
||||
@@ -198,6 +185,17 @@ int main(int argc, char** argv) {
|
||||
return -1;
|
||||
case Core::System::ResultStatus::Success:
|
||||
break; // Expected case
|
||||
default:
|
||||
if (static_cast<u32>(load_result) >
|
||||
static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) {
|
||||
const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);
|
||||
const u16 error_id = static_cast<u16>(load_result) - loader_id;
|
||||
LOG_CRITICAL(Frontend,
|
||||
"While attempting to load the ROM requested, an error occured. Please "
|
||||
"refer to the yuzu wiki for more information or the yuzu discord for "
|
||||
"additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
|
||||
loader_id, error_id, Loader::GetMessageForResultStatus(error_id));
|
||||
}
|
||||
}
|
||||
|
||||
Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "SDL");
|
||||
|
||||
Reference in New Issue
Block a user