Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 218ae888f3 | |||
| 89b8801a97 | |||
| 00207cc965 | |||
| e86b26cd2b | |||
| 41890a84be | |||
| 23d3cd7604 | |||
| d6cddffb78 | |||
| 520e4e5d4b | |||
| b8fbd125e6 | |||
| cb2209d06a | |||
| 854ac468b9 | |||
| 150a3c0890 | |||
| d9ee5b874c | |||
| 8a1bcc3d30 | |||
| 00e7190e29 | |||
| 1efdb4897e | |||
| c13fbe6a41 | |||
| 76ef6e5c2b | |||
| 34510bcda8 | |||
| 085b388a7a | |||
| 9dec087fca | |||
| c0e320ad0d | |||
| 845607481c | |||
| bbeb859122 | |||
| 9d296f8a35 | |||
| 30ce9b2b5c | |||
| 5dfcf7cf26 | |||
| 37b23efece | |||
| a1868286b0 | |||
| 28e36de56f | |||
| c05c8a7a06 | |||
| d0ed3ff4b7 | |||
| d9ee58a3b5 | |||
| d6b7195192 | |||
| 15e0c4c4ec | |||
| 52746ed8dc | |||
| 5b0a9f8ba8 | |||
| c39c8e6982 | |||
| 06c1f75f21 |
@@ -26,7 +26,6 @@ using Vector = Dynarmic::A64::Vector;
|
||||
class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks {
|
||||
public:
|
||||
explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {}
|
||||
~ARM_Dynarmic_Callbacks() = default;
|
||||
|
||||
u8 MemoryRead8(u64 vaddr) override {
|
||||
return Memory::Read8(vaddr);
|
||||
|
||||
@@ -29,7 +29,7 @@ class ARM_Dynarmic final : public ARM_Interface {
|
||||
public:
|
||||
ARM_Dynarmic(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
|
||||
std::size_t core_index);
|
||||
~ARM_Dynarmic();
|
||||
~ARM_Dynarmic() override;
|
||||
|
||||
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) override;
|
||||
@@ -76,7 +76,7 @@ private:
|
||||
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
|
||||
public:
|
||||
explicit DynarmicExclusiveMonitor(std::size_t core_count);
|
||||
~DynarmicExclusiveMonitor();
|
||||
~DynarmicExclusiveMonitor() override;
|
||||
|
||||
void SetExclusive(std::size_t core_index, VAddr addr) override;
|
||||
void ClearExclusive() override;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Core {
|
||||
class ARM_Unicorn final : public ARM_Interface {
|
||||
public:
|
||||
explicit ARM_Unicorn(Timing::CoreTiming& core_timing);
|
||||
~ARM_Unicorn();
|
||||
~ARM_Unicorn() override;
|
||||
|
||||
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) override;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "core/file_sys/fsmitm_romfsbuild.h"
|
||||
@@ -97,7 +98,8 @@ struct RomFSBuildFileContext {
|
||||
VirtualFile source;
|
||||
};
|
||||
|
||||
static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, std::size_t path_len) {
|
||||
static u32 romfs_calc_path_hash(u32 parent, std::string_view path, u32 start,
|
||||
std::size_t path_len) {
|
||||
u32 hash = parent ^ 123456789;
|
||||
for (u32 i = 0; i < path_len; i++) {
|
||||
hash = (hash >> 5) | (hash << 27);
|
||||
|
||||
@@ -10,14 +10,6 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
bool operator>=(TitleType lhs, TitleType rhs) {
|
||||
return static_cast<std::size_t>(lhs) >= static_cast<std::size_t>(rhs);
|
||||
}
|
||||
|
||||
bool operator<=(TitleType lhs, TitleType rhs) {
|
||||
return static_cast<std::size_t>(lhs) <= static_cast<std::size_t>(rhs);
|
||||
}
|
||||
|
||||
CNMT::CNMT(VirtualFile file) {
|
||||
if (file->ReadObject(&header) != sizeof(CNMTHeader))
|
||||
return;
|
||||
|
||||
@@ -29,9 +29,6 @@ enum class TitleType : u8 {
|
||||
DeltaTitle = 0x83,
|
||||
};
|
||||
|
||||
bool operator>=(TitleType lhs, TitleType rhs);
|
||||
bool operator<=(TitleType lhs, TitleType rhs);
|
||||
|
||||
enum class ContentRecordType : u8 {
|
||||
Meta = 0,
|
||||
Program = 1,
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
@@ -17,28 +16,30 @@ ProgramMetadata::ProgramMetadata() = default;
|
||||
ProgramMetadata::~ProgramMetadata() = default;
|
||||
|
||||
Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
|
||||
std::size_t total_size = static_cast<std::size_t>(file->GetSize());
|
||||
if (total_size < sizeof(Header))
|
||||
const std::size_t total_size = file->GetSize();
|
||||
if (total_size < sizeof(Header)) {
|
||||
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())
|
||||
if (sizeof(Header) != file->ReadObject(&npdm_header)) {
|
||||
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())
|
||||
if (sizeof(AcidHeader) != file->ReadObject(&acid_header, npdm_header.acid_offset)) {
|
||||
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))
|
||||
if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) {
|
||||
return Loader::ResultStatus::ErrorBadACIHeader;
|
||||
}
|
||||
|
||||
if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset))
|
||||
if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) {
|
||||
return Loader::ResultStatus::ErrorBadFileAccessControl;
|
||||
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
|
||||
}
|
||||
|
||||
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) {
|
||||
return Loader::ResultStatus::ErrorBadFileAccessHeader;
|
||||
}
|
||||
|
||||
aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32));
|
||||
const u64 read_size = aci_header.kac_size;
|
||||
|
||||
@@ -58,7 +58,6 @@ public:
|
||||
void Print() const;
|
||||
|
||||
private:
|
||||
// TODO(DarkLordZach): BitField is not trivially copyable.
|
||||
struct Header {
|
||||
std::array<char, 4> magic;
|
||||
std::array<u8, 8> reserved;
|
||||
@@ -85,7 +84,6 @@ private:
|
||||
|
||||
static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong");
|
||||
|
||||
// TODO(DarkLordZach): BitField is not trivially copyable.
|
||||
struct AcidHeader {
|
||||
std::array<u8, 0x100> signature;
|
||||
std::array<u8, 0x100> nca_modulus;
|
||||
|
||||
@@ -16,8 +16,10 @@ namespace FileSys {
|
||||
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
|
||||
|
||||
std::string SaveDataDescriptor::DebugInfo() const {
|
||||
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}]",
|
||||
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id);
|
||||
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, "
|
||||
"rank={}, index={}]",
|
||||
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
|
||||
static_cast<u8>(rank), index);
|
||||
}
|
||||
|
||||
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
|
||||
@@ -28,7 +30,7 @@ SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save
|
||||
|
||||
SaveDataFactory::~SaveDataFactory() = default;
|
||||
|
||||
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) {
|
||||
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) {
|
||||
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
|
||||
if (meta.zero_1 != 0) {
|
||||
LOG_WARNING(Service_FS,
|
||||
|
||||
@@ -32,12 +32,19 @@ enum class SaveDataType : u8 {
|
||||
CacheStorage = 5,
|
||||
};
|
||||
|
||||
enum class SaveDataRank : u8 {
|
||||
Primary,
|
||||
Secondary,
|
||||
};
|
||||
|
||||
struct SaveDataDescriptor {
|
||||
u64_le title_id;
|
||||
u128 user_id;
|
||||
u64_le save_id;
|
||||
SaveDataType type;
|
||||
INSERT_PADDING_BYTES(7);
|
||||
SaveDataRank rank;
|
||||
u16_le index;
|
||||
INSERT_PADDING_BYTES(4);
|
||||
u64_le zero_1;
|
||||
u64_le zero_2;
|
||||
u64_le zero_3;
|
||||
@@ -57,7 +64,7 @@ public:
|
||||
explicit SaveDataFactory(VirtualDir dir);
|
||||
~SaveDataFactory();
|
||||
|
||||
ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
|
||||
ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta);
|
||||
|
||||
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
|
||||
|
||||
|
||||
@@ -1339,6 +1339,20 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
|
||||
"called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
|
||||
mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
|
||||
|
||||
if (Memory::IsKernelVirtualAddress(mutex_addr)) {
|
||||
LOG_ERROR(
|
||||
Kernel_SVC,
|
||||
"Given mutex address must not be within the kernel address space. address=0x{:016X}",
|
||||
mutex_addr);
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
if (!Common::IsWordAligned(mutex_addr)) {
|
||||
LOG_ERROR(Kernel_SVC, "Given mutex address must be word-aligned. address=0x{:016X}",
|
||||
mutex_addr);
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
|
||||
const auto& handle_table = current_process->GetHandleTable();
|
||||
SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
|
||||
@@ -119,10 +119,6 @@ union ResultCode {
|
||||
BitField<0, 9, ErrorModule> module;
|
||||
BitField<9, 13, u32> description;
|
||||
|
||||
// The last bit of `level` is checked by apps and the kernel to determine if a result code is an
|
||||
// error
|
||||
BitField<31, 1, u32> is_error;
|
||||
|
||||
constexpr explicit ResultCode(u32 raw) : raw(raw) {}
|
||||
|
||||
constexpr ResultCode(ErrorModule module_, u32 description_)
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/audio/audin_u.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
@@ -33,7 +30,6 @@ public:
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~IAudioIn() = default;
|
||||
};
|
||||
|
||||
AudInU::AudInU() : ServiceFramework("audin:u") {
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/service/audio/audrec_u.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
@@ -30,7 +27,6 @@ public:
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~IFinalOutputRecorder() = default;
|
||||
};
|
||||
|
||||
AudRecU::AudRecU() : ServiceFramework("audrec:u") {
|
||||
|
||||
@@ -197,13 +197,16 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa
|
||||
|
||||
ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_,
|
||||
FileSys::Mode mode) const {
|
||||
std::string path(FileUtil::SanitizePath(path_));
|
||||
auto npath = path;
|
||||
while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\'))
|
||||
npath = npath.substr(1);
|
||||
const std::string path(FileUtil::SanitizePath(path_));
|
||||
std::string_view npath = path;
|
||||
while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) {
|
||||
npath.remove_prefix(1);
|
||||
}
|
||||
|
||||
auto file = backing->GetFileRelative(npath);
|
||||
if (file == nullptr)
|
||||
if (file == nullptr) {
|
||||
return FileSys::ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (mode == FileSys::Mode::Append) {
|
||||
return MakeResult<FileSys::VirtualFile>(
|
||||
@@ -319,15 +322,15 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
FileSys::SaveDataDescriptor save_struct) {
|
||||
const FileSys::SaveDataDescriptor& descriptor) {
|
||||
LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
|
||||
static_cast<u8>(space), save_struct.DebugInfo());
|
||||
static_cast<u8>(space), descriptor.DebugInfo());
|
||||
|
||||
if (save_data_factory == nullptr) {
|
||||
return FileSys::ERROR_ENTITY_NOT_FOUND;
|
||||
}
|
||||
|
||||
return save_data_factory->Open(space, save_struct);
|
||||
return save_data_factory->Open(space, descriptor);
|
||||
}
|
||||
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) {
|
||||
|
||||
@@ -46,7 +46,7 @@ ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess();
|
||||
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
|
||||
FileSys::ContentRecordType type);
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
FileSys::SaveDataDescriptor save_struct);
|
||||
const FileSys::SaveDataDescriptor& descriptor);
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
|
||||
ResultVal<FileSys::VirtualDir> OpenSDMC();
|
||||
|
||||
|
||||
@@ -604,7 +604,9 @@ private:
|
||||
u64_le save_id;
|
||||
u64_le title_id;
|
||||
u64_le save_image_size;
|
||||
INSERT_PADDING_BYTES(0x28);
|
||||
u16_le index;
|
||||
FileSys::SaveDataRank rank;
|
||||
INSERT_PADDING_BYTES(0x25);
|
||||
};
|
||||
static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
|
||||
|
||||
@@ -767,16 +769,17 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_FS, "called.");
|
||||
|
||||
struct Parameters {
|
||||
FileSys::SaveDataSpaceId save_data_space_id;
|
||||
FileSys::SaveDataDescriptor descriptor;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto parameters = rp.PopRaw<Parameters>();
|
||||
|
||||
auto space_id = rp.PopRaw<FileSys::SaveDataSpaceId>();
|
||||
auto unk = rp.Pop<u32>();
|
||||
LOG_INFO(Service_FS, "called with unknown={:08X}", unk);
|
||||
|
||||
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
|
||||
|
||||
auto dir = OpenSaveData(space_id, save_struct);
|
||||
|
||||
auto dir = OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
|
||||
if (dir.Failed()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
|
||||
rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
|
||||
|
||||
@@ -18,7 +18,7 @@ class nvmap;
|
||||
class nvdisp_disp0 final : public nvdevice {
|
||||
public:
|
||||
explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev);
|
||||
~nvdisp_disp0();
|
||||
~nvdisp_disp0() override;
|
||||
|
||||
u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Service::Nvidia {
|
||||
class NVDRV final : public ServiceFramework<NVDRV> {
|
||||
public:
|
||||
NVDRV(std::shared_ptr<Module> nvdrv, const char* name);
|
||||
~NVDRV();
|
||||
~NVDRV() override;
|
||||
|
||||
private:
|
||||
void Open(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Service::Nvidia {
|
||||
class NVMEMP final : public ServiceFramework<NVMEMP> {
|
||||
public:
|
||||
NVMEMP();
|
||||
~NVMEMP();
|
||||
~NVMEMP() override;
|
||||
|
||||
private:
|
||||
void Cmd0(Kernel::HLERequestContext& ctx);
|
||||
|
||||
@@ -90,7 +90,7 @@ private:
|
||||
Kernel::HLERequestContext& ctx);
|
||||
|
||||
ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker);
|
||||
~ServiceFrameworkBase();
|
||||
~ServiceFrameworkBase() override;
|
||||
|
||||
void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
|
||||
void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info);
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Service::Set {
|
||||
class SET_CAL final : public ServiceFramework<SET_CAL> {
|
||||
public:
|
||||
explicit SET_CAL();
|
||||
~SET_CAL();
|
||||
~SET_CAL() override;
|
||||
};
|
||||
|
||||
} // namespace Service::Set
|
||||
|
||||
@@ -64,7 +64,6 @@ public:
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~ISslContext() = default;
|
||||
|
||||
private:
|
||||
void SetOption(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -498,7 +498,6 @@ public:
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~IHOSBinderDriver() = default;
|
||||
|
||||
private:
|
||||
enum class TransactionId {
|
||||
@@ -692,7 +691,6 @@ public:
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~ISystemDisplayService() = default;
|
||||
|
||||
private:
|
||||
void SetLayerZ(Kernel::HLERequestContext& ctx) {
|
||||
@@ -818,7 +816,6 @@ public:
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
~IManagerDisplayService() = default;
|
||||
|
||||
private:
|
||||
void CloseDisplay(Kernel::HLERequestContext& ctx) {
|
||||
@@ -884,7 +881,6 @@ private:
|
||||
class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
|
||||
public:
|
||||
explicit IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
|
||||
~IApplicationDisplayService() = default;
|
||||
|
||||
private:
|
||||
enum class ConvertedScaleMode : u64 {
|
||||
|
||||
@@ -22,7 +22,7 @@ class AppLoader_NCA;
|
||||
class AppLoader_XCI final : public AppLoader {
|
||||
public:
|
||||
explicit AppLoader_XCI(FileSys::VirtualFile file);
|
||||
~AppLoader_XCI();
|
||||
~AppLoader_XCI() override;
|
||||
|
||||
/**
|
||||
* Returns the type of the file
|
||||
|
||||
@@ -59,7 +59,7 @@ ThreadManager::ThreadManager(Core::System& system, VideoCore::RendererBase& rend
|
||||
Tegra::DmaPusher& dma_pusher)
|
||||
: system{system}, thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)} {
|
||||
synchronization_event = system.CoreTiming().RegisterEvent(
|
||||
"GPUThreadSynch", [this](u64 fence, int) { state.WaitForSynchronization(fence); });
|
||||
"GPUThreadSynch", [this](u64 fence, s64) { state.WaitForSynchronization(fence); });
|
||||
}
|
||||
|
||||
ThreadManager::~ThreadManager() {
|
||||
|
||||
@@ -223,27 +223,21 @@ void MacroInterpreter::ProcessResult(ResultOperation operation, u32 reg, u32 res
|
||||
}
|
||||
|
||||
u32 MacroInterpreter::FetchParameter() {
|
||||
ASSERT(next_parameter_index < parameters.size());
|
||||
return parameters[next_parameter_index++];
|
||||
return parameters.at(next_parameter_index++);
|
||||
}
|
||||
|
||||
u32 MacroInterpreter::GetRegister(u32 register_id) const {
|
||||
// Register 0 is supposed to always return 0.
|
||||
if (register_id == 0)
|
||||
return 0;
|
||||
|
||||
ASSERT(register_id < registers.size());
|
||||
return registers[register_id];
|
||||
return registers.at(register_id);
|
||||
}
|
||||
|
||||
void MacroInterpreter::SetRegister(u32 register_id, u32 value) {
|
||||
// Register 0 is supposed to always return 0. NOP is implemented as a store to the zero
|
||||
// register.
|
||||
if (register_id == 0)
|
||||
// Register 0 is hardwired as the zero register.
|
||||
// Ensure no writes to it actually occur.
|
||||
if (register_id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(register_id < registers.size());
|
||||
registers[register_id] = value;
|
||||
registers.at(register_id) = value;
|
||||
}
|
||||
|
||||
void MacroInterpreter::SetMethodAddress(u32 address) {
|
||||
|
||||
@@ -77,16 +77,17 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
|
||||
return gpu_addr;
|
||||
}
|
||||
|
||||
GPUVAddr MemoryManager::FindFreeRegion(GPUVAddr region_start, u64 size) {
|
||||
GPUVAddr MemoryManager::FindFreeRegion(GPUVAddr region_start, u64 size) const {
|
||||
// Find the first Free VMA.
|
||||
const VMAHandle vma_handle{std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) {
|
||||
if (vma.second.type != VirtualMemoryArea::Type::Unmapped) {
|
||||
return false;
|
||||
}
|
||||
const VMAHandle vma_handle{
|
||||
std::find_if(vma_map.begin(), vma_map.end(), [region_start, size](const auto& vma) {
|
||||
if (vma.second.type != VirtualMemoryArea::Type::Unmapped) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const VAddr vma_end{vma.second.base + vma.second.size};
|
||||
return vma_end > region_start && vma_end >= region_start + size;
|
||||
})};
|
||||
const VAddr vma_end{vma.second.base + vma.second.size};
|
||||
return vma_end > region_start && vma_end >= region_start + size;
|
||||
})};
|
||||
|
||||
if (vma_handle == vma_map.end()) {
|
||||
return {};
|
||||
@@ -99,12 +100,12 @@ bool MemoryManager::IsAddressValid(GPUVAddr addr) const {
|
||||
return (addr >> page_bits) < page_table.pointers.size();
|
||||
}
|
||||
|
||||
std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) {
|
||||
std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) const {
|
||||
if (!IsAddressValid(addr)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VAddr cpu_addr{page_table.backing_addr[addr >> page_bits]};
|
||||
const VAddr cpu_addr{page_table.backing_addr[addr >> page_bits]};
|
||||
if (cpu_addr) {
|
||||
return cpu_addr + (addr & page_mask);
|
||||
}
|
||||
@@ -113,7 +114,7 @@ std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr addr) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T MemoryManager::Read(GPUVAddr addr) {
|
||||
T MemoryManager::Read(GPUVAddr addr) const {
|
||||
if (!IsAddressValid(addr)) {
|
||||
return {};
|
||||
}
|
||||
@@ -165,10 +166,10 @@ void MemoryManager::Write(GPUVAddr addr, T data) {
|
||||
}
|
||||
}
|
||||
|
||||
template u8 MemoryManager::Read<u8>(GPUVAddr addr);
|
||||
template u16 MemoryManager::Read<u16>(GPUVAddr addr);
|
||||
template u32 MemoryManager::Read<u32>(GPUVAddr addr);
|
||||
template u64 MemoryManager::Read<u64>(GPUVAddr addr);
|
||||
template u8 MemoryManager::Read<u8>(GPUVAddr addr) const;
|
||||
template u16 MemoryManager::Read<u16>(GPUVAddr addr) const;
|
||||
template u32 MemoryManager::Read<u32>(GPUVAddr addr) const;
|
||||
template u64 MemoryManager::Read<u64>(GPUVAddr addr) const;
|
||||
template void MemoryManager::Write<u8>(GPUVAddr addr, u8 data);
|
||||
template void MemoryManager::Write<u16>(GPUVAddr addr, u16 data);
|
||||
template void MemoryManager::Write<u32>(GPUVAddr addr, u32 data);
|
||||
@@ -179,8 +180,8 @@ u8* MemoryManager::GetPointer(GPUVAddr addr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
u8* page_pointer{page_table.pointers[addr >> page_bits]};
|
||||
if (page_pointer) {
|
||||
u8* const page_pointer{page_table.pointers[addr >> page_bits]};
|
||||
if (page_pointer != nullptr) {
|
||||
return page_pointer + (addr & page_mask);
|
||||
}
|
||||
|
||||
@@ -188,7 +189,21 @@ u8* MemoryManager::GetPointer(GPUVAddr addr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) {
|
||||
const u8* MemoryManager::GetPointer(GPUVAddr addr) const {
|
||||
if (!IsAddressValid(addr)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const u8* const page_pointer{page_table.pointers[addr >> page_bits]};
|
||||
if (page_pointer != nullptr) {
|
||||
return page_pointer + (addr & page_mask);
|
||||
}
|
||||
|
||||
LOG_ERROR(HW_GPU, "Unknown GetPointer @ 0x{:016X}", addr);
|
||||
return {};
|
||||
}
|
||||
|
||||
void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const {
|
||||
std::memcpy(dest_buffer, GetPointer(src_addr), size);
|
||||
}
|
||||
void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size) {
|
||||
|
||||
@@ -50,17 +50,18 @@ public:
|
||||
GPUVAddr MapBufferEx(VAddr cpu_addr, u64 size);
|
||||
GPUVAddr MapBufferEx(VAddr cpu_addr, GPUVAddr addr, u64 size);
|
||||
GPUVAddr UnmapBuffer(GPUVAddr addr, u64 size);
|
||||
std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr);
|
||||
std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
|
||||
|
||||
template <typename T>
|
||||
T Read(GPUVAddr addr);
|
||||
T Read(GPUVAddr addr) const;
|
||||
|
||||
template <typename T>
|
||||
void Write(GPUVAddr addr, T data);
|
||||
|
||||
u8* GetPointer(GPUVAddr addr);
|
||||
const u8* GetPointer(GPUVAddr addr) const;
|
||||
|
||||
void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size);
|
||||
void ReadBlock(GPUVAddr src_addr, void* dest_buffer, std::size_t size) const;
|
||||
void WriteBlock(GPUVAddr dest_addr, const void* src_buffer, std::size_t size);
|
||||
void CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, std::size_t size);
|
||||
|
||||
@@ -127,7 +128,7 @@ private:
|
||||
void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
|
||||
|
||||
/// Finds a free (unmapped region) of the specified size starting at the specified address.
|
||||
GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size);
|
||||
GPUVAddr FindFreeRegion(GPUVAddr region_start, u64 size) const;
|
||||
|
||||
private:
|
||||
static constexpr u64 page_bits{16};
|
||||
|
||||
@@ -69,10 +69,10 @@ public:
|
||||
shader_source += '\n';
|
||||
}
|
||||
|
||||
std::string GenerateTemporal() {
|
||||
std::string temporal = "tmp";
|
||||
temporal += std::to_string(temporal_index++);
|
||||
return temporal;
|
||||
std::string GenerateTemporary() {
|
||||
std::string temporary = "tmp";
|
||||
temporary += std::to_string(temporary_index++);
|
||||
return temporary;
|
||||
}
|
||||
|
||||
std::string GetResult() {
|
||||
@@ -87,7 +87,7 @@ private:
|
||||
}
|
||||
|
||||
std::string shader_source;
|
||||
u32 temporal_index = 1;
|
||||
u32 temporary_index = 1;
|
||||
};
|
||||
|
||||
/// Generates code to use for a swizzle operation.
|
||||
@@ -426,9 +426,14 @@ private:
|
||||
std::string Visit(Node node) {
|
||||
if (const auto operation = std::get_if<OperationNode>(node)) {
|
||||
const auto operation_index = static_cast<std::size_t>(operation->GetCode());
|
||||
if (operation_index >= operation_decompilers.size()) {
|
||||
UNREACHABLE_MSG("Out of bounds operation: {}", operation_index);
|
||||
return {};
|
||||
}
|
||||
const auto decompiler = operation_decompilers[operation_index];
|
||||
if (decompiler == nullptr) {
|
||||
UNREACHABLE_MSG("Operation decompiler {} not defined", operation_index);
|
||||
UNREACHABLE_MSG("Undefined operation: {}", operation_index);
|
||||
return {};
|
||||
}
|
||||
return (this->*decompiler)(*operation);
|
||||
|
||||
@@ -540,7 +545,7 @@ private:
|
||||
|
||||
} else if (std::holds_alternative<OperationNode>(*offset)) {
|
||||
// Indirect access
|
||||
const std::string final_offset = code.GenerateTemporal();
|
||||
const std::string final_offset = code.GenerateTemporary();
|
||||
code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4) & " +
|
||||
std::to_string(MAX_CONSTBUFFER_ELEMENTS - 1) + ';');
|
||||
return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()),
|
||||
@@ -587,9 +592,9 @@ private:
|
||||
// There's a bug in NVidia's proprietary drivers that makes precise fail on fragment shaders
|
||||
const std::string precise = stage != ShaderStage::Fragment ? "precise " : "";
|
||||
|
||||
const std::string temporal = code.GenerateTemporal();
|
||||
code.AddLine(precise + "float " + temporal + " = " + value + ';');
|
||||
return temporal;
|
||||
const std::string temporary = code.GenerateTemporary();
|
||||
code.AddLine(precise + "float " + temporary + " = " + value + ';');
|
||||
return temporary;
|
||||
}
|
||||
|
||||
std::string VisitOperand(Operation operation, std::size_t operand_index) {
|
||||
@@ -601,9 +606,9 @@ private:
|
||||
return Visit(operand);
|
||||
}
|
||||
|
||||
const std::string temporal = code.GenerateTemporal();
|
||||
code.AddLine("float " + temporal + " = " + Visit(operand) + ';');
|
||||
return temporal;
|
||||
const std::string temporary = code.GenerateTemporary();
|
||||
code.AddLine("float " + temporary + " = " + Visit(operand) + ';');
|
||||
return temporary;
|
||||
}
|
||||
|
||||
std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) {
|
||||
|
||||
@@ -54,8 +54,6 @@ add_executable(yuzu
|
||||
debugger/graphics/graphics_breakpoints.cpp
|
||||
debugger/graphics/graphics_breakpoints.h
|
||||
debugger/graphics/graphics_breakpoints_p.h
|
||||
debugger/graphics/graphics_surface.cpp
|
||||
debugger/graphics/graphics_surface.h
|
||||
debugger/console.cpp
|
||||
debugger/console.h
|
||||
debugger/profiler.cpp
|
||||
|
||||
@@ -1,460 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QComboBox>
|
||||
#include <QDebug>
|
||||
#include <QFileDialog>
|
||||
#include <QLabel>
|
||||
#include <QMouseEvent>
|
||||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QSpinBox>
|
||||
#include "common/vector_math.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/textures/decoders.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
#include "yuzu/debugger/graphics/graphics_surface.h"
|
||||
#include "yuzu/util/spinbox.h"
|
||||
|
||||
static Tegra::Texture::TextureFormat ConvertToTextureFormat(
|
||||
Tegra::RenderTargetFormat render_target_format) {
|
||||
switch (render_target_format) {
|
||||
case Tegra::RenderTargetFormat::RGBA8_UNORM:
|
||||
return Tegra::Texture::TextureFormat::A8R8G8B8;
|
||||
case Tegra::RenderTargetFormat::RGB10_A2_UNORM:
|
||||
return Tegra::Texture::TextureFormat::A2B10G10R10;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented RT format");
|
||||
return Tegra::Texture::TextureFormat::A8R8G8B8;
|
||||
}
|
||||
}
|
||||
|
||||
SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_)
|
||||
: QLabel(parent), surface_widget(surface_widget_) {}
|
||||
|
||||
SurfacePicture::~SurfacePicture() = default;
|
||||
|
||||
void SurfacePicture::mousePressEvent(QMouseEvent* event) {
|
||||
// Only do something while the left mouse button is held down
|
||||
if (!(event->buttons() & Qt::LeftButton))
|
||||
return;
|
||||
|
||||
if (pixmap() == nullptr)
|
||||
return;
|
||||
|
||||
if (surface_widget)
|
||||
surface_widget->Pick(event->x() * pixmap()->width() / width(),
|
||||
event->y() * pixmap()->height() / height());
|
||||
}
|
||||
|
||||
void SurfacePicture::mouseMoveEvent(QMouseEvent* event) {
|
||||
// We also want to handle the event if the user moves the mouse while holding down the LMB
|
||||
mousePressEvent(event);
|
||||
}
|
||||
|
||||
GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context,
|
||||
QWidget* parent)
|
||||
: BreakPointObserverDock(debug_context, tr("Maxwell Surface Viewer"), parent),
|
||||
surface_source(Source::RenderTarget0) {
|
||||
setObjectName("MaxwellSurface");
|
||||
|
||||
surface_source_list = new QComboBox;
|
||||
surface_source_list->addItem(tr("Render Target 0"));
|
||||
surface_source_list->addItem(tr("Render Target 1"));
|
||||
surface_source_list->addItem(tr("Render Target 2"));
|
||||
surface_source_list->addItem(tr("Render Target 3"));
|
||||
surface_source_list->addItem(tr("Render Target 4"));
|
||||
surface_source_list->addItem(tr("Render Target 5"));
|
||||
surface_source_list->addItem(tr("Render Target 6"));
|
||||
surface_source_list->addItem(tr("Render Target 7"));
|
||||
surface_source_list->addItem(tr("Z Buffer"));
|
||||
surface_source_list->addItem(tr("Custom"));
|
||||
surface_source_list->setCurrentIndex(static_cast<int>(surface_source));
|
||||
|
||||
surface_address_control = new CSpinBox;
|
||||
surface_address_control->SetBase(16);
|
||||
surface_address_control->SetRange(0, 0x7FFFFFFFFFFFFFFF);
|
||||
surface_address_control->SetPrefix("0x");
|
||||
|
||||
unsigned max_dimension = 16384; // TODO: Find actual maximum
|
||||
|
||||
surface_width_control = new QSpinBox;
|
||||
surface_width_control->setRange(0, max_dimension);
|
||||
|
||||
surface_height_control = new QSpinBox;
|
||||
surface_height_control->setRange(0, max_dimension);
|
||||
|
||||
surface_picker_x_control = new QSpinBox;
|
||||
surface_picker_x_control->setRange(0, max_dimension - 1);
|
||||
|
||||
surface_picker_y_control = new QSpinBox;
|
||||
surface_picker_y_control->setRange(0, max_dimension - 1);
|
||||
|
||||
surface_format_control = new QComboBox;
|
||||
|
||||
// Color formats sorted by Maxwell texture format index
|
||||
surface_format_control->addItem(tr("None"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("A8R8G8B8"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("Unknown"));
|
||||
surface_format_control->addItem(tr("DXT1"));
|
||||
surface_format_control->addItem(tr("DXT23"));
|
||||
surface_format_control->addItem(tr("DXT45"));
|
||||
surface_format_control->addItem(tr("DXN1"));
|
||||
surface_format_control->addItem(tr("DXN2"));
|
||||
|
||||
surface_info_label = new QLabel();
|
||||
surface_info_label->setWordWrap(true);
|
||||
|
||||
surface_picture_label = new SurfacePicture(0, this);
|
||||
surface_picture_label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
surface_picture_label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||
surface_picture_label->setScaledContents(false);
|
||||
|
||||
auto scroll_area = new QScrollArea();
|
||||
scroll_area->setBackgroundRole(QPalette::Dark);
|
||||
scroll_area->setWidgetResizable(false);
|
||||
scroll_area->setWidget(surface_picture_label);
|
||||
|
||||
save_surface = new QPushButton(QIcon::fromTheme("document-save"), tr("Save"));
|
||||
|
||||
// Connections
|
||||
connect(this, &GraphicsSurfaceWidget::Update, this, &GraphicsSurfaceWidget::OnUpdate);
|
||||
connect(surface_source_list,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
&GraphicsSurfaceWidget::OnSurfaceSourceChanged);
|
||||
connect(surface_address_control, &CSpinBox::ValueChanged, this,
|
||||
&GraphicsSurfaceWidget::OnSurfaceAddressChanged);
|
||||
connect(surface_width_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||
this, &GraphicsSurfaceWidget::OnSurfaceWidthChanged);
|
||||
connect(surface_height_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||
this, &GraphicsSurfaceWidget::OnSurfaceHeightChanged);
|
||||
connect(surface_format_control,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
&GraphicsSurfaceWidget::OnSurfaceFormatChanged);
|
||||
connect(surface_picker_x_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||
this, &GraphicsSurfaceWidget::OnSurfacePickerXChanged);
|
||||
connect(surface_picker_y_control, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||
this, &GraphicsSurfaceWidget::OnSurfacePickerYChanged);
|
||||
connect(save_surface, &QPushButton::clicked, this, &GraphicsSurfaceWidget::SaveSurface);
|
||||
|
||||
auto main_widget = new QWidget;
|
||||
auto main_layout = new QVBoxLayout;
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Source:")));
|
||||
sub_layout->addWidget(surface_source_list);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("GPU Address:")));
|
||||
sub_layout->addWidget(surface_address_control);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Width:")));
|
||||
sub_layout->addWidget(surface_width_control);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Height:")));
|
||||
sub_layout->addWidget(surface_height_control);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Format:")));
|
||||
sub_layout->addWidget(surface_format_control);
|
||||
main_layout->addLayout(sub_layout);
|
||||
}
|
||||
main_layout->addWidget(scroll_area);
|
||||
|
||||
auto info_layout = new QHBoxLayout;
|
||||
{
|
||||
auto xy_layout = new QVBoxLayout;
|
||||
{
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("X:")));
|
||||
sub_layout->addWidget(surface_picker_x_control);
|
||||
xy_layout->addLayout(sub_layout);
|
||||
}
|
||||
{
|
||||
auto sub_layout = new QHBoxLayout;
|
||||
sub_layout->addWidget(new QLabel(tr("Y:")));
|
||||
sub_layout->addWidget(surface_picker_y_control);
|
||||
xy_layout->addLayout(sub_layout);
|
||||
}
|
||||
}
|
||||
info_layout->addLayout(xy_layout);
|
||||
surface_info_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
info_layout->addWidget(surface_info_label);
|
||||
}
|
||||
main_layout->addLayout(info_layout);
|
||||
|
||||
main_layout->addWidget(save_surface);
|
||||
main_widget->setLayout(main_layout);
|
||||
setWidget(main_widget);
|
||||
|
||||
// Load current data - TODO: Make sure this works when emulation is not running
|
||||
if (debug_context && debug_context->at_breakpoint) {
|
||||
emit Update();
|
||||
widget()->setEnabled(debug_context->at_breakpoint);
|
||||
} else {
|
||||
widget()->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnBreakPointHit(Tegra::DebugContext::Event event, void* data) {
|
||||
emit Update();
|
||||
widget()->setEnabled(true);
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnResumed() {
|
||||
widget()->setEnabled(false);
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) {
|
||||
surface_source = static_cast<Source>(new_value);
|
||||
emit Update();
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) {
|
||||
if (surface_address != new_value) {
|
||||
surface_address = static_cast<GPUVAddr>(new_value);
|
||||
|
||||
surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||
emit Update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnSurfaceWidthChanged(int new_value) {
|
||||
if (surface_width != static_cast<unsigned>(new_value)) {
|
||||
surface_width = static_cast<unsigned>(new_value);
|
||||
|
||||
surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||
emit Update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnSurfaceHeightChanged(int new_value) {
|
||||
if (surface_height != static_cast<unsigned>(new_value)) {
|
||||
surface_height = static_cast<unsigned>(new_value);
|
||||
|
||||
surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||
emit Update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnSurfaceFormatChanged(int new_value) {
|
||||
if (surface_format != static_cast<Tegra::Texture::TextureFormat>(new_value)) {
|
||||
surface_format = static_cast<Tegra::Texture::TextureFormat>(new_value);
|
||||
|
||||
surface_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
|
||||
emit Update();
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnSurfacePickerXChanged(int new_value) {
|
||||
if (surface_picker_x != new_value) {
|
||||
surface_picker_x = new_value;
|
||||
Pick(surface_picker_x, surface_picker_y);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnSurfacePickerYChanged(int new_value) {
|
||||
if (surface_picker_y != new_value) {
|
||||
surface_picker_y = new_value;
|
||||
Pick(surface_picker_x, surface_picker_y);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::Pick(int x, int y) {
|
||||
surface_picker_x_control->setValue(x);
|
||||
surface_picker_y_control->setValue(y);
|
||||
|
||||
if (x < 0 || x >= static_cast<int>(surface_width) || y < 0 ||
|
||||
y >= static_cast<int>(surface_height)) {
|
||||
surface_info_label->setText(tr("Pixel out of bounds"));
|
||||
surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
return;
|
||||
}
|
||||
|
||||
surface_info_label->setText(QString("Raw: <Unimplemented>\n(%1)").arg("<Unimplemented>"));
|
||||
surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::OnUpdate() {
|
||||
auto& gpu = Core::System::GetInstance().GPU();
|
||||
|
||||
QPixmap pixmap;
|
||||
|
||||
switch (surface_source) {
|
||||
case Source::RenderTarget0:
|
||||
case Source::RenderTarget1:
|
||||
case Source::RenderTarget2:
|
||||
case Source::RenderTarget3:
|
||||
case Source::RenderTarget4:
|
||||
case Source::RenderTarget5:
|
||||
case Source::RenderTarget6:
|
||||
case Source::RenderTarget7: {
|
||||
// TODO: Store a reference to the registers in the debug context instead of accessing them
|
||||
// directly...
|
||||
|
||||
const auto& registers = gpu.Maxwell3D().regs;
|
||||
const auto& rt = registers.rt[static_cast<std::size_t>(surface_source) -
|
||||
static_cast<std::size_t>(Source::RenderTarget0)];
|
||||
|
||||
surface_address = rt.Address();
|
||||
surface_width = rt.width;
|
||||
surface_height = rt.height;
|
||||
if (rt.format != Tegra::RenderTargetFormat::NONE) {
|
||||
surface_format = ConvertToTextureFormat(rt.format);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Source::Custom: {
|
||||
// Keep user-specified values
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
qDebug() << "Unknown surface source " << static_cast<int>(surface_source);
|
||||
break;
|
||||
}
|
||||
|
||||
surface_address_control->SetValue(surface_address);
|
||||
surface_width_control->setValue(surface_width);
|
||||
surface_height_control->setValue(surface_height);
|
||||
surface_format_control->setCurrentIndex(static_cast<int>(surface_format));
|
||||
|
||||
if (surface_address == 0) {
|
||||
surface_picture_label->hide();
|
||||
surface_info_label->setText(tr("(invalid surface address)"));
|
||||
surface_info_label->setAlignment(Qt::AlignCenter);
|
||||
surface_picker_x_control->setEnabled(false);
|
||||
surface_picker_y_control->setEnabled(false);
|
||||
save_surface->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Implement a good way to visualize alpha components!
|
||||
|
||||
QImage decoded_image(surface_width, surface_height, QImage::Format_ARGB32);
|
||||
|
||||
// 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(
|
||||
gpu.MemoryManager().GetPointer(surface_address), 1, 1,
|
||||
Tegra::Texture::BytesPerPixel(surface_format), surface_width, surface_height, 1U);
|
||||
|
||||
auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format,
|
||||
surface_width, surface_height);
|
||||
|
||||
surface_picture_label->show();
|
||||
|
||||
for (unsigned int y = 0; y < surface_height; ++y) {
|
||||
for (unsigned int x = 0; x < surface_width; ++x) {
|
||||
Common::Vec4<u8> color;
|
||||
color[0] = texture_data[x + y * surface_width + 0];
|
||||
color[1] = texture_data[x + y * surface_width + 1];
|
||||
color[2] = texture_data[x + y * surface_width + 2];
|
||||
color[3] = texture_data[x + y * surface_width + 3];
|
||||
decoded_image.setPixel(x, y, qRgba(color.r(), color.g(), color.b(), color.a()));
|
||||
}
|
||||
}
|
||||
|
||||
pixmap = QPixmap::fromImage(decoded_image);
|
||||
surface_picture_label->setPixmap(pixmap);
|
||||
surface_picture_label->resize(pixmap.size());
|
||||
|
||||
// Update the info with pixel data
|
||||
surface_picker_x_control->setEnabled(true);
|
||||
surface_picker_y_control->setEnabled(true);
|
||||
Pick(surface_picker_x, surface_picker_y);
|
||||
|
||||
// Enable saving the converted pixmap to file
|
||||
save_surface->setEnabled(true);
|
||||
}
|
||||
|
||||
void GraphicsSurfaceWidget::SaveSurface() {
|
||||
QString png_filter = tr("Portable Network Graphic (*.png)");
|
||||
QString bin_filter = tr("Binary data (*.bin)");
|
||||
|
||||
QString selectedFilter;
|
||||
QString filename = QFileDialog::getSaveFileName(
|
||||
this, tr("Save Surface"),
|
||||
QString("texture-0x%1.png").arg(QString::number(surface_address, 16)),
|
||||
QString("%1;;%2").arg(png_filter, bin_filter), &selectedFilter);
|
||||
|
||||
if (filename.isEmpty()) {
|
||||
// If the user canceled the dialog, don't save anything.
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedFilter == png_filter) {
|
||||
const QPixmap* pixmap = surface_picture_label->pixmap();
|
||||
ASSERT_MSG(pixmap != nullptr, "No pixmap set");
|
||||
|
||||
QFile file(filename);
|
||||
file.open(QIODevice::WriteOnly);
|
||||
if (pixmap)
|
||||
pixmap->save(&file, "PNG");
|
||||
} else if (selectedFilter == bin_filter) {
|
||||
auto& gpu = Core::System::GetInstance().GPU();
|
||||
std::optional<VAddr> address = gpu.MemoryManager().GpuToCpuAddress(surface_address);
|
||||
|
||||
const u8* buffer = Memory::GetPointer(*address);
|
||||
ASSERT_MSG(buffer != nullptr, "Memory not accessible");
|
||||
|
||||
QFile file(filename);
|
||||
file.open(QIODevice::WriteOnly);
|
||||
int size = surface_width * surface_height * Tegra::Texture::BytesPerPixel(surface_format);
|
||||
QByteArray data(reinterpret_cast<const char*>(buffer), size);
|
||||
file.write(data);
|
||||
} else {
|
||||
UNREACHABLE_MSG("Unhandled filter selected");
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
#include "yuzu/debugger/graphics/graphics_breakpoint_observer.h"
|
||||
|
||||
class QComboBox;
|
||||
class QSpinBox;
|
||||
class CSpinBox;
|
||||
|
||||
class GraphicsSurfaceWidget;
|
||||
|
||||
class SurfacePicture : public QLabel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SurfacePicture(QWidget* parent = nullptr,
|
||||
GraphicsSurfaceWidget* surface_widget = nullptr);
|
||||
~SurfacePicture() override;
|
||||
|
||||
protected slots:
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
|
||||
private:
|
||||
GraphicsSurfaceWidget* surface_widget;
|
||||
};
|
||||
|
||||
class GraphicsSurfaceWidget : public BreakPointObserverDock {
|
||||
Q_OBJECT
|
||||
|
||||
using Event = Tegra::DebugContext::Event;
|
||||
|
||||
enum class Source {
|
||||
RenderTarget0 = 0,
|
||||
RenderTarget1 = 1,
|
||||
RenderTarget2 = 2,
|
||||
RenderTarget3 = 3,
|
||||
RenderTarget4 = 4,
|
||||
RenderTarget5 = 5,
|
||||
RenderTarget6 = 6,
|
||||
RenderTarget7 = 7,
|
||||
ZBuffer = 8,
|
||||
Custom = 9,
|
||||
};
|
||||
|
||||
public:
|
||||
explicit GraphicsSurfaceWidget(std::shared_ptr<Tegra::DebugContext> debug_context,
|
||||
QWidget* parent = nullptr);
|
||||
void Pick(int x, int y);
|
||||
|
||||
public slots:
|
||||
void OnSurfaceSourceChanged(int new_value);
|
||||
void OnSurfaceAddressChanged(qint64 new_value);
|
||||
void OnSurfaceWidthChanged(int new_value);
|
||||
void OnSurfaceHeightChanged(int new_value);
|
||||
void OnSurfaceFormatChanged(int new_value);
|
||||
void OnSurfacePickerXChanged(int new_value);
|
||||
void OnSurfacePickerYChanged(int new_value);
|
||||
void OnUpdate();
|
||||
|
||||
signals:
|
||||
void Update();
|
||||
|
||||
private:
|
||||
void OnBreakPointHit(Tegra::DebugContext::Event event, void* data) override;
|
||||
void OnResumed() override;
|
||||
|
||||
void SaveSurface();
|
||||
|
||||
QComboBox* surface_source_list;
|
||||
CSpinBox* surface_address_control;
|
||||
QSpinBox* surface_width_control;
|
||||
QSpinBox* surface_height_control;
|
||||
QComboBox* surface_format_control;
|
||||
|
||||
SurfacePicture* surface_picture_label;
|
||||
QSpinBox* surface_picker_x_control;
|
||||
QSpinBox* surface_picker_y_control;
|
||||
QLabel* surface_info_label;
|
||||
QPushButton* save_surface;
|
||||
|
||||
Source surface_source;
|
||||
GPUVAddr surface_address;
|
||||
unsigned surface_width;
|
||||
unsigned surface_height;
|
||||
Tegra::Texture::TextureFormat surface_format;
|
||||
int surface_picker_x = 0;
|
||||
int surface_picker_y = 0;
|
||||
};
|
||||
@@ -90,7 +90,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include "yuzu/configuration/configure_dialog.h"
|
||||
#include "yuzu/debugger/console.h"
|
||||
#include "yuzu/debugger/graphics/graphics_breakpoints.h"
|
||||
#include "yuzu/debugger/graphics/graphics_surface.h"
|
||||
#include "yuzu/debugger/profiler.h"
|
||||
#include "yuzu/debugger/wait_tree.h"
|
||||
#include "yuzu/discord.h"
|
||||
@@ -478,11 +477,6 @@ void GMainWindow::InitializeDebugWidgets() {
|
||||
graphicsBreakpointsWidget->hide();
|
||||
debug_menu->addAction(graphicsBreakpointsWidget->toggleViewAction());
|
||||
|
||||
graphicsSurfaceWidget = new GraphicsSurfaceWidget(debug_context, this);
|
||||
addDockWidget(Qt::RightDockWidgetArea, graphicsSurfaceWidget);
|
||||
graphicsSurfaceWidget->hide();
|
||||
debug_menu->addAction(graphicsSurfaceWidget->toggleViewAction());
|
||||
|
||||
waitTreeWidget = new WaitTreeWidget(this);
|
||||
addDockWidget(Qt::LeftDockWidgetArea, waitTreeWidget);
|
||||
waitTreeWidget->hide();
|
||||
|
||||
@@ -23,7 +23,6 @@ class EmuThread;
|
||||
class GameList;
|
||||
class GImageInfo;
|
||||
class GraphicsBreakPointsWidget;
|
||||
class GraphicsSurfaceWidget;
|
||||
class GRenderWindow;
|
||||
class LoadingScreen;
|
||||
class MicroProfileDialog;
|
||||
@@ -238,7 +237,6 @@ private:
|
||||
ProfilerWidget* profilerWidget;
|
||||
MicroProfileDialog* microProfileDialog;
|
||||
GraphicsBreakPointsWidget* graphicsBreakpointsWidget;
|
||||
GraphicsSurfaceWidget* graphicsSurfaceWidget;
|
||||
WaitTreeWidget* waitTreeWidget;
|
||||
|
||||
QAction* actions_recent_files[max_recent_files_item];
|
||||
|
||||
Reference in New Issue
Block a user