Compare commits
4 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa31decea8 | ||
|
|
26705ba6ad | ||
|
|
6714dd3381 | ||
|
|
ba079516cf |
@@ -8,7 +8,7 @@
|
||||
#include "audio_core/codec.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace AudioCore {
|
||||
@@ -72,7 +72,7 @@ private:
|
||||
EffectInStatus info{};
|
||||
};
|
||||
AudioRenderer::AudioRenderer(AudioRendererParameter params,
|
||||
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event)
|
||||
Kernel::SharedPtr<Kernel::Event> buffer_event)
|
||||
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
|
||||
effects(params.effect_count) {
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
namespace Kernel {
|
||||
class WritableEvent;
|
||||
class Event;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
@@ -208,8 +208,7 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size
|
||||
|
||||
class AudioRenderer {
|
||||
public:
|
||||
AudioRenderer(AudioRendererParameter params,
|
||||
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event);
|
||||
AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
|
||||
~AudioRenderer();
|
||||
|
||||
std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
|
||||
@@ -225,7 +224,7 @@ private:
|
||||
class VoiceState;
|
||||
|
||||
AudioRendererParameter worker_params;
|
||||
Kernel::SharedPtr<Kernel::WritableEvent> buffer_event;
|
||||
Kernel::SharedPtr<Kernel::Event> buffer_event;
|
||||
std::vector<VoiceState> voices;
|
||||
std::vector<EffectState> effects;
|
||||
std::unique_ptr<AudioOut> audio_out;
|
||||
|
||||
@@ -63,10 +63,6 @@ add_library(core STATIC
|
||||
file_sys/sdmc_factory.h
|
||||
file_sys/submission_package.cpp
|
||||
file_sys/submission_package.h
|
||||
file_sys/system_archive/ng_word.cpp
|
||||
file_sys/system_archive/ng_word.h
|
||||
file_sys/system_archive/system_archive.cpp
|
||||
file_sys/system_archive/system_archive.h
|
||||
file_sys/vfs.cpp
|
||||
file_sys/vfs.h
|
||||
file_sys/vfs_concat.cpp
|
||||
@@ -101,6 +97,8 @@ add_library(core STATIC
|
||||
hle/kernel/client_session.cpp
|
||||
hle/kernel/client_session.h
|
||||
hle/kernel/errors.h
|
||||
hle/kernel/event.cpp
|
||||
hle/kernel/event.h
|
||||
hle/kernel/handle_table.cpp
|
||||
hle/kernel/handle_table.h
|
||||
hle/kernel/hle_ipc.cpp
|
||||
@@ -113,8 +111,6 @@ add_library(core STATIC
|
||||
hle/kernel/object.h
|
||||
hle/kernel/process.cpp
|
||||
hle/kernel/process.h
|
||||
hle/kernel/readable_event.cpp
|
||||
hle/kernel/readable_event.h
|
||||
hle/kernel/resource_limit.cpp
|
||||
hle/kernel/resource_limit.h
|
||||
hle/kernel/scheduler.cpp
|
||||
@@ -137,8 +133,6 @@ add_library(core STATIC
|
||||
hle/kernel/vm_manager.h
|
||||
hle/kernel/wait_object.cpp
|
||||
hle/kernel/wait_object.h
|
||||
hle/kernel/writable_event.cpp
|
||||
hle/kernel/writable_event.h
|
||||
hle/lock.cpp
|
||||
hle/lock.h
|
||||
hle/result.h
|
||||
|
||||
@@ -246,6 +246,7 @@ std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
|
||||
}
|
||||
|
||||
std::vector<TicketRaw> out;
|
||||
u32 magic{};
|
||||
for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) {
|
||||
if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 &&
|
||||
buffer[offset + 3] == 0x0) {
|
||||
@@ -793,7 +794,7 @@ void KeyManager::DeriveBase() {
|
||||
|
||||
void KeyManager::DeriveETicket(PartitionDataManager& data) {
|
||||
// ETicket keys
|
||||
const auto es = Service::FileSystem::GetUnionContents().GetEntry(
|
||||
const auto es = Service::FileSystem::GetUnionContents()->GetEntry(
|
||||
0x0100000000000033, FileSys::ContentRecordType::Program);
|
||||
|
||||
if (es == nullptr)
|
||||
|
||||
@@ -75,12 +75,12 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
|
||||
// Game Updates
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
const auto update = installed.GetEntry(update_tid, ContentRecordType::Program);
|
||||
const auto update = installed->GetEntry(update_tid, ContentRecordType::Program);
|
||||
|
||||
if (update != nullptr && update->GetExeFS() != nullptr &&
|
||||
update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
|
||||
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
|
||||
FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0)));
|
||||
FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0)));
|
||||
exefs = update->GetExeFS();
|
||||
}
|
||||
|
||||
@@ -281,13 +281,13 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
|
||||
|
||||
// Game Updates
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
const auto update = installed.GetEntryRaw(update_tid, type);
|
||||
const auto update = installed->GetEntryRaw(update_tid, type);
|
||||
if (update != nullptr) {
|
||||
const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset);
|
||||
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
|
||||
new_nca->GetRomFS() != nullptr) {
|
||||
LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
|
||||
FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0)));
|
||||
FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0)));
|
||||
romfs = new_nca->GetRomFS();
|
||||
}
|
||||
} else if (update_raw != nullptr) {
|
||||
@@ -329,8 +329,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
|
||||
if (nacp != nullptr) {
|
||||
out.insert_or_assign("Update", nacp->GetVersionString());
|
||||
} else {
|
||||
if (installed.HasEntry(update_tid, ContentRecordType::Program)) {
|
||||
const auto meta_ver = installed.GetEntryVersion(update_tid);
|
||||
if (installed->HasEntry(update_tid, ContentRecordType::Program)) {
|
||||
const auto meta_ver = installed->GetEntryVersion(update_tid);
|
||||
if (meta_ver.value_or(0) == 0) {
|
||||
out.insert_or_assign("Update", "");
|
||||
} else {
|
||||
@@ -383,13 +383,14 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
|
||||
}
|
||||
|
||||
// DLC
|
||||
const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
|
||||
const auto dlc_entries = installed->ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
|
||||
std::vector<RegisteredCacheEntry> dlc_match;
|
||||
dlc_match.reserve(dlc_entries.size());
|
||||
std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
|
||||
[this, &installed](const RegisteredCacheEntry& entry) {
|
||||
return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id &&
|
||||
installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
|
||||
installed->GetEntry(entry)->GetStatus() ==
|
||||
Loader::ResultStatus::Success;
|
||||
});
|
||||
if (!dlc_match.empty()) {
|
||||
// Ensure sorted so DLC IDs show in order.
|
||||
@@ -410,7 +411,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
|
||||
std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
|
||||
const auto installed{Service::FileSystem::GetUnionContents()};
|
||||
|
||||
const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control);
|
||||
const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
|
||||
if (base_control_nca == nullptr)
|
||||
return {};
|
||||
|
||||
|
||||
@@ -63,6 +63,10 @@ u32 ProgramMetadata::GetMainThreadStackSize() const {
|
||||
return npdm_header.main_stack_size;
|
||||
}
|
||||
|
||||
u32 ProgramMetadata::GetSystemResourceSize() const {
|
||||
return npdm_header.system_resource_size;
|
||||
}
|
||||
|
||||
u64 ProgramMetadata::GetTitleID() const {
|
||||
return aci_header.title_id;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
u32 GetMainThreadStackSize() const;
|
||||
u64 GetTitleID() const;
|
||||
u64 GetFilesystemPermissions() const;
|
||||
u32 GetSystemResourceSize() const;
|
||||
|
||||
void Print() const;
|
||||
|
||||
@@ -68,7 +69,8 @@ private:
|
||||
u8 reserved_3;
|
||||
u8 main_thread_priority;
|
||||
u8 main_thread_cpu;
|
||||
std::array<u8, 8> reserved_4;
|
||||
std::array<u8, 4> reserved_4;
|
||||
u32 system_resource_size;
|
||||
u32_le process_category;
|
||||
u32_le main_stack_size;
|
||||
std::array<u8, 0x10> application_name;
|
||||
|
||||
@@ -107,41 +107,42 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
|
||||
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
|
||||
std::string_view path) const {
|
||||
const auto file = dir->GetFileRelative(path);
|
||||
if (file != nullptr) {
|
||||
if (file != nullptr)
|
||||
return file;
|
||||
}
|
||||
|
||||
const auto nca_dir = dir->GetDirectoryRelative(path);
|
||||
if (nca_dir == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (nca_dir != nullptr) {
|
||||
const auto nca_dir = dir->GetDirectoryRelative(path);
|
||||
VirtualFile file = nullptr;
|
||||
|
||||
const auto files = nca_dir->GetFiles();
|
||||
if (files.size() == 1 && files[0]->GetName() == "00") {
|
||||
return files[0];
|
||||
}
|
||||
|
||||
std::vector<VirtualFile> concat;
|
||||
// Since the files are a two-digit hex number, max is FF.
|
||||
for (std::size_t i = 0; i < 0x100; ++i) {
|
||||
auto next = nca_dir->GetFile(fmt::format("{:02X}", i));
|
||||
if (next != nullptr) {
|
||||
concat.push_back(std::move(next));
|
||||
const auto files = nca_dir->GetFiles();
|
||||
if (files.size() == 1 && files[0]->GetName() == "00") {
|
||||
file = files[0];
|
||||
} else {
|
||||
next = nca_dir->GetFile(fmt::format("{:02x}", i));
|
||||
if (next != nullptr) {
|
||||
concat.push_back(std::move(next));
|
||||
} else {
|
||||
break;
|
||||
std::vector<VirtualFile> concat;
|
||||
// Since the files are a two-digit hex number, max is FF.
|
||||
for (std::size_t i = 0; i < 0x100; ++i) {
|
||||
auto next = nca_dir->GetFile(fmt::format("{:02X}", i));
|
||||
if (next != nullptr) {
|
||||
concat.push_back(std::move(next));
|
||||
} else {
|
||||
next = nca_dir->GetFile(fmt::format("{:02x}", i));
|
||||
if (next != nullptr)
|
||||
concat.push_back(std::move(next));
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (concat.empty())
|
||||
return nullptr;
|
||||
|
||||
file = ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
if (concat.empty()) {
|
||||
return nullptr;
|
||||
return file;
|
||||
}
|
||||
|
||||
return ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
||||
|
||||
@@ -48,7 +48,7 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte
|
||||
|
||||
switch (storage) {
|
||||
case StorageId::None:
|
||||
res = Service::FileSystem::GetUnionContents().GetEntry(title_id, type);
|
||||
res = Service::FileSystem::GetUnionContents()->GetEntry(title_id, type);
|
||||
break;
|
||||
case StorageId::NandSystem:
|
||||
res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type);
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/system_archive/ng_word.h"
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
|
||||
namespace FileSys::SystemArchive {
|
||||
|
||||
namespace NgWord1Data {
|
||||
|
||||
constexpr std::size_t NUMBER_WORD_TXT_FILES = 0x10;
|
||||
|
||||
// Should this archive replacement mysteriously not work on a future game, consider updating.
|
||||
constexpr std::array<u8, 4> VERSION_DAT{0x0, 0x0, 0x0, 0x19}; // 5.1.0 System Version
|
||||
|
||||
constexpr std::array<u8, 30> WORD_TXT{
|
||||
0xFE, 0xFF, 0x00, 0x5E, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x79, 0x00, 0x62, 0x00,
|
||||
0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0A,
|
||||
}; // "^verybadword$" in UTF-16
|
||||
|
||||
} // namespace NgWord1Data
|
||||
|
||||
VirtualDir NgWord1() {
|
||||
std::vector<VirtualFile> files(NgWord1Data::NUMBER_WORD_TXT_FILES);
|
||||
|
||||
for (std::size_t i = 0; i < NgWord1Data::NUMBER_WORD_TXT_FILES; ++i) {
|
||||
files[i] = std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>(
|
||||
NgWord1Data::WORD_TXT, fmt::format("{}.txt", i));
|
||||
}
|
||||
|
||||
files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>(
|
||||
NgWord1Data::WORD_TXT, "common.txt"));
|
||||
files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::VERSION_DAT.size()>>(
|
||||
NgWord1Data::VERSION_DAT, "version.dat"));
|
||||
|
||||
return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data");
|
||||
}
|
||||
|
||||
} // namespace FileSys::SystemArchive
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
|
||||
namespace FileSys::SystemArchive {
|
||||
|
||||
VirtualDir NgWord1();
|
||||
|
||||
} // namespace FileSys::SystemArchive
|
||||
@@ -1,91 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <functional>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/system_archive/ng_word.h"
|
||||
#include "core/file_sys/system_archive/system_archive.h"
|
||||
|
||||
namespace FileSys::SystemArchive {
|
||||
|
||||
constexpr u64 SYSTEM_ARCHIVE_BASE_TITLE_ID = 0x0100000000000800;
|
||||
constexpr std::size_t SYSTEM_ARCHIVE_COUNT = 0x28;
|
||||
|
||||
using SystemArchiveSupplier = std::function<VirtualDir()>;
|
||||
|
||||
struct SystemArchiveDescriptor {
|
||||
u64 title_id;
|
||||
const char* name;
|
||||
SystemArchiveSupplier supplier;
|
||||
};
|
||||
|
||||
const std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHIVES = {{
|
||||
{0x0100000000000800, "CertStore", nullptr},
|
||||
{0x0100000000000801, "ErrorMessage", nullptr},
|
||||
{0x0100000000000802, "MiiModel", nullptr},
|
||||
{0x0100000000000803, "BrowserDll", nullptr},
|
||||
{0x0100000000000804, "Help", nullptr},
|
||||
{0x0100000000000805, "SharedFont", nullptr},
|
||||
{0x0100000000000806, "NgWord", &NgWord1},
|
||||
{0x0100000000000807, "SsidList", nullptr},
|
||||
{0x0100000000000808, "Dictionary", nullptr},
|
||||
{0x0100000000000809, "SystemVersion", nullptr},
|
||||
{0x010000000000080A, "AvatarImage", nullptr},
|
||||
{0x010000000000080B, "LocalNews", nullptr},
|
||||
{0x010000000000080C, "Eula", nullptr},
|
||||
{0x010000000000080D, "UrlBlackList", nullptr},
|
||||
{0x010000000000080E, "TimeZoneBinary", nullptr},
|
||||
{0x010000000000080F, "CertStoreCruiser", nullptr},
|
||||
{0x0100000000000810, "FontNintendoExtension", nullptr},
|
||||
{0x0100000000000811, "FontStandard", nullptr},
|
||||
{0x0100000000000812, "FontKorean", nullptr},
|
||||
{0x0100000000000813, "FontChineseTraditional", nullptr},
|
||||
{0x0100000000000814, "FontChineseSimple", nullptr},
|
||||
{0x0100000000000815, "FontBfcpx", nullptr},
|
||||
{0x0100000000000816, "SystemUpdate", nullptr},
|
||||
{0x0100000000000817, "0100000000000817", nullptr},
|
||||
{0x0100000000000818, "FirmwareDebugSettings", nullptr},
|
||||
{0x0100000000000819, "BootImagePackage", nullptr},
|
||||
{0x010000000000081A, "BootImagePackageSafe", nullptr},
|
||||
{0x010000000000081B, "BootImagePackageExFat", nullptr},
|
||||
{0x010000000000081C, "BootImagePackageExFatSafe", nullptr},
|
||||
{0x010000000000081D, "FatalMessage", nullptr},
|
||||
{0x010000000000081E, "ControllerIcon", nullptr},
|
||||
{0x010000000000081F, "PlatformConfigIcosa", nullptr},
|
||||
{0x0100000000000820, "PlatformConfigCopper", nullptr},
|
||||
{0x0100000000000821, "PlatformConfigHoag", nullptr},
|
||||
{0x0100000000000822, "ControllerFirmware", nullptr},
|
||||
{0x0100000000000823, "NgWord2", nullptr},
|
||||
{0x0100000000000824, "PlatformConfigIcosaMariko", nullptr},
|
||||
{0x0100000000000825, "ApplicationBlackList", nullptr},
|
||||
{0x0100000000000826, "RebootlessSystemUpdateVersion", nullptr},
|
||||
{0x0100000000000827, "ContentActionTable", nullptr},
|
||||
}};
|
||||
|
||||
VirtualFile SynthesizeSystemArchive(const u64 title_id) {
|
||||
if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id)
|
||||
return nullptr;
|
||||
|
||||
const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID];
|
||||
|
||||
LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id);
|
||||
|
||||
if (desc.supplier == nullptr)
|
||||
return nullptr;
|
||||
|
||||
const auto dir = desc.supplier();
|
||||
|
||||
if (dir == nullptr)
|
||||
return nullptr;
|
||||
|
||||
const auto romfs = CreateRomFS(dir);
|
||||
|
||||
if (romfs == nullptr)
|
||||
return nullptr;
|
||||
|
||||
LOG_INFO(Service_FS, " - System archive generation successful!");
|
||||
return romfs;
|
||||
}
|
||||
} // namespace FileSys::SystemArchive
|
||||
@@ -1,14 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs_types.h"
|
||||
|
||||
namespace FileSys::SystemArchive {
|
||||
|
||||
VirtualFile SynthesizeSystemArchive(u64 title_id);
|
||||
|
||||
} // namespace FileSys::SystemArchive
|
||||
@@ -384,28 +384,6 @@ bool VfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
|
||||
return success;
|
||||
}
|
||||
|
||||
bool VfsDirectory::CleanSubdirectoryRecursive(std::string_view name) {
|
||||
auto dir = GetSubdirectory(name);
|
||||
if (dir == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
for (const auto& file : dir->GetFiles()) {
|
||||
if (!dir->DeleteFile(file->GetName())) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& sdir : dir->GetSubdirectories()) {
|
||||
if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
|
||||
const auto f1 = GetFile(src);
|
||||
auto f2 = CreateFile(dest);
|
||||
@@ -453,34 +431,10 @@ std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(std::string_view name)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFileAbsolute(std::string_view path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFileRelative(std::string_view path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateDirectoryAbsolute(std::string_view path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateDirectoryRelative(std::string_view path) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectory::DeleteSubdirectory(std::string_view name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectory::CleanSubdirectoryRecursive(std::string_view name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -245,18 +245,12 @@ public:
|
||||
// any failure.
|
||||
virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(std::string_view path);
|
||||
|
||||
// Deletes the subdirectory with the given name and returns true on success.
|
||||
// Deletes the subdirectory with name and returns true on success.
|
||||
virtual bool DeleteSubdirectory(std::string_view name) = 0;
|
||||
|
||||
// Deletes all subdirectories and files within the provided directory and then deletes
|
||||
// the directory itself. Returns true on success.
|
||||
// Deletes all subdirectories and files of subdirectory with name recirsively and then deletes
|
||||
// the subdirectory. Returns true on success.
|
||||
virtual bool DeleteSubdirectoryRecursive(std::string_view name);
|
||||
|
||||
// Deletes all subdirectories and files within the provided directory.
|
||||
// Unlike DeleteSubdirectoryRecursive, this does not delete the provided directory.
|
||||
virtual bool CleanSubdirectoryRecursive(std::string_view name);
|
||||
|
||||
// Returns whether or not the file with name name was deleted successfully.
|
||||
// Returnes whether or not the file with name name was deleted successfully.
|
||||
virtual bool DeleteFile(std::string_view name) = 0;
|
||||
|
||||
// Returns whether or not this directory was renamed to name.
|
||||
@@ -282,13 +276,7 @@ public:
|
||||
bool IsReadable() const override;
|
||||
std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
|
||||
std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
|
||||
std::shared_ptr<VfsFile> CreateFileAbsolute(std::string_view path) override;
|
||||
std::shared_ptr<VfsFile> CreateFileRelative(std::string_view path) override;
|
||||
std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(std::string_view path) override;
|
||||
std::shared_ptr<VfsDirectory> CreateDirectoryRelative(std::string_view path) override;
|
||||
bool DeleteSubdirectory(std::string_view name) override;
|
||||
bool DeleteSubdirectoryRecursive(std::string_view name) override;
|
||||
bool CleanSubdirectoryRecursive(std::string_view name) override;
|
||||
bool DeleteFile(std::string_view name) override;
|
||||
bool Rename(std::string_view name) override;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
|
||||
|
||||
@@ -4,63 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
// An implementation of VfsFile that is backed by a statically-sized array
|
||||
template <std::size_t size>
|
||||
class ArrayVfsFile : public VfsFile {
|
||||
public:
|
||||
ArrayVfsFile(std::array<u8, size> data, std::string name = "", VirtualDir parent = nullptr)
|
||||
: data(data), name(std::move(name)), parent(std::move(parent)) {}
|
||||
|
||||
std::string GetName() const override {
|
||||
return name;
|
||||
}
|
||||
|
||||
std::size_t GetSize() const override {
|
||||
return size;
|
||||
}
|
||||
|
||||
bool Resize(std::size_t new_size) override {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override {
|
||||
return parent;
|
||||
}
|
||||
|
||||
bool IsWritable() const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsReadable() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t Read(u8* data_, std::size_t length, std::size_t offset) const override {
|
||||
const auto read = std::min(length, size - offset);
|
||||
std::memcpy(data_, data.data() + offset, read);
|
||||
return read;
|
||||
}
|
||||
|
||||
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Rename(std::string_view name) override {
|
||||
this->name = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<u8, size> data;
|
||||
std::string name;
|
||||
VirtualDir parent;
|
||||
};
|
||||
|
||||
// An implementation of VfsFile that is backed by a vector optionally supplied upon construction
|
||||
class VectorVfsFile : public VfsFile {
|
||||
public:
|
||||
|
||||
@@ -4,37 +4,46 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/assert.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ReadableEvent::ReadableEvent(KernelCore& kernel) : WaitObject{kernel} {}
|
||||
ReadableEvent::~ReadableEvent() = default;
|
||||
Event::Event(KernelCore& kernel) : WaitObject{kernel} {}
|
||||
Event::~Event() = default;
|
||||
|
||||
bool ReadableEvent::ShouldWait(Thread* thread) const {
|
||||
SharedPtr<Event> Event::Create(KernelCore& kernel, ResetType reset_type, std::string name) {
|
||||
SharedPtr<Event> evt(new Event(kernel));
|
||||
|
||||
evt->signaled = false;
|
||||
evt->reset_type = reset_type;
|
||||
evt->name = std::move(name);
|
||||
|
||||
return evt;
|
||||
}
|
||||
|
||||
bool Event::ShouldWait(Thread* thread) const {
|
||||
return !signaled;
|
||||
}
|
||||
|
||||
void ReadableEvent::Acquire(Thread* thread) {
|
||||
void Event::Acquire(Thread* thread) {
|
||||
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
|
||||
|
||||
if (reset_type == ResetType::OneShot)
|
||||
signaled = false;
|
||||
}
|
||||
|
||||
void ReadableEvent::Signal() {
|
||||
void Event::Signal() {
|
||||
signaled = true;
|
||||
WakeupAllWaitingThreads();
|
||||
}
|
||||
|
||||
void ReadableEvent::Clear() {
|
||||
void Event::Clear() {
|
||||
signaled = false;
|
||||
}
|
||||
|
||||
void ReadableEvent::WakeupAllWaitingThreads() {
|
||||
void Event::WakeupAllWaitingThreads() {
|
||||
WaitObject::WakeupAllWaitingThreads();
|
||||
|
||||
if (reset_type == ResetType::Pulse)
|
||||
@@ -11,52 +11,49 @@
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
class ReadableEvent;
|
||||
class WritableEvent;
|
||||
|
||||
struct EventPair {
|
||||
SharedPtr<ReadableEvent> readable;
|
||||
SharedPtr<WritableEvent> writable;
|
||||
};
|
||||
|
||||
class WritableEvent final : public Object {
|
||||
class Event final : public WaitObject {
|
||||
public:
|
||||
~WritableEvent() override;
|
||||
|
||||
/**
|
||||
* Creates an event
|
||||
* @param kernel The kernel instance to create this event under.
|
||||
* @param reset_type ResetType describing how to create event
|
||||
* @param name Optional name of event
|
||||
*/
|
||||
static EventPair CreateEventPair(KernelCore& kernel, ResetType reset_type,
|
||||
std::string name = "Unknown");
|
||||
static SharedPtr<Event> Create(KernelCore& kernel, ResetType reset_type,
|
||||
std::string name = "Unknown");
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "WritableEvent";
|
||||
return "Event";
|
||||
}
|
||||
std::string GetName() const override {
|
||||
return name;
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::WritableEvent;
|
||||
static const HandleType HANDLE_TYPE = HandleType::Event;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
SharedPtr<ReadableEvent> GetReadableEvent() const;
|
||||
ResetType GetResetType() const {
|
||||
return reset_type;
|
||||
}
|
||||
|
||||
ResetType GetResetType() const;
|
||||
bool ShouldWait(Thread* thread) const override;
|
||||
void Acquire(Thread* thread) override;
|
||||
|
||||
void WakeupAllWaitingThreads() override;
|
||||
|
||||
void Signal();
|
||||
void Clear();
|
||||
bool IsSignaled() const;
|
||||
|
||||
private:
|
||||
explicit WritableEvent(KernelCore& kernel);
|
||||
explicit Event(KernelCore& kernel);
|
||||
~Event() override;
|
||||
|
||||
SharedPtr<ReadableEvent> readable;
|
||||
ResetType reset_type; ///< Current ResetType
|
||||
|
||||
bool signaled; ///< Whether the event has already been signaled
|
||||
std::string name; ///< Name of event (optional)
|
||||
};
|
||||
|
||||
@@ -42,10 +42,9 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
|
||||
u16 generation = next_generation++;
|
||||
|
||||
// Overflow count so it fits in the 15 bits dedicated to the generation in the handle.
|
||||
// Horizon OS uses zero to represent an invalid handle, so skip to 1.
|
||||
if (next_generation >= (1 << 15)) {
|
||||
// CTR-OS doesn't use generation 0, so skip straight to 1.
|
||||
if (next_generation >= (1 << 15))
|
||||
next_generation = 1;
|
||||
}
|
||||
|
||||
generations[slot] = generation;
|
||||
objects[slot] = std::move(obj);
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
namespace Kernel {
|
||||
|
||||
enum KernelHandle : Handle {
|
||||
InvalidHandle = 0,
|
||||
CurrentThread = 0xFFFF8000,
|
||||
CurrentProcess = 0xFFFF8001,
|
||||
};
|
||||
|
||||
@@ -15,14 +15,13 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -37,9 +36,11 @@ void SessionRequestHandler::ClientDisconnected(const SharedPtr<ServerSession>& s
|
||||
boost::range::remove_erase(connected_sessions, server_session);
|
||||
}
|
||||
|
||||
SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
|
||||
SharedPtr<Thread> thread, const std::string& reason, u64 timeout, WakeupCallback&& callback,
|
||||
SharedPtr<WritableEvent> writable_event) {
|
||||
SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
|
||||
const std::string& reason, u64 timeout,
|
||||
WakeupCallback&& callback,
|
||||
Kernel::SharedPtr<Kernel::Event> event) {
|
||||
|
||||
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
|
||||
thread->SetWakeupCallback([context = *this, callback](
|
||||
ThreadWakeupReason reason, SharedPtr<Thread> thread,
|
||||
@@ -50,25 +51,23 @@ SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
|
||||
return true;
|
||||
});
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
if (!writable_event) {
|
||||
if (!event) {
|
||||
// Create event if not provided
|
||||
const auto pair = WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
"HLE Pause Event: " + reason);
|
||||
writable_event = pair.writable;
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
|
||||
}
|
||||
|
||||
const auto readable_event{writable_event->GetReadableEvent()};
|
||||
writable_event->Clear();
|
||||
event->Clear();
|
||||
thread->SetStatus(ThreadStatus::WaitHLEEvent);
|
||||
thread->SetWaitObjects({readable_event});
|
||||
readable_event->AddWaitingThread(thread);
|
||||
thread->SetWaitObjects({event});
|
||||
event->AddWaitingThread(thread);
|
||||
|
||||
if (timeout > 0) {
|
||||
thread->WakeAfterDelay(timeout);
|
||||
}
|
||||
|
||||
return writable_event;
|
||||
return event;
|
||||
}
|
||||
|
||||
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
|
||||
|
||||
@@ -24,11 +24,10 @@ class ServiceFrameworkBase;
|
||||
namespace Kernel {
|
||||
|
||||
class Domain;
|
||||
class Event;
|
||||
class HandleTable;
|
||||
class HLERequestContext;
|
||||
class Process;
|
||||
class ReadableEvent;
|
||||
class WritableEvent;
|
||||
|
||||
/**
|
||||
* Interface implemented by HLE Session handlers.
|
||||
@@ -120,13 +119,12 @@ public:
|
||||
* @param callback Callback to be invoked when the thread is resumed. This callback must write
|
||||
* the entire command response once again, regardless of the state of it before this function
|
||||
* was called.
|
||||
* @param writable_event Event to use to wake up the thread. If unspecified, an event will be
|
||||
* created.
|
||||
* @param event Event to use to wake up the thread. If unspecified, an event will be created.
|
||||
* @returns Event that when signaled will resume the thread and call the callback function.
|
||||
*/
|
||||
SharedPtr<WritableEvent> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
|
||||
u64 timeout, WakeupCallback&& callback,
|
||||
SharedPtr<WritableEvent> writable_event = nullptr);
|
||||
SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
|
||||
u64 timeout, WakeupCallback&& callback,
|
||||
Kernel::SharedPtr<Kernel::Event> event = nullptr);
|
||||
|
||||
/// Populates this context with data from the requesting process/thread.
|
||||
ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
|
||||
|
||||
@@ -13,7 +13,7 @@ Object::~Object() = default;
|
||||
|
||||
bool Object::IsWaitable() const {
|
||||
switch (GetHandleType()) {
|
||||
case HandleType::ReadableEvent:
|
||||
case HandleType::Event:
|
||||
case HandleType::Thread:
|
||||
case HandleType::Timer:
|
||||
case HandleType::ServerPort:
|
||||
@@ -21,7 +21,6 @@ bool Object::IsWaitable() const {
|
||||
return true;
|
||||
|
||||
case HandleType::Unknown:
|
||||
case HandleType::WritableEvent:
|
||||
case HandleType::SharedMemory:
|
||||
case HandleType::Process:
|
||||
case HandleType::AddressArbiter:
|
||||
|
||||
@@ -19,8 +19,7 @@ using Handle = u32;
|
||||
|
||||
enum class HandleType : u32 {
|
||||
Unknown,
|
||||
WritableEvent,
|
||||
ReadableEvent,
|
||||
Event,
|
||||
SharedMemory,
|
||||
Thread,
|
||||
Process,
|
||||
@@ -34,9 +33,9 @@ enum class HandleType : u32 {
|
||||
};
|
||||
|
||||
enum class ResetType {
|
||||
OneShot, ///< Reset automatically on object acquisition
|
||||
Sticky, ///< Never reset automatically
|
||||
Pulse, ///< Reset automatically on wakeup
|
||||
OneShot,
|
||||
Sticky,
|
||||
Pulse,
|
||||
};
|
||||
|
||||
class Object : NonCopyable {
|
||||
|
||||
@@ -44,13 +44,9 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
|
||||
return process;
|
||||
}
|
||||
|
||||
SharedPtr<ResourceLimit> Process::GetResourceLimit() const {
|
||||
return resource_limit;
|
||||
}
|
||||
|
||||
void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
|
||||
system_resource_size = metadata.GetSystemResourceSize();
|
||||
program_id = metadata.GetTitleID();
|
||||
ideal_processor = metadata.GetMainThreadCore();
|
||||
is_64bit_process = metadata.Is64BitProgram();
|
||||
vm_manager.Reset(metadata.GetAddressSpaceType());
|
||||
}
|
||||
|
||||
@@ -170,8 +170,21 @@ public:
|
||||
return program_id;
|
||||
}
|
||||
|
||||
/// Gets the extra system resources we can allocate which is typically passed to
|
||||
/// MapPhysicalMemory
|
||||
u32 GetSystemResourceSize() const {
|
||||
return system_resource_size;
|
||||
}
|
||||
|
||||
/// Gets the resource limit descriptor for this process
|
||||
SharedPtr<ResourceLimit> GetResourceLimit() const;
|
||||
ResourceLimit& GetResourceLimit() {
|
||||
return *resource_limit;
|
||||
}
|
||||
|
||||
/// Gets the resource limit descriptor for this process
|
||||
const ResourceLimit& GetResourceLimit() const {
|
||||
return *resource_limit;
|
||||
}
|
||||
|
||||
/// Gets the default CPU ID for this process
|
||||
u8 GetDefaultProcessorID() const {
|
||||
@@ -272,6 +285,9 @@ private:
|
||||
/// Title ID corresponding to the process
|
||||
u64 program_id;
|
||||
|
||||
/// The extra system resources we can allocate which is typically passed to MapPhysicalMemory
|
||||
u32 system_resource_size = 0;
|
||||
|
||||
/// Resource limit descriptor for this process
|
||||
SharedPtr<ResourceLimit> resource_limit;
|
||||
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
class WritableEvent;
|
||||
|
||||
class ReadableEvent final : public WaitObject {
|
||||
friend class WritableEvent;
|
||||
|
||||
public:
|
||||
~ReadableEvent() override;
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "ReadableEvent";
|
||||
}
|
||||
std::string GetName() const override {
|
||||
return name;
|
||||
}
|
||||
|
||||
ResetType GetResetType() const {
|
||||
return reset_type;
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::ReadableEvent;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
bool ShouldWait(Thread* thread) const override;
|
||||
void Acquire(Thread* thread) override;
|
||||
|
||||
void WakeupAllWaitingThreads() override;
|
||||
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
explicit ReadableEvent(KernelCore& kernel);
|
||||
|
||||
void Signal();
|
||||
|
||||
ResetType reset_type;
|
||||
bool signaled;
|
||||
|
||||
std::string name; ///< Name of event (optional)
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -20,49 +20,24 @@
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/mutex.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/hle/kernel/svc_wrap.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Kernel {
|
||||
namespace {
|
||||
|
||||
// Checks if address + size is greater than the given address
|
||||
// This can return false if the size causes an overflow of a 64-bit type
|
||||
// or if the given size is zero.
|
||||
constexpr bool IsValidAddressRange(VAddr address, u64 size) {
|
||||
return address + size > address;
|
||||
}
|
||||
|
||||
// Checks if a given address range lies within a larger address range.
|
||||
constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
|
||||
VAddr address_range_end) {
|
||||
const VAddr end_address = address + size - 1;
|
||||
return address_range_begin <= address && end_address <= address_range_end - 1;
|
||||
}
|
||||
|
||||
bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) {
|
||||
return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(),
|
||||
vm.GetAddressSpaceEndAddress());
|
||||
}
|
||||
|
||||
bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
|
||||
return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(),
|
||||
vm.GetNewMapRegionEndAddress());
|
||||
}
|
||||
|
||||
// 8 GiB
|
||||
constexpr u64 MAIN_MEMORY_SIZE = 0x200000000;
|
||||
|
||||
@@ -104,16 +79,16 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
if (!IsInsideAddressSpace(vm_manager, src_addr, size)) {
|
||||
if (!vm_manager.IsInsideAddressSpace(src_addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
|
||||
"Source is not inside the address space, addr=0x{:016X}, size=0x{:016X}",
|
||||
src_addr, size);
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) {
|
||||
if (!vm_manager.IsInsideNewMapRegion(dst_addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Destination is not within the new map region, addr=0x{:016X}, size=0x{:016X}",
|
||||
"Destination is not inside the new map region, addr=0x{:016X}, size=0x{:016X}",
|
||||
dst_addr, size);
|
||||
return ERR_INVALID_MEMORY_RANGE;
|
||||
}
|
||||
@@ -139,6 +114,7 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
enum class ResourceLimitValueType {
|
||||
CurrentValue,
|
||||
@@ -171,7 +147,6 @@ ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_ty
|
||||
|
||||
return MakeResult(resource_limit_object->GetMaxResourceValue(type));
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
/// Set the process heap to a given Size. It can both extend and shrink the heap.
|
||||
static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
|
||||
@@ -231,7 +206,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
auto& vm_manager = current_process->VMManager();
|
||||
|
||||
if (!IsInsideAddressSpace(vm_manager, addr, size)) {
|
||||
if (!vm_manager.IsInsideAddressSpace(addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
|
||||
size);
|
||||
@@ -245,8 +220,8 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
|
||||
}
|
||||
|
||||
LOG_WARNING(Kernel_SVC, "Uniformity check on protected memory is not implemented.");
|
||||
// TODO: Performs a uniformity check to make sure only protected memory is changed (it doesn't
|
||||
// make sense to allow changing permissions on kernel memory itself, etc).
|
||||
// TODO: Performs a uniformity check to make sure only protected memory is changed (it
|
||||
// doesn't make sense to allow changing permissions on kernel memory itself, etc).
|
||||
|
||||
const auto converted_permissions = SharedMemory::ConvertPermissions(permission);
|
||||
|
||||
@@ -620,10 +595,10 @@ static void Break(u32 reason, u64 info1, u64 info2) {
|
||||
}
|
||||
|
||||
if (!break_reason.signal_debugger) {
|
||||
LOG_CRITICAL(
|
||||
Debug_Emulated,
|
||||
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
|
||||
reason, info1, info2);
|
||||
LOG_CRITICAL(Debug_Emulated,
|
||||
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, "
|
||||
"info2=0x{:016X}",
|
||||
reason, info1, info2);
|
||||
handle_debug_buffer(info1, info2);
|
||||
ASSERT(false);
|
||||
|
||||
@@ -662,7 +637,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
TotalMemoryUsage = 6,
|
||||
TotalHeapUsage = 7,
|
||||
IsCurrentProcessBeingDebugged = 8,
|
||||
RegisterResourceLimit = 9,
|
||||
ResourceHandleLimit = 9,
|
||||
IdleTickCount = 10,
|
||||
RandomEntropy = 11,
|
||||
PerformanceCounter = 0xF0000002,
|
||||
@@ -672,7 +647,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
NewMapRegionBaseAddr = 14,
|
||||
NewMapRegionSize = 15,
|
||||
// 3.0.0+
|
||||
IsVirtualAddressMemoryEnabled = 16,
|
||||
SystemResourceSize = 16,
|
||||
PersonalMmHeapUsage = 17,
|
||||
TitleId = 18,
|
||||
// 4.0.0+
|
||||
@@ -682,137 +657,37 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
ThreadTickCount = 0xF0000002,
|
||||
};
|
||||
|
||||
const auto info_id_type = static_cast<GetInfoType>(info_id);
|
||||
const auto* current_process = Core::CurrentProcess();
|
||||
const auto& vm_manager = current_process->VMManager();
|
||||
|
||||
switch (info_id_type) {
|
||||
switch (static_cast<GetInfoType>(info_id)) {
|
||||
case GetInfoType::AllowedCpuIdBitmask:
|
||||
*result = current_process->GetAllowedProcessorMask();
|
||||
break;
|
||||
case GetInfoType::AllowedThreadPrioBitmask:
|
||||
*result = current_process->GetAllowedThreadPriorityMask();
|
||||
break;
|
||||
case GetInfoType::MapRegionBaseAddr:
|
||||
*result = vm_manager.GetMapRegionBaseAddress();
|
||||
break;
|
||||
case GetInfoType::MapRegionSize:
|
||||
*result = vm_manager.GetMapRegionSize();
|
||||
break;
|
||||
case GetInfoType::HeapRegionBaseAddr:
|
||||
*result = vm_manager.GetHeapRegionBaseAddress();
|
||||
break;
|
||||
case GetInfoType::HeapRegionSize:
|
||||
case GetInfoType::ASLRRegionBaseAddr:
|
||||
case GetInfoType::ASLRRegionSize:
|
||||
case GetInfoType::NewMapRegionBaseAddr:
|
||||
case GetInfoType::NewMapRegionSize:
|
||||
*result = vm_manager.GetHeapRegionSize();
|
||||
break;
|
||||
case GetInfoType::TotalMemoryUsage:
|
||||
*result = vm_manager.GetTotalMemoryUsage();
|
||||
break;
|
||||
case GetInfoType::TotalHeapUsage:
|
||||
case GetInfoType::IsVirtualAddressMemoryEnabled:
|
||||
case GetInfoType::PersonalMmHeapUsage:
|
||||
case GetInfoType::TitleId:
|
||||
case GetInfoType::UserExceptionContextAddr: {
|
||||
if (info_sub_id != 0) {
|
||||
return ERR_INVALID_ENUM_VALUE;
|
||||
}
|
||||
|
||||
const auto& current_process_handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
const auto process = current_process_handle_table.Get<Process>(static_cast<Handle>(handle));
|
||||
if (!process) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
switch (info_id_type) {
|
||||
case GetInfoType::AllowedCpuIdBitmask:
|
||||
*result = process->GetAllowedProcessorMask();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::AllowedThreadPrioBitmask:
|
||||
*result = process->GetAllowedThreadPriorityMask();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::MapRegionBaseAddr:
|
||||
*result = process->VMManager().GetMapRegionBaseAddress();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::MapRegionSize:
|
||||
*result = process->VMManager().GetMapRegionSize();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::HeapRegionBaseAddr:
|
||||
*result = process->VMManager().GetHeapRegionBaseAddress();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::HeapRegionSize:
|
||||
*result = process->VMManager().GetHeapRegionSize();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::ASLRRegionBaseAddr:
|
||||
*result = process->VMManager().GetASLRRegionBaseAddress();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::ASLRRegionSize:
|
||||
*result = process->VMManager().GetASLRRegionSize();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::NewMapRegionBaseAddr:
|
||||
*result = process->VMManager().GetNewMapRegionBaseAddress();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::NewMapRegionSize:
|
||||
*result = process->VMManager().GetNewMapRegionSize();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::TotalMemoryUsage:
|
||||
*result = process->VMManager().GetTotalMemoryUsage();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::TotalHeapUsage:
|
||||
*result = process->VMManager().GetTotalHeapUsage();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::IsVirtualAddressMemoryEnabled:
|
||||
*result = process->IsVirtualMemoryEnabled();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::TitleId:
|
||||
*result = process->GetTitleID();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::UserExceptionContextAddr:
|
||||
LOG_WARNING(Kernel_SVC,
|
||||
"(STUBBED) Attempted to query user exception context address, returned 0");
|
||||
*result = 0;
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) Unimplemented svcGetInfo id=0x{:016X}", info_id);
|
||||
return ERR_INVALID_ENUM_VALUE;
|
||||
}
|
||||
|
||||
*result = vm_manager.GetTotalHeapUsage();
|
||||
break;
|
||||
case GetInfoType::IsCurrentProcessBeingDebugged:
|
||||
*result = 0;
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::RegisterResourceLimit: {
|
||||
if (handle != 0) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (info_sub_id != 0) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
Process* const current_process = Core::CurrentProcess();
|
||||
HandleTable& handle_table = current_process->GetHandleTable();
|
||||
const auto resource_limit = current_process->GetResourceLimit();
|
||||
if (!resource_limit) {
|
||||
*result = KernelHandle::InvalidHandle;
|
||||
// Yes, the kernel considers this a successful operation.
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
const auto table_result = handle_table.Create(resource_limit);
|
||||
if (table_result.Failed()) {
|
||||
return table_result.Code();
|
||||
}
|
||||
|
||||
*result = *table_result;
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
break;
|
||||
case GetInfoType::RandomEntropy:
|
||||
if (handle != 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
|
||||
@@ -826,15 +701,40 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
*result = Core::CurrentProcess()->GetRandomEntropy(info_sub_id);
|
||||
*result = current_process->GetRandomEntropy(info_sub_id);
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
break;
|
||||
case GetInfoType::ASLRRegionBaseAddr:
|
||||
*result = vm_manager.GetASLRRegionBaseAddress();
|
||||
break;
|
||||
case GetInfoType::ASLRRegionSize:
|
||||
*result = vm_manager.GetASLRRegionSize();
|
||||
break;
|
||||
case GetInfoType::NewMapRegionBaseAddr:
|
||||
*result = vm_manager.GetNewMapRegionBaseAddress();
|
||||
break;
|
||||
case GetInfoType::NewMapRegionSize:
|
||||
*result = vm_manager.GetNewMapRegionSize();
|
||||
break;
|
||||
case GetInfoType::SystemResourceSize:
|
||||
*result = current_process->GetSystemResourceSize();
|
||||
break;
|
||||
case GetInfoType::PersonalMmHeapUsage:
|
||||
*result = vm_manager.GetPersonalMmHeapUsage();
|
||||
break;
|
||||
case GetInfoType::TitleId:
|
||||
*result = current_process->GetTitleID();
|
||||
break;
|
||||
case GetInfoType::PrivilegedProcessId:
|
||||
LOG_WARNING(Kernel_SVC,
|
||||
"(STUBBED) Attempted to query privileged process id bounds, returned 0");
|
||||
*result = 0;
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
break;
|
||||
case GetInfoType::UserExceptionContextAddr:
|
||||
LOG_WARNING(Kernel_SVC,
|
||||
"(STUBBED) Attempted to query user exception context address, returned 0");
|
||||
*result = 0;
|
||||
break;
|
||||
case GetInfoType::ThreadTickCount: {
|
||||
constexpr u64 num_cpus = 4;
|
||||
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
|
||||
@@ -844,7 +744,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
}
|
||||
|
||||
const auto thread =
|
||||
Core::CurrentProcess()->GetHandleTable().Get<Thread>(static_cast<Handle>(handle));
|
||||
current_process->GetHandleTable().Get<Thread>(static_cast<Handle>(handle));
|
||||
if (!thread) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
|
||||
static_cast<Handle>(handle));
|
||||
@@ -867,13 +767,14 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
|
||||
}
|
||||
|
||||
*result = out_ticks;
|
||||
return RESULT_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) Unimplemented svcGetInfo id=0x{:016X}", info_id);
|
||||
return ERR_INVALID_ENUM_VALUE;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// Sets the thread activity
|
||||
@@ -943,10 +844,10 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
|
||||
LOG_TRACE(Kernel_SVC, "called");
|
||||
|
||||
if (priority > THREADPRIO_LOWEST) {
|
||||
LOG_ERROR(
|
||||
Kernel_SVC,
|
||||
"An invalid priority was specified, expected {} but got {} for thread_handle={:08X}",
|
||||
THREADPRIO_LOWEST, priority, handle);
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"An invalid priority was specified, expected {} but got {} for "
|
||||
"thread_handle={:08X}",
|
||||
THREADPRIO_LOWEST, priority, handle);
|
||||
return ERR_INVALID_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
@@ -1119,9 +1020,9 @@ static void ExitProcess() {
|
||||
static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top,
|
||||
u32 priority, s32 processor_id) {
|
||||
LOG_TRACE(Kernel_SVC,
|
||||
"called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, "
|
||||
"called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, "
|
||||
"threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
|
||||
entry_point, arg, stack_top, priority, processor_id, *out_handle);
|
||||
entry_point, name, arg, stack_top, priority, processor_id, *out_handle);
|
||||
|
||||
if (priority > THREADPRIO_LOWEST) {
|
||||
LOG_ERROR(Kernel_SVC, "An invalid priority was specified, expected {} but got {}",
|
||||
@@ -1217,10 +1118,10 @@ static void SleepThread(s64 nanoseconds) {
|
||||
/// Wait process wide key atomic
|
||||
static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_variable_addr,
|
||||
Handle thread_handle, s64 nano_seconds) {
|
||||
LOG_TRACE(
|
||||
Kernel_SVC,
|
||||
"called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
|
||||
mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
|
||||
LOG_TRACE(Kernel_SVC,
|
||||
"called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, "
|
||||
"timeout={}",
|
||||
mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
|
||||
@@ -1438,7 +1339,7 @@ static ResultCode ResetSignal(Handle handle) {
|
||||
LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
auto event = handle_table.Get<ReadableEvent>(handle);
|
||||
auto event = handle_table.Get<Event>(handle);
|
||||
|
||||
ASSERT(event != nullptr);
|
||||
|
||||
@@ -1528,7 +1429,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
return ERR_INVALID_COMBINATION;
|
||||
}
|
||||
|
||||
/// This value is used to only change the affinity mask without changing the current ideal core.
|
||||
/// This value is used to only change the affinity mask without changing the current ideal
|
||||
/// core.
|
||||
static constexpr u32 OnlyChangeMask = static_cast<u32>(-3);
|
||||
|
||||
if (core == OnlyChangeMask) {
|
||||
@@ -1597,67 +1499,17 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode CreateEvent(Handle* write_handle, Handle* read_handle) {
|
||||
LOG_DEBUG(Kernel_SVC, "called");
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
const auto [readable_event, writable_event] =
|
||||
WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent");
|
||||
|
||||
HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable();
|
||||
|
||||
const auto write_create_result = handle_table.Create(writable_event);
|
||||
if (write_create_result.Failed()) {
|
||||
return write_create_result.Code();
|
||||
}
|
||||
*write_handle = *write_create_result;
|
||||
|
||||
const auto read_create_result = handle_table.Create(readable_event);
|
||||
if (read_create_result.Failed()) {
|
||||
handle_table.Close(*write_create_result);
|
||||
return read_create_result.Code();
|
||||
}
|
||||
*read_handle = *read_create_result;
|
||||
|
||||
LOG_DEBUG(Kernel_SVC,
|
||||
"successful. Writable event handle=0x{:08X}, Readable event handle=0x{:08X}",
|
||||
*write_create_result, *read_create_result);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode ClearEvent(Handle handle) {
|
||||
LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
|
||||
|
||||
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
|
||||
auto writable_event = handle_table.Get<WritableEvent>(handle);
|
||||
if (writable_event) {
|
||||
writable_event->Clear();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
auto readable_event = handle_table.Get<ReadableEvent>(handle);
|
||||
if (readable_event) {
|
||||
readable_event->Clear();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
LOG_ERROR(Kernel_SVC, "Event handle does not exist, handle=0x{:08X}", handle);
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
static ResultCode SignalEvent(Handle handle) {
|
||||
LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle);
|
||||
|
||||
HandleTable& handle_table = Core::CurrentProcess()->GetHandleTable();
|
||||
auto writable_event = handle_table.Get<WritableEvent>(handle);
|
||||
|
||||
if (!writable_event) {
|
||||
LOG_ERROR(Kernel_SVC, "Non-existent writable event handle used (0x{:08X})", handle);
|
||||
SharedPtr<Event> evt = handle_table.Get<Event>(handle);
|
||||
if (evt == nullptr) {
|
||||
LOG_ERROR(Kernel_SVC, "Event handle does not exist, handle=0x{:08X}", handle);
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
writable_event->Signal();
|
||||
evt->Clear();
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1757,17 +1609,103 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource
|
||||
|
||||
const auto set_result = resource_limit_object->SetLimitValue(type, static_cast<s64>(value));
|
||||
if (set_result.IsError()) {
|
||||
LOG_ERROR(
|
||||
Kernel_SVC,
|
||||
"Attempted to lower resource limit ({}) for category '{}' below its current value ({})",
|
||||
resource_limit_object->GetMaxResourceValue(type), resource_type,
|
||||
resource_limit_object->GetCurrentResourceValue(type));
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Attempted to lower resource limit ({}) for category '{}' below its current "
|
||||
"value ({})",
|
||||
resource_limit_object->GetMaxResourceValue(type), resource_type,
|
||||
resource_limit_object->GetCurrentResourceValue(type));
|
||||
return set_result;
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode MapPhysicalMemory(VAddr addr, u64 size) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:08X}, size=0x{:X}", addr, size);
|
||||
|
||||
if (!Common::Is4KBAligned(addr)) {
|
||||
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Size is 0");
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (!Common::Is4KBAligned(size)) {
|
||||
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (!IsValidAddressRange(addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Address is not a valid address range, addr=0x{:016X}, size=0x{:016X}", addr,
|
||||
size);
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
auto& vm_manager = current_process->VMManager();
|
||||
|
||||
if (current_process->GetSystemResourceSize() == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "The system resource size is 0");
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (!vm_manager.IsInsideMapRegion(addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Destination does not fit within the map region, addr=0x{:016X}, "
|
||||
"size=0x{:016X}",
|
||||
addr, size);
|
||||
return ERR_INVALID_MEMORY_RANGE;
|
||||
}
|
||||
|
||||
return vm_manager.MapPhysicalMemory(addr, size);
|
||||
}
|
||||
|
||||
static ResultCode UnmapPhysicalMemory(VAddr addr, u64 size) {
|
||||
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:08X}, size=0x{:X}", addr, size);
|
||||
if (!Common::Is4KBAligned(addr)) {
|
||||
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Size is 0");
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (!Common::Is4KBAligned(size)) {
|
||||
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (!IsValidAddressRange(addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Address is not a valid address range, addr=0x{:016X}, size=0x{:016X}", addr,
|
||||
size);
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
auto* const current_process = Core::CurrentProcess();
|
||||
auto& vm_manager = current_process->VMManager();
|
||||
|
||||
if (current_process->GetSystemResourceSize() == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "The system resource size is 0");
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (!vm_manager.IsInsideMapRegion(addr, size)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Destination does not fit within the map region, addr=0x{:016X}, "
|
||||
"size=0x{:016X}",
|
||||
addr, size);
|
||||
return ERR_INVALID_MEMORY_RANGE;
|
||||
}
|
||||
|
||||
return vm_manager.UnmapPhysicalMemory(addr, size);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct FunctionDef {
|
||||
using Func = void();
|
||||
@@ -1796,7 +1734,7 @@ static const FunctionDef SVC_Table[] = {
|
||||
{0x0E, SvcWrap<GetThreadCoreMask>, "GetThreadCoreMask"},
|
||||
{0x0F, SvcWrap<SetThreadCoreMask>, "SetThreadCoreMask"},
|
||||
{0x10, SvcWrap<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"},
|
||||
{0x11, SvcWrap<SignalEvent>, "SignalEvent"},
|
||||
{0x11, nullptr, "SignalEvent"},
|
||||
{0x12, SvcWrap<ClearEvent>, "ClearEvent"},
|
||||
{0x13, SvcWrap<MapSharedMemory>, "MapSharedMemory"},
|
||||
{0x14, SvcWrap<UnmapSharedMemory>, "UnmapSharedMemory"},
|
||||
@@ -1823,8 +1761,8 @@ static const FunctionDef SVC_Table[] = {
|
||||
{0x29, SvcWrap<GetInfo>, "GetInfo"},
|
||||
{0x2A, nullptr, "FlushEntireDataCache"},
|
||||
{0x2B, nullptr, "FlushDataCache"},
|
||||
{0x2C, nullptr, "MapPhysicalMemory"},
|
||||
{0x2D, nullptr, "UnmapPhysicalMemory"},
|
||||
{0x2C, SvcWrap<MapPhysicalMemory>, "MapPhysicalMemory"},
|
||||
{0x2D, SvcWrap<UnmapPhysicalMemory>, "UnmapPhysicalMemory"},
|
||||
{0x2E, nullptr, "GetFutureThreadInfo"},
|
||||
{0x2F, nullptr, "GetLastThreadInfo"},
|
||||
{0x30, SvcWrap<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"},
|
||||
@@ -1848,7 +1786,7 @@ static const FunctionDef SVC_Table[] = {
|
||||
{0x42, nullptr, "ReplyAndReceiveLight"},
|
||||
{0x43, nullptr, "ReplyAndReceive"},
|
||||
{0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
|
||||
{0x45, SvcWrap<CreateEvent>, "CreateEvent"},
|
||||
{0x45, nullptr, "CreateEvent"},
|
||||
{0x46, nullptr, "Unknown"},
|
||||
{0x47, nullptr, "Unknown"},
|
||||
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
|
||||
|
||||
@@ -59,19 +59,6 @@ void SvcWrap() {
|
||||
FuncReturn(retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32*, u32*)>
|
||||
void SvcWrap() {
|
||||
u32 param_1 = 0;
|
||||
u32 param_2 = 0;
|
||||
const u32 retval = func(¶m_1, ¶m_2).raw;
|
||||
|
||||
auto& arm_interface = Core::CurrentArmInterface();
|
||||
arm_interface.SetReg(1, param_1);
|
||||
arm_interface.SetReg(2, param_2);
|
||||
|
||||
FuncReturn(retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32*, u64)>
|
||||
void SvcWrap() {
|
||||
u32 param_1 = 0;
|
||||
@@ -158,6 +145,11 @@ void SvcWrap() {
|
||||
FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64, u64)>
|
||||
void SvcWrap() {
|
||||
FuncReturn(func(Param(0), Param(1)).raw);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32*, u64, u64, s64)>
|
||||
void SvcWrap() {
|
||||
u32 param_1 = 0;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
@@ -163,6 +164,219 @@ ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
|
||||
return MakeResult<VAddr>(target);
|
||||
}
|
||||
|
||||
constexpr bool AreOverlapping(VAddr addr_start, VAddr addr_end, VAddr region_start,
|
||||
VAddr region_end) {
|
||||
return std::max(addr_end, region_end) - std::min(addr_start, region_start) <
|
||||
(addr_end - addr_start) + (region_end - region_start);
|
||||
}
|
||||
|
||||
ResultCode VMManager::MapPhysicalMemory(VAddr addr, u64 size) {
|
||||
const auto base = GetMapRegionBaseAddress();
|
||||
const auto end = GetMapRegionEndAddress();
|
||||
|
||||
if (!IsInsideMapRegion(addr, size)) {
|
||||
LOG_ERROR(
|
||||
Kernel,
|
||||
"Address and size does not fall inside the map region, addr=0x{:016X}, size=0x{:016X}",
|
||||
addr, size);
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
// We have nothing mapped, we can just map directly
|
||||
if (personal_heap_usage == 0) {
|
||||
const auto result = MapMemoryBlock(addr, std::make_shared<std::vector<u8>>(size, 0), 0,
|
||||
size, MemoryState::Mapped);
|
||||
personal_heap_usage += size;
|
||||
return result.Code();
|
||||
}
|
||||
|
||||
auto vma = FindVMA(base);
|
||||
u64 remaining_to_map = size;
|
||||
auto last_result = RESULT_SUCCESS;
|
||||
// Needed just in case we fail to map a region, we'll unmap everything.
|
||||
std::vector<std::pair<u64, u64>> mapped_regions;
|
||||
while (vma != vma_map.end() && vma->second.base <= end && remaining_to_map > 0) {
|
||||
const auto vma_start = vma->second.base;
|
||||
const auto vma_end = vma_start + vma->second.size;
|
||||
const auto is_mapped = vma->second.meminfo_state == MemoryState::Mapped;
|
||||
// Something failed, lets bail out
|
||||
if (last_result.IsError()) {
|
||||
break;
|
||||
}
|
||||
last_result = RESULT_SUCCESS;
|
||||
|
||||
// Allows us to use continue without worrying about incrementing the vma
|
||||
SCOPE_EXIT({ vma++; });
|
||||
|
||||
// We're out of range now, we can just break. We should be done with everything now
|
||||
if (vma_start > addr + size - 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We're not processing addresses yet, lets keep skipping
|
||||
if (!AreOverlapping(addr, addr + size, vma_start, vma_end)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we fall within the vma, get the offset of where we begin in the said vma
|
||||
const auto offset_in_vma = vma_start + ((addr + size - remaining_to_map) - vma_start);
|
||||
const auto remaining_vma_size = (vma_end - offset_in_vma);
|
||||
// Our vma is already mapped
|
||||
if (is_mapped) {
|
||||
if (remaining_vma_size >= remaining_to_map) {
|
||||
// Our region we need is already mapped
|
||||
break;
|
||||
} else {
|
||||
// We are partially mapped, Make note of it and move on
|
||||
remaining_to_map -= remaining_vma_size;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// We're not mapped, so lets map some space
|
||||
if (remaining_vma_size >= remaining_to_map) {
|
||||
// We can fit everything in this region, lets finish off the mapping
|
||||
last_result = MapMemoryBlock(offset_in_vma,
|
||||
std::make_shared<std::vector<u8>>(remaining_to_map, 0),
|
||||
0, remaining_to_map, MemoryState::Mapped)
|
||||
.Code();
|
||||
if (last_result.IsSuccess()) {
|
||||
personal_heap_usage += remaining_to_map;
|
||||
mapped_regions.push_back(std::make_pair(offset_in_vma, remaining_to_map));
|
||||
} else {
|
||||
LOG_ERROR(Kernel,
|
||||
"Failed to map entire VMA with error 0x{:X}, addr=0x{:016X}, "
|
||||
"size=0x{:016X}, vma_start={:016X}, vma_end={:016X}, "
|
||||
"offset_in_vma={:016X}, remaining_to_map={:016X}",
|
||||
last_result.raw, addr, size, vma_start, vma_end, offset_in_vma,
|
||||
remaining_to_map);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
// We can do a partial mapping here
|
||||
last_result =
|
||||
MapMemoryBlock(offset_in_vma,
|
||||
std::make_shared<std::vector<u8>>(remaining_vma_size, 0), 0,
|
||||
remaining_vma_size, MemoryState::Mapped)
|
||||
.Code();
|
||||
|
||||
// Update our usage and continue to the next vma
|
||||
if (last_result.IsSuccess()) {
|
||||
personal_heap_usage += remaining_vma_size;
|
||||
remaining_to_map -= remaining_vma_size;
|
||||
mapped_regions.push_back(std::make_pair(offset_in_vma, remaining_vma_size));
|
||||
} else {
|
||||
LOG_ERROR(Kernel,
|
||||
"Failed to map partial VMA with error 0x{:X}, addr=0x{:016X}, "
|
||||
"size=0x{:016X}, vma_start={:016X}, vma_end={:016X}, "
|
||||
"offset_in_vma={:016X}, remaining_to_map={:016X}",
|
||||
last_result.raw, addr, size, vma_start, vma_end, offset_in_vma,
|
||||
remaining_to_map);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We failed to map something, lets unmap everything we mapped
|
||||
if (last_result.IsError() && !mapped_regions.empty()) {
|
||||
for (const auto [mapped_addr, mapped_size] : mapped_regions) {
|
||||
if (UnmapRange(mapped_addr, mapped_size).IsSuccess()) {
|
||||
personal_heap_usage -= mapped_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return last_result;
|
||||
}
|
||||
|
||||
ResultCode VMManager::UnmapPhysicalMemory(VAddr addr, u64 size) {
|
||||
const auto base = GetMapRegionBaseAddress();
|
||||
const auto end = GetMapRegionEndAddress();
|
||||
|
||||
if (!IsInsideMapRegion(addr, size)) {
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
// We have nothing mapped, we can just map directly
|
||||
if (personal_heap_usage == 0) {
|
||||
LOG_WARNING(Kernel, "Unmap physical memory called when our personal usage is empty");
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
auto vma = FindVMA(base);
|
||||
u64 remaining_to_unmap = size;
|
||||
// Needed just in case we fail to map a region, we'll unmap everything.
|
||||
std::vector<std::pair<u64, u64>> unmapped_regions;
|
||||
while (vma != vma_map.end() && vma->second.base <= end && remaining_to_unmap > 0) {
|
||||
const auto vma_start = vma->second.base;
|
||||
const auto vma_end = vma_start + vma->second.size;
|
||||
const auto is_unmapped = vma->second.meminfo_state != MemoryState::Mapped;
|
||||
// Allows us to use continue without worrying about incrementing the vma
|
||||
SCOPE_EXIT({ vma++; });
|
||||
|
||||
// We're out of range now, we can just break. We should be done with everything now
|
||||
if (vma_start > addr + size - 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We're not processing addresses yet, lets keep skipping
|
||||
if (!AreOverlapping(addr, addr + size, vma_start, vma_end)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto offset_in_vma = vma_start + ((addr + size - remaining_to_unmap) - vma_start);
|
||||
const auto remaining_vma_size = (vma_end - offset_in_vma);
|
||||
// Our vma is already unmapped
|
||||
if (is_unmapped) {
|
||||
if (remaining_vma_size >= remaining_to_unmap) {
|
||||
// Our region we need is already unmapped
|
||||
break;
|
||||
} else {
|
||||
// We are partially unmapped, Make note of it and move on
|
||||
remaining_to_unmap -= remaining_vma_size;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// We're mapped, so lets unmap
|
||||
if (remaining_vma_size >= remaining_to_unmap) {
|
||||
// The rest of what we need to unmap fits in this region
|
||||
unmapped_regions.push_back(std::make_pair(offset_in_vma, remaining_to_unmap));
|
||||
break;
|
||||
} else {
|
||||
// We only partially fit here, lets unmap what we can
|
||||
// Update our usage and continue to the next vma
|
||||
remaining_to_unmap -= remaining_vma_size;
|
||||
unmapped_regions.push_back(std::make_pair(offset_in_vma, remaining_vma_size));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto last_result = RESULT_SUCCESS;
|
||||
if (!unmapped_regions.empty()) {
|
||||
for (auto it = unmapped_regions.begin(); it != unmapped_regions.end(); ++it) {
|
||||
last_result = UnmapRange((*it).first, (*it).second);
|
||||
if (last_result.IsSuccess()) {
|
||||
personal_heap_usage -= (*it).second;
|
||||
} else {
|
||||
LOG_ERROR(Kernel,
|
||||
"Failed to unmap region addr=0x{:016X}, size=0x{:016X} with error=0x{:X}",
|
||||
(*it).first, (*it).second, last_result.raw);
|
||||
while (it != unmapped_regions.begin()) {
|
||||
if (MapMemoryBlock((*it).first,
|
||||
std::make_shared<std::vector<u8>>((*it).second, 0), 0,
|
||||
(*it).second, MemoryState::Mapped)
|
||||
.Succeeded()) {
|
||||
personal_heap_usage += (*it).second;
|
||||
}
|
||||
--it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return last_result;
|
||||
}
|
||||
|
||||
ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
|
||||
MemoryState state,
|
||||
Memory::MemoryHookPointer mmio_handler) {
|
||||
@@ -202,8 +416,8 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
|
||||
const VAddr target_end = target + size;
|
||||
|
||||
const VMAIter end = vma_map.end();
|
||||
// The comparison against the end of the range must be done using addresses since VMAs can be
|
||||
// merged during this process, causing invalidation of the iterators.
|
||||
// The comparison against the end of the range must be done using addresses since VMAs can
|
||||
// be merged during this process, causing invalidation of the iterators.
|
||||
while (vma != end && vma->second.base < target_end) {
|
||||
vma = std::next(Unmap(vma));
|
||||
}
|
||||
@@ -234,8 +448,8 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
|
||||
const VAddr target_end = target + size;
|
||||
|
||||
const VMAIter end = vma_map.end();
|
||||
// The comparison against the end of the range must be done using addresses since VMAs can be
|
||||
// merged during this process, causing invalidation of the iterators.
|
||||
// The comparison against the end of the range must be done using addresses since VMAs can
|
||||
// be merged during this process, causing invalidation of the iterators.
|
||||
while (vma != end && vma->second.base < target_end) {
|
||||
vma = std::next(StripIterConstness(Reprotect(vma, new_perms)));
|
||||
}
|
||||
@@ -323,8 +537,8 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, Mem
|
||||
}
|
||||
|
||||
void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
|
||||
// If this ever proves to have a noticeable performance impact, allow users of the function to
|
||||
// specify a specific range of addresses to limit the scan to.
|
||||
// If this ever proves to have a noticeable performance impact, allow users of the function
|
||||
// to specify a specific range of addresses to limit the scan to.
|
||||
for (const auto& p : vma_map) {
|
||||
const VirtualMemoryArea& vma = p.second;
|
||||
if (block == vma.backing_block.get()) {
|
||||
@@ -419,8 +633,8 @@ VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) {
|
||||
VirtualMemoryArea& old_vma = vma_handle->second;
|
||||
VirtualMemoryArea new_vma = old_vma; // Make a copy of the VMA
|
||||
|
||||
// For now, don't allow no-op VMA splits (trying to split at a boundary) because it's probably
|
||||
// a bug. This restriction might be removed later.
|
||||
// For now, don't allow no-op VMA splits (trying to split at a boundary) because it's
|
||||
// probably a bug. This restriction might be removed later.
|
||||
ASSERT(offset_in_vma < old_vma.size);
|
||||
ASSERT(offset_in_vma > 0);
|
||||
|
||||
@@ -685,4 +899,22 @@ u64 VMManager::GetTLSIORegionSize() const {
|
||||
return tls_io_region_end - tls_io_region_base;
|
||||
}
|
||||
|
||||
u64 VMManager::GetPersonalMmHeapUsage() const {
|
||||
return personal_heap_usage;
|
||||
}
|
||||
|
||||
bool VMManager::IsInsideAddressSpace(VAddr address, u64 size) const {
|
||||
return IsInsideAddressRange(address, size, GetAddressSpaceBaseAddress(),
|
||||
GetAddressSpaceEndAddress());
|
||||
}
|
||||
|
||||
bool VMManager::IsInsideNewMapRegion(VAddr address, u64 size) const {
|
||||
return IsInsideAddressRange(address, size, GetNewMapRegionBaseAddress(),
|
||||
GetNewMapRegionEndAddress());
|
||||
}
|
||||
|
||||
bool VMManager::IsInsideMapRegion(VAddr address, u64 size) const {
|
||||
return IsInsideAddressRange(address, size, GetMapRegionBaseAddress(), GetMapRegionEndAddress());
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -18,6 +18,20 @@ enum class ProgramAddressSpaceType : u8;
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
// Checks if address + size is greater than the given address
|
||||
// This can return false if the size causes an overflow of a 64-bit type
|
||||
// or if the given size is zero.
|
||||
constexpr bool IsValidAddressRange(VAddr address, u64 size) {
|
||||
return address + size > address;
|
||||
}
|
||||
|
||||
// Checks if a given address range lies within a larger address range.
|
||||
constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
|
||||
VAddr address_range_end) {
|
||||
const VAddr end_address = address + size - 1;
|
||||
return address_range_begin <= address && end_address <= address_range_end - 1;
|
||||
}
|
||||
|
||||
enum class VMAType : u8 {
|
||||
/// VMA represents an unmapped region of the address space.
|
||||
Free,
|
||||
@@ -165,6 +179,30 @@ public:
|
||||
*/
|
||||
ResultVal<VAddr> FindFreeRegion(u64 size) const;
|
||||
|
||||
/**
|
||||
* Maps memory to the PersonalMmHeap region at a given address. MapPhysicalMemory will not remap
|
||||
* any regions. The goal of MapPhysicalMemory is to "fill" a regions empty space given an offset
|
||||
* and a size. Any memory which is already mapped in the subsection we want to allocate is
|
||||
* ignored and we only map the remaining data needed. Reminder that we're not remapping, just
|
||||
* filling the space we want to fill. This is typically used with "PersonalMmHeap" which allows
|
||||
* processes to have extra resources mapped. Typically this is seen with 5.0.0+ games and
|
||||
* sysmodule specifically. The PersonalMmHeapSize is pulled from the NPDM and is passed to
|
||||
* loader when the process is created which is in turn passed to the kernel when
|
||||
* svcCreateProcess is called
|
||||
*
|
||||
* @param target The address of where you want to map
|
||||
* @param size The size of the memory you want to map
|
||||
*/
|
||||
ResultCode MapPhysicalMemory(VAddr target, u64 size);
|
||||
|
||||
/**
|
||||
* Unmaps memory from the PersonalMmHeap region at a given address.
|
||||
*
|
||||
* @param target The address of where you want to unmap
|
||||
* @param size The size of the memory you want to unmap
|
||||
*/
|
||||
ResultCode UnmapPhysicalMemory(VAddr target, u64 size);
|
||||
|
||||
/**
|
||||
* Maps a memory-mapped IO region at a given address.
|
||||
*
|
||||
@@ -276,6 +314,13 @@ public:
|
||||
/// Gets the total size of the TLS IO region in bytes.
|
||||
u64 GetTLSIORegionSize() const;
|
||||
|
||||
/// Gets the total size of the PersonalMmHeap region in bytes.
|
||||
u64 GetPersonalMmHeapUsage() const;
|
||||
|
||||
bool IsInsideAddressSpace(VAddr address, u64 size) const;
|
||||
bool IsInsideNewMapRegion(VAddr address, u64 size) const;
|
||||
bool IsInsideMapRegion(VAddr address, u64 size) const;
|
||||
|
||||
/// Each VMManager has its own page table, which is set as the main one when the owning process
|
||||
/// is scheduled.
|
||||
Memory::PageTable page_table;
|
||||
@@ -359,5 +404,7 @@ private:
|
||||
VAddr heap_start = 0;
|
||||
VAddr heap_end = 0;
|
||||
u64 heap_used = 0;
|
||||
|
||||
u64 personal_heap_usage = 0;
|
||||
};
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/assert.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
WritableEvent::WritableEvent(KernelCore& kernel) : Object{kernel} {}
|
||||
WritableEvent::~WritableEvent() = default;
|
||||
|
||||
EventPair WritableEvent::CreateEventPair(KernelCore& kernel, ResetType reset_type,
|
||||
std::string name) {
|
||||
SharedPtr<WritableEvent> writable_event(new WritableEvent(kernel));
|
||||
SharedPtr<ReadableEvent> readable_event(new ReadableEvent(kernel));
|
||||
|
||||
writable_event->name = name + ":Writable";
|
||||
writable_event->readable = readable_event;
|
||||
readable_event->name = name + ":Readable";
|
||||
readable_event->signaled = false;
|
||||
readable_event->reset_type = reset_type;
|
||||
|
||||
return {std::move(readable_event), std::move(writable_event)};
|
||||
}
|
||||
|
||||
SharedPtr<ReadableEvent> WritableEvent::GetReadableEvent() const {
|
||||
return readable;
|
||||
}
|
||||
|
||||
ResetType WritableEvent::GetResetType() const {
|
||||
return readable->reset_type;
|
||||
}
|
||||
|
||||
void WritableEvent::Signal() {
|
||||
readable->Signal();
|
||||
}
|
||||
|
||||
void WritableEvent::Clear() {
|
||||
readable->Clear();
|
||||
}
|
||||
|
||||
bool WritableEvent::IsSignaled() const {
|
||||
return readable->signaled;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -9,11 +9,9 @@
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applet_ae.h"
|
||||
@@ -210,8 +208,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
|
||||
"ISelfController:LaunchableEvent");
|
||||
launchable_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "ISelfController:LaunchableEvent");
|
||||
}
|
||||
|
||||
ISelfController::~ISelfController() = default;
|
||||
@@ -297,11 +295,11 @@ void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) {
|
||||
void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
launchable_event.writable->Signal();
|
||||
launchable_event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(launchable_event.readable);
|
||||
rb.PushCopyObjects(launchable_event);
|
||||
}
|
||||
|
||||
void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) {
|
||||
@@ -350,38 +348,36 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
|
||||
|
||||
AppletMessageQueue::AppletMessageQueue() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
|
||||
"AMMessageQueue:OnMessageRecieved");
|
||||
on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot, "AMMessageQueue:OperationModeChanged");
|
||||
on_new_message = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky,
|
||||
"AMMessageQueue:OnMessageRecieved");
|
||||
on_operation_mode_changed = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"AMMessageQueue:OperationModeChanged");
|
||||
}
|
||||
|
||||
AppletMessageQueue::~AppletMessageQueue() = default;
|
||||
|
||||
const Kernel::SharedPtr<Kernel::ReadableEvent>& AppletMessageQueue::GetMesssageRecieveEvent()
|
||||
const {
|
||||
return on_new_message.readable;
|
||||
const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetMesssageRecieveEvent() const {
|
||||
return on_new_message;
|
||||
}
|
||||
|
||||
const Kernel::SharedPtr<Kernel::ReadableEvent>& AppletMessageQueue::GetOperationModeChangedEvent()
|
||||
const {
|
||||
return on_operation_mode_changed.readable;
|
||||
const Kernel::SharedPtr<Kernel::Event>& AppletMessageQueue::GetOperationModeChangedEvent() const {
|
||||
return on_operation_mode_changed;
|
||||
}
|
||||
|
||||
void AppletMessageQueue::PushMessage(AppletMessage msg) {
|
||||
messages.push(msg);
|
||||
on_new_message.writable->Signal();
|
||||
on_new_message->Signal();
|
||||
}
|
||||
|
||||
AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
|
||||
if (messages.empty()) {
|
||||
on_new_message.writable->Clear();
|
||||
on_new_message->Clear();
|
||||
return AppletMessage::NoMessage;
|
||||
}
|
||||
auto msg = messages.front();
|
||||
messages.pop();
|
||||
if (messages.empty()) {
|
||||
on_new_message.writable->Clear();
|
||||
on_new_message->Clear();
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
@@ -393,7 +389,7 @@ std::size_t AppletMessageQueue::GetMessageCount() const {
|
||||
void AppletMessageQueue::OperationModeChanged() {
|
||||
PushMessage(AppletMessage::OperationModeChanged);
|
||||
PushMessage(AppletMessage::PerformanceModeChanged);
|
||||
on_operation_mode_changed.writable->Signal();
|
||||
on_operation_mode_changed->Signal();
|
||||
}
|
||||
|
||||
ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue)
|
||||
@@ -430,6 +426,9 @@ ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_q
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "ICommonStateGetter:Event");
|
||||
}
|
||||
|
||||
ICommonStateGetter::~ICommonStateGetter() = default;
|
||||
@@ -565,8 +564,8 @@ private:
|
||||
void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
applet->GetBroker().SignalStateChanged();
|
||||
const auto event = applet->GetBroker().GetStateChangedEvent();
|
||||
event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class Event;
|
||||
}
|
||||
|
||||
namespace Service {
|
||||
namespace NVFlinger {
|
||||
class NVFlinger;
|
||||
@@ -49,8 +52,8 @@ public:
|
||||
AppletMessageQueue();
|
||||
~AppletMessageQueue();
|
||||
|
||||
const Kernel::SharedPtr<Kernel::ReadableEvent>& GetMesssageRecieveEvent() const;
|
||||
const Kernel::SharedPtr<Kernel::ReadableEvent>& GetOperationModeChangedEvent() const;
|
||||
const Kernel::SharedPtr<Kernel::Event>& GetMesssageRecieveEvent() const;
|
||||
const Kernel::SharedPtr<Kernel::Event>& GetOperationModeChangedEvent() const;
|
||||
void PushMessage(AppletMessage msg);
|
||||
AppletMessage PopMessage();
|
||||
std::size_t GetMessageCount() const;
|
||||
@@ -58,8 +61,8 @@ public:
|
||||
|
||||
private:
|
||||
std::queue<AppletMessage> messages;
|
||||
Kernel::EventPair on_new_message;
|
||||
Kernel::EventPair on_operation_mode_changed;
|
||||
Kernel::SharedPtr<Kernel::Event> on_new_message;
|
||||
Kernel::SharedPtr<Kernel::Event> on_operation_mode_changed;
|
||||
};
|
||||
|
||||
class IWindowController final : public ServiceFramework<IWindowController> {
|
||||
@@ -119,7 +122,7 @@ private:
|
||||
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
Kernel::EventPair launchable_event;
|
||||
Kernel::SharedPtr<Kernel::Event> launchable_event;
|
||||
u32 idle_time_detection_extension = 0;
|
||||
};
|
||||
|
||||
@@ -148,6 +151,7 @@ private:
|
||||
void GetBootMode(Kernel::HLERequestContext& ctx);
|
||||
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
|
||||
|
||||
Kernel::SharedPtr<Kernel::Event> event;
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applets.h"
|
||||
|
||||
@@ -15,11 +14,11 @@ namespace Service::AM::Applets {
|
||||
|
||||
AppletDataBroker::AppletDataBroker() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
state_changed_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:StateChangedEvent");
|
||||
pop_out_data_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopDataOutEvent");
|
||||
pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair(
|
||||
state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"ILibraryAppletAccessor:StateChangedEvent");
|
||||
pop_out_data_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"ILibraryAppletAccessor:PopDataOutEvent");
|
||||
pop_interactive_out_data_event = Kernel::Event::Create(
|
||||
kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
|
||||
}
|
||||
|
||||
@@ -67,7 +66,7 @@ void AppletDataBroker::PushNormalDataFromGame(IStorage storage) {
|
||||
|
||||
void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
|
||||
out_channel.push(std::make_unique<IStorage>(storage));
|
||||
pop_out_data_event.writable->Signal();
|
||||
pop_out_data_event->Signal();
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
|
||||
@@ -76,23 +75,23 @@ void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
|
||||
|
||||
void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
|
||||
out_interactive_channel.push(std::make_unique<IStorage>(storage));
|
||||
pop_interactive_out_data_event.writable->Signal();
|
||||
pop_interactive_out_data_event->Signal();
|
||||
}
|
||||
|
||||
void AppletDataBroker::SignalStateChanged() const {
|
||||
state_changed_event.writable->Signal();
|
||||
state_changed_event->Signal();
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetNormalDataEvent() const {
|
||||
return pop_out_data_event.readable;
|
||||
Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetNormalDataEvent() const {
|
||||
return pop_out_data_event;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetInteractiveDataEvent() const {
|
||||
return pop_interactive_out_data_event.readable;
|
||||
Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetInteractiveDataEvent() const {
|
||||
return pop_interactive_out_data_event;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetStateChangedEvent() const {
|
||||
return state_changed_event.readable;
|
||||
Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetStateChangedEvent() const {
|
||||
return state_changed_event;
|
||||
}
|
||||
|
||||
Applet::Applet() = default;
|
||||
|
||||
@@ -8,10 +8,13 @@
|
||||
#include <queue>
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Kernel {
|
||||
class Event;
|
||||
}
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
class IStorage;
|
||||
@@ -37,9 +40,9 @@ public:
|
||||
|
||||
void SignalStateChanged() const;
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> GetNormalDataEvent() const;
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> GetInteractiveDataEvent() const;
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> GetStateChangedEvent() const;
|
||||
Kernel::SharedPtr<Kernel::Event> GetNormalDataEvent() const;
|
||||
Kernel::SharedPtr<Kernel::Event> GetInteractiveDataEvent() const;
|
||||
Kernel::SharedPtr<Kernel::Event> GetStateChangedEvent() const;
|
||||
|
||||
private:
|
||||
// Queues are named from applet's perspective
|
||||
@@ -56,13 +59,13 @@ private:
|
||||
// PopInteractiveDataToGame and PushInteractiveDataFromApplet
|
||||
std::queue<std::unique_ptr<IStorage>> out_interactive_channel;
|
||||
|
||||
Kernel::EventPair state_changed_event;
|
||||
Kernel::SharedPtr<Kernel::Event> state_changed_event;
|
||||
|
||||
// Signaled on PushNormalDataFromApplet
|
||||
Kernel::EventPair pop_out_data_event;
|
||||
Kernel::SharedPtr<Kernel::Event> pop_out_data_event;
|
||||
|
||||
// Signaled on PushInteractiveDataFromApplet
|
||||
Kernel::EventPair pop_interactive_out_data_event;
|
||||
Kernel::SharedPtr<Kernel::Event> pop_interactive_out_data_event;
|
||||
};
|
||||
|
||||
class Applet {
|
||||
|
||||
@@ -13,10 +13,8 @@
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/aoc/aoc_u.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
@@ -34,14 +32,14 @@ static std::vector<u64> AccumulateAOCTitleIDs() {
|
||||
std::vector<u64> add_on_content;
|
||||
const auto rcu = FileSystem::GetUnionContents();
|
||||
const auto list =
|
||||
rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
|
||||
rcu->ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
|
||||
std::transform(list.begin(), list.end(), std::back_inserter(add_on_content),
|
||||
[](const FileSys::RegisteredCacheEntry& rce) { return rce.title_id; });
|
||||
add_on_content.erase(
|
||||
std::remove_if(
|
||||
add_on_content.begin(), add_on_content.end(),
|
||||
[&rcu](u64 tid) {
|
||||
return rcu.GetEntry(tid, FileSys::ContentRecordType::Data)->GetStatus() !=
|
||||
return rcu->GetEntry(tid, FileSys::ContentRecordType::Data)->GetStatus() !=
|
||||
Loader::ResultStatus::Success;
|
||||
}),
|
||||
add_on_content.end());
|
||||
@@ -63,8 +61,8 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
|
||||
"GetAddOnContentListChanged:Event");
|
||||
aoc_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky,
|
||||
"GetAddOnContentListChanged:Event");
|
||||
}
|
||||
|
||||
AOC_U::~AOC_U() = default;
|
||||
@@ -146,7 +144,7 @@ void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(aoc_change_event.readable);
|
||||
rb.PushCopyObjects(aoc_change_event);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
|
||||
@@ -6,10 +6,6 @@
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class WritableEvent;
|
||||
}
|
||||
|
||||
namespace Service::AOC {
|
||||
|
||||
class AOC_U final : public ServiceFramework<AOC_U> {
|
||||
@@ -25,7 +21,7 @@ private:
|
||||
void GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::vector<u64> add_on_content;
|
||||
Kernel::EventPair aoc_change_event;
|
||||
Kernel::SharedPtr<Kernel::Event> aoc_change_event;
|
||||
};
|
||||
|
||||
/// Registers all AOC services with the specified service manager.
|
||||
|
||||
@@ -13,10 +13,8 @@
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/audio/audout_u.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@@ -48,8 +46,8 @@ class IAudioOut final : public ServiceFramework<IAudioOut> {
|
||||
public:
|
||||
IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name,
|
||||
std::string&& unique_name)
|
||||
: ServiceFramework("IAudioOut"), audio_core(audio_core),
|
||||
device_name(std::move(device_name)), audio_params(audio_params) {
|
||||
: ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params),
|
||||
device_name(std::move(device_name)) {
|
||||
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
|
||||
@@ -69,12 +67,11 @@ public:
|
||||
|
||||
// This is the event handle used to check if the audio buffer was released
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
|
||||
"IAudioOutBufferReleased");
|
||||
buffer_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
|
||||
|
||||
stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
|
||||
std::move(unique_name),
|
||||
[=]() { buffer_event.writable->Signal(); });
|
||||
std::move(unique_name), [=]() { buffer_event->Signal(); });
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -124,7 +121,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(buffer_event.readable);
|
||||
rb.PushCopyObjects(buffer_event);
|
||||
}
|
||||
|
||||
void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
|
||||
@@ -190,8 +187,8 @@ private:
|
||||
|
||||
AudoutParams audio_params{};
|
||||
|
||||
/// This is the event handle used to check if the audio buffer was released
|
||||
Kernel::EventPair buffer_event;
|
||||
/// This is the evend handle used to check if the audio buffer was released
|
||||
Kernel::SharedPtr<Kernel::Event> buffer_event;
|
||||
};
|
||||
|
||||
void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
@@ -12,10 +12,8 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/audio/audren_u.h"
|
||||
|
||||
namespace Service::Audio {
|
||||
@@ -43,14 +41,14 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
system_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
|
||||
"IAudioRenderer:SystemEvent");
|
||||
renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event.writable);
|
||||
system_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
|
||||
renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event);
|
||||
}
|
||||
|
||||
private:
|
||||
void UpdateAudioCallback() {
|
||||
system_event.writable->Signal();
|
||||
system_event->Signal();
|
||||
}
|
||||
|
||||
void GetSampleRate(Kernel::HLERequestContext& ctx) {
|
||||
@@ -114,7 +112,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(system_event.readable);
|
||||
rb.PushCopyObjects(system_event);
|
||||
}
|
||||
|
||||
void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
|
||||
@@ -137,7 +135,7 @@ private:
|
||||
rb.Push(rendering_time_limit_percent);
|
||||
}
|
||||
|
||||
Kernel::EventPair system_event;
|
||||
Kernel::SharedPtr<Kernel::Event> system_event;
|
||||
std::unique_ptr<AudioCore::AudioRenderer> renderer;
|
||||
u32 rendering_time_limit_percent = 100;
|
||||
};
|
||||
@@ -164,8 +162,8 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
"IAudioOutBufferReleasedEvent");
|
||||
buffer_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"IAudioOutBufferReleasedEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -209,11 +207,11 @@ private:
|
||||
void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
|
||||
buffer_event.writable->Signal();
|
||||
buffer_event->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(buffer_event.readable);
|
||||
rb.PushCopyObjects(buffer_event);
|
||||
}
|
||||
|
||||
void GetActiveChannelCount(Kernel::HLERequestContext& ctx) {
|
||||
@@ -224,7 +222,7 @@ private:
|
||||
rb.Push<u32>(1);
|
||||
}
|
||||
|
||||
Kernel::EventPair buffer_event;
|
||||
Kernel::SharedPtr<Kernel::Event> buffer_event;
|
||||
|
||||
}; // namespace Audio
|
||||
|
||||
|
||||
@@ -4,10 +4,8 @@
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/btdrv/btdrv.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
@@ -32,22 +30,20 @@ public:
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
register_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
"BT:RegisterEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
void RegisterEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
register_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "BT:RegisterEvent");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(register_event.readable);
|
||||
rb.PushCopyObjects(register_event);
|
||||
}
|
||||
|
||||
Kernel::EventPair register_event;
|
||||
Kernel::SharedPtr<Kernel::Event> register_event;
|
||||
};
|
||||
|
||||
class BtDrv final : public ServiceFramework<BtDrv> {
|
||||
|
||||
@@ -6,10 +6,8 @@
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/btm/btm.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@@ -55,55 +53,53 @@ public:
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
"IBtmUserCore:ScanEvent");
|
||||
connection_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConnectionEvent");
|
||||
service_discovery = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery");
|
||||
config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
"IBtmUserCore:ConfigEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
void GetScanEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
scan_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ScanEvent");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(scan_event.readable);
|
||||
rb.PushCopyObjects(scan_event);
|
||||
}
|
||||
|
||||
void GetConnectionEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
connection_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"IBtmUserCore:ConnectionEvent");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(connection_event.readable);
|
||||
rb.PushCopyObjects(connection_event);
|
||||
}
|
||||
|
||||
void GetDiscoveryEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
service_discovery =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(service_discovery.readable);
|
||||
rb.PushCopyObjects(service_discovery);
|
||||
}
|
||||
|
||||
void GetConfigEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_BTM, "(STUBBED) called");
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
config_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConfigEvent");
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(config_event.readable);
|
||||
rb.PushCopyObjects(config_event);
|
||||
}
|
||||
|
||||
Kernel::EventPair scan_event;
|
||||
Kernel::EventPair connection_event;
|
||||
Kernel::EventPair service_discovery;
|
||||
Kernel::EventPair config_event;
|
||||
Kernel::SharedPtr<Kernel::Event> scan_event;
|
||||
Kernel::SharedPtr<Kernel::Event> connection_event;
|
||||
Kernel::SharedPtr<Kernel::Event> service_discovery;
|
||||
Kernel::SharedPtr<Kernel::Event> config_event;
|
||||
};
|
||||
|
||||
class BTM_USR final : public ServiceFramework<BTM_USR> {
|
||||
|
||||
@@ -17,13 +17,11 @@ public:
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "SubmitContext"},
|
||||
{1, nullptr, "CreateReport"},
|
||||
{2, nullptr, "SetInitialLaunchSettingsCompletionTime"},
|
||||
{3, nullptr, "ClearInitialLaunchSettingsCompletionTime"},
|
||||
{4, nullptr, "UpdatePowerOnTime"},
|
||||
{5, nullptr, "UpdateAwakeTime"},
|
||||
{6, nullptr, "SubmitMultipleCategoryContext"},
|
||||
{7, nullptr, "UpdateApplicationLaunchTime"},
|
||||
{8, nullptr, "ClearApplicationLaunchTime"},
|
||||
{2, nullptr, "Unknown1"},
|
||||
{3, nullptr, "Unknown2"},
|
||||
{4, nullptr, "Unknown3"},
|
||||
{5, nullptr, "Unknown4"},
|
||||
{6, nullptr, "Unknown5"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -113,18 +113,6 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
|
||||
const std::string sanitized_path(FileUtil::SanitizePath(path));
|
||||
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(sanitized_path));
|
||||
|
||||
if (!dir->CleanSubdirectoryRecursive(FileUtil::GetFilename(sanitized_path))) {
|
||||
// TODO(DarkLordZach): Find a better error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
|
||||
const std::string& dest_path_) const {
|
||||
std::string src_path(FileUtil::SanitizePath(src_path_));
|
||||
@@ -341,9 +329,20 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
|
||||
return sdmc_factory->Open();
|
||||
}
|
||||
|
||||
FileSys::RegisteredCacheUnion GetUnionContents() {
|
||||
return FileSys::RegisteredCacheUnion{
|
||||
{GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}};
|
||||
std::shared_ptr<FileSys::RegisteredCacheUnion> registered_cache_union;
|
||||
|
||||
std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
|
||||
if (registered_cache_union == nullptr) {
|
||||
registered_cache_union =
|
||||
std::make_shared<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
|
||||
GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
|
||||
}
|
||||
|
||||
return registered_cache_union;
|
||||
}
|
||||
|
||||
void ClearUnionContents() {
|
||||
registered_cache_union = nullptr;
|
||||
}
|
||||
|
||||
FileSys::RegisteredCache* GetSystemNANDContents() {
|
||||
@@ -396,6 +395,7 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
|
||||
bis_factory = nullptr;
|
||||
save_data_factory = nullptr;
|
||||
sdmc_factory = nullptr;
|
||||
ClearUnionContents();
|
||||
}
|
||||
|
||||
auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
|
||||
|
||||
@@ -48,7 +48,8 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
|
||||
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
|
||||
ResultVal<FileSys::VirtualDir> OpenSDMC();
|
||||
|
||||
FileSys::RegisteredCacheUnion GetUnionContents();
|
||||
std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
|
||||
void ClearUnionContents();
|
||||
|
||||
FileSys::RegisteredCache* GetSystemNANDContents();
|
||||
FileSys::RegisteredCache* GetUserNANDContents();
|
||||
@@ -112,18 +113,6 @@ public:
|
||||
*/
|
||||
ResultCode DeleteDirectoryRecursively(const std::string& path) const;
|
||||
|
||||
/**
|
||||
* Cleans the specified directory. This is similar to DeleteDirectoryRecursively,
|
||||
* in that it deletes all the contents of the specified directory, however, this
|
||||
* function does *not* delete the directory itself. It only deletes everything
|
||||
* within it.
|
||||
*
|
||||
* @param path Path relative to the archive.
|
||||
*
|
||||
* @return Result of the operation.
|
||||
*/
|
||||
ResultCode CleanDirectoryRecursively(const std::string& path) const;
|
||||
|
||||
/**
|
||||
* Rename a File specified by its path
|
||||
* @param src_path Source path relative to the archive
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include "core/file_sys/nca_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/file_sys/system_archive/system_archive.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
@@ -292,7 +291,7 @@ public:
|
||||
{10, &IFileSystem::Commit, "Commit"},
|
||||
{11, nullptr, "GetFreeSpaceSize"},
|
||||
{12, nullptr, "GetTotalSpaceSize"},
|
||||
{13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
|
||||
{13, nullptr, "CleanDirectoryRecursively"},
|
||||
{14, nullptr, "GetFileTimeStampRaw"},
|
||||
{15, nullptr, "QueryEntry"},
|
||||
};
|
||||
@@ -362,16 +361,6 @@ public:
|
||||
rb.Push(backend.DeleteDirectoryRecursively(name));
|
||||
}
|
||||
|
||||
void CleanDirectoryRecursively(Kernel::HLERequestContext& ctx) {
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called. Directory: {}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.CleanDirectoryRecursively(name));
|
||||
}
|
||||
|
||||
void RenameFile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
@@ -832,15 +821,6 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
|
||||
auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
|
||||
|
||||
if (data.Failed()) {
|
||||
const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
|
||||
|
||||
if (archive != nullptr) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface(std::make_shared<IStorage>(archive));
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(DarkLordZach): Find the right error code to use here
|
||||
LOG_ERROR(Service_FS,
|
||||
"could not open data storage with title_id={:016X}, storage_id={:02X}", title_id,
|
||||
|
||||
@@ -12,9 +12,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/hid/controllers/npad.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
@@ -169,8 +167,8 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
|
||||
|
||||
void Controller_NPad::OnInit() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
styleset_changed_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
|
||||
styleset_changed_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
|
||||
|
||||
if (!IsControllerActivated()) {
|
||||
return;
|
||||
@@ -496,7 +494,7 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
|
||||
had_controller_update = true;
|
||||
}
|
||||
if (had_controller_update) {
|
||||
styleset_changed_event.writable->Signal();
|
||||
styleset_changed_event->Signal();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -511,7 +509,7 @@ std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
|
||||
}
|
||||
|
||||
void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
|
||||
styleset_changed_event.writable->Signal();
|
||||
styleset_changed_event->Signal();
|
||||
hold_type = joy_hold_type;
|
||||
}
|
||||
|
||||
@@ -520,9 +518,8 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
|
||||
}
|
||||
|
||||
void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
|
||||
const std::size_t npad_index = NPadIdToIndex(npad_id);
|
||||
ASSERT(npad_index < shared_memory_entries.size());
|
||||
shared_memory_entries[npad_index].pad_assignment = assignment_mode;
|
||||
ASSERT(npad_id < shared_memory_entries.size());
|
||||
shared_memory_entries[npad_id].pad_assignment = assignment_mode;
|
||||
}
|
||||
|
||||
void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
|
||||
@@ -541,11 +538,11 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
|
||||
last_processed_vibration = vibrations.back();
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent() const {
|
||||
Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const {
|
||||
// TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
|
||||
// be signalled at least once, and signaled after a new controller is connected?
|
||||
styleset_changed_event.writable->Signal();
|
||||
return styleset_changed_event.readable;
|
||||
styleset_changed_event->Signal();
|
||||
return styleset_changed_event;
|
||||
}
|
||||
|
||||
Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/hid/controllers/controller_base.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
@@ -109,7 +108,7 @@ public:
|
||||
void VibrateController(const std::vector<u32>& controller_ids,
|
||||
const std::vector<Vibration>& vibrations);
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent() const;
|
||||
Kernel::SharedPtr<Kernel::Event> GetStyleSetChangedEvent() const;
|
||||
Vibration GetLastVibration() const;
|
||||
|
||||
void AddNewController(NPadControllerType controller);
|
||||
@@ -304,7 +303,7 @@ private:
|
||||
sticks;
|
||||
std::vector<u32> supported_npad_id_types{};
|
||||
NpadHoldType hold_type{NpadHoldType::Vertical};
|
||||
Kernel::EventPair styleset_changed_event;
|
||||
Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
|
||||
Vibration last_processed_vibration{};
|
||||
std::array<ControllerHolder, 10> connected_controllers{};
|
||||
bool can_controllers_vibrate{true};
|
||||
|
||||
@@ -13,9 +13,8 @@
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/hid/irs.h"
|
||||
#include "core/hle/service/hid/xcd.h"
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
@@ -25,8 +23,8 @@ constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
"IUser:NFCTagDetected");
|
||||
nfc_tag_load =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:NFCTagDetected");
|
||||
}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
@@ -65,10 +63,10 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
deactivate_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
|
||||
availability_change_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot, "IUser:AvailabilityChangeEvent");
|
||||
deactivate_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
|
||||
availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"IUser:AvailabilityChangeEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -166,7 +164,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(deactivate_event.readable);
|
||||
rb.PushCopyObjects(deactivate_event);
|
||||
}
|
||||
|
||||
void StopDetection(Kernel::HLERequestContext& ctx) {
|
||||
@@ -175,7 +173,7 @@ private:
|
||||
switch (device_state) {
|
||||
case DeviceState::TagFound:
|
||||
case DeviceState::TagNearby:
|
||||
deactivate_event.writable->Signal();
|
||||
deactivate_event->Signal();
|
||||
device_state = DeviceState::Initialized;
|
||||
break;
|
||||
case DeviceState::SearchingForTag:
|
||||
@@ -266,7 +264,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(availability_change_event.readable);
|
||||
rb.PushCopyObjects(availability_change_event);
|
||||
}
|
||||
|
||||
void GetRegisterInfo(Kernel::HLERequestContext& ctx) {
|
||||
@@ -321,8 +319,8 @@ private:
|
||||
const u32 npad_id{0}; // Player 1 controller
|
||||
State state{State::NonInitialized};
|
||||
DeviceState device_state{DeviceState::Initialized};
|
||||
Kernel::EventPair deactivate_event;
|
||||
Kernel::EventPair availability_change_event;
|
||||
Kernel::SharedPtr<Kernel::Event> deactivate_event;
|
||||
Kernel::SharedPtr<Kernel::Event> availability_change_event;
|
||||
const Module::Interface& nfp_interface;
|
||||
};
|
||||
|
||||
@@ -341,14 +339,12 @@ bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
|
||||
}
|
||||
|
||||
std::memcpy(&amiibo, buffer.data(), sizeof(amiibo));
|
||||
nfc_tag_load.writable->Signal();
|
||||
nfc_tag_load->Signal();
|
||||
return true;
|
||||
}
|
||||
|
||||
const Kernel::SharedPtr<Kernel::ReadableEvent>& Module::Interface::GetNFCEvent() const {
|
||||
return nfc_tag_load.readable;
|
||||
const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const {
|
||||
return nfc_tag_load;
|
||||
}
|
||||
|
||||
const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const {
|
||||
return amiibo;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Service::NFP {
|
||||
@@ -34,11 +33,11 @@ public:
|
||||
|
||||
void CreateUserInterface(Kernel::HLERequestContext& ctx);
|
||||
bool LoadAmiibo(const std::vector<u8>& buffer);
|
||||
const Kernel::SharedPtr<Kernel::ReadableEvent>& GetNFCEvent() const;
|
||||
const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const;
|
||||
const AmiiboFile& GetAmiiboBuffer() const;
|
||||
|
||||
private:
|
||||
Kernel::EventPair nfc_tag_load{};
|
||||
Kernel::SharedPtr<Kernel::Event> nfc_tag_load{};
|
||||
AmiiboFile amiibo{};
|
||||
|
||||
protected:
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/nifm/nifm.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
@@ -58,10 +56,8 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
"IRequest:Event1");
|
||||
event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
"IRequest:Event2");
|
||||
event1 = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IRequest:Event1");
|
||||
event2 = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IRequest:Event2");
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -92,7 +88,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(event1.readable, event2.readable);
|
||||
rb.PushCopyObjects(event1, event2);
|
||||
}
|
||||
|
||||
void Cancel(Kernel::HLERequestContext& ctx) {
|
||||
@@ -109,7 +105,7 @@ private:
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
Kernel::EventPair event1, event2;
|
||||
Kernel::SharedPtr<Kernel::Event> event1, event2;
|
||||
};
|
||||
|
||||
class INetworkProfile final : public ServiceFramework<INetworkProfile> {
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
#include <ctime>
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/nim/nim.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
@@ -140,18 +138,19 @@ public:
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
finished_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::OneShot,
|
||||
"IEnsureNetworkClockAvailabilityService:FinishEvent");
|
||||
finished_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||
"IEnsureNetworkClockAvailabilityService:FinishEvent");
|
||||
}
|
||||
|
||||
private:
|
||||
Kernel::EventPair finished_event;
|
||||
Kernel::SharedPtr<Kernel::Event> finished_event;
|
||||
|
||||
void StartTask(Kernel::HLERequestContext& ctx) {
|
||||
// No need to connect to the internet, just finish the task straight away.
|
||||
LOG_DEBUG(Service_NIM, "called");
|
||||
finished_event.writable->Signal();
|
||||
|
||||
finished_event->Signal();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
@@ -161,7 +160,7 @@ private:
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(finished_event.readable);
|
||||
rb.PushCopyObjects(finished_event);
|
||||
}
|
||||
|
||||
void GetResult(Kernel::HLERequestContext& ctx) {
|
||||
@@ -173,7 +172,8 @@ private:
|
||||
|
||||
void Cancel(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_NIM, "called");
|
||||
finished_event.writable->Clear();
|
||||
|
||||
finished_event->Clear();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/nvdrv/interface.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
|
||||
@@ -71,7 +69,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(query_event.readable);
|
||||
rb.PushCopyObjects(query_event);
|
||||
rb.Push<u32>(0);
|
||||
}
|
||||
|
||||
@@ -129,8 +127,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
|
||||
RegisterHandlers(functions);
|
||||
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
|
||||
"NVDRV::query_event");
|
||||
query_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "NVDRV::query_event");
|
||||
}
|
||||
|
||||
NVDRV::~NVDRV() = default;
|
||||
|
||||
@@ -5,13 +5,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
class WritableEvent;
|
||||
}
|
||||
|
||||
namespace Service::Nvidia {
|
||||
|
||||
class NVDRV final : public ServiceFramework<NVDRV> {
|
||||
@@ -34,7 +31,7 @@ private:
|
||||
|
||||
u64 pid{};
|
||||
|
||||
Kernel::EventPair query_event;
|
||||
Kernel::SharedPtr<Kernel::Event> query_event;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvidia
|
||||
|
||||
@@ -7,17 +7,14 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
|
||||
"BufferQueue NativeHandle");
|
||||
buffer_wait_event =
|
||||
Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "BufferQueue NativeHandle");
|
||||
}
|
||||
|
||||
BufferQueue::~BufferQueue() = default;
|
||||
@@ -31,7 +28,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
|
||||
buffer.status = Buffer::Status::Free;
|
||||
|
||||
queue.emplace_back(buffer);
|
||||
buffer_wait_event.writable->Signal();
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
|
||||
std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
|
||||
@@ -90,7 +87,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
|
||||
ASSERT(itr->status == Buffer::Status::Acquired);
|
||||
itr->status = Buffer::Status::Free;
|
||||
|
||||
buffer_wait_event.writable->Signal();
|
||||
buffer_wait_event->Signal();
|
||||
}
|
||||
|
||||
u32 BufferQueue::Query(QueryType type) {
|
||||
@@ -107,12 +104,4 @@ u32 BufferQueue::Query(QueryType type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::WritableEvent> BufferQueue::GetWritableBufferWaitEvent() const {
|
||||
return buffer_wait_event.writable;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> BufferQueue::GetBufferWaitEvent() const {
|
||||
return buffer_wait_event.readable;
|
||||
}
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/math_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
struct EventType;
|
||||
@@ -87,16 +86,16 @@ public:
|
||||
return id;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const;
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> GetBufferWaitEvent() const;
|
||||
Kernel::SharedPtr<Kernel::Event> GetBufferWaitEvent() const {
|
||||
return buffer_wait_event;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 id;
|
||||
u64 layer_id;
|
||||
|
||||
std::vector<Buffer> queue;
|
||||
Kernel::EventPair buffer_wait_event;
|
||||
Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
|
||||
};
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
@@ -86,8 +83,9 @@ u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) {
|
||||
return layer.buffer_queue->GetId();
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::GetVsyncEvent(u64 display_id) {
|
||||
return GetDisplay(display_id).vsync_event.readable;
|
||||
Kernel::SharedPtr<Kernel::Event> NVFlinger::GetVsyncEvent(u64 display_id) {
|
||||
const auto& display = GetDisplay(display_id);
|
||||
return display.vsync_event;
|
||||
}
|
||||
|
||||
std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const {
|
||||
@@ -119,7 +117,7 @@ Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) {
|
||||
void NVFlinger::Compose() {
|
||||
for (auto& display : displays) {
|
||||
// Trigger vsync for this display at the end of drawing
|
||||
SCOPE_EXIT({ display.vsync_event.writable->Signal(); });
|
||||
SCOPE_EXIT({ display.vsync_event->Signal(); });
|
||||
|
||||
// Don't do anything for displays without layers.
|
||||
if (display.layers.empty())
|
||||
@@ -166,8 +164,7 @@ Layer::~Layer() = default;
|
||||
|
||||
Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Pulse,
|
||||
fmt::format("Display VSync Event {}", id));
|
||||
vsync_event = Kernel::Event::Create(kernel, Kernel::ResetType::Pulse, "Display VSync Event");
|
||||
}
|
||||
|
||||
Display::~Display() = default;
|
||||
|
||||
@@ -10,17 +10,12 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class ReadableEvent;
|
||||
class WritableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::Nvidia {
|
||||
class Module;
|
||||
}
|
||||
@@ -45,7 +40,7 @@ struct Display {
|
||||
std::string name;
|
||||
|
||||
std::vector<Layer> layers;
|
||||
Kernel::EventPair vsync_event;
|
||||
Kernel::SharedPtr<Kernel::Event> vsync_event;
|
||||
};
|
||||
|
||||
class NVFlinger final {
|
||||
@@ -66,7 +61,7 @@ public:
|
||||
u32 GetBufferQueueId(u64 display_id, u64 layer_id);
|
||||
|
||||
/// Gets the vsync event for the specified display.
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> GetVsyncEvent(u64 display_id);
|
||||
Kernel::SharedPtr<Kernel::Event> GetVsyncEvent(u64 display_id);
|
||||
|
||||
/// Obtains a buffer queue identified by the id.
|
||||
std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const;
|
||||
|
||||
@@ -73,7 +73,7 @@ public:
|
||||
{3, nullptr, "Populate"},
|
||||
{4, nullptr, "PostBufferAsync"},
|
||||
{5, nullptr, "GetXferReport"},
|
||||
{6, nullptr, "PostBufferMultiAsync"},
|
||||
{6, nullptr, "Unknown2"},
|
||||
{7, nullptr, "Unknown3"},
|
||||
{8, nullptr, "Unknown4"},
|
||||
};
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
#include "common/swap.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
@@ -543,14 +542,12 @@ private:
|
||||
// Repeat TransactParcel DequeueBuffer when a buffer is available
|
||||
auto buffer_queue = nv_flinger->GetBufferQueue(id);
|
||||
std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
|
||||
ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer.");
|
||||
|
||||
IGBPDequeueBufferResponseParcel response{*slot};
|
||||
ctx.WriteBuffer(response.Serialize());
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
},
|
||||
buffer_queue->GetWritableBufferWaitEvent());
|
||||
buffer_queue->GetBufferWaitEvent());
|
||||
}
|
||||
} else if (transaction == TransactionId::RequestBuffer) {
|
||||
IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
@@ -145,7 +146,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
|
||||
const VAddr load_addr = next_load_addr;
|
||||
const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
|
||||
const auto tentative_next_load_addr =
|
||||
AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments, pm);
|
||||
AppLoader_NSO::LoadModule(*module_file, load_addr, should_pass_arguments, pm);
|
||||
if (!tentative_next_load_addr) {
|
||||
return ResultStatus::ErrorLoadingNSO;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
@@ -128,8 +129,9 @@ static constexpr u32 PageAlignSize(u32 size) {
|
||||
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
|
||||
}
|
||||
|
||||
static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,
|
||||
const std::string& name, VAddr load_base) {
|
||||
/*static*/ bool AppLoader_NRO::LoadNro(const std::vector<u8>& data, const std::string& name,
|
||||
VAddr load_base) {
|
||||
|
||||
if (data.size() < sizeof(NroHeader)) {
|
||||
return {};
|
||||
}
|
||||
@@ -187,7 +189,7 @@ static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,
|
||||
|
||||
// Load codeset for current process
|
||||
codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
|
||||
process.LoadModule(std::move(codeset), load_base);
|
||||
Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
|
||||
|
||||
// Register module with GDBStub
|
||||
GDBStub::RegisterModule(name, load_base, load_base);
|
||||
@@ -195,9 +197,8 @@ static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& file,
|
||||
VAddr load_base) {
|
||||
return LoadNroImpl(process, file.ReadAllBytes(), file.GetName(), load_base);
|
||||
bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
|
||||
return AppLoader_NRO::LoadNro(file.ReadAllBytes(), file.GetName(), load_base);
|
||||
}
|
||||
|
||||
ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
|
||||
@@ -208,7 +209,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
|
||||
// Load NRO
|
||||
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
|
||||
|
||||
if (!LoadNro(process, *file, base_address)) {
|
||||
if (!LoadNro(*file, base_address)) {
|
||||
return ResultStatus::ErrorLoadingNRO;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,6 @@ namespace FileSys {
|
||||
class NACP;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class Process;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
/// Loads an NRO file
|
||||
@@ -45,8 +41,10 @@ public:
|
||||
ResultStatus ReadTitle(std::string& title) override;
|
||||
bool IsRomFSUpdatable() const override;
|
||||
|
||||
static bool LoadNro(const std::vector<u8>& data, const std::string& name, VAddr load_base);
|
||||
|
||||
private:
|
||||
bool LoadNro(Kernel::Process& process, const FileSys::VfsFile& file, VAddr load_base);
|
||||
bool LoadNro(const FileSys::VfsFile& file, VAddr load_base);
|
||||
|
||||
std::vector<u8> icon_data;
|
||||
std::unique_ptr<FileSys::NACP> nacp;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
@@ -92,8 +93,7 @@ static constexpr u32 PageAlignSize(u32 size) {
|
||||
return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
|
||||
}
|
||||
|
||||
std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
|
||||
const FileSys::VfsFile& file, VAddr load_base,
|
||||
std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAddr load_base,
|
||||
bool should_pass_arguments,
|
||||
std::optional<FileSys::PatchManager> pm) {
|
||||
if (file.GetSize() < sizeof(NsoHeader))
|
||||
@@ -166,7 +166,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
|
||||
|
||||
// Load codeset for current process
|
||||
codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
|
||||
process.LoadModule(std::move(codeset), load_base);
|
||||
Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
|
||||
|
||||
// Register module with GDBStub
|
||||
GDBStub::RegisterModule(file.GetName(), load_base, load_base);
|
||||
@@ -181,7 +181,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
|
||||
|
||||
// Load module
|
||||
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
|
||||
if (!LoadModule(process, *file, base_address, true)) {
|
||||
if (!LoadModule(*file, base_address, true)) {
|
||||
return ResultStatus::ErrorLoadingNSO;
|
||||
}
|
||||
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
#include "core/loader/linker.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Kernel {
|
||||
class Process;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
|
||||
constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
|
||||
@@ -41,8 +37,8 @@ public:
|
||||
return IdentifyType(file);
|
||||
}
|
||||
|
||||
static std::optional<VAddr> LoadModule(Kernel::Process& process, const FileSys::VfsFile& file,
|
||||
VAddr load_base, bool should_pass_arguments,
|
||||
static std::optional<VAddr> LoadModule(const FileSys::VfsFile& file, VAddr load_base,
|
||||
bool should_pass_arguments,
|
||||
std::optional<FileSys::PatchManager> pm = {});
|
||||
|
||||
ResultStatus Load(Kernel::Process& process) override;
|
||||
|
||||
142
src/video_core/command_processor.cpp
Normal file
142
src/video_core/command_processor.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/tracer/recorder.h"
|
||||
#include "video_core/command_processor.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/kepler_memory.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/maxwell_compute.h"
|
||||
#include "video_core/engines/maxwell_dma.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
enum class BufferMethods {
|
||||
BindObject = 0,
|
||||
CountBufferMethods = 0x40,
|
||||
};
|
||||
|
||||
MICROPROFILE_DEFINE(ProcessCommandLists, "GPU", "Execute command buffer", MP_RGB(128, 128, 192));
|
||||
|
||||
void GPU::ProcessCommandLists(const std::vector<CommandListHeader>& commands) {
|
||||
MICROPROFILE_SCOPE(ProcessCommandLists);
|
||||
|
||||
// On entering GPU code, assume all memory may be touched by the ARM core.
|
||||
maxwell_3d->dirty_flags.OnMemoryWrite();
|
||||
|
||||
auto WriteReg = [this](u32 method, u32 subchannel, u32 value, u32 remaining_params) {
|
||||
LOG_TRACE(HW_GPU,
|
||||
"Processing method {:08X} on subchannel {} value "
|
||||
"{:08X} remaining params {}",
|
||||
method, subchannel, value, remaining_params);
|
||||
|
||||
ASSERT(subchannel < bound_engines.size());
|
||||
|
||||
if (method == static_cast<u32>(BufferMethods::BindObject)) {
|
||||
// Bind the current subchannel to the desired engine id.
|
||||
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", subchannel, value);
|
||||
bound_engines[subchannel] = static_cast<EngineID>(value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method < static_cast<u32>(BufferMethods::CountBufferMethods)) {
|
||||
// TODO(Subv): Research and implement these methods.
|
||||
LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
const EngineID engine = bound_engines[subchannel];
|
||||
|
||||
switch (engine) {
|
||||
case EngineID::FERMI_TWOD_A:
|
||||
fermi_2d->WriteReg(method, value);
|
||||
break;
|
||||
case EngineID::MAXWELL_B:
|
||||
maxwell_3d->WriteReg(method, value, remaining_params);
|
||||
break;
|
||||
case EngineID::MAXWELL_COMPUTE_B:
|
||||
maxwell_compute->WriteReg(method, value);
|
||||
break;
|
||||
case EngineID::MAXWELL_DMA_COPY_A:
|
||||
maxwell_dma->WriteReg(method, value);
|
||||
break;
|
||||
case EngineID::KEPLER_INLINE_TO_MEMORY_B:
|
||||
kepler_memory->WriteReg(method, value);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented engine");
|
||||
}
|
||||
};
|
||||
|
||||
for (auto entry : commands) {
|
||||
Tegra::GPUVAddr address = entry.Address();
|
||||
u32 size = entry.sz;
|
||||
const std::optional<VAddr> head_address = memory_manager->GpuToCpuAddress(address);
|
||||
VAddr current_addr = *head_address;
|
||||
while (current_addr < *head_address + size * sizeof(CommandHeader)) {
|
||||
const CommandHeader header = {Memory::Read32(current_addr)};
|
||||
current_addr += sizeof(u32);
|
||||
|
||||
switch (header.mode.Value()) {
|
||||
case SubmissionMode::IncreasingOld:
|
||||
case SubmissionMode::Increasing: {
|
||||
// Increase the method value with each argument.
|
||||
for (unsigned i = 0; i < header.arg_count; ++i) {
|
||||
WriteReg(header.method + i, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - i - 1);
|
||||
current_addr += sizeof(u32);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SubmissionMode::NonIncreasingOld:
|
||||
case SubmissionMode::NonIncreasing: {
|
||||
// Use the same method value for all arguments.
|
||||
for (unsigned i = 0; i < header.arg_count; ++i) {
|
||||
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - i - 1);
|
||||
current_addr += sizeof(u32);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SubmissionMode::IncreaseOnce: {
|
||||
ASSERT(header.arg_count.Value() >= 1);
|
||||
|
||||
// Use the original method for the first argument and then the next method for all
|
||||
// other arguments.
|
||||
WriteReg(header.method, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - 1);
|
||||
current_addr += sizeof(u32);
|
||||
|
||||
for (unsigned i = 1; i < header.arg_count; ++i) {
|
||||
WriteReg(header.method + 1, header.subchannel, Memory::Read32(current_addr),
|
||||
header.arg_count - i - 1);
|
||||
current_addr += sizeof(u32);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SubmissionMode::Inline: {
|
||||
// The register value is stored in the bits 16-28 as an immediate
|
||||
WriteReg(header.method, header.subchannel, header.inline_data, 0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Tegra
|
||||
@@ -42,7 +42,6 @@ public:
|
||||
static constexpr std::size_t NumVertexArrays = 32;
|
||||
static constexpr std::size_t NumVertexAttributes = 32;
|
||||
static constexpr std::size_t NumTextureSamplers = 32;
|
||||
static constexpr std::size_t NumClipDistances = 8;
|
||||
static constexpr std::size_t MaxShaderProgram = 6;
|
||||
static constexpr std::size_t MaxShaderStage = 5;
|
||||
// Maximum number of const buffers per shader stage.
|
||||
|
||||
@@ -128,8 +128,10 @@ enum class BufferMethods {
|
||||
};
|
||||
|
||||
void GPU::CallMethod(const MethodCall& method_call) {
|
||||
LOG_TRACE(HW_GPU, "Processing method {:08X} on subchannel {}", method_call.method,
|
||||
method_call.subchannel);
|
||||
LOG_TRACE(HW_GPU,
|
||||
"Processing method {:08X} on subchannel {} value "
|
||||
"{:08X} remaining params {}",
|
||||
MethCall.method, MethCall.subchannel, value, remaining_params);
|
||||
|
||||
ASSERT(method_call.subchannel < bound_engines.size());
|
||||
|
||||
@@ -141,12 +143,6 @@ void GPU::CallMethod(const MethodCall& method_call) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (method_call.method < static_cast<u32>(BufferMethods::CountBufferMethods)) {
|
||||
// TODO(Subv): Research and implement these methods.
|
||||
LOG_ERROR(HW_GPU, "Special buffer methods other than Bind are not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
const EngineID engine = bound_engines[method_call.subchannel];
|
||||
|
||||
switch (engine) {
|
||||
|
||||
@@ -88,6 +88,19 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
|
||||
state.texture_units[i].sampler = texture_samplers[i].sampler.handle;
|
||||
}
|
||||
|
||||
GLint ext_num;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &ext_num);
|
||||
for (GLint i = 0; i < ext_num; i++) {
|
||||
const std::string_view extension{
|
||||
reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i))};
|
||||
|
||||
if (extension == "GL_ARB_direct_state_access") {
|
||||
has_ARB_direct_state_access = true;
|
||||
} else if (extension == "GL_ARB_multi_bind") {
|
||||
has_ARB_multi_bind = true;
|
||||
}
|
||||
}
|
||||
|
||||
OpenGLState::ApplyDefaultState();
|
||||
|
||||
// Create render framebuffer
|
||||
@@ -282,7 +295,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
// shaders. The constbuffer bindpoint starts after the shader stage configuration bind points.
|
||||
u32 current_constbuffer_bindpoint = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage;
|
||||
u32 current_texture_bindpoint = 0;
|
||||
std::array<bool, Maxwell::NumClipDistances> clip_distances{};
|
||||
|
||||
for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
||||
const auto& shader_config = gpu.regs.shader_config[index];
|
||||
@@ -343,22 +355,12 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader,
|
||||
primitive_mode, current_texture_bindpoint);
|
||||
|
||||
// Workaround for Intel drivers.
|
||||
// When a clip distance is enabled but not set in the shader it crops parts of the screen
|
||||
// (sometimes it's half the screen, sometimes three quarters). To avoid this, enable the
|
||||
// clip distances only when it's written by a shader stage.
|
||||
for (std::size_t i = 0; i < Maxwell::NumClipDistances; ++i) {
|
||||
clip_distances[i] |= shader->GetShaderEntries().clip_distances[i];
|
||||
}
|
||||
|
||||
// When VertexA is enabled, we have dual vertex shaders
|
||||
if (program == Maxwell::ShaderProgram::VertexA) {
|
||||
// VertexB was combined with VertexA, so we skip the VertexB iteration
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
SyncClipEnabled(clip_distances);
|
||||
}
|
||||
|
||||
std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
|
||||
@@ -441,7 +443,7 @@ void RasterizerOpenGL::ConfigureFramebuffers(OpenGLState& current_state, bool us
|
||||
// TODO(bunnei): Figure out how the below register works. According to envytools, this should be
|
||||
// used to enable multiple render targets. However, it is left unset on all games that I have
|
||||
// tested.
|
||||
UNIMPLEMENTED_IF(regs.rt_separate_frag_data != 0);
|
||||
ASSERT_MSG(regs.rt_separate_frag_data == 0, "Unimplemented");
|
||||
|
||||
// Bind the framebuffer surfaces
|
||||
current_state.draw.draw_framebuffer = framebuffer.handle;
|
||||
@@ -640,6 +642,7 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
SyncCullMode();
|
||||
SyncPrimitiveRestart();
|
||||
SyncScissorTest(state);
|
||||
SyncClipEnabled();
|
||||
// Alpha Testing is synced on shaders.
|
||||
SyncTransformFeedback();
|
||||
SyncPointState();
|
||||
@@ -1016,23 +1019,20 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
|
||||
state.depth_clamp.near_plane = regs.view_volume_clip_control.depth_clamp_near != 0;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncClipEnabled(
|
||||
const std::array<bool, Maxwell::Regs::NumClipDistances>& clip_mask) {
|
||||
|
||||
void RasterizerOpenGL::SyncClipEnabled() {
|
||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||
const std::array<bool, Maxwell::Regs::NumClipDistances> reg_state{
|
||||
regs.clip_distance_enabled.c0 != 0, regs.clip_distance_enabled.c1 != 0,
|
||||
regs.clip_distance_enabled.c2 != 0, regs.clip_distance_enabled.c3 != 0,
|
||||
regs.clip_distance_enabled.c4 != 0, regs.clip_distance_enabled.c5 != 0,
|
||||
regs.clip_distance_enabled.c6 != 0, regs.clip_distance_enabled.c7 != 0};
|
||||
|
||||
for (std::size_t i = 0; i < Maxwell::Regs::NumClipDistances; ++i) {
|
||||
state.clip_distance[i] = reg_state[i] && clip_mask[i];
|
||||
}
|
||||
state.clip_distance[0] = regs.clip_distance_enabled.c0 != 0;
|
||||
state.clip_distance[1] = regs.clip_distance_enabled.c1 != 0;
|
||||
state.clip_distance[2] = regs.clip_distance_enabled.c2 != 0;
|
||||
state.clip_distance[3] = regs.clip_distance_enabled.c3 != 0;
|
||||
state.clip_distance[4] = regs.clip_distance_enabled.c4 != 0;
|
||||
state.clip_distance[5] = regs.clip_distance_enabled.c5 != 0;
|
||||
state.clip_distance[6] = regs.clip_distance_enabled.c6 != 0;
|
||||
state.clip_distance[7] = regs.clip_distance_enabled.c7 != 0;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncClipCoef() {
|
||||
UNIMPLEMENTED();
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncCullMode() {
|
||||
|
||||
@@ -60,6 +60,20 @@ public:
|
||||
bool AccelerateDrawBatch(bool is_indexed) override;
|
||||
void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) override;
|
||||
|
||||
/// OpenGL shader generated for a given Maxwell register state
|
||||
struct MaxwellShader {
|
||||
/// OpenGL shader resource
|
||||
OGLProgram shader;
|
||||
};
|
||||
|
||||
struct VertexShader {
|
||||
OGLShader shader;
|
||||
};
|
||||
|
||||
struct FragmentShader {
|
||||
OGLShader shader;
|
||||
};
|
||||
|
||||
/// Maximum supported size that a constbuffer can have in bytes.
|
||||
static constexpr std::size_t MaxConstbufferSize = 0x10000;
|
||||
static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0,
|
||||
@@ -128,8 +142,7 @@ private:
|
||||
void SyncViewport(OpenGLState& current_state);
|
||||
|
||||
/// Syncs the clip enabled status to match the guest state
|
||||
void SyncClipEnabled(
|
||||
const std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances>& clip_mask);
|
||||
void SyncClipEnabled();
|
||||
|
||||
/// Syncs the clip coefficients to match the guest state
|
||||
void SyncClipCoef();
|
||||
@@ -180,6 +193,9 @@ private:
|
||||
/// but are needed for correct emulation
|
||||
void CheckExtensions();
|
||||
|
||||
bool has_ARB_direct_state_access = false;
|
||||
bool has_ARB_multi_bind = false;
|
||||
|
||||
OpenGLState state;
|
||||
|
||||
RasterizerCacheOpenGL res_cache;
|
||||
|
||||
@@ -405,6 +405,138 @@ void SwizzleFunc(const MortonSwizzleMode& mode, const SurfaceParams& params,
|
||||
}
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_BlitSurface, "OpenGL", "BlitSurface", MP_RGB(128, 192, 64));
|
||||
static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
|
||||
GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0,
|
||||
GLenum dst_attachment = 0, std::size_t cubemap_face = 0) {
|
||||
MICROPROFILE_SCOPE(OpenGL_BlitSurface);
|
||||
|
||||
const auto& src_params{src_surface->GetSurfaceParams()};
|
||||
const auto& dst_params{dst_surface->GetSurfaceParams()};
|
||||
|
||||
OpenGLState prev_state{OpenGLState::GetCurState()};
|
||||
SCOPE_EXIT({ prev_state.Apply(); });
|
||||
|
||||
OpenGLState state;
|
||||
state.draw.read_framebuffer = read_fb_handle;
|
||||
state.draw.draw_framebuffer = draw_fb_handle;
|
||||
// Set sRGB enabled if the destination surfaces need it
|
||||
state.framebuffer_srgb.enabled = dst_params.srgb_conversion;
|
||||
state.ApplyFramebufferState();
|
||||
|
||||
u32 buffers{};
|
||||
|
||||
if (src_params.type == SurfaceType::ColorTexture) {
|
||||
switch (src_params.target) {
|
||||
case SurfaceTarget::Texture2D:
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
|
||||
GL_TEXTURE_2D, src_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
0, 0);
|
||||
break;
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
glFramebufferTexture2D(
|
||||
GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
|
||||
static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face),
|
||||
src_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(
|
||||
GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0);
|
||||
break;
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
|
||||
src_surface->Texture().handle, 0, 0);
|
||||
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0);
|
||||
break;
|
||||
case SurfaceTarget::Texture3D:
|
||||
glFramebufferTexture3D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
|
||||
SurfaceTargetToGL(src_params.target),
|
||||
src_surface->Texture().handle, 0, 0);
|
||||
glFramebufferTexture3D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
SurfaceTargetToGL(src_params.target), 0, 0, 0);
|
||||
break;
|
||||
default:
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
|
||||
GL_TEXTURE_2D, src_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (dst_params.target) {
|
||||
case SurfaceTarget::Texture2D:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
|
||||
GL_TEXTURE_2D, dst_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
0, 0);
|
||||
break;
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
glFramebufferTexture2D(
|
||||
GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
|
||||
static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face),
|
||||
dst_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(
|
||||
GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0);
|
||||
break;
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
|
||||
dst_surface->Texture().handle, 0, 0);
|
||||
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0);
|
||||
break;
|
||||
|
||||
case SurfaceTarget::Texture3D:
|
||||
glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
|
||||
SurfaceTargetToGL(dst_params.target),
|
||||
dst_surface->Texture().handle, 0, 0);
|
||||
glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
SurfaceTargetToGL(dst_params.target), 0, 0, 0);
|
||||
break;
|
||||
default:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
|
||||
GL_TEXTURE_2D, dst_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
buffers = GL_COLOR_BUFFER_BIT;
|
||||
} else if (src_params.type == SurfaceType::Depth) {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
|
||||
GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
|
||||
src_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
|
||||
GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
|
||||
dst_surface->Texture().handle, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
buffers = GL_DEPTH_BUFFER_BIT;
|
||||
} else if (src_params.type == SurfaceType::DepthStencil) {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
|
||||
GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
src_surface->Texture().handle, 0);
|
||||
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
|
||||
GL_TEXTURE_2D, 0, 0);
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
|
||||
dst_surface->Texture().handle, 0);
|
||||
|
||||
buffers = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
|
||||
}
|
||||
|
||||
const auto& rect{src_params.GetRect()};
|
||||
glBlitFramebuffer(rect.left, rect.bottom, rect.right, rect.top, rect.left, rect.bottom,
|
||||
rect.right, rect.top, buffers,
|
||||
buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void FastCopySurface(const Surface& src_surface, const Surface& dst_surface) {
|
||||
const auto& src_params{src_surface->GetSurfaceParams()};
|
||||
const auto& dst_params{dst_surface->GetSurfaceParams()};
|
||||
@@ -709,10 +841,9 @@ void CachedSurface::LoadGLBuffer() {
|
||||
const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl};
|
||||
gl_buffer[0].assign(texture_src_data, texture_src_data_end);
|
||||
}
|
||||
for (u32 i = 0; i < params.max_mip_level; i++) {
|
||||
for (u32 i = 0; i < params.max_mip_level; i++)
|
||||
ConvertFormatAsNeeded_LoadGLBuffer(gl_buffer[i], params.pixel_format, params.MipWidth(i),
|
||||
params.MipHeight(i), params.MipDepth(i));
|
||||
}
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
|
||||
@@ -1032,10 +1163,7 @@ void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface,
|
||||
const Surface& dst_surface) {
|
||||
const auto& src_params{src_surface->GetSurfaceParams()};
|
||||
const auto& dst_params{dst_surface->GetSurfaceParams()};
|
||||
|
||||
// Flush enough memory for both the source and destination surface
|
||||
FlushRegion(src_params.addr, std::max(src_params.MemorySize(), dst_params.MemorySize()));
|
||||
|
||||
FlushRegion(src_params.addr, dst_params.MemorySize());
|
||||
LoadSurface(dst_surface);
|
||||
}
|
||||
|
||||
@@ -1061,9 +1189,20 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
|
||||
return new_surface;
|
||||
}
|
||||
|
||||
// If the format is the same, just do a framebuffer blit. This is significantly faster than
|
||||
// using PBOs. The is also likely less accurate, as textures will be converted rather than
|
||||
// reinterpreted. When use_accurate_gpu_emulation setting is enabled, perform a more accurate
|
||||
// surface copy, where pixels are reinterpreted as a new format (without conversion). This
|
||||
// code path uses OpenGL PBOs and is quite slow.
|
||||
const bool is_blit{old_params.pixel_format == new_params.pixel_format};
|
||||
|
||||
switch (new_params.target) {
|
||||
case SurfaceTarget::Texture2D:
|
||||
CopySurface(old_surface, new_surface, copy_pbo.handle);
|
||||
if (is_blit) {
|
||||
BlitSurface(old_surface, new_surface, read_framebuffer.handle, draw_framebuffer.handle);
|
||||
} else {
|
||||
CopySurface(old_surface, new_surface, copy_pbo.handle);
|
||||
}
|
||||
break;
|
||||
case SurfaceTarget::Texture3D:
|
||||
AccurateCopySurface(old_surface, new_surface);
|
||||
|
||||
@@ -201,53 +201,14 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ShaderScopedScope {
|
||||
public:
|
||||
explicit ShaderScopedScope(T& writer, std::string_view begin_expr, std::string end_expr)
|
||||
: writer(writer), end_expr(std::move(end_expr)) {
|
||||
|
||||
if (begin_expr.empty()) {
|
||||
writer.AddLine('{');
|
||||
} else {
|
||||
writer.AddExpression(begin_expr);
|
||||
writer.AddLine(" {");
|
||||
}
|
||||
++writer.scope;
|
||||
}
|
||||
|
||||
ShaderScopedScope(const ShaderScopedScope&) = delete;
|
||||
|
||||
~ShaderScopedScope() {
|
||||
--writer.scope;
|
||||
if (end_expr.empty()) {
|
||||
writer.AddLine('}');
|
||||
} else {
|
||||
writer.AddExpression("} ");
|
||||
writer.AddExpression(end_expr);
|
||||
writer.AddLine(';');
|
||||
}
|
||||
}
|
||||
|
||||
ShaderScopedScope& operator=(const ShaderScopedScope&) = delete;
|
||||
|
||||
private:
|
||||
T& writer;
|
||||
std::string end_expr;
|
||||
};
|
||||
|
||||
class ShaderWriter {
|
||||
public:
|
||||
void AddExpression(std::string_view text) {
|
||||
void AddLine(std::string_view text) {
|
||||
DEBUG_ASSERT(scope >= 0);
|
||||
if (!text.empty()) {
|
||||
AppendIndentation();
|
||||
}
|
||||
shader_source += text;
|
||||
}
|
||||
|
||||
void AddLine(std::string_view text) {
|
||||
AddExpression(text);
|
||||
AddNewLine();
|
||||
}
|
||||
|
||||
@@ -267,11 +228,6 @@ public:
|
||||
return std::move(shader_source);
|
||||
}
|
||||
|
||||
ShaderScopedScope<ShaderWriter> Scope(std::string_view begin_expr = {},
|
||||
std::string end_expr = {}) {
|
||||
return ShaderScopedScope(*this, begin_expr, end_expr);
|
||||
}
|
||||
|
||||
int scope = 0;
|
||||
|
||||
private:
|
||||
@@ -355,7 +311,7 @@ public:
|
||||
// Default - do nothing
|
||||
return value;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unimplemented conversion size: {}", static_cast<u32>(size));
|
||||
UNIMPLEMENTED_MSG("Unimplemented conversion size: {}", static_cast<u32>(size));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,7 +525,6 @@ public:
|
||||
((header.vtg.clip_distances >> index) & 1) == 0,
|
||||
"Shader is setting gl_ClipDistance{} without enabling it in the header", index);
|
||||
|
||||
clip_distances[index] = true;
|
||||
fixed_pipeline_output_attributes_used.insert(attribute);
|
||||
shader.AddLine(dest + '[' + std::to_string(index) + "] = " + src + ';');
|
||||
break;
|
||||
@@ -647,11 +602,6 @@ public:
|
||||
return used_samplers;
|
||||
}
|
||||
|
||||
/// Returns an array of the used clip distances.
|
||||
const std::array<bool, Maxwell::NumClipDistances>& GetClipDistances() const {
|
||||
return clip_distances;
|
||||
}
|
||||
|
||||
/// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
|
||||
/// necessary.
|
||||
std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
|
||||
@@ -860,12 +810,14 @@ private:
|
||||
}
|
||||
|
||||
if (precise && stage != Maxwell3D::Regs::ShaderStage::Fragment) {
|
||||
const auto scope = shader.Scope();
|
||||
|
||||
shader.AddLine('{');
|
||||
++shader.scope;
|
||||
// This avoids optimizations of constant propagation and keeps the code as the original
|
||||
// Sadly using the precise keyword causes "linking" errors on fragment shaders.
|
||||
shader.AddLine("precise float tmp = " + src + ';');
|
||||
shader.AddLine(dest + " = tmp;");
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
} else {
|
||||
shader.AddLine(dest + " = " + src + ';');
|
||||
}
|
||||
@@ -1023,7 +975,6 @@ private:
|
||||
const std::string& suffix;
|
||||
const Tegra::Shader::Header& header;
|
||||
std::unordered_set<Attribute::Index> fixed_pipeline_output_attributes_used;
|
||||
std::array<bool, Maxwell::NumClipDistances> clip_distances{};
|
||||
u64 local_memory_size;
|
||||
};
|
||||
|
||||
@@ -1046,8 +997,7 @@ public:
|
||||
|
||||
/// Returns entries in the shader that are useful for external functions
|
||||
ShaderEntries GetEntries() const {
|
||||
return {regs.GetConstBuffersDeclarations(), regs.GetSamplers(), regs.GetClipDistances(),
|
||||
shader_length};
|
||||
return {regs.GetConstBuffersDeclarations(), regs.GetSamplers(), shader_length};
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -1343,7 +1293,15 @@ private:
|
||||
regs.SetRegisterToInteger(dest, true, 0, result, 1, 1);
|
||||
}
|
||||
|
||||
void WriteTexsInstruction(const Instruction& instr, const std::string& texture) {
|
||||
void WriteTexsInstruction(const Instruction& instr, const std::string& coord,
|
||||
const std::string& texture) {
|
||||
// Add an extra scope and declare the texture coords inside to prevent
|
||||
// overwriting them in case they are used as outputs of the texs instruction.
|
||||
shader.AddLine('{');
|
||||
++shader.scope;
|
||||
shader.AddLine(coord);
|
||||
shader.AddLine("vec4 texture_tmp = " + texture + ';');
|
||||
|
||||
// TEXS has two destination registers and a swizzle. The first two elements in the swizzle
|
||||
// go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1
|
||||
|
||||
@@ -1355,17 +1313,19 @@ private:
|
||||
|
||||
if (written_components < 2) {
|
||||
// Write the first two swizzle components to gpr0 and gpr0+1
|
||||
regs.SetRegisterToFloat(instr.gpr0, component, texture, 1, 4, false,
|
||||
regs.SetRegisterToFloat(instr.gpr0, component, "texture_tmp", 1, 4, false,
|
||||
written_components % 2);
|
||||
} else {
|
||||
ASSERT(instr.texs.HasTwoDestinations());
|
||||
// Write the rest of the swizzle components to gpr28 and gpr28+1
|
||||
regs.SetRegisterToFloat(instr.gpr28, component, texture, 1, 4, false,
|
||||
regs.SetRegisterToFloat(instr.gpr28, component, "texture_tmp", 1, 4, false,
|
||||
written_components % 2);
|
||||
}
|
||||
|
||||
++written_components;
|
||||
}
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
}
|
||||
|
||||
static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) {
|
||||
@@ -1388,10 +1348,12 @@ private:
|
||||
* top.
|
||||
*/
|
||||
void EmitPushToFlowStack(u32 target) {
|
||||
const auto scope = shader.Scope();
|
||||
|
||||
shader.AddLine('{');
|
||||
++shader.scope;
|
||||
shader.AddLine("flow_stack[flow_stack_top] = " + std::to_string(target) + "u;");
|
||||
shader.AddLine("flow_stack_top++;");
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1399,11 +1361,13 @@ private:
|
||||
* popped address and decrementing the stack top.
|
||||
*/
|
||||
void EmitPopFromFlowStack() {
|
||||
const auto scope = shader.Scope();
|
||||
|
||||
shader.AddLine('{');
|
||||
++shader.scope;
|
||||
shader.AddLine("flow_stack_top--;");
|
||||
shader.AddLine("jmp_to = flow_stack[flow_stack_top];");
|
||||
shader.AddLine("break;");
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
}
|
||||
|
||||
/// Writes the output values from a fragment shader to the corresponding GLSL output variables.
|
||||
@@ -1515,161 +1479,6 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<size_t, std::string> ValidateAndGetCoordinateElement(
|
||||
const Tegra::Shader::TextureType texture_type, const bool depth_compare,
|
||||
const bool is_array, const bool lod_bias_enabled, size_t max_coords, size_t max_inputs) {
|
||||
const size_t coord_count = TextureCoordinates(texture_type);
|
||||
|
||||
size_t total_coord_count = coord_count + (is_array ? 1 : 0) + (depth_compare ? 1 : 0);
|
||||
const size_t total_reg_count = total_coord_count + (lod_bias_enabled ? 1 : 0);
|
||||
if (total_coord_count > max_coords || total_reg_count > max_inputs) {
|
||||
UNIMPLEMENTED_MSG("Unsupported Texture operation");
|
||||
total_coord_count = std::min(total_coord_count, max_coords);
|
||||
}
|
||||
// 1D.DC opengl is using a vec3 but 2nd component is ignored later.
|
||||
total_coord_count +=
|
||||
(depth_compare && !is_array && texture_type == Tegra::Shader::TextureType::Texture1D)
|
||||
? 1
|
||||
: 0;
|
||||
|
||||
constexpr std::array<const char*, 5> coord_container{
|
||||
{"", "float coord = (", "vec2 coord = vec2(", "vec3 coord = vec3(",
|
||||
"vec4 coord = vec4("}};
|
||||
|
||||
return std::pair<size_t, std::string>(coord_count, coord_container[total_coord_count]);
|
||||
}
|
||||
|
||||
std::string GetTextureCode(const Tegra::Shader::Instruction& instr,
|
||||
const Tegra::Shader::TextureType texture_type,
|
||||
const Tegra::Shader::TextureProcessMode process_mode,
|
||||
const bool depth_compare, const bool is_array,
|
||||
const size_t bias_offset) {
|
||||
|
||||
if ((texture_type == Tegra::Shader::TextureType::Texture3D &&
|
||||
(is_array || depth_compare)) ||
|
||||
(texture_type == Tegra::Shader::TextureType::TextureCube && is_array &&
|
||||
depth_compare)) {
|
||||
UNIMPLEMENTED_MSG("This method is not supported.");
|
||||
}
|
||||
|
||||
const std::string sampler =
|
||||
GetSampler(instr.sampler, texture_type, is_array, depth_compare);
|
||||
|
||||
const bool lod_needed = process_mode == Tegra::Shader::TextureProcessMode::LZ ||
|
||||
process_mode == Tegra::Shader::TextureProcessMode::LL ||
|
||||
process_mode == Tegra::Shader::TextureProcessMode::LLA;
|
||||
|
||||
const bool gl_lod_supported = !(
|
||||
(texture_type == Tegra::Shader::TextureType::Texture2D && is_array && depth_compare) ||
|
||||
(texture_type == Tegra::Shader::TextureType::TextureCube && !is_array &&
|
||||
depth_compare));
|
||||
|
||||
const std::string read_method = lod_needed && gl_lod_supported ? "textureLod(" : "texture(";
|
||||
std::string texture = read_method + sampler + ", coord";
|
||||
|
||||
if (process_mode != Tegra::Shader::TextureProcessMode::None) {
|
||||
if (process_mode == Tegra::Shader::TextureProcessMode::LZ) {
|
||||
if (gl_lod_supported) {
|
||||
texture += ", 0";
|
||||
} else {
|
||||
// Lod 0 is emulated by a big negative bias
|
||||
// in scenarios that are not supported by glsl
|
||||
texture += ", -1000";
|
||||
}
|
||||
} else {
|
||||
// If present, lod or bias are always stored in the register indexed by the
|
||||
// gpr20
|
||||
// field with an offset depending on the usage of the other registers
|
||||
texture += ',' + regs.GetRegisterAsFloat(instr.gpr20.Value() + bias_offset);
|
||||
}
|
||||
}
|
||||
texture += ")";
|
||||
return texture;
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> GetTEXCode(
|
||||
const Instruction& instr, const Tegra::Shader::TextureType texture_type,
|
||||
const Tegra::Shader::TextureProcessMode process_mode, const bool depth_compare,
|
||||
const bool is_array) {
|
||||
const bool lod_bias_enabled = (process_mode != Tegra::Shader::TextureProcessMode::None &&
|
||||
process_mode != Tegra::Shader::TextureProcessMode::LZ);
|
||||
|
||||
const auto [coord_count, coord_dcl] = ValidateAndGetCoordinateElement(
|
||||
texture_type, depth_compare, is_array, lod_bias_enabled, 4, 5);
|
||||
// If enabled arrays index is always stored in the gpr8 field
|
||||
const u64 array_register = instr.gpr8.Value();
|
||||
// First coordinate index is the gpr8 or gpr8 + 1 when arrays are used
|
||||
const u64 coord_register = array_register + (is_array ? 1 : 0);
|
||||
|
||||
std::string coord = coord_dcl;
|
||||
for (size_t i = 0; i < coord_count;) {
|
||||
coord += regs.GetRegisterAsFloat(coord_register + i);
|
||||
++i;
|
||||
if (i != coord_count) {
|
||||
coord += ',';
|
||||
}
|
||||
}
|
||||
// 1D.DC in opengl the 2nd component is ignored.
|
||||
if (depth_compare && !is_array && texture_type == Tegra::Shader::TextureType::Texture1D) {
|
||||
coord += ",0.0";
|
||||
}
|
||||
if (depth_compare) {
|
||||
// Depth is always stored in the register signaled by gpr20
|
||||
// or in the next register if lod or bias are used
|
||||
const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
|
||||
coord += ',' + regs.GetRegisterAsFloat(depth_register);
|
||||
}
|
||||
if (is_array) {
|
||||
coord += ',' + regs.GetRegisterAsInteger(array_register);
|
||||
}
|
||||
coord += ");";
|
||||
return std::make_pair(
|
||||
coord, GetTextureCode(instr, texture_type, process_mode, depth_compare, is_array, 0));
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> GetTEXSCode(
|
||||
const Instruction& instr, const Tegra::Shader::TextureType texture_type,
|
||||
const Tegra::Shader::TextureProcessMode process_mode, const bool depth_compare,
|
||||
const bool is_array) {
|
||||
const bool lod_bias_enabled = (process_mode != Tegra::Shader::TextureProcessMode::None &&
|
||||
process_mode != Tegra::Shader::TextureProcessMode::LZ);
|
||||
|
||||
const auto [coord_count, coord_dcl] = ValidateAndGetCoordinateElement(
|
||||
texture_type, depth_compare, is_array, lod_bias_enabled, 4, 4);
|
||||
// If enabled arrays index is always stored in the gpr8 field
|
||||
const u64 array_register = instr.gpr8.Value();
|
||||
// First coordinate index is stored in gpr8 field or (gpr8 + 1) when arrays are used
|
||||
const u64 coord_register = array_register + (is_array ? 1 : 0);
|
||||
const u64 last_coord_register =
|
||||
(is_array || !(lod_bias_enabled || depth_compare) || (coord_count > 2))
|
||||
? static_cast<u64>(instr.gpr20.Value())
|
||||
: coord_register + 1;
|
||||
|
||||
std::string coord = coord_dcl;
|
||||
for (size_t i = 0; i < coord_count; ++i) {
|
||||
const bool last = (i == (coord_count - 1)) && (coord_count > 1);
|
||||
coord += regs.GetRegisterAsFloat(last ? last_coord_register : coord_register + i);
|
||||
if (!last) {
|
||||
coord += ',';
|
||||
}
|
||||
}
|
||||
|
||||
if (depth_compare) {
|
||||
// Depth is always stored in the register signaled by gpr20
|
||||
// or in the next register if lod or bias are used
|
||||
const u64 depth_register = instr.gpr20.Value() + (lod_bias_enabled ? 1 : 0);
|
||||
coord += ',' + regs.GetRegisterAsFloat(depth_register);
|
||||
}
|
||||
if (is_array) {
|
||||
coord += ',' + regs.GetRegisterAsInteger(array_register);
|
||||
}
|
||||
coord += ");";
|
||||
|
||||
return std::make_pair(coord,
|
||||
GetTextureCode(instr, texture_type, process_mode, depth_compare,
|
||||
is_array, (coord_count > 2 ? 1 : 0)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a single instruction from Tegra to GLSL.
|
||||
* @param offset the offset of the Tegra shader instruction.
|
||||
@@ -2470,7 +2279,8 @@ private:
|
||||
UNIMPLEMENTED_IF(instr.conversion.selector);
|
||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||
"Condition codes generation in I2F is not implemented");
|
||||
std::string op_a;
|
||||
|
||||
std::string op_a{};
|
||||
|
||||
if (instr.is_b_gpr) {
|
||||
op_a =
|
||||
@@ -2626,7 +2436,10 @@ private:
|
||||
case OpCode::Id::LD_C: {
|
||||
UNIMPLEMENTED_IF(instr.ld_c.unknown != 0);
|
||||
|
||||
const auto scope = shader.Scope();
|
||||
// Add an extra scope and declare the index register inside to prevent
|
||||
// overwriting it in case it is used as an output of the LD instruction.
|
||||
shader.AddLine("{");
|
||||
++shader.scope;
|
||||
|
||||
shader.AddLine("uint index = (" + regs.GetRegisterAsInteger(instr.gpr8, 0, false) +
|
||||
" / 4) & (MAX_CONSTBUFFER_ELEMENTS - 1);");
|
||||
@@ -2652,13 +2465,19 @@ private:
|
||||
UNIMPLEMENTED_MSG("Unhandled type: {}",
|
||||
static_cast<unsigned>(instr.ld_c.type.Value()));
|
||||
}
|
||||
|
||||
--shader.scope;
|
||||
shader.AddLine("}");
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::LD_L: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.ld_l.unknown == 1, "LD_L Unhandled mode: {}",
|
||||
static_cast<unsigned>(instr.ld_l.unknown.Value()));
|
||||
|
||||
const auto scope = shader.Scope();
|
||||
// Add an extra scope and declare the index register inside to prevent
|
||||
// overwriting it in case it is used as an output of the LD instruction.
|
||||
shader.AddLine('{');
|
||||
++shader.scope;
|
||||
|
||||
std::string op = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + " + " +
|
||||
std::to_string(instr.smem_imm.Value()) + ')';
|
||||
@@ -2675,6 +2494,9 @@ private:
|
||||
UNIMPLEMENTED_MSG("LD_L Unhandled type: {}",
|
||||
static_cast<unsigned>(instr.ldst_sl.type.Value()));
|
||||
}
|
||||
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::ST_A: {
|
||||
@@ -2709,7 +2531,10 @@ private:
|
||||
UNIMPLEMENTED_IF_MSG(instr.st_l.unknown == 0, "ST_L Unhandled mode: {}",
|
||||
static_cast<unsigned>(instr.st_l.unknown.Value()));
|
||||
|
||||
const auto scope = shader.Scope();
|
||||
// Add an extra scope and declare the index register inside to prevent
|
||||
// overwriting it in case it is used as an output of the LD instruction.
|
||||
shader.AddLine('{');
|
||||
++shader.scope;
|
||||
|
||||
std::string op = '(' + regs.GetRegisterAsInteger(instr.gpr8, 0, false) + " + " +
|
||||
std::to_string(instr.smem_imm.Value()) + ')';
|
||||
@@ -2724,28 +2549,179 @@ private:
|
||||
UNIMPLEMENTED_MSG("ST_L Unhandled type: {}",
|
||||
static_cast<unsigned>(instr.ldst_sl.type.Value()));
|
||||
}
|
||||
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::TEX: {
|
||||
Tegra::Shader::TextureType texture_type{instr.tex.texture_type};
|
||||
std::string coord;
|
||||
const bool is_array = instr.tex.array != 0;
|
||||
const bool depth_compare =
|
||||
instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
|
||||
const auto process_mode = instr.tex.GetTextureProcessMode();
|
||||
|
||||
UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
|
||||
"NODEP is not implemented");
|
||||
UNIMPLEMENTED_IF_MSG(instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
|
||||
"AOFFI is not implemented");
|
||||
|
||||
const auto [coord, texture] =
|
||||
GetTEXCode(instr, texture_type, process_mode, depth_compare, is_array);
|
||||
|
||||
const auto scope = shader.Scope();
|
||||
shader.AddLine(coord);
|
||||
const bool depth_compare =
|
||||
instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
|
||||
u32 num_coordinates = TextureCoordinates(texture_type);
|
||||
u32 start_index = 0;
|
||||
std::string array_elem;
|
||||
if (is_array) {
|
||||
array_elem = regs.GetRegisterAsInteger(instr.gpr8);
|
||||
start_index = 1;
|
||||
}
|
||||
const auto process_mode = instr.tex.GetTextureProcessMode();
|
||||
u32 start_index_b = 0;
|
||||
std::string lod_value;
|
||||
if (process_mode != Tegra::Shader::TextureProcessMode::LZ &&
|
||||
process_mode != Tegra::Shader::TextureProcessMode::None) {
|
||||
start_index_b = 1;
|
||||
lod_value = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
}
|
||||
|
||||
std::string depth_value;
|
||||
if (depth_compare) {
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
|
||||
} else {
|
||||
depth_value = regs.GetRegisterAsFloat(instr.gpr20.Value() + start_index_b);
|
||||
}
|
||||
|
||||
bool depth_compare_extra = false;
|
||||
|
||||
switch (num_coordinates) {
|
||||
case 1: {
|
||||
const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index);
|
||||
if (is_array) {
|
||||
if (depth_compare) {
|
||||
coord = "vec3 coords = vec3(" + x + ", " + depth_value + ", " +
|
||||
array_elem + ");";
|
||||
} else {
|
||||
coord = "vec2 coords = vec2(" + x + ", " + array_elem + ");";
|
||||
}
|
||||
} else {
|
||||
if (depth_compare) {
|
||||
coord = "vec2 coords = vec2(" + x + ", " + depth_value + ");";
|
||||
} else {
|
||||
coord = "float coords = " + x + ';';
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index);
|
||||
const std::string y =
|
||||
regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 1);
|
||||
if (is_array) {
|
||||
if (depth_compare) {
|
||||
coord = "vec4 coords = vec4(" + x + ", " + y + ", " + depth_value +
|
||||
", " + array_elem + ");";
|
||||
} else {
|
||||
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + array_elem + ");";
|
||||
}
|
||||
} else {
|
||||
if (depth_compare) {
|
||||
coord =
|
||||
"vec3 coords = vec3(" + x + ", " + y + ", " + depth_value + ");";
|
||||
} else {
|
||||
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index);
|
||||
const std::string y =
|
||||
regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 1);
|
||||
const std::string z =
|
||||
regs.GetRegisterAsFloat(instr.gpr8.Value() + start_index + 2);
|
||||
if (is_array) {
|
||||
depth_compare_extra = depth_compare;
|
||||
coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " +
|
||||
array_elem + ");";
|
||||
} else {
|
||||
if (depth_compare) {
|
||||
coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " +
|
||||
depth_value + ");";
|
||||
} else {
|
||||
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unhandled coordinates number {}",
|
||||
static_cast<u32>(num_coordinates));
|
||||
|
||||
// Fallback to interpreting as a 2D texture for now
|
||||
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
|
||||
texture_type = Tegra::Shader::TextureType::Texture2D;
|
||||
}
|
||||
|
||||
const std::string sampler =
|
||||
GetSampler(instr.sampler, texture_type, is_array, depth_compare);
|
||||
// Add an extra scope and declare the texture coords inside to prevent
|
||||
// overwriting them in case they are used as outputs of the texs instruction.
|
||||
|
||||
shader.AddLine('{');
|
||||
++shader.scope;
|
||||
shader.AddLine(coord);
|
||||
std::string texture;
|
||||
|
||||
switch (instr.tex.GetTextureProcessMode()) {
|
||||
case Tegra::Shader::TextureProcessMode::None: {
|
||||
if (!depth_compare_extra) {
|
||||
texture = "texture(" + sampler + ", coords)";
|
||||
} else {
|
||||
texture = "texture(" + sampler + ", coords, " + depth_value + ')';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Tegra::Shader::TextureProcessMode::LZ: {
|
||||
if (!depth_compare_extra) {
|
||||
texture = "textureLod(" + sampler + ", coords, 0.0)";
|
||||
} else {
|
||||
texture = "texture(" + sampler + ", coords, " + depth_value + ')';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Tegra::Shader::TextureProcessMode::LB:
|
||||
case Tegra::Shader::TextureProcessMode::LBA: {
|
||||
// TODO: Figure if A suffix changes the equation at all.
|
||||
if (!depth_compare_extra) {
|
||||
texture = "texture(" + sampler + ", coords, " + lod_value + ')';
|
||||
} else {
|
||||
texture = "texture(" + sampler + ", coords, " + depth_value + ')';
|
||||
LOG_WARNING(HW_GPU,
|
||||
"OpenGL Limitation: can't set bias value along depth compare");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Tegra::Shader::TextureProcessMode::LL:
|
||||
case Tegra::Shader::TextureProcessMode::LLA: {
|
||||
// TODO: Figure if A suffix changes the equation at all.
|
||||
if (!depth_compare_extra) {
|
||||
texture = "textureLod(" + sampler + ", coords, " + lod_value + ')';
|
||||
} else {
|
||||
texture = "texture(" + sampler + ", coords, " + depth_value + ')';
|
||||
LOG_WARNING(HW_GPU,
|
||||
"OpenGL Limitation: can't set lod value along depth compare");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (!depth_compare_extra) {
|
||||
texture = "texture(" + sampler + ", coords)";
|
||||
} else {
|
||||
texture = "texture(" + sampler + ", coords, " + depth_value + ')';
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
|
||||
static_cast<u32>(instr.tex.GetTextureProcessMode()));
|
||||
}
|
||||
}
|
||||
if (!depth_compare) {
|
||||
shader.AddLine("vec4 texture_tmp = " + texture + ';');
|
||||
std::size_t dest_elem{};
|
||||
for (std::size_t elem = 0; elem < 4; ++elem) {
|
||||
@@ -2757,33 +2733,138 @@ private:
|
||||
dest_elem);
|
||||
++dest_elem;
|
||||
}
|
||||
} else {
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
|
||||
}
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::TEXS: {
|
||||
Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()};
|
||||
const bool is_array{instr.texs.IsArrayTexture()};
|
||||
const bool depth_compare =
|
||||
instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
|
||||
const auto process_mode = instr.texs.GetTextureProcessMode();
|
||||
bool is_array{instr.texs.IsArrayTexture()};
|
||||
|
||||
UNIMPLEMENTED_IF_MSG(instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
|
||||
"NODEP is not implemented");
|
||||
|
||||
const auto scope = shader.Scope();
|
||||
|
||||
const auto [coord, texture] =
|
||||
GetTEXSCode(instr, texture_type, process_mode, depth_compare, is_array);
|
||||
|
||||
shader.AddLine(coord);
|
||||
|
||||
if (!depth_compare) {
|
||||
shader.AddLine("vec4 texture_tmp = " + texture + ';');
|
||||
|
||||
} else {
|
||||
shader.AddLine("vec4 texture_tmp = vec4(" + texture + ");");
|
||||
const bool depth_compare =
|
||||
instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
|
||||
u32 num_coordinates = TextureCoordinates(texture_type);
|
||||
const auto process_mode = instr.texs.GetTextureProcessMode();
|
||||
std::string lod_value;
|
||||
std::string coord;
|
||||
u32 lod_offset = 0;
|
||||
if (process_mode == Tegra::Shader::TextureProcessMode::LL) {
|
||||
if (num_coordinates > 2) {
|
||||
lod_value = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
|
||||
lod_offset = 2;
|
||||
} else {
|
||||
lod_value = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
lod_offset = 1;
|
||||
}
|
||||
}
|
||||
|
||||
switch (num_coordinates) {
|
||||
case 1: {
|
||||
coord = "float coords = " + regs.GetRegisterAsFloat(instr.gpr8) + ';';
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (is_array) {
|
||||
if (depth_compare) {
|
||||
const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
|
||||
const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
const std::string z = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
|
||||
coord = "vec4 coords = vec4(" + x + ", " + y + ", " + z + ", " + index +
|
||||
");";
|
||||
} else {
|
||||
const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
|
||||
const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");";
|
||||
}
|
||||
} else {
|
||||
if (lod_offset != 0) {
|
||||
if (depth_compare) {
|
||||
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
const std::string y =
|
||||
regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
const std::string z =
|
||||
regs.GetRegisterAsFloat(instr.gpr20.Value() + lod_offset);
|
||||
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
|
||||
} else {
|
||||
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
const std::string y =
|
||||
regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
|
||||
}
|
||||
} else {
|
||||
if (depth_compare) {
|
||||
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
const std::string y =
|
||||
regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
|
||||
} else {
|
||||
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unhandled coordinates number {}",
|
||||
static_cast<u32>(num_coordinates));
|
||||
|
||||
// Fallback to interpreting as a 2D texture for now
|
||||
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
|
||||
const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
|
||||
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
|
||||
texture_type = Tegra::Shader::TextureType::Texture2D;
|
||||
is_array = false;
|
||||
}
|
||||
const std::string sampler =
|
||||
GetSampler(instr.sampler, texture_type, is_array, depth_compare);
|
||||
std::string texture;
|
||||
switch (process_mode) {
|
||||
case Tegra::Shader::TextureProcessMode::None: {
|
||||
texture = "texture(" + sampler + ", coords)";
|
||||
break;
|
||||
}
|
||||
case Tegra::Shader::TextureProcessMode::LZ: {
|
||||
if (depth_compare && is_array) {
|
||||
texture = "texture(" + sampler + ", coords)";
|
||||
} else {
|
||||
texture = "textureLod(" + sampler + ", coords, 0.0)";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Tegra::Shader::TextureProcessMode::LL: {
|
||||
texture = "textureLod(" + sampler + ", coords, " + lod_value + ')';
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
texture = "texture(" + sampler + ", coords)";
|
||||
UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
|
||||
static_cast<u32>(instr.texs.GetTextureProcessMode()));
|
||||
}
|
||||
}
|
||||
if (!depth_compare) {
|
||||
WriteTexsInstruction(instr, coord, texture);
|
||||
} else {
|
||||
WriteTexsInstruction(instr, coord, "vec4(" + texture + ')');
|
||||
}
|
||||
|
||||
WriteTexsInstruction(instr, "texture_tmp");
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::TLDS: {
|
||||
@@ -2802,12 +2883,15 @@ private:
|
||||
|
||||
u32 extra_op_offset = 0;
|
||||
|
||||
ShaderScopedScope scope = shader.Scope();
|
||||
// Scope to avoid variable name overlaps.
|
||||
shader.AddLine('{');
|
||||
++shader.scope;
|
||||
std::string coords;
|
||||
|
||||
switch (texture_type) {
|
||||
case Tegra::Shader::TextureType::Texture1D: {
|
||||
const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
|
||||
shader.AddLine("float coords = " + x + ';');
|
||||
coords = "float coords = " + x + ';';
|
||||
break;
|
||||
}
|
||||
case Tegra::Shader::TextureType::Texture2D: {
|
||||
@@ -2816,7 +2900,7 @@ private:
|
||||
const std::string x = regs.GetRegisterAsInteger(instr.gpr8);
|
||||
const std::string y = regs.GetRegisterAsInteger(instr.gpr20);
|
||||
// shader.AddLine("ivec2 coords = ivec2(" + x + ", " + y + ");");
|
||||
shader.AddLine("ivec2 coords = ivec2(" + x + ", " + y + ");");
|
||||
coords = "ivec2 coords = ivec2(" + x + ", " + y + ");";
|
||||
extra_op_offset = 1;
|
||||
break;
|
||||
}
|
||||
@@ -2825,29 +2909,35 @@ private:
|
||||
}
|
||||
const std::string sampler =
|
||||
GetSampler(instr.sampler, texture_type, is_array, false);
|
||||
std::string texture = "texelFetch(" + sampler + ", coords, 0)";
|
||||
switch (instr.tlds.GetTextureProcessMode()) {
|
||||
case Tegra::Shader::TextureProcessMode::LZ: {
|
||||
texture = "texelFetch(" + sampler + ", coords, 0)";
|
||||
break;
|
||||
}
|
||||
case Tegra::Shader::TextureProcessMode::LL: {
|
||||
shader.AddLine(
|
||||
"float lod = " +
|
||||
regs.GetRegisterAsInteger(instr.gpr20.Value() + extra_op_offset) + ';');
|
||||
texture = "texelFetch(" + sampler + ", coords, lod)";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
texture = "texelFetch(" + sampler + ", coords, 0)";
|
||||
UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
|
||||
static_cast<u32>(instr.tlds.GetTextureProcessMode()));
|
||||
}
|
||||
}
|
||||
WriteTexsInstruction(instr, coords, texture);
|
||||
|
||||
const std::string texture = [&]() {
|
||||
switch (instr.tlds.GetTextureProcessMode()) {
|
||||
case Tegra::Shader::TextureProcessMode::LZ:
|
||||
return "texelFetch(" + sampler + ", coords, 0)";
|
||||
case Tegra::Shader::TextureProcessMode::LL:
|
||||
shader.AddLine(
|
||||
"float lod = " +
|
||||
regs.GetRegisterAsInteger(instr.gpr20.Value() + extra_op_offset) + ';');
|
||||
return "texelFetch(" + sampler + ", coords, lod)";
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unhandled texture process mode {}",
|
||||
static_cast<u32>(instr.tlds.GetTextureProcessMode()));
|
||||
return "texelFetch(" + sampler + ", coords, 0)";
|
||||
}
|
||||
}();
|
||||
|
||||
WriteTexsInstruction(instr, texture);
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::TLD4: {
|
||||
ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D);
|
||||
ASSERT(instr.tld4.array == 0);
|
||||
std::string coord;
|
||||
|
||||
UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
|
||||
"NODEP is not implemented");
|
||||
@@ -2864,7 +2954,10 @@ private:
|
||||
if (depth_compare)
|
||||
num_coordinates += 1;
|
||||
|
||||
const auto scope = shader.Scope();
|
||||
// Add an extra scope and declare the texture coords inside to prevent
|
||||
// overwriting them in case they are used as outputs of the texs instruction.
|
||||
shader.AddLine('{');
|
||||
++shader.scope;
|
||||
|
||||
switch (num_coordinates) {
|
||||
case 2: {
|
||||
@@ -2895,19 +2988,23 @@ private:
|
||||
const std::string texture = "textureGather(" + sampler + ", coords, " +
|
||||
std::to_string(instr.tld4.component) + ')';
|
||||
|
||||
if (depth_compare) {
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
|
||||
} else {
|
||||
if (!depth_compare) {
|
||||
shader.AddLine("vec4 texture_tmp = " + texture + ';');
|
||||
std::size_t dest_elem{};
|
||||
for (std::size_t elem = 0; elem < 4; ++elem) {
|
||||
if (!instr.tex.IsComponentEnabled(elem)) {
|
||||
// Skip disabled components
|
||||
continue;
|
||||
}
|
||||
regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem);
|
||||
regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false,
|
||||
dest_elem);
|
||||
++dest_elem;
|
||||
}
|
||||
} else {
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
|
||||
}
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::TLD4S: {
|
||||
@@ -2918,7 +3015,10 @@ private:
|
||||
instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
|
||||
"AOFFI is not implemented");
|
||||
|
||||
const auto scope = shader.Scope();
|
||||
// Scope to avoid variable name overlaps.
|
||||
shader.AddLine('{');
|
||||
++shader.scope;
|
||||
std::string coords;
|
||||
|
||||
const bool depth_compare =
|
||||
instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
|
||||
@@ -2927,29 +3027,33 @@ private:
|
||||
// TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
|
||||
const std::string sampler = GetSampler(
|
||||
instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare);
|
||||
if (depth_compare) {
|
||||
if (!depth_compare) {
|
||||
coords = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
|
||||
} else {
|
||||
// Note: TLD4S coordinate encoding works just like TEXS's
|
||||
const std::string op_y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
shader.AddLine("vec3 coords = vec3(" + op_a + ", " + op_y + ", " + op_b + ");");
|
||||
coords = "vec3 coords = vec3(" + op_a + ", " + op_y + ", " + op_b + ");";
|
||||
}
|
||||
const std::string texture = "textureGather(" + sampler + ", coords, " +
|
||||
std::to_string(instr.tld4s.component) + ')';
|
||||
|
||||
if (!depth_compare) {
|
||||
WriteTexsInstruction(instr, coords, texture);
|
||||
} else {
|
||||
shader.AddLine("vec2 coords = vec2(" + op_a + ", " + op_b + ");");
|
||||
WriteTexsInstruction(instr, coords, "vec4(" + texture + ')');
|
||||
}
|
||||
|
||||
std::string texture = "textureGather(" + sampler + ", coords, " +
|
||||
std::to_string(instr.tld4s.component) + ')';
|
||||
if (depth_compare) {
|
||||
texture = "vec4(" + texture + ')';
|
||||
}
|
||||
WriteTexsInstruction(instr, texture);
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::TXQ: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
|
||||
"NODEP is not implemented");
|
||||
|
||||
const auto scope = shader.Scope();
|
||||
|
||||
// TODO: The new commits on the texture refactor, change the way samplers work.
|
||||
++shader.scope;
|
||||
shader.AddLine('{');
|
||||
// TODO: the new commits on the texture refactor, change the way samplers work.
|
||||
// Sadly, not all texture instructions specify the type of texture their sampler
|
||||
// uses. This must be fixed at a later instance.
|
||||
const std::string sampler =
|
||||
@@ -2960,8 +3064,7 @@ private:
|
||||
regs.GetRegisterAsInteger(instr.gpr8) + ')';
|
||||
const std::string mip_level = "textureQueryLevels(" + sampler + ')';
|
||||
shader.AddLine("ivec2 sizes = " + texture + ';');
|
||||
|
||||
regs.SetRegisterToInteger(instr.gpr0.Value() + 0, true, 0, "sizes.x", 1, 1);
|
||||
regs.SetRegisterToInteger(instr.gpr0, true, 0, "sizes.x", 1, 1);
|
||||
regs.SetRegisterToInteger(instr.gpr0.Value() + 1, true, 0, "sizes.y", 1, 1);
|
||||
regs.SetRegisterToInteger(instr.gpr0.Value() + 2, true, 0, "0", 1, 1);
|
||||
regs.SetRegisterToInteger(instr.gpr0.Value() + 3, true, 0, mip_level, 1, 1);
|
||||
@@ -2972,6 +3075,8 @@ private:
|
||||
static_cast<u32>(instr.txq.query_type.Value()));
|
||||
}
|
||||
}
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::TMML: {
|
||||
@@ -2986,18 +3091,17 @@ private:
|
||||
const std::string sampler =
|
||||
GetSampler(instr.sampler, texture_type, is_array, false);
|
||||
|
||||
const auto scope = shader.Scope();
|
||||
|
||||
// TODO: Add coordinates for different samplers once other texture types are
|
||||
// TODO: add coordinates for different samplers once other texture types are
|
||||
// implemented.
|
||||
std::string coord;
|
||||
switch (texture_type) {
|
||||
case Tegra::Shader::TextureType::Texture1D: {
|
||||
shader.AddLine("float coords = " + x + ';');
|
||||
coord = "float coords = " + x + ';';
|
||||
break;
|
||||
}
|
||||
case Tegra::Shader::TextureType::Texture2D: {
|
||||
const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
|
||||
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -3005,15 +3109,22 @@ private:
|
||||
|
||||
// Fallback to interpreting as a 2D texture for now
|
||||
const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");");
|
||||
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
|
||||
texture_type = Tegra::Shader::TextureType::Texture2D;
|
||||
}
|
||||
|
||||
// Add an extra scope and declare the texture coords inside to prevent
|
||||
// overwriting them in case they are used as outputs of the texs instruction.
|
||||
shader.AddLine('{');
|
||||
++shader.scope;
|
||||
shader.AddLine(coord);
|
||||
const std::string texture = "textureQueryLod(" + sampler + ", coords)";
|
||||
shader.AddLine("vec2 tmp = " + texture + " * vec2(256.0, 256.0);");
|
||||
const std::string tmp = "vec2 tmp = " + texture + "*vec2(256.0, 256.0);";
|
||||
shader.AddLine(tmp);
|
||||
|
||||
regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(tmp.y)", 1, 1);
|
||||
regs.SetRegisterToInteger(instr.gpr0.Value() + 1, false, 0, "uint(tmp.x)", 1, 1);
|
||||
--shader.scope;
|
||||
shader.AddLine('}');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -3852,4 +3963,4 @@ std::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code, u
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace OpenGL::GLShader::Decompiler
|
||||
} // namespace OpenGL::GLShader::Decompiler
|
||||
|
||||
@@ -163,7 +163,6 @@ private:
|
||||
struct ShaderEntries {
|
||||
std::vector<ConstBufferEntry> const_buffer_entries;
|
||||
std::vector<SamplerEntry> texture_samplers;
|
||||
std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> clip_distances;
|
||||
std::size_t shader_length;
|
||||
};
|
||||
|
||||
|
||||
@@ -60,17 +60,6 @@ public:
|
||||
}
|
||||
|
||||
void ApplyTo(OpenGLState& state) {
|
||||
UpdatePipeline();
|
||||
state.draw.shader_program = 0;
|
||||
state.draw.program_pipeline = pipeline.handle;
|
||||
state.geometry_shaders.enabled = (gs != 0);
|
||||
}
|
||||
|
||||
private:
|
||||
void UpdatePipeline() {
|
||||
// Avoid updating the pipeline when values have no changed
|
||||
if (old_vs == vs && old_fs == fs && old_gs == gs)
|
||||
return;
|
||||
// Workaround for AMD bug
|
||||
glUseProgramStages(pipeline.handle,
|
||||
GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT,
|
||||
@@ -79,16 +68,14 @@ private:
|
||||
glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, vs);
|
||||
glUseProgramStages(pipeline.handle, GL_GEOMETRY_SHADER_BIT, gs);
|
||||
glUseProgramStages(pipeline.handle, GL_FRAGMENT_SHADER_BIT, fs);
|
||||
|
||||
// Update the old values
|
||||
old_vs = vs;
|
||||
old_fs = fs;
|
||||
old_gs = gs;
|
||||
state.draw.shader_program = 0;
|
||||
state.draw.program_pipeline = pipeline.handle;
|
||||
state.geometry_shaders.enabled = (gs != 0);
|
||||
}
|
||||
|
||||
private:
|
||||
OGLPipeline pipeline;
|
||||
GLuint vs{}, fs{}, gs{};
|
||||
GLuint old_vs{}, old_fs{}, old_gs{};
|
||||
};
|
||||
|
||||
} // namespace OpenGL::GLShader
|
||||
|
||||
@@ -125,75 +125,6 @@ enum class SurfaceTarget {
|
||||
TextureCubeArray,
|
||||
};
|
||||
|
||||
constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{
|
||||
1, // ABGR8U
|
||||
1, // ABGR8S
|
||||
1, // ABGR8UI
|
||||
1, // B5G6R5U
|
||||
1, // A2B10G10R10U
|
||||
1, // A1B5G5R5U
|
||||
1, // R8U
|
||||
1, // R8UI
|
||||
1, // RGBA16F
|
||||
1, // RGBA16U
|
||||
1, // RGBA16UI
|
||||
1, // R11FG11FB10F
|
||||
1, // RGBA32UI
|
||||
4, // DXT1
|
||||
4, // DXT23
|
||||
4, // DXT45
|
||||
4, // DXN1
|
||||
4, // DXN2UNORM
|
||||
4, // DXN2SNORM
|
||||
4, // BC7U
|
||||
4, // BC6H_UF16
|
||||
4, // BC6H_SF16
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8U
|
||||
1, // G8R8S
|
||||
1, // BGRA8
|
||||
1, // RGBA32F
|
||||
1, // RG32F
|
||||
1, // R32F
|
||||
1, // R16F
|
||||
1, // R16U
|
||||
1, // R16S
|
||||
1, // R16UI
|
||||
1, // R16I
|
||||
1, // RG16
|
||||
1, // RG16F
|
||||
1, // RG16UI
|
||||
1, // RG16I
|
||||
1, // RG16S
|
||||
1, // RGB32F
|
||||
1, // RGBA8_SRGB
|
||||
1, // RG8U
|
||||
1, // RG8S
|
||||
1, // RG32UI
|
||||
1, // R32UI
|
||||
4, // ASTC_2D_8X8
|
||||
4, // ASTC_2D_8X5
|
||||
4, // ASTC_2D_5X4
|
||||
1, // BGRA8_SRGB
|
||||
4, // DXT1_SRGB
|
||||
4, // DXT23_SRGB
|
||||
4, // DXT45_SRGB
|
||||
4, // BC7U_SRGB
|
||||
4, // ASTC_2D_4X4_SRGB
|
||||
4, // ASTC_2D_8X8_SRGB
|
||||
4, // ASTC_2D_8X5_SRGB
|
||||
4, // ASTC_2D_5X4_SRGB
|
||||
4, // ASTC_2D_5X5
|
||||
4, // ASTC_2D_5X5_SRGB
|
||||
4, // ASTC_2D_10X8
|
||||
4, // ASTC_2D_10X8_SRGB
|
||||
1, // Z32F
|
||||
1, // Z16
|
||||
1, // Z24S8
|
||||
1, // S8Z24
|
||||
1, // Z32FS8
|
||||
}};
|
||||
|
||||
/**
|
||||
* Gets the compression factor for the specified PixelFormat. This applies to just the
|
||||
* "compressed width" and "compressed height", not the overall compression factor of a
|
||||
@@ -204,237 +135,304 @@ static constexpr u32 GetCompressionFactor(PixelFormat format) {
|
||||
if (format == PixelFormat::Invalid)
|
||||
return 0;
|
||||
|
||||
constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{
|
||||
1, // ABGR8U
|
||||
1, // ABGR8S
|
||||
1, // ABGR8UI
|
||||
1, // B5G6R5U
|
||||
1, // A2B10G10R10U
|
||||
1, // A1B5G5R5U
|
||||
1, // R8U
|
||||
1, // R8UI
|
||||
1, // RGBA16F
|
||||
1, // RGBA16U
|
||||
1, // RGBA16UI
|
||||
1, // R11FG11FB10F
|
||||
1, // RGBA32UI
|
||||
4, // DXT1
|
||||
4, // DXT23
|
||||
4, // DXT45
|
||||
4, // DXN1
|
||||
4, // DXN2UNORM
|
||||
4, // DXN2SNORM
|
||||
4, // BC7U
|
||||
4, // BC6H_UF16
|
||||
4, // BC6H_SF16
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8U
|
||||
1, // G8R8S
|
||||
1, // BGRA8
|
||||
1, // RGBA32F
|
||||
1, // RG32F
|
||||
1, // R32F
|
||||
1, // R16F
|
||||
1, // R16U
|
||||
1, // R16S
|
||||
1, // R16UI
|
||||
1, // R16I
|
||||
1, // RG16
|
||||
1, // RG16F
|
||||
1, // RG16UI
|
||||
1, // RG16I
|
||||
1, // RG16S
|
||||
1, // RGB32F
|
||||
1, // RGBA8_SRGB
|
||||
1, // RG8U
|
||||
1, // RG8S
|
||||
1, // RG32UI
|
||||
1, // R32UI
|
||||
4, // ASTC_2D_8X8
|
||||
4, // ASTC_2D_8X5
|
||||
4, // ASTC_2D_5X4
|
||||
1, // BGRA8_SRGB
|
||||
4, // DXT1_SRGB
|
||||
4, // DXT23_SRGB
|
||||
4, // DXT45_SRGB
|
||||
4, // BC7U_SRGB
|
||||
4, // ASTC_2D_4X4_SRGB
|
||||
4, // ASTC_2D_8X8_SRGB
|
||||
4, // ASTC_2D_8X5_SRGB
|
||||
4, // ASTC_2D_5X4_SRGB
|
||||
4, // ASTC_2D_5X5
|
||||
4, // ASTC_2D_5X5_SRGB
|
||||
4, // ASTC_2D_10X8
|
||||
4, // ASTC_2D_10X8_SRGB
|
||||
1, // Z32F
|
||||
1, // Z16
|
||||
1, // Z24S8
|
||||
1, // S8Z24
|
||||
1, // Z32FS8
|
||||
}};
|
||||
|
||||
ASSERT(static_cast<std::size_t>(format) < compression_factor_table.size());
|
||||
return compression_factor_table[static_cast<std::size_t>(format)];
|
||||
}
|
||||
|
||||
constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
|
||||
1, // ABGR8U
|
||||
1, // ABGR8S
|
||||
1, // ABGR8UI
|
||||
1, // B5G6R5U
|
||||
1, // A2B10G10R10U
|
||||
1, // A1B5G5R5U
|
||||
1, // R8U
|
||||
1, // R8UI
|
||||
1, // RGBA16F
|
||||
1, // RGBA16U
|
||||
1, // RGBA16UI
|
||||
1, // R11FG11FB10F
|
||||
1, // RGBA32UI
|
||||
4, // DXT1
|
||||
4, // DXT23
|
||||
4, // DXT45
|
||||
4, // DXN1
|
||||
4, // DXN2UNORM
|
||||
4, // DXN2SNORM
|
||||
4, // BC7U
|
||||
4, // BC6H_UF16
|
||||
4, // BC6H_SF16
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8U
|
||||
1, // G8R8S
|
||||
1, // BGRA8
|
||||
1, // RGBA32F
|
||||
1, // RG32F
|
||||
1, // R32F
|
||||
1, // R16F
|
||||
1, // R16U
|
||||
1, // R16S
|
||||
1, // R16UI
|
||||
1, // R16I
|
||||
1, // RG16
|
||||
1, // RG16F
|
||||
1, // RG16UI
|
||||
1, // RG16I
|
||||
1, // RG16S
|
||||
1, // RGB32F
|
||||
1, // RGBA8_SRGB
|
||||
1, // RG8U
|
||||
1, // RG8S
|
||||
1, // RG32UI
|
||||
1, // R32UI
|
||||
8, // ASTC_2D_8X8
|
||||
8, // ASTC_2D_8X5
|
||||
5, // ASTC_2D_5X4
|
||||
1, // BGRA8_SRGB
|
||||
4, // DXT1_SRGB
|
||||
4, // DXT23_SRGB
|
||||
4, // DXT45_SRGB
|
||||
4, // BC7U_SRGB
|
||||
4, // ASTC_2D_4X4_SRGB
|
||||
8, // ASTC_2D_8X8_SRGB
|
||||
8, // ASTC_2D_8X5_SRGB
|
||||
5, // ASTC_2D_5X4_SRGB
|
||||
5, // ASTC_2D_5X5
|
||||
5, // ASTC_2D_5X5_SRGB
|
||||
10, // ASTC_2D_10X8
|
||||
10, // ASTC_2D_10X8_SRGB
|
||||
1, // Z32F
|
||||
1, // Z16
|
||||
1, // Z24S8
|
||||
1, // S8Z24
|
||||
1, // Z32FS8
|
||||
}};
|
||||
|
||||
static constexpr u32 GetDefaultBlockWidth(PixelFormat format) {
|
||||
if (format == PixelFormat::Invalid)
|
||||
return 0;
|
||||
|
||||
constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
|
||||
1, // ABGR8U
|
||||
1, // ABGR8S
|
||||
1, // ABGR8UI
|
||||
1, // B5G6R5U
|
||||
1, // A2B10G10R10U
|
||||
1, // A1B5G5R5U
|
||||
1, // R8U
|
||||
1, // R8UI
|
||||
1, // RGBA16F
|
||||
1, // RGBA16U
|
||||
1, // RGBA16UI
|
||||
1, // R11FG11FB10F
|
||||
1, // RGBA32UI
|
||||
4, // DXT1
|
||||
4, // DXT23
|
||||
4, // DXT45
|
||||
4, // DXN1
|
||||
4, // DXN2UNORM
|
||||
4, // DXN2SNORM
|
||||
4, // BC7U
|
||||
4, // BC6H_UF16
|
||||
4, // BC6H_SF16
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8U
|
||||
1, // G8R8S
|
||||
1, // BGRA8
|
||||
1, // RGBA32F
|
||||
1, // RG32F
|
||||
1, // R32F
|
||||
1, // R16F
|
||||
1, // R16U
|
||||
1, // R16S
|
||||
1, // R16UI
|
||||
1, // R16I
|
||||
1, // RG16
|
||||
1, // RG16F
|
||||
1, // RG16UI
|
||||
1, // RG16I
|
||||
1, // RG16S
|
||||
1, // RGB32F
|
||||
1, // RGBA8_SRGB
|
||||
1, // RG8U
|
||||
1, // RG8S
|
||||
1, // RG32UI
|
||||
1, // R32UI
|
||||
8, // ASTC_2D_8X8
|
||||
8, // ASTC_2D_8X5
|
||||
5, // ASTC_2D_5X4
|
||||
1, // BGRA8_SRGB
|
||||
4, // DXT1_SRGB
|
||||
4, // DXT23_SRGB
|
||||
4, // DXT45_SRGB
|
||||
4, // BC7U_SRGB
|
||||
4, // ASTC_2D_4X4_SRGB
|
||||
8, // ASTC_2D_8X8_SRGB
|
||||
8, // ASTC_2D_8X5_SRGB
|
||||
5, // ASTC_2D_5X4_SRGB
|
||||
5, // ASTC_2D_5X5
|
||||
5, // ASTC_2D_5X5_SRGB
|
||||
10, // ASTC_2D_10X8
|
||||
10, // ASTC_2D_10X8_SRGB
|
||||
1, // Z32F
|
||||
1, // Z16
|
||||
1, // Z24S8
|
||||
1, // S8Z24
|
||||
1, // Z32FS8
|
||||
}};
|
||||
ASSERT(static_cast<std::size_t>(format) < block_width_table.size());
|
||||
return block_width_table[static_cast<std::size_t>(format)];
|
||||
}
|
||||
|
||||
constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
|
||||
1, // ABGR8U
|
||||
1, // ABGR8S
|
||||
1, // ABGR8UI
|
||||
1, // B5G6R5U
|
||||
1, // A2B10G10R10U
|
||||
1, // A1B5G5R5U
|
||||
1, // R8U
|
||||
1, // R8UI
|
||||
1, // RGBA16F
|
||||
1, // RGBA16U
|
||||
1, // RGBA16UI
|
||||
1, // R11FG11FB10F
|
||||
1, // RGBA32UI
|
||||
4, // DXT1
|
||||
4, // DXT23
|
||||
4, // DXT45
|
||||
4, // DXN1
|
||||
4, // DXN2UNORM
|
||||
4, // DXN2SNORM
|
||||
4, // BC7U
|
||||
4, // BC6H_UF16
|
||||
4, // BC6H_SF16
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8U
|
||||
1, // G8R8S
|
||||
1, // BGRA8
|
||||
1, // RGBA32F
|
||||
1, // RG32F
|
||||
1, // R32F
|
||||
1, // R16F
|
||||
1, // R16U
|
||||
1, // R16S
|
||||
1, // R16UI
|
||||
1, // R16I
|
||||
1, // RG16
|
||||
1, // RG16F
|
||||
1, // RG16UI
|
||||
1, // RG16I
|
||||
1, // RG16S
|
||||
1, // RGB32F
|
||||
1, // RGBA8_SRGB
|
||||
1, // RG8U
|
||||
1, // RG8S
|
||||
1, // RG32UI
|
||||
1, // R32UI
|
||||
8, // ASTC_2D_8X8
|
||||
5, // ASTC_2D_8X5
|
||||
4, // ASTC_2D_5X4
|
||||
1, // BGRA8_SRGB
|
||||
4, // DXT1_SRGB
|
||||
4, // DXT23_SRGB
|
||||
4, // DXT45_SRGB
|
||||
4, // BC7U_SRGB
|
||||
4, // ASTC_2D_4X4_SRGB
|
||||
8, // ASTC_2D_8X8_SRGB
|
||||
5, // ASTC_2D_8X5_SRGB
|
||||
4, // ASTC_2D_5X4_SRGB
|
||||
5, // ASTC_2D_5X5
|
||||
5, // ASTC_2D_5X5_SRGB
|
||||
8, // ASTC_2D_10X8
|
||||
8, // ASTC_2D_10X8_SRGB
|
||||
1, // Z32F
|
||||
1, // Z16
|
||||
1, // Z24S8
|
||||
1, // S8Z24
|
||||
1, // Z32FS8
|
||||
}};
|
||||
|
||||
static constexpr u32 GetDefaultBlockHeight(PixelFormat format) {
|
||||
if (format == PixelFormat::Invalid)
|
||||
return 0;
|
||||
|
||||
constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
|
||||
1, // ABGR8U
|
||||
1, // ABGR8S
|
||||
1, // ABGR8UI
|
||||
1, // B5G6R5U
|
||||
1, // A2B10G10R10U
|
||||
1, // A1B5G5R5U
|
||||
1, // R8U
|
||||
1, // R8UI
|
||||
1, // RGBA16F
|
||||
1, // RGBA16U
|
||||
1, // RGBA16UI
|
||||
1, // R11FG11FB10F
|
||||
1, // RGBA32UI
|
||||
4, // DXT1
|
||||
4, // DXT23
|
||||
4, // DXT45
|
||||
4, // DXN1
|
||||
4, // DXN2UNORM
|
||||
4, // DXN2SNORM
|
||||
4, // BC7U
|
||||
4, // BC6H_UF16
|
||||
4, // BC6H_SF16
|
||||
4, // ASTC_2D_4X4
|
||||
1, // G8R8U
|
||||
1, // G8R8S
|
||||
1, // BGRA8
|
||||
1, // RGBA32F
|
||||
1, // RG32F
|
||||
1, // R32F
|
||||
1, // R16F
|
||||
1, // R16U
|
||||
1, // R16S
|
||||
1, // R16UI
|
||||
1, // R16I
|
||||
1, // RG16
|
||||
1, // RG16F
|
||||
1, // RG16UI
|
||||
1, // RG16I
|
||||
1, // RG16S
|
||||
1, // RGB32F
|
||||
1, // RGBA8_SRGB
|
||||
1, // RG8U
|
||||
1, // RG8S
|
||||
1, // RG32UI
|
||||
1, // R32UI
|
||||
8, // ASTC_2D_8X8
|
||||
5, // ASTC_2D_8X5
|
||||
4, // ASTC_2D_5X4
|
||||
1, // BGRA8_SRGB
|
||||
4, // DXT1_SRGB
|
||||
4, // DXT23_SRGB
|
||||
4, // DXT45_SRGB
|
||||
4, // BC7U_SRGB
|
||||
4, // ASTC_2D_4X4_SRGB
|
||||
8, // ASTC_2D_8X8_SRGB
|
||||
5, // ASTC_2D_8X5_SRGB
|
||||
4, // ASTC_2D_5X4_SRGB
|
||||
5, // ASTC_2D_5X5
|
||||
5, // ASTC_2D_5X5_SRGB
|
||||
8, // ASTC_2D_10X8
|
||||
8, // ASTC_2D_10X8_SRGB
|
||||
1, // Z32F
|
||||
1, // Z16
|
||||
1, // Z24S8
|
||||
1, // S8Z24
|
||||
1, // Z32FS8
|
||||
}};
|
||||
|
||||
ASSERT(static_cast<std::size_t>(format) < block_height_table.size());
|
||||
return block_height_table[static_cast<std::size_t>(format)];
|
||||
}
|
||||
|
||||
constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
|
||||
32, // ABGR8U
|
||||
32, // ABGR8S
|
||||
32, // ABGR8UI
|
||||
16, // B5G6R5U
|
||||
32, // A2B10G10R10U
|
||||
16, // A1B5G5R5U
|
||||
8, // R8U
|
||||
8, // R8UI
|
||||
64, // RGBA16F
|
||||
64, // RGBA16U
|
||||
64, // RGBA16UI
|
||||
32, // R11FG11FB10F
|
||||
128, // RGBA32UI
|
||||
64, // DXT1
|
||||
128, // DXT23
|
||||
128, // DXT45
|
||||
64, // DXN1
|
||||
128, // DXN2UNORM
|
||||
128, // DXN2SNORM
|
||||
128, // BC7U
|
||||
128, // BC6H_UF16
|
||||
128, // BC6H_SF16
|
||||
128, // ASTC_2D_4X4
|
||||
16, // G8R8U
|
||||
16, // G8R8S
|
||||
32, // BGRA8
|
||||
128, // RGBA32F
|
||||
64, // RG32F
|
||||
32, // R32F
|
||||
16, // R16F
|
||||
16, // R16U
|
||||
16, // R16S
|
||||
16, // R16UI
|
||||
16, // R16I
|
||||
32, // RG16
|
||||
32, // RG16F
|
||||
32, // RG16UI
|
||||
32, // RG16I
|
||||
32, // RG16S
|
||||
96, // RGB32F
|
||||
32, // RGBA8_SRGB
|
||||
16, // RG8U
|
||||
16, // RG8S
|
||||
64, // RG32UI
|
||||
32, // R32UI
|
||||
128, // ASTC_2D_8X8
|
||||
128, // ASTC_2D_8X5
|
||||
128, // ASTC_2D_5X4
|
||||
32, // BGRA8_SRGB
|
||||
64, // DXT1_SRGB
|
||||
128, // DXT23_SRGB
|
||||
128, // DXT45_SRGB
|
||||
128, // BC7U
|
||||
128, // ASTC_2D_4X4_SRGB
|
||||
128, // ASTC_2D_8X8_SRGB
|
||||
128, // ASTC_2D_8X5_SRGB
|
||||
128, // ASTC_2D_5X4_SRGB
|
||||
128, // ASTC_2D_5X5
|
||||
128, // ASTC_2D_5X5_SRGB
|
||||
128, // ASTC_2D_10X8
|
||||
128, // ASTC_2D_10X8_SRGB
|
||||
32, // Z32F
|
||||
16, // Z16
|
||||
32, // Z24S8
|
||||
32, // S8Z24
|
||||
64, // Z32FS8
|
||||
}};
|
||||
|
||||
static constexpr u32 GetFormatBpp(PixelFormat format) {
|
||||
if (format == PixelFormat::Invalid)
|
||||
return 0;
|
||||
|
||||
constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
|
||||
32, // ABGR8U
|
||||
32, // ABGR8S
|
||||
32, // ABGR8UI
|
||||
16, // B5G6R5U
|
||||
32, // A2B10G10R10U
|
||||
16, // A1B5G5R5U
|
||||
8, // R8U
|
||||
8, // R8UI
|
||||
64, // RGBA16F
|
||||
64, // RGBA16U
|
||||
64, // RGBA16UI
|
||||
32, // R11FG11FB10F
|
||||
128, // RGBA32UI
|
||||
64, // DXT1
|
||||
128, // DXT23
|
||||
128, // DXT45
|
||||
64, // DXN1
|
||||
128, // DXN2UNORM
|
||||
128, // DXN2SNORM
|
||||
128, // BC7U
|
||||
128, // BC6H_UF16
|
||||
128, // BC6H_SF16
|
||||
128, // ASTC_2D_4X4
|
||||
16, // G8R8U
|
||||
16, // G8R8S
|
||||
32, // BGRA8
|
||||
128, // RGBA32F
|
||||
64, // RG32F
|
||||
32, // R32F
|
||||
16, // R16F
|
||||
16, // R16U
|
||||
16, // R16S
|
||||
16, // R16UI
|
||||
16, // R16I
|
||||
32, // RG16
|
||||
32, // RG16F
|
||||
32, // RG16UI
|
||||
32, // RG16I
|
||||
32, // RG16S
|
||||
96, // RGB32F
|
||||
32, // RGBA8_SRGB
|
||||
16, // RG8U
|
||||
16, // RG8S
|
||||
64, // RG32UI
|
||||
32, // R32UI
|
||||
128, // ASTC_2D_8X8
|
||||
128, // ASTC_2D_8X5
|
||||
128, // ASTC_2D_5X4
|
||||
32, // BGRA8_SRGB
|
||||
64, // DXT1_SRGB
|
||||
128, // DXT23_SRGB
|
||||
128, // DXT45_SRGB
|
||||
128, // BC7U
|
||||
128, // ASTC_2D_4X4_SRGB
|
||||
128, // ASTC_2D_8X8_SRGB
|
||||
128, // ASTC_2D_8X5_SRGB
|
||||
128, // ASTC_2D_5X4_SRGB
|
||||
128, // ASTC_2D_5X5
|
||||
128, // ASTC_2D_5X5_SRGB
|
||||
128, // ASTC_2D_10X8
|
||||
128, // ASTC_2D_10X8_SRGB
|
||||
32, // Z32F
|
||||
16, // Z16
|
||||
32, // Z24S8
|
||||
32, // S8Z24
|
||||
64, // Z32FS8
|
||||
}};
|
||||
|
||||
ASSERT(static_cast<std::size_t>(format) < bpp_table.size());
|
||||
return bpp_table[static_cast<std::size_t>(format)];
|
||||
}
|
||||
|
||||
@@ -16,14 +16,15 @@ class ConfigureAudio : public QWidget {
|
||||
|
||||
public:
|
||||
explicit ConfigureAudio(QWidget* parent = nullptr);
|
||||
~ConfigureAudio() override;
|
||||
~ConfigureAudio();
|
||||
|
||||
void applyConfiguration();
|
||||
void retranslateUi();
|
||||
|
||||
private:
|
||||
public slots:
|
||||
void updateAudioDevices(int sink_index);
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
void setOutputSinkFromSinkID();
|
||||
void setAudioDeviceFromDeviceID();
|
||||
|
||||
@@ -16,12 +16,13 @@ class ConfigureDebug : public QWidget {
|
||||
|
||||
public:
|
||||
explicit ConfigureDebug(QWidget* parent = nullptr);
|
||||
~ConfigureDebug() override;
|
||||
~ConfigureDebug();
|
||||
|
||||
void applyConfiguration();
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureDebug> ui;
|
||||
};
|
||||
|
||||
@@ -18,12 +18,13 @@ class ConfigureDialog : public QDialog {
|
||||
|
||||
public:
|
||||
explicit ConfigureDialog(QWidget* parent, const HotkeyRegistry& registry);
|
||||
~ConfigureDialog() override;
|
||||
~ConfigureDialog();
|
||||
|
||||
void applyConfiguration();
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureDialog> ui;
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ class ConfigureGameList : public QWidget {
|
||||
|
||||
public:
|
||||
explicit ConfigureGameList(QWidget* parent = nullptr);
|
||||
~ConfigureGameList() override;
|
||||
~ConfigureGameList();
|
||||
|
||||
void applyConfiguration();
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class ConfigureGeneral : public QWidget {
|
||||
|
||||
public:
|
||||
explicit ConfigureGeneral(QWidget* parent = nullptr);
|
||||
~ConfigureGeneral() override;
|
||||
~ConfigureGeneral();
|
||||
|
||||
void PopulateHotkeyList(const HotkeyRegistry& registry);
|
||||
void applyConfiguration();
|
||||
|
||||
@@ -16,13 +16,14 @@ class ConfigureGraphics : public QWidget {
|
||||
|
||||
public:
|
||||
explicit ConfigureGraphics(QWidget* parent = nullptr);
|
||||
~ConfigureGraphics() override;
|
||||
~ConfigureGraphics();
|
||||
|
||||
void applyConfiguration();
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureGraphics> ui;
|
||||
QColor bg_color;
|
||||
};
|
||||
|
||||
@@ -88,8 +88,6 @@ ConfigureInput::ConfigureInput(QWidget* parent)
|
||||
[this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); });
|
||||
}
|
||||
|
||||
ConfigureInput::~ConfigureInput() = default;
|
||||
|
||||
void ConfigureInput::OnDockedModeChanged(bool last_state, bool new_state) {
|
||||
if (ui->use_docked_mode->isChecked() && ui->handheld_connected->isChecked()) {
|
||||
ui->handheld_connected->setChecked(false);
|
||||
|
||||
@@ -25,7 +25,6 @@ class ConfigureInput : public QWidget {
|
||||
|
||||
public:
|
||||
explicit ConfigureInput(QWidget* parent = nullptr);
|
||||
~ConfigureInput() override;
|
||||
|
||||
/// Save all button configurations to settings file
|
||||
void applyConfiguration();
|
||||
|
||||
@@ -17,17 +17,18 @@ class ConfigureWeb : public QWidget {
|
||||
|
||||
public:
|
||||
explicit ConfigureWeb(QWidget* parent = nullptr);
|
||||
~ConfigureWeb() override;
|
||||
~ConfigureWeb();
|
||||
|
||||
void applyConfiguration();
|
||||
void retranslateUi();
|
||||
|
||||
private:
|
||||
public slots:
|
||||
void RefreshTelemetryID();
|
||||
void OnLoginChanged();
|
||||
void VerifyLogin();
|
||||
void OnLoginVerified();
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
|
||||
bool user_verified = true;
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/mutex.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/readable_event.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/timer.h"
|
||||
@@ -153,8 +153,8 @@ QString WaitTreeWaitObject::GetText() const {
|
||||
|
||||
std::unique_ptr<WaitTreeWaitObject> WaitTreeWaitObject::make(const Kernel::WaitObject& object) {
|
||||
switch (object.GetHandleType()) {
|
||||
case Kernel::HandleType::ReadableEvent:
|
||||
return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::ReadableEvent&>(object));
|
||||
case Kernel::HandleType::Event:
|
||||
return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::Event&>(object));
|
||||
case Kernel::HandleType::Timer:
|
||||
return std::make_unique<WaitTreeTimer>(static_cast<const Kernel::Timer&>(object));
|
||||
case Kernel::HandleType::Thread:
|
||||
@@ -332,7 +332,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
|
||||
return list;
|
||||
}
|
||||
|
||||
WaitTreeEvent::WaitTreeEvent(const Kernel::ReadableEvent& object) : WaitTreeWaitObject(object) {}
|
||||
WaitTreeEvent::WaitTreeEvent(const Kernel::Event& object) : WaitTreeWaitObject(object) {}
|
||||
WaitTreeEvent::~WaitTreeEvent() = default;
|
||||
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
|
||||
@@ -340,8 +340,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
|
||||
|
||||
list.push_back(std::make_unique<WaitTreeText>(
|
||||
tr("reset type = %1")
|
||||
.arg(GetResetTypeQString(
|
||||
static_cast<const Kernel::ReadableEvent&>(object).GetResetType()))));
|
||||
.arg(GetResetTypeQString(static_cast<const Kernel::Event&>(object).GetResetType()))));
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
class EmuThread;
|
||||
|
||||
namespace Kernel {
|
||||
class ReadableEvent;
|
||||
class WaitObject;
|
||||
class Event;
|
||||
class Thread;
|
||||
class Timer;
|
||||
} // namespace Kernel
|
||||
@@ -144,7 +144,7 @@ public:
|
||||
class WaitTreeEvent : public WaitTreeWaitObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WaitTreeEvent(const Kernel::ReadableEvent& object);
|
||||
explicit WaitTreeEvent(const Kernel::Event& object);
|
||||
~WaitTreeEvent() override;
|
||||
|
||||
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
|
||||
|
||||
@@ -214,7 +214,6 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
|
||||
tree_view->setEditTriggers(QHeaderView::NoEditTriggers);
|
||||
tree_view->setUniformRowHeights(true);
|
||||
tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
tree_view->setStyleSheet("QTreeView{ border: none; }");
|
||||
|
||||
item_model->insertColumns(0, UISettings::values.show_add_ons ? COLUMN_COUNT : COLUMN_COUNT - 1);
|
||||
item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name"));
|
||||
|
||||
@@ -86,35 +86,6 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
|
||||
out.chop(1);
|
||||
return out;
|
||||
}
|
||||
|
||||
QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::string& name,
|
||||
const std::vector<u8>& icon, Loader::AppLoader& loader,
|
||||
u64 program_id, const CompatibilityList& compatibility_list,
|
||||
const FileSys::PatchManager& patch) {
|
||||
const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||
|
||||
// The game list uses this as compatibility number for untested games
|
||||
QString compatibility{"99"};
|
||||
if (it != compatibility_list.end()) {
|
||||
compatibility = it->second.first;
|
||||
}
|
||||
|
||||
QList<QStandardItem*> list{
|
||||
new GameListItemPath(
|
||||
FormatGameName(path), icon, QString::fromStdString(name),
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader.GetFileType())), program_id),
|
||||
new GameListItemCompat(compatibility),
|
||||
new GameListItem(QString::fromStdString(Loader::GetFileTypeString(loader.GetFileType()))),
|
||||
new GameListItemSize(FileUtil::GetSize(path)),
|
||||
};
|
||||
|
||||
if (UISettings::values.show_add_ons) {
|
||||
list.insert(
|
||||
2, new GameListItem(FormatPatchNameVersions(patch, loader, loader.IsRomFSUpdatable())));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan,
|
||||
@@ -126,11 +97,11 @@ GameListWorker::~GameListWorker() = default;
|
||||
|
||||
void GameListWorker::AddInstalledTitlesToGameList() {
|
||||
const auto cache = Service::FileSystem::GetUnionContents();
|
||||
const auto installed_games = cache.ListEntriesFilter(FileSys::TitleType::Application,
|
||||
FileSys::ContentRecordType::Program);
|
||||
const auto installed_games = cache->ListEntriesFilter(FileSys::TitleType::Application,
|
||||
FileSys::ContentRecordType::Program);
|
||||
|
||||
for (const auto& game : installed_games) {
|
||||
const auto file = cache.GetEntryUnparsed(game);
|
||||
const auto file = cache->GetEntryUnparsed(game);
|
||||
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
|
||||
if (!loader)
|
||||
continue;
|
||||
@@ -141,19 +112,40 @@ void GameListWorker::AddInstalledTitlesToGameList() {
|
||||
loader->ReadProgramId(program_id);
|
||||
|
||||
const FileSys::PatchManager patch{program_id};
|
||||
const auto control = cache.GetEntry(game.title_id, FileSys::ContentRecordType::Control);
|
||||
const auto control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);
|
||||
if (control != nullptr)
|
||||
GetMetadataFromControlNCA(patch, *control, icon, name);
|
||||
|
||||
emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id,
|
||||
compatibility_list, patch));
|
||||
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||
|
||||
// The game list uses this as compatibility number for untested games
|
||||
QString compatibility("99");
|
||||
if (it != compatibility_list.end())
|
||||
compatibility = it->second.first;
|
||||
|
||||
QList<QStandardItem*> list{
|
||||
new GameListItemPath(
|
||||
FormatGameName(file->GetFullPath()), icon, QString::fromStdString(name),
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
|
||||
program_id),
|
||||
new GameListItemCompat(compatibility),
|
||||
new GameListItem(
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
|
||||
new GameListItemSize(file->GetSize()),
|
||||
};
|
||||
|
||||
if (UISettings::values.show_add_ons) {
|
||||
list.insert(2, new GameListItem(FormatPatchNameVersions(patch, *loader)));
|
||||
}
|
||||
|
||||
emit EntryReady(list);
|
||||
}
|
||||
|
||||
const auto control_data = cache.ListEntriesFilter(FileSys::TitleType::Application,
|
||||
FileSys::ContentRecordType::Control);
|
||||
const auto control_data = cache->ListEntriesFilter(FileSys::TitleType::Application,
|
||||
FileSys::ContentRecordType::Control);
|
||||
|
||||
for (const auto& entry : control_data) {
|
||||
auto nca = cache.GetEntry(entry);
|
||||
auto nca = cache->GetEntry(entry);
|
||||
if (nca != nullptr) {
|
||||
nca_control_map.insert_or_assign(entry.title_id, std::move(nca));
|
||||
}
|
||||
@@ -163,14 +155,14 @@ void GameListWorker::AddInstalledTitlesToGameList() {
|
||||
void GameListWorker::FillControlMap(const std::string& dir_path) {
|
||||
const auto nca_control_callback = [this](u64* num_entries_out, const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
if (stop_processing) {
|
||||
// Breaks the callback loop
|
||||
return false;
|
||||
}
|
||||
std::string physical_name = directory + DIR_SEP + virtual_name;
|
||||
|
||||
const std::string physical_name = directory + DIR_SEP + virtual_name;
|
||||
const QFileInfo file_info(QString::fromStdString(physical_name));
|
||||
if (!file_info.isDir() && file_info.suffix() == QStringLiteral("nca")) {
|
||||
if (stop_processing)
|
||||
return false; // Breaks the callback loop.
|
||||
|
||||
bool is_dir = FileUtil::IsDirectory(physical_name);
|
||||
QFileInfo file_info(physical_name.c_str());
|
||||
if (!is_dir && file_info.suffix().toStdString() == "nca") {
|
||||
auto nca =
|
||||
std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
|
||||
if (nca->GetType() == FileSys::NCAContentType::Control) {
|
||||
@@ -187,13 +179,12 @@ void GameListWorker::FillControlMap(const std::string& dir_path) {
|
||||
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion) {
|
||||
const auto callback = [this, recursion](u64* num_entries_out, const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
if (stop_processing) {
|
||||
// Breaks the callback loop.
|
||||
return false;
|
||||
}
|
||||
std::string physical_name = directory + DIR_SEP + virtual_name;
|
||||
|
||||
const std::string physical_name = directory + DIR_SEP + virtual_name;
|
||||
const bool is_dir = FileUtil::IsDirectory(physical_name);
|
||||
if (stop_processing)
|
||||
return false; // Breaks the callback loop.
|
||||
|
||||
bool is_dir = FileUtil::IsDirectory(physical_name);
|
||||
if (!is_dir &&
|
||||
(HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
|
||||
std::unique_ptr<Loader::AppLoader> loader =
|
||||
@@ -223,8 +214,30 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
|
||||
}
|
||||
}
|
||||
|
||||
emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id,
|
||||
compatibility_list, patch));
|
||||
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||
|
||||
// The game list uses this as compatibility number for untested games
|
||||
QString compatibility("99");
|
||||
if (it != compatibility_list.end())
|
||||
compatibility = it->second.first;
|
||||
|
||||
QList<QStandardItem*> list{
|
||||
new GameListItemPath(
|
||||
FormatGameName(physical_name), icon, QString::fromStdString(name),
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
|
||||
program_id),
|
||||
new GameListItemCompat(compatibility),
|
||||
new GameListItem(
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
|
||||
new GameListItemSize(FileUtil::GetSize(physical_name)),
|
||||
};
|
||||
|
||||
if (UISettings::values.show_add_ons) {
|
||||
list.insert(2, new GameListItem(FormatPatchNameVersions(
|
||||
patch, *loader, loader->IsRomFSUpdatable())));
|
||||
}
|
||||
|
||||
emit EntryReady(std::move(list));
|
||||
} else if (is_dir && recursion > 0) {
|
||||
watch_list.append(QString::fromStdString(physical_name));
|
||||
AddFstEntriesToGameList(physical_name, recursion - 1);
|
||||
|
||||
@@ -518,8 +518,6 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
|
||||
QStringList GMainWindow::GetUnsupportedGLExtensions() {
|
||||
QStringList unsupported_ext;
|
||||
|
||||
if (!GLAD_GL_ARB_direct_state_access)
|
||||
unsupported_ext.append("ARB_direct_state_access");
|
||||
if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
|
||||
unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev");
|
||||
if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
|
||||
@@ -907,7 +905,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
|
||||
}
|
||||
|
||||
const auto installed = Service::FileSystem::GetUnionContents();
|
||||
const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id);
|
||||
auto romfs_title_id = SelectRomFSDumpTarget(*installed, program_id);
|
||||
|
||||
if (!romfs_title_id) {
|
||||
failed();
|
||||
@@ -922,7 +920,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
|
||||
if (*romfs_title_id == program_id) {
|
||||
romfs = file;
|
||||
} else {
|
||||
romfs = installed.GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
|
||||
romfs = installed->GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
|
||||
}
|
||||
|
||||
const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
|
||||
|
||||
@@ -111,8 +111,6 @@ void EmuWindow_SDL2::Fullscreen() {
|
||||
bool EmuWindow_SDL2::SupportsRequiredGLExtensions() {
|
||||
std::vector<std::string> unsupported_ext;
|
||||
|
||||
if (!GLAD_GL_ARB_direct_state_access)
|
||||
unsupported_ext.push_back("ARB_direct_state_access");
|
||||
if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
|
||||
unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev");
|
||||
if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
|
||||
|
||||
Reference in New Issue
Block a user