Compare commits
65 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37b23efece | ||
|
|
66be5150d6 | ||
|
|
7c1af3aa10 | ||
|
|
e6a9459b04 | ||
|
|
f7d6e08688 | ||
|
|
9959c95966 | ||
|
|
8502cda17a | ||
|
|
09789c3ffc | ||
|
|
88a3c05b7b | ||
|
|
7f506be2ee | ||
|
|
3fd5998d84 | ||
|
|
e8f3d85ea5 | ||
|
|
e5bb07a973 | ||
|
|
5ba5f82082 | ||
|
|
3f8c9b25d8 | ||
|
|
872d480c60 | ||
|
|
ba4e1adda1 | ||
|
|
7c31661869 | ||
|
|
0e2f617abc | ||
|
|
acde8d3f68 | ||
|
|
a56c4ac91b | ||
|
|
d6374b2522 | ||
|
|
d7438d067f | ||
|
|
a655b59cef | ||
|
|
a973271b8c | ||
|
|
ea8f633dc0 | ||
|
|
140cd5e209 | ||
|
|
a6d5ff05dc | ||
|
|
908f24eb88 | ||
|
|
7931a68d4e | ||
|
|
580e3564c9 | ||
|
|
74a4a50470 | ||
|
|
774fa0b828 | ||
|
|
65ae1ac4e5 | ||
|
|
a504bad3fb | ||
|
|
7ccb0b16cd | ||
|
|
6b629f4816 | ||
|
|
e796351a0d | ||
|
|
57279e1981 | ||
|
|
28719ee3b4 | ||
|
|
cb2bce8006 | ||
|
|
628153cccd | ||
|
|
4555b63750 | ||
|
|
4366241739 | ||
|
|
20cc0b8d3c | ||
|
|
2d70c30fb2 | ||
|
|
26d0381161 | ||
|
|
d09e98f566 | ||
|
|
108be41316 | ||
|
|
c6147a439d | ||
|
|
4fad477aeb | ||
|
|
c791192d64 | ||
|
|
6a1a2d4aa5 | ||
|
|
74cee1b65d | ||
|
|
798d76f4c7 | ||
|
|
746dab407e | ||
|
|
c1ba3e3d4a | ||
|
|
1650593927 | ||
|
|
7d88fc83bf | ||
|
|
d68716efdc | ||
|
|
758d84db9a | ||
|
|
96d518a59f | ||
|
|
f27c65eb91 | ||
|
|
996ddb202b | ||
|
|
9d411699d8 |
@@ -91,6 +91,8 @@ add_library(common STATIC
|
||||
logging/log.h
|
||||
logging/text_formatter.cpp
|
||||
logging/text_formatter.h
|
||||
lz4_compression.cpp
|
||||
lz4_compression.h
|
||||
math_util.h
|
||||
memory_hook.cpp
|
||||
memory_hook.h
|
||||
@@ -136,3 +138,4 @@ endif()
|
||||
create_target_directory_groups(common)
|
||||
|
||||
target_link_libraries(common PUBLIC Boost::boost fmt microprofile)
|
||||
target_link_libraries(common PRIVATE lz4_static)
|
||||
|
||||
76
src/common/lz4_compression.cpp
Normal file
76
src/common/lz4_compression.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <lz4hc.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/lz4_compression.h"
|
||||
|
||||
namespace Common::Compression {
|
||||
|
||||
std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size) {
|
||||
ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size");
|
||||
|
||||
const auto source_size_int = static_cast<int>(source_size);
|
||||
const int max_compressed_size = LZ4_compressBound(source_size_int);
|
||||
std::vector<u8> compressed(max_compressed_size);
|
||||
|
||||
const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source),
|
||||
reinterpret_cast<char*>(compressed.data()),
|
||||
source_size_int, max_compressed_size);
|
||||
|
||||
if (compressed_size <= 0) {
|
||||
// Compression failed
|
||||
return {};
|
||||
}
|
||||
|
||||
compressed.resize(compressed_size);
|
||||
|
||||
return compressed;
|
||||
}
|
||||
|
||||
std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size,
|
||||
s32 compression_level) {
|
||||
ASSERT_MSG(source_size <= LZ4_MAX_INPUT_SIZE, "Source size exceeds LZ4 maximum input size");
|
||||
|
||||
compression_level = std::clamp(compression_level, LZ4HC_CLEVEL_MIN, LZ4HC_CLEVEL_MAX);
|
||||
|
||||
const auto source_size_int = static_cast<int>(source_size);
|
||||
const int max_compressed_size = LZ4_compressBound(source_size_int);
|
||||
std::vector<u8> compressed(max_compressed_size);
|
||||
|
||||
const int compressed_size = LZ4_compress_HC(
|
||||
reinterpret_cast<const char*>(source), reinterpret_cast<char*>(compressed.data()),
|
||||
source_size_int, max_compressed_size, compression_level);
|
||||
|
||||
if (compressed_size <= 0) {
|
||||
// Compression failed
|
||||
return {};
|
||||
}
|
||||
|
||||
compressed.resize(compressed_size);
|
||||
|
||||
return compressed;
|
||||
}
|
||||
|
||||
std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size) {
|
||||
return CompressDataLZ4HC(source, source_size, LZ4HC_CLEVEL_MAX);
|
||||
}
|
||||
|
||||
std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed,
|
||||
std::size_t uncompressed_size) {
|
||||
std::vector<u8> uncompressed(uncompressed_size);
|
||||
const int size_check = LZ4_decompress_safe(reinterpret_cast<const char*>(compressed.data()),
|
||||
reinterpret_cast<char*>(uncompressed.data()),
|
||||
static_cast<int>(compressed.size()),
|
||||
static_cast<int>(uncompressed.size()));
|
||||
if (static_cast<int>(uncompressed_size) != size_check) {
|
||||
// Decompression failed
|
||||
return {};
|
||||
}
|
||||
return uncompressed;
|
||||
}
|
||||
|
||||
} // namespace Common::Compression
|
||||
55
src/common/lz4_compression.h
Normal file
55
src/common/lz4_compression.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common::Compression {
|
||||
|
||||
/**
|
||||
* Compresses a source memory region with LZ4 and returns the compressed data in a vector.
|
||||
*
|
||||
* @param source the uncompressed source memory region.
|
||||
* @param source_size the size in bytes of the uncompressed source memory region.
|
||||
*
|
||||
* @return the compressed data.
|
||||
*/
|
||||
std::vector<u8> CompressDataLZ4(const u8* source, std::size_t source_size);
|
||||
|
||||
/**
|
||||
* Utilizes the LZ4 subalgorithm LZ4HC with the specified compression level. Higher compression
|
||||
* levels result in a smaller compressed size, but require more CPU time for compression. The
|
||||
* compression level has almost no impact on decompression speed. Data compressed with LZ4HC can
|
||||
* also be decompressed with the default LZ4 decompression.
|
||||
*
|
||||
* @param source the uncompressed source memory region.
|
||||
* @param source_size the size in bytes of the uncompressed source memory region.
|
||||
* @param compression_level the used compression level. Should be between 3 and 12.
|
||||
*
|
||||
* @return the compressed data.
|
||||
*/
|
||||
std::vector<u8> CompressDataLZ4HC(const u8* source, std::size_t source_size, s32 compression_level);
|
||||
|
||||
/**
|
||||
* Utilizes the LZ4 subalgorithm LZ4HC with the highest possible compression level.
|
||||
*
|
||||
* @param source the uncompressed source memory region.
|
||||
* @param source_size the size in bytes of the uncompressed source memory region.
|
||||
*
|
||||
* @return the compressed data.
|
||||
*/
|
||||
std::vector<u8> CompressDataLZ4HCMax(const u8* source, std::size_t source_size);
|
||||
|
||||
/**
|
||||
* Decompresses a source memory region with LZ4 and returns the uncompressed data in a vector.
|
||||
*
|
||||
* @param compressed the compressed source memory region.
|
||||
* @param uncompressed_size the size in bytes of the uncompressed data.
|
||||
*
|
||||
* @return the decompressed data.
|
||||
*/
|
||||
std::vector<u8> DecompressDataLZ4(const std::vector<u8>& compressed, std::size_t uncompressed_size);
|
||||
|
||||
} // namespace Common::Compression
|
||||
@@ -458,7 +458,7 @@ add_library(core STATIC
|
||||
create_target_directory_groups(core)
|
||||
|
||||
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt mbedtls opus unicorn open_source_archives)
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
|
||||
target_link_libraries(core PRIVATE web_service)
|
||||
|
||||
@@ -67,7 +67,7 @@ std::string NACP::GetDeveloperName(Language language) const {
|
||||
}
|
||||
|
||||
u64 NACP::GetTitleId() const {
|
||||
return raw.title_id;
|
||||
return raw.save_data_owner_id;
|
||||
}
|
||||
|
||||
u64 NACP::GetDLCBaseTitleId() const {
|
||||
@@ -80,11 +80,11 @@ std::string NACP::GetVersionString() const {
|
||||
}
|
||||
|
||||
u64 NACP::GetDefaultNormalSaveSize() const {
|
||||
return raw.normal_save_data_size;
|
||||
return raw.user_account_save_data_size;
|
||||
}
|
||||
|
||||
u64 NACP::GetDefaultJournalSaveSize() const {
|
||||
return raw.journal_sava_data_size;
|
||||
return raw.user_account_save_data_journal_size;
|
||||
}
|
||||
|
||||
std::vector<u8> NACP::GetRawBytes() const {
|
||||
|
||||
@@ -38,23 +38,35 @@ struct RawNACP {
|
||||
u8 video_capture_mode;
|
||||
bool data_loss_confirmation;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u64_le title_id;
|
||||
u64_le presence_group_id;
|
||||
std::array<u8, 0x20> rating_age;
|
||||
std::array<char, 0x10> version_string;
|
||||
u64_le dlc_base_title_id;
|
||||
u64_le title_id_2;
|
||||
u64_le normal_save_data_size;
|
||||
u64_le journal_sava_data_size;
|
||||
INSERT_PADDING_BYTES(0x18);
|
||||
u64_le product_code;
|
||||
u64_le save_data_owner_id;
|
||||
u64_le user_account_save_data_size;
|
||||
u64_le user_account_save_data_journal_size;
|
||||
u64_le device_save_data_size;
|
||||
u64_le device_save_data_journal_size;
|
||||
u64_le bcat_delivery_cache_storage_size;
|
||||
char application_error_code_category[8];
|
||||
std::array<u64_le, 0x8> local_communication;
|
||||
u8 logo_type;
|
||||
u8 logo_handling;
|
||||
bool runtime_add_on_content_install;
|
||||
INSERT_PADDING_BYTES(5);
|
||||
u64_le title_id_update;
|
||||
std::array<u8, 0x40> bcat_passphrase;
|
||||
INSERT_PADDING_BYTES(0xEC0);
|
||||
u64_le seed_for_pseudo_device_id;
|
||||
std::array<u8, 0x41> bcat_passphrase;
|
||||
INSERT_PADDING_BYTES(7);
|
||||
u64_le user_account_save_data_max_size;
|
||||
u64_le user_account_save_data_max_journal_size;
|
||||
u64_le device_save_data_max_size;
|
||||
u64_le device_save_data_max_journal_size;
|
||||
u64_le temporary_storage_size;
|
||||
u64_le cache_storage_size;
|
||||
u64_le cache_storage_journal_size;
|
||||
u64_le cache_storage_data_and_journal_max_size;
|
||||
u64_le cache_storage_max_index;
|
||||
INSERT_PADDING_BYTES(0xE70);
|
||||
};
|
||||
static_assert(sizeof(RawNACP) == 0x4000, "RawNACP has incorrect size.");
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
@@ -17,28 +16,30 @@ ProgramMetadata::ProgramMetadata() = default;
|
||||
ProgramMetadata::~ProgramMetadata() = default;
|
||||
|
||||
Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
|
||||
std::size_t total_size = static_cast<std::size_t>(file->GetSize());
|
||||
if (total_size < sizeof(Header))
|
||||
const std::size_t total_size = file->GetSize();
|
||||
if (total_size < sizeof(Header)) {
|
||||
return Loader::ResultStatus::ErrorBadNPDMHeader;
|
||||
}
|
||||
|
||||
// TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable.
|
||||
std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header));
|
||||
if (sizeof(Header) != npdm_header_data.size())
|
||||
if (sizeof(Header) != file->ReadObject(&npdm_header)) {
|
||||
return Loader::ResultStatus::ErrorBadNPDMHeader;
|
||||
std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header));
|
||||
}
|
||||
|
||||
std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset);
|
||||
if (sizeof(AcidHeader) != acid_header_data.size())
|
||||
if (sizeof(AcidHeader) != file->ReadObject(&acid_header, npdm_header.acid_offset)) {
|
||||
return Loader::ResultStatus::ErrorBadACIDHeader;
|
||||
std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader));
|
||||
}
|
||||
|
||||
if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset))
|
||||
if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) {
|
||||
return Loader::ResultStatus::ErrorBadACIHeader;
|
||||
}
|
||||
|
||||
if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset))
|
||||
if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) {
|
||||
return Loader::ResultStatus::ErrorBadFileAccessControl;
|
||||
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset))
|
||||
}
|
||||
|
||||
if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) {
|
||||
return Loader::ResultStatus::ErrorBadFileAccessHeader;
|
||||
}
|
||||
|
||||
aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32));
|
||||
const u64 read_size = aci_header.kac_size;
|
||||
|
||||
@@ -58,7 +58,6 @@ public:
|
||||
void Print() const;
|
||||
|
||||
private:
|
||||
// TODO(DarkLordZach): BitField is not trivially copyable.
|
||||
struct Header {
|
||||
std::array<char, 4> magic;
|
||||
std::array<u8, 8> reserved;
|
||||
@@ -85,7 +84,6 @@ private:
|
||||
|
||||
static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong");
|
||||
|
||||
// TODO(DarkLordZach): BitField is not trivially copyable.
|
||||
struct AcidHeader {
|
||||
std::array<u8, 0x100> signature;
|
||||
std::array<u8, 0x100> nca_modulus;
|
||||
|
||||
@@ -115,7 +115,7 @@ struct KernelCore::Impl {
|
||||
|
||||
// Creates the default system resource limit
|
||||
void InitializeSystemResourceLimit(KernelCore& kernel) {
|
||||
system_resource_limit = ResourceLimit::Create(kernel, "System");
|
||||
system_resource_limit = ResourceLimit::Create(kernel);
|
||||
|
||||
// If setting the default system values fails, then something seriously wrong has occurred.
|
||||
ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000)
|
||||
@@ -191,6 +191,10 @@ const Process* KernelCore::CurrentProcess() const {
|
||||
return impl->current_process;
|
||||
}
|
||||
|
||||
const std::vector<SharedPtr<Process>>& KernelCore::GetProcessList() const {
|
||||
return impl->process_list;
|
||||
}
|
||||
|
||||
void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
|
||||
impl->named_ports.emplace(std::move(name), std::move(port));
|
||||
}
|
||||
|
||||
@@ -72,6 +72,9 @@ public:
|
||||
/// Retrieves a const pointer to the current process.
|
||||
const Process* CurrentProcess() const;
|
||||
|
||||
/// Retrieves the list of processes.
|
||||
const std::vector<SharedPtr<Process>>& GetProcessList() const;
|
||||
|
||||
/// Adds a port to the named port table
|
||||
void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ bool Object::IsWaitable() const {
|
||||
case HandleType::WritableEvent:
|
||||
case HandleType::SharedMemory:
|
||||
case HandleType::TransferMemory:
|
||||
case HandleType::AddressArbiter:
|
||||
case HandleType::ResourceLimit:
|
||||
case HandleType::ClientPort:
|
||||
case HandleType::ClientSession:
|
||||
|
||||
@@ -25,7 +25,6 @@ enum class HandleType : u32 {
|
||||
TransferMemory,
|
||||
Thread,
|
||||
Process,
|
||||
AddressArbiter,
|
||||
ResourceLimit,
|
||||
ClientPort,
|
||||
ServerPort,
|
||||
|
||||
@@ -80,6 +80,14 @@ u64 Process::GetTotalPhysicalMemoryUsed() const {
|
||||
return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size;
|
||||
}
|
||||
|
||||
void Process::RegisterThread(const Thread* thread) {
|
||||
thread_list.push_back(thread);
|
||||
}
|
||||
|
||||
void Process::UnregisterThread(const Thread* thread) {
|
||||
thread_list.remove(thread);
|
||||
}
|
||||
|
||||
ResultCode Process::ClearSignalState() {
|
||||
if (status == ProcessStatus::Exited) {
|
||||
LOG_ERROR(Kernel, "called on a terminated process instance.");
|
||||
@@ -249,7 +257,7 @@ void Process::Acquire(Thread* thread) {
|
||||
ASSERT_MSG(!ShouldWait(thread), "Object unavailable!");
|
||||
}
|
||||
|
||||
bool Process::ShouldWait(Thread* thread) const {
|
||||
bool Process::ShouldWait(const Thread* thread) const {
|
||||
return !is_signaled;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstddef>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
@@ -189,6 +190,19 @@ public:
|
||||
/// Retrieves the total physical memory used by this process in bytes.
|
||||
u64 GetTotalPhysicalMemoryUsed() const;
|
||||
|
||||
/// Gets the list of all threads created with this process as their owner.
|
||||
const std::list<const Thread*>& GetThreadList() const {
|
||||
return thread_list;
|
||||
}
|
||||
|
||||
/// Registers a thread as being created under this process,
|
||||
/// adding it to this process' thread list.
|
||||
void RegisterThread(const Thread* thread);
|
||||
|
||||
/// Unregisters a thread from this process, removing it
|
||||
/// from this process' thread list.
|
||||
void UnregisterThread(const Thread* thread);
|
||||
|
||||
/// Clears the signaled state of the process if and only if it's signaled.
|
||||
///
|
||||
/// @pre The process must not be already terminated. If this is called on a
|
||||
@@ -237,7 +251,7 @@ private:
|
||||
~Process() override;
|
||||
|
||||
/// Checks if the specified thread should wait until this process is available.
|
||||
bool ShouldWait(Thread* thread) const override;
|
||||
bool ShouldWait(const Thread* thread) const override;
|
||||
|
||||
/// Acquires/locks this process for the specified thread if it's available.
|
||||
void Acquire(Thread* thread) override;
|
||||
@@ -308,6 +322,9 @@ private:
|
||||
/// Random values for svcGetInfo RandomEntropy
|
||||
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
|
||||
|
||||
/// List of threads that are running with this process as their owner.
|
||||
std::list<const Thread*> thread_list;
|
||||
|
||||
/// System context
|
||||
Core::System& system;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Kernel {
|
||||
ReadableEvent::ReadableEvent(KernelCore& kernel) : WaitObject{kernel} {}
|
||||
ReadableEvent::~ReadableEvent() = default;
|
||||
|
||||
bool ReadableEvent::ShouldWait(Thread* thread) const {
|
||||
bool ReadableEvent::ShouldWait(const Thread* thread) const {
|
||||
return !signaled;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
bool ShouldWait(Thread* thread) const override;
|
||||
bool ShouldWait(const Thread* thread) const override;
|
||||
void Acquire(Thread* thread) override;
|
||||
|
||||
/// Unconditionally clears the readable event's state.
|
||||
|
||||
@@ -16,11 +16,8 @@ constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
|
||||
ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
|
||||
ResourceLimit::~ResourceLimit() = default;
|
||||
|
||||
SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string name) {
|
||||
SharedPtr<ResourceLimit> resource_limit(new ResourceLimit(kernel));
|
||||
|
||||
resource_limit->name = std::move(name);
|
||||
return resource_limit;
|
||||
SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel) {
|
||||
return new ResourceLimit(kernel);
|
||||
}
|
||||
|
||||
s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
|
||||
|
||||
@@ -31,16 +31,14 @@ constexpr bool IsValidResourceType(ResourceType type) {
|
||||
|
||||
class ResourceLimit final : public Object {
|
||||
public:
|
||||
/**
|
||||
* Creates a resource limit object.
|
||||
*/
|
||||
static SharedPtr<ResourceLimit> Create(KernelCore& kernel, std::string name = "Unknown");
|
||||
/// Creates a resource limit object.
|
||||
static SharedPtr<ResourceLimit> Create(KernelCore& kernel);
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "ResourceLimit";
|
||||
}
|
||||
std::string GetName() const override {
|
||||
return name;
|
||||
return GetTypeName();
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::ResourceLimit;
|
||||
@@ -95,9 +93,6 @@ private:
|
||||
ResourceArray limits{};
|
||||
/// Current resource limit values.
|
||||
ResourceArray values{};
|
||||
|
||||
/// Name of resource limit object.
|
||||
std::string name;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -30,7 +30,7 @@ void ServerPort::AppendPendingSession(SharedPtr<ServerSession> pending_session)
|
||||
pending_sessions.push_back(std::move(pending_session));
|
||||
}
|
||||
|
||||
bool ServerPort::ShouldWait(Thread* thread) const {
|
||||
bool ServerPort::ShouldWait(const Thread* thread) const {
|
||||
// If there are no pending sessions, we wait until a new one is added.
|
||||
return pending_sessions.empty();
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
/// waiting to be accepted by this port.
|
||||
void AppendPendingSession(SharedPtr<ServerSession> pending_session);
|
||||
|
||||
bool ShouldWait(Thread* thread) const override;
|
||||
bool ShouldWait(const Thread* thread) const override;
|
||||
void Acquire(Thread* thread) override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -46,7 +46,7 @@ ResultVal<SharedPtr<ServerSession>> ServerSession::Create(KernelCore& kernel, st
|
||||
return MakeResult(std::move(server_session));
|
||||
}
|
||||
|
||||
bool ServerSession::ShouldWait(Thread* thread) const {
|
||||
bool ServerSession::ShouldWait(const Thread* thread) const {
|
||||
// Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
|
||||
if (parent->client == nullptr)
|
||||
return false;
|
||||
|
||||
@@ -82,7 +82,7 @@ public:
|
||||
*/
|
||||
ResultCode HandleSyncRequest(SharedPtr<Thread> thread);
|
||||
|
||||
bool ShouldWait(Thread* thread) const override;
|
||||
bool ShouldWait(const Thread* thread) const override;
|
||||
|
||||
void Acquire(Thread* thread) override;
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -119,7 +118,15 @@ ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermi
|
||||
ConvertPermissions(permissions));
|
||||
}
|
||||
|
||||
ResultCode SharedMemory::Unmap(Process& target_process, VAddr address) {
|
||||
ResultCode SharedMemory::Unmap(Process& target_process, VAddr address, u64 unmap_size) {
|
||||
if (unmap_size != size) {
|
||||
LOG_ERROR(Kernel,
|
||||
"Invalid size passed to Unmap. Size must be equal to the size of the "
|
||||
"memory managed. Shared memory size=0x{:016X}, Unmap size=0x{:016X}",
|
||||
size, unmap_size);
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
// TODO(Subv): Verify what happens if the application tries to unmap an address that is not
|
||||
// mapped to a SharedMemory.
|
||||
return target_process.VMManager().UnmapRange(address, size);
|
||||
|
||||
@@ -104,11 +104,17 @@ public:
|
||||
|
||||
/**
|
||||
* Unmaps a shared memory block from the specified address in system memory
|
||||
*
|
||||
* @param target_process Process from which to unmap the memory block.
|
||||
* @param address Address in system memory where the shared memory block is mapped
|
||||
* @param address Address in system memory where the shared memory block is mapped.
|
||||
* @param unmap_size The amount of bytes to unmap from this shared memory instance.
|
||||
*
|
||||
* @return Result code of the unmap operation
|
||||
*
|
||||
* @pre The given size to unmap must be the same size as the amount of memory managed by
|
||||
* the SharedMemory instance itself, otherwise ERR_INVALID_SIZE will be returned.
|
||||
*/
|
||||
ResultCode Unmap(Process& target_process, VAddr address);
|
||||
ResultCode Unmap(Process& target_process, VAddr address, u64 unmap_size);
|
||||
|
||||
/**
|
||||
* Gets a pointer to the shared memory block
|
||||
|
||||
@@ -1140,7 +1140,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
|
||||
return ERR_INVALID_MEMORY_RANGE;
|
||||
}
|
||||
|
||||
return shared_memory->Unmap(*current_process, addr);
|
||||
return shared_memory->Unmap(*current_process, addr, size);
|
||||
}
|
||||
|
||||
static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address,
|
||||
@@ -1983,6 +1983,83 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids,
|
||||
u32 out_process_ids_size) {
|
||||
LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
|
||||
out_process_ids, out_process_ids_size);
|
||||
|
||||
// If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail.
|
||||
if ((out_process_ids_size & 0xF0000000) != 0) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}",
|
||||
out_process_ids_size);
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
const auto& kernel = Core::System::GetInstance().Kernel();
|
||||
const auto& vm_manager = kernel.CurrentProcess()->VMManager();
|
||||
const auto total_copy_size = out_process_ids_size * sizeof(u64);
|
||||
|
||||
if (out_process_ids_size > 0 &&
|
||||
!vm_manager.IsWithinAddressSpace(out_process_ids, total_copy_size)) {
|
||||
LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
|
||||
out_process_ids, out_process_ids + total_copy_size);
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const auto& process_list = kernel.GetProcessList();
|
||||
const auto num_processes = process_list.size();
|
||||
const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes);
|
||||
|
||||
for (std::size_t i = 0; i < copy_amount; ++i) {
|
||||
Memory::Write64(out_process_ids, process_list[i]->GetProcessID());
|
||||
out_process_ids += sizeof(u64);
|
||||
}
|
||||
|
||||
*out_num_processes = static_cast<u32>(num_processes);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thread_ids_size,
|
||||
Handle debug_handle) {
|
||||
// TODO: Handle this case when debug events are supported.
|
||||
UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
|
||||
|
||||
LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
|
||||
out_thread_ids, out_thread_ids_size);
|
||||
|
||||
// If the size is negative or larger than INT32_MAX / sizeof(u64)
|
||||
if ((out_thread_ids_size & 0xF0000000) != 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
|
||||
out_thread_ids_size);
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
const auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess();
|
||||
const auto& vm_manager = current_process->VMManager();
|
||||
const auto total_copy_size = out_thread_ids_size * sizeof(u64);
|
||||
|
||||
if (out_thread_ids_size > 0 &&
|
||||
!vm_manager.IsWithinAddressSpace(out_thread_ids, total_copy_size)) {
|
||||
LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
|
||||
out_thread_ids, out_thread_ids + total_copy_size);
|
||||
return ERR_INVALID_ADDRESS_STATE;
|
||||
}
|
||||
|
||||
const auto& thread_list = current_process->GetThreadList();
|
||||
const auto num_threads = thread_list.size();
|
||||
const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
|
||||
|
||||
auto list_iter = thread_list.cbegin();
|
||||
for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
|
||||
Memory::Write64(out_thread_ids, (*list_iter)->GetThreadID());
|
||||
out_thread_ids += sizeof(u64);
|
||||
}
|
||||
|
||||
*out_num_threads = static_cast<u32>(num_threads);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct FunctionDef {
|
||||
using Func = void();
|
||||
@@ -2095,8 +2172,8 @@ static const FunctionDef SVC_Table[] = {
|
||||
{0x62, nullptr, "TerminateDebugProcess"},
|
||||
{0x63, nullptr, "GetDebugEvent"},
|
||||
{0x64, nullptr, "ContinueDebugEvent"},
|
||||
{0x65, nullptr, "GetProcessList"},
|
||||
{0x66, nullptr, "GetThreadList"},
|
||||
{0x65, SvcWrap<GetProcessList>, "GetProcessList"},
|
||||
{0x66, SvcWrap<GetThreadList>, "GetThreadList"},
|
||||
{0x67, nullptr, "GetDebugThreadContext"},
|
||||
{0x68, nullptr, "SetDebugThreadContext"},
|
||||
{0x69, nullptr, "QueryDebugProcessMemory"},
|
||||
|
||||
@@ -78,6 +78,14 @@ void SvcWrap() {
|
||||
FuncReturn(retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u32*, u64, u32)>
|
||||
void SvcWrap() {
|
||||
u32 param_1 = 0;
|
||||
const u32 retval = func(¶m_1, Param(1), static_cast<u32>(Param(2))).raw;
|
||||
Core::CurrentArmInterface().SetReg(1, param_1);
|
||||
FuncReturn(retval);
|
||||
}
|
||||
|
||||
template <ResultCode func(u64*, u32)>
|
||||
void SvcWrap() {
|
||||
u64 param_1 = 0;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
bool Thread::ShouldWait(Thread* thread) const {
|
||||
bool Thread::ShouldWait(const Thread* thread) const {
|
||||
return status != ThreadStatus::Dead;
|
||||
}
|
||||
|
||||
@@ -62,6 +62,8 @@ void Thread::Stop() {
|
||||
}
|
||||
wait_objects.clear();
|
||||
|
||||
owner_process->UnregisterThread(this);
|
||||
|
||||
// Mark the TLS slot in the thread's page as free.
|
||||
owner_process->FreeTLSSlot(tls_address);
|
||||
}
|
||||
@@ -202,6 +204,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
|
||||
thread->scheduler->AddThread(thread);
|
||||
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
|
||||
|
||||
thread->owner_process->RegisterThread(thread.get());
|
||||
|
||||
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
|
||||
// to initialize the context
|
||||
ResetThreadContext(thread->context, stack_top, entry_point, arg);
|
||||
@@ -229,16 +233,16 @@ void Thread::SetWaitSynchronizationOutput(s32 output) {
|
||||
context.cpu_registers[1] = output;
|
||||
}
|
||||
|
||||
s32 Thread::GetWaitObjectIndex(WaitObject* object) const {
|
||||
s32 Thread::GetWaitObjectIndex(const WaitObject* object) const {
|
||||
ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything");
|
||||
auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object);
|
||||
const auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object);
|
||||
return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1);
|
||||
}
|
||||
|
||||
VAddr Thread::GetCommandBufferAddress() const {
|
||||
// Offset from the start of TLS at which the IPC command buffer begins.
|
||||
static constexpr int CommandHeaderOffset = 0x80;
|
||||
return GetTLSAddress() + CommandHeaderOffset;
|
||||
constexpr u64 command_header_offset = 0x80;
|
||||
return GetTLSAddress() + command_header_offset;
|
||||
}
|
||||
|
||||
void Thread::SetStatus(ThreadStatus new_status) {
|
||||
@@ -367,7 +371,7 @@ void Thread::ChangeScheduler() {
|
||||
system.CpuCore(processor_id).PrepareReschedule();
|
||||
}
|
||||
|
||||
bool Thread::AllWaitObjectsReady() {
|
||||
bool Thread::AllWaitObjectsReady() const {
|
||||
return std::none_of(
|
||||
wait_objects.begin(), wait_objects.end(),
|
||||
[this](const SharedPtr<WaitObject>& object) { return object->ShouldWait(this); });
|
||||
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
bool ShouldWait(Thread* thread) const override;
|
||||
bool ShouldWait(const Thread* thread) const override;
|
||||
void Acquire(Thread* thread) override;
|
||||
|
||||
/**
|
||||
@@ -205,7 +205,7 @@ public:
|
||||
* object in the list.
|
||||
* @param object Object to query the index of.
|
||||
*/
|
||||
s32 GetWaitObjectIndex(WaitObject* object) const;
|
||||
s32 GetWaitObjectIndex(const WaitObject* object) const;
|
||||
|
||||
/**
|
||||
* Stops a thread, invalidating it from further use
|
||||
@@ -299,7 +299,7 @@ public:
|
||||
}
|
||||
|
||||
/// Determines whether all the objects this thread is waiting on are ready.
|
||||
bool AllWaitObjectsReady();
|
||||
bool AllWaitObjectsReady() const;
|
||||
|
||||
const MutexWaitingThreads& GetMutexWaitingThreads() const {
|
||||
return wait_mutex_threads;
|
||||
|
||||
@@ -14,8 +14,8 @@ namespace Kernel {
|
||||
TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {}
|
||||
TransferMemory::~TransferMemory() = default;
|
||||
|
||||
SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address,
|
||||
size_t size, MemoryPermission permissions) {
|
||||
SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address, u64 size,
|
||||
MemoryPermission permissions) {
|
||||
SharedPtr<TransferMemory> transfer_memory{new TransferMemory(kernel)};
|
||||
|
||||
transfer_memory->base_address = base_address;
|
||||
@@ -26,7 +26,15 @@ SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_
|
||||
return transfer_memory;
|
||||
}
|
||||
|
||||
ResultCode TransferMemory::MapMemory(VAddr address, size_t size, MemoryPermission permissions) {
|
||||
const u8* TransferMemory::GetPointer() const {
|
||||
return backing_block.get()->data();
|
||||
}
|
||||
|
||||
u64 TransferMemory::GetSize() const {
|
||||
return memory_size;
|
||||
}
|
||||
|
||||
ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission permissions) {
|
||||
if (memory_size != size) {
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
@@ -39,13 +47,13 @@ ResultCode TransferMemory::MapMemory(VAddr address, size_t size, MemoryPermissio
|
||||
return ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
backing_block = std::make_shared<std::vector<u8>>(size);
|
||||
|
||||
const auto map_state = owner_permissions == MemoryPermission::None
|
||||
? MemoryState::TransferMemoryIsolated
|
||||
: MemoryState::TransferMemory;
|
||||
auto& vm_manager = owner_process->VMManager();
|
||||
const auto map_result = vm_manager.MapMemoryBlock(
|
||||
address, std::make_shared<std::vector<u8>>(size), 0, size, map_state);
|
||||
|
||||
const auto map_result = vm_manager.MapMemoryBlock(address, backing_block, 0, size, map_state);
|
||||
if (map_result.Failed()) {
|
||||
return map_result.Code();
|
||||
}
|
||||
@@ -54,7 +62,7 @@ ResultCode TransferMemory::MapMemory(VAddr address, size_t size, MemoryPermissio
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode TransferMemory::UnmapMemory(VAddr address, size_t size) {
|
||||
ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) {
|
||||
if (memory_size != size) {
|
||||
return ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
union ResultCode;
|
||||
@@ -25,7 +28,7 @@ class TransferMemory final : public Object {
|
||||
public:
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory;
|
||||
|
||||
static SharedPtr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, size_t size,
|
||||
static SharedPtr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, u64 size,
|
||||
MemoryPermission permissions);
|
||||
|
||||
TransferMemory(const TransferMemory&) = delete;
|
||||
@@ -46,6 +49,12 @@ public:
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
/// Gets a pointer to the backing block of this instance.
|
||||
const u8* GetPointer() const;
|
||||
|
||||
/// Gets the size of the memory backing this instance in bytes.
|
||||
u64 GetSize() const;
|
||||
|
||||
/// Attempts to map transfer memory with the given range and memory permissions.
|
||||
///
|
||||
/// @param address The base address to being mapping memory at.
|
||||
@@ -56,7 +65,7 @@ public:
|
||||
/// the same values that were given when creating the transfer memory
|
||||
/// instance.
|
||||
///
|
||||
ResultCode MapMemory(VAddr address, size_t size, MemoryPermission permissions);
|
||||
ResultCode MapMemory(VAddr address, u64 size, MemoryPermission permissions);
|
||||
|
||||
/// Unmaps the transfer memory with the given range
|
||||
///
|
||||
@@ -66,17 +75,20 @@ public:
|
||||
/// @pre The given address and size must be the same as the ones used
|
||||
/// to create the transfer memory instance.
|
||||
///
|
||||
ResultCode UnmapMemory(VAddr address, size_t size);
|
||||
ResultCode UnmapMemory(VAddr address, u64 size);
|
||||
|
||||
private:
|
||||
explicit TransferMemory(KernelCore& kernel);
|
||||
~TransferMemory() override;
|
||||
|
||||
/// Memory block backing this instance.
|
||||
std::shared_ptr<std::vector<u8>> backing_block;
|
||||
|
||||
/// The base address for the memory managed by this instance.
|
||||
VAddr base_address = 0;
|
||||
|
||||
/// Size of the memory, in bytes, that this instance manages.
|
||||
size_t memory_size = 0;
|
||||
u64 memory_size = 0;
|
||||
|
||||
/// The memory permissions that are applied to this instance.
|
||||
MemoryPermission owner_permissions{};
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
* @param thread The thread about which we're deciding.
|
||||
* @return True if the current thread should wait due to this object being unavailable
|
||||
*/
|
||||
virtual bool ShouldWait(Thread* thread) const = 0;
|
||||
virtual bool ShouldWait(const Thread* thread) const = 0;
|
||||
|
||||
/// Acquire/lock the object for the specified thread if it is available
|
||||
virtual void Acquire(Thread* thread) = 0;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "core/hle/kernel/kernel.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/transfer_memory.h"
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
@@ -239,8 +239,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
{0, nullptr, "Exit"},
|
||||
{1, &ISelfController::LockExit, "LockExit"},
|
||||
{2, &ISelfController::UnlockExit, "UnlockExit"},
|
||||
{3, nullptr, "EnterFatalSection"},
|
||||
{4, nullptr, "LeaveFatalSection"},
|
||||
{3, &ISelfController::EnterFatalSection, "EnterFatalSection"},
|
||||
{4, &ISelfController::LeaveFatalSection, "LeaveFatalSection"},
|
||||
{9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
|
||||
{10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
|
||||
{11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"},
|
||||
@@ -285,41 +285,54 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
|
||||
ISelfController::~ISelfController() = default;
|
||||
|
||||
void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
|
||||
// Takes 3 input u8s with each field located immediately after the previous
|
||||
// u8, these are bool flags. No output.
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
struct FocusHandlingModeParams {
|
||||
u8 unknown0;
|
||||
u8 unknown1;
|
||||
u8 unknown2;
|
||||
};
|
||||
auto flags = rp.PopRaw<FocusHandlingModeParams>();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) {
|
||||
void ISelfController::LockExit(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
bool flag = rp.Pop<bool>();
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag);
|
||||
void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ISelfController::EnterFatalSection(Kernel::HLERequestContext& ctx) {
|
||||
++num_fatal_sections_entered;
|
||||
LOG_DEBUG(Service_AM, "called. Num fatal sections entered: {}", num_fatal_sections_entered);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ISelfController::LeaveFatalSection(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called.");
|
||||
|
||||
// Entry and exit of fatal sections must be balanced.
|
||||
if (num_fatal_sections_entered == 0) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode{ErrorModule::AM, 512});
|
||||
return;
|
||||
}
|
||||
|
||||
--num_fatal_sections_entered;
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
launchable_event.writable->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(launchable_event.readable);
|
||||
}
|
||||
|
||||
void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
@@ -337,6 +350,42 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
bool flag = rp.Pop<bool>();
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
|
||||
// Takes 3 input u8s with each field located immediately after the previous
|
||||
// u8, these are bool flags. No output.
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
struct FocusHandlingModeParams {
|
||||
u8 unknown0;
|
||||
u8 unknown1;
|
||||
u8 unknown2;
|
||||
};
|
||||
const auto flags = rp.PopRaw<FocusHandlingModeParams>();
|
||||
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called. unknown0={}, unknown1={}, unknown2={}",
|
||||
flags.unknown0, flags.unknown1, flags.unknown2);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
|
||||
// Takes 3 input u8s with each field located immediately after the previous
|
||||
// u8, these are bool flags. No output.
|
||||
@@ -349,30 +398,6 @@ void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext&
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ISelfController::LockExit(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
launchable_event.writable->Signal();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(launchable_event.readable);
|
||||
}
|
||||
|
||||
void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
@@ -907,19 +932,19 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
|
||||
rp.SetCurrentOffset(3);
|
||||
const auto handle{rp.Pop<Kernel::Handle>()};
|
||||
|
||||
const auto shared_mem =
|
||||
Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::SharedMemory>(
|
||||
const auto transfer_mem =
|
||||
Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(
|
||||
handle);
|
||||
|
||||
if (shared_mem == nullptr) {
|
||||
if (transfer_mem == nullptr) {
|
||||
LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultCode(-1));
|
||||
return;
|
||||
}
|
||||
|
||||
const u8* mem_begin = shared_mem->GetPointer();
|
||||
const u8* mem_end = mem_begin + shared_mem->GetSize();
|
||||
const u8* const mem_begin = transfer_mem->GetPointer();
|
||||
const u8* const mem_end = mem_begin + transfer_mem->GetSize();
|
||||
std::vector<u8> memory{mem_begin, mem_end};
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
@@ -117,17 +117,19 @@ public:
|
||||
~ISelfController() override;
|
||||
|
||||
private:
|
||||
void SetFocusHandlingMode(Kernel::HLERequestContext& ctx);
|
||||
void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx);
|
||||
void SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx);
|
||||
void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx);
|
||||
void LockExit(Kernel::HLERequestContext& ctx);
|
||||
void UnlockExit(Kernel::HLERequestContext& ctx);
|
||||
void EnterFatalSection(Kernel::HLERequestContext& ctx);
|
||||
void LeaveFatalSection(Kernel::HLERequestContext& ctx);
|
||||
void GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx);
|
||||
void SetScreenShotPermission(Kernel::HLERequestContext& ctx);
|
||||
void SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx);
|
||||
void SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx);
|
||||
void SetFocusHandlingMode(Kernel::HLERequestContext& ctx);
|
||||
void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx);
|
||||
void SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx);
|
||||
void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx);
|
||||
void SetScreenShotPermission(Kernel::HLERequestContext& ctx);
|
||||
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
|
||||
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
|
||||
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
|
||||
@@ -135,6 +137,7 @@ private:
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
Kernel::EventPair launchable_event;
|
||||
u32 idle_time_detection_extension = 0;
|
||||
u64 num_fatal_sections_entered = 0;
|
||||
};
|
||||
|
||||
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
|
||||
|
||||
@@ -150,7 +150,6 @@ private:
|
||||
void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
|
||||
const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
|
||||
|
||||
@@ -194,12 +193,9 @@ private:
|
||||
void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
ctx.WriteBuffer(DefaultDevice);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(1); // Amount of audio devices
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
@@ -184,7 +185,6 @@ public:
|
||||
private:
|
||||
void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
constexpr std::array<char, 15> audio_interface{{"AudioInterface"}};
|
||||
ctx.WriteBuffer(audio_interface);
|
||||
@@ -195,13 +195,13 @@ private:
|
||||
}
|
||||
|
||||
void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
f32 volume = static_cast<f32>(rp.Pop<u32>());
|
||||
const f32 volume = rp.Pop<f32>();
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
const auto device_name_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(device_name_buffer);
|
||||
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called. name={}, volume={}", name, volume);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -209,7 +209,6 @@ private:
|
||||
|
||||
void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
constexpr std::array<char, 12> audio_interface{{"AudioDevice"}};
|
||||
ctx.WriteBuffer(audio_interface);
|
||||
|
||||
@@ -315,61 +315,53 @@ public:
|
||||
void CreateFile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
u64 mode = rp.Pop<u64>();
|
||||
u32 size = rp.Pop<u32>();
|
||||
const u64 mode = rp.Pop<u64>();
|
||||
const u32 size = rp.Pop<u32>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size);
|
||||
LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, mode, size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.CreateFile(name, size));
|
||||
}
|
||||
|
||||
void DeleteFile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called file {}", name);
|
||||
LOG_DEBUG(Service_FS, "called. file={}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.DeleteFile(name));
|
||||
}
|
||||
|
||||
void CreateDirectory(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called directory {}", name);
|
||||
LOG_DEBUG(Service_FS, "called. directory={}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.CreateDirectory(name));
|
||||
}
|
||||
|
||||
void DeleteDirectory(Kernel::HLERequestContext& ctx) {
|
||||
const IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called directory {}", name);
|
||||
LOG_DEBUG(Service_FS, "called. directory={}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.DeleteDirectory(name));
|
||||
}
|
||||
|
||||
void DeleteDirectoryRecursively(Kernel::HLERequestContext& ctx) {
|
||||
const IPC::RequestParser rp{ctx};
|
||||
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called directory {}", name);
|
||||
LOG_DEBUG(Service_FS, "called. directory={}", name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.DeleteDirectoryRecursively(name));
|
||||
@@ -386,18 +378,16 @@ public:
|
||||
}
|
||||
|
||||
void RenameFile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
std::vector<u8> buffer;
|
||||
buffer.resize(ctx.BufferDescriptorX()[0].Size());
|
||||
Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
|
||||
std::string src_name = Common::StringFromBuffer(buffer);
|
||||
const std::string src_name = Common::StringFromBuffer(buffer);
|
||||
|
||||
buffer.resize(ctx.BufferDescriptorX()[1].Size());
|
||||
Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size());
|
||||
std::string dst_name = Common::StringFromBuffer(buffer);
|
||||
const std::string dst_name = Common::StringFromBuffer(buffer);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name);
|
||||
LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(backend.RenameFile(src_name, dst_name));
|
||||
@@ -406,12 +396,12 @@ public:
|
||||
void OpenFile(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
|
||||
const auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
|
||||
|
||||
LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode));
|
||||
LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, static_cast<u32>(mode));
|
||||
|
||||
auto result = backend.OpenFile(name, mode);
|
||||
if (result.Failed()) {
|
||||
@@ -430,13 +420,13 @@ public:
|
||||
void OpenDirectory(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
// TODO(Subv): Implement this filter.
|
||||
u32 filter_flags = rp.Pop<u32>();
|
||||
const u32 filter_flags = rp.Pop<u32>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags);
|
||||
LOG_DEBUG(Service_FS, "called. directory={}, filter={}", name, filter_flags);
|
||||
|
||||
auto result = backend.OpenDirectory(name);
|
||||
if (result.Failed()) {
|
||||
@@ -453,12 +443,10 @@ public:
|
||||
}
|
||||
|
||||
void GetEntryType(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto file_buffer = ctx.ReadBuffer();
|
||||
const std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
LOG_DEBUG(Service_FS, "called file {}", name);
|
||||
LOG_DEBUG(Service_FS, "called. file={}", name);
|
||||
|
||||
auto result = backend.GetEntryType(name);
|
||||
if (result.Failed()) {
|
||||
|
||||
@@ -8,12 +8,20 @@
|
||||
namespace Service::Sockets {
|
||||
|
||||
void SFDNSRES::GetAddrInfo(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
struct Parameters {
|
||||
u8 use_nsd_resolve;
|
||||
u32 unknown;
|
||||
u64 process_id;
|
||||
};
|
||||
|
||||
LOG_WARNING(Service, "(STUBBED) called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto parameters = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_WARNING(Service,
|
||||
"(STUBBED) called. use_nsd_resolve={}, unknown=0x{:08X}, process_id=0x{:016X}",
|
||||
parameters.use_nsd_resolve, parameters.unknown, parameters.process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,9 +26,7 @@ Module::Interface::~Interface() = default;
|
||||
void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SPL, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
std::size_t size = ctx.GetWriteBufferSize();
|
||||
const std::size_t size = ctx.GetWriteBufferSize();
|
||||
|
||||
std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max());
|
||||
std::vector<u8> data(size);
|
||||
|
||||
@@ -68,9 +68,16 @@ public:
|
||||
|
||||
private:
|
||||
void SetOption(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called");
|
||||
struct Parameters {
|
||||
u8 enable;
|
||||
u32 option;
|
||||
};
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto parameters = rp.PopRaw<Parameters>();
|
||||
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called. enable={}, option={}", parameters.enable,
|
||||
parameters.option);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -1037,7 +1037,6 @@ private:
|
||||
void ListDisplays(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_VI, "(STUBBED) called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
DisplayInfo display_info;
|
||||
display_info.width *= static_cast<u64>(Settings::values.resolution_factor);
|
||||
display_info.height *= static_cast<u64>(Settings::values.resolution_factor);
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
|
||||
#include <cinttypes>
|
||||
#include <vector>
|
||||
#include <lz4.h>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/lz4_compression.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
@@ -35,15 +36,11 @@ static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
|
||||
|
||||
std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
|
||||
const NSOSegmentHeader& header) {
|
||||
std::vector<u8> uncompressed_data(header.size);
|
||||
const int bytes_uncompressed =
|
||||
LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
|
||||
reinterpret_cast<char*>(uncompressed_data.data()),
|
||||
static_cast<int>(compressed_data.size()), header.size);
|
||||
const std::vector<u8> uncompressed_data =
|
||||
Common::Compression::DecompressDataLZ4(compressed_data, header.size);
|
||||
|
||||
ASSERT_MSG(bytes_uncompressed == static_cast<int>(header.size) &&
|
||||
bytes_uncompressed == static_cast<int>(uncompressed_data.size()),
|
||||
"{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
|
||||
ASSERT_MSG(uncompressed_data.size() == static_cast<int>(header.size), "{} != {}", header.size,
|
||||
uncompressed_data.size());
|
||||
|
||||
return uncompressed_data;
|
||||
}
|
||||
|
||||
@@ -128,7 +128,9 @@ if (ENABLE_VULKAN)
|
||||
renderer_vulkan/vk_scheduler.cpp
|
||||
renderer_vulkan/vk_scheduler.h
|
||||
renderer_vulkan/vk_stream_buffer.cpp
|
||||
renderer_vulkan/vk_stream_buffer.h)
|
||||
renderer_vulkan/vk_stream_buffer.h
|
||||
renderer_vulkan/vk_swapchain.cpp
|
||||
renderer_vulkan/vk_swapchain.h)
|
||||
|
||||
target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include)
|
||||
target_compile_definitions(video_core PRIVATE HAS_VULKAN)
|
||||
@@ -137,4 +139,4 @@ endif()
|
||||
create_target_directory_groups(video_core)
|
||||
|
||||
target_link_libraries(video_core PUBLIC common core)
|
||||
target_link_libraries(video_core PRIVATE glad lz4_static)
|
||||
target_link_libraries(video_core PRIVATE glad)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
namespace VideoCommon {
|
||||
|
||||
GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer)
|
||||
: Tegra::GPU(system, renderer), gpu_thread{renderer, *dma_pusher} {}
|
||||
: Tegra::GPU(system, renderer), gpu_thread{system, renderer, *dma_pusher} {}
|
||||
|
||||
GPUAsynch::~GPUAsynch() = default;
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/frontend/scope_acquire_window_context.h"
|
||||
#include "video_core/dma_pusher.h"
|
||||
#include "video_core/gpu.h"
|
||||
@@ -36,7 +39,6 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
|
||||
dma_pusher.Push(std::move(submit_list->entries));
|
||||
dma_pusher.DispatchCalls();
|
||||
} else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) {
|
||||
state.DecrementFramesCounter();
|
||||
renderer.SwapBuffers(std::move(data->framebuffer));
|
||||
} else if (const auto data = std::get_if<FlushRegionCommand>(&next.data)) {
|
||||
renderer.Rasterizer().FlushRegion(data->addr, data->size);
|
||||
@@ -47,13 +49,18 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
state.signaled_fence = next.fence;
|
||||
state.TrySynchronize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ThreadManager::ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher)
|
||||
: renderer{renderer}, thread{RunThread, std::ref(renderer), std::ref(dma_pusher),
|
||||
std::ref(state)} {}
|
||||
ThreadManager::ThreadManager(Core::System& system, VideoCore::RendererBase& renderer,
|
||||
Tegra::DmaPusher& dma_pusher)
|
||||
: system{system}, thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)} {
|
||||
synchronization_event = system.CoreTiming().RegisterEvent(
|
||||
"GPUThreadSynch", [this](u64 fence, int) { state.WaitForSynchronization(fence); });
|
||||
}
|
||||
|
||||
ThreadManager::~ThreadManager() {
|
||||
// Notify GPU thread that a shutdown is pending
|
||||
@@ -62,14 +69,14 @@ ThreadManager::~ThreadManager() {
|
||||
}
|
||||
|
||||
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
|
||||
PushCommand(SubmitListCommand(std::move(entries)));
|
||||
const u64 fence{PushCommand(SubmitListCommand(std::move(entries)))};
|
||||
const s64 synchronization_ticks{Core::Timing::usToCycles(9000)};
|
||||
system.CoreTiming().ScheduleEvent(synchronization_ticks, synchronization_event, fence);
|
||||
}
|
||||
|
||||
void ThreadManager::SwapBuffers(
|
||||
std::optional<std::reference_wrapper<const Tegra::FramebufferConfig>> framebuffer) {
|
||||
state.IncrementFramesCounter();
|
||||
PushCommand(SwapBuffersCommand(std::move(framebuffer)));
|
||||
state.WaitForFrames();
|
||||
}
|
||||
|
||||
void ThreadManager::FlushRegion(CacheAddr addr, u64 size) {
|
||||
@@ -79,7 +86,7 @@ void ThreadManager::FlushRegion(CacheAddr addr, u64 size) {
|
||||
void ThreadManager::InvalidateRegion(CacheAddr addr, u64 size) {
|
||||
if (state.queue.Empty()) {
|
||||
// It's quicker to invalidate a single region on the CPU if the queue is already empty
|
||||
renderer.Rasterizer().InvalidateRegion(addr, size);
|
||||
system.Renderer().Rasterizer().InvalidateRegion(addr, size);
|
||||
} else {
|
||||
PushCommand(InvalidateRegionCommand(addr, size));
|
||||
}
|
||||
@@ -90,9 +97,25 @@ void ThreadManager::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
|
||||
InvalidateRegion(addr, size);
|
||||
}
|
||||
|
||||
void ThreadManager::PushCommand(CommandData&& command_data) {
|
||||
state.queue.Push(CommandDataContainer(std::move(command_data)));
|
||||
u64 ThreadManager::PushCommand(CommandData&& command_data) {
|
||||
const u64 fence{++state.last_fence};
|
||||
state.queue.Push(CommandDataContainer(std::move(command_data), fence));
|
||||
state.SignalCommands();
|
||||
return fence;
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
|
||||
void SynchState::WaitForSynchronization(u64 fence) {
|
||||
if (signaled_fence >= fence) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for the GPU to be idle (all commands to be executed)
|
||||
{
|
||||
MICROPROFILE_SCOPE(GPU_wait);
|
||||
std::unique_lock<std::mutex> lock{synchronization_mutex};
|
||||
synchronization_condition.wait(lock, [this, fence] { return signaled_fence >= fence; });
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::GPUThread
|
||||
|
||||
@@ -19,9 +19,12 @@ struct FramebufferConfig;
|
||||
class DmaPusher;
|
||||
} // namespace Tegra
|
||||
|
||||
namespace VideoCore {
|
||||
class RendererBase;
|
||||
} // namespace VideoCore
|
||||
namespace Core {
|
||||
class System;
|
||||
namespace Timing {
|
||||
struct EventType;
|
||||
} // namespace Timing
|
||||
} // namespace Core
|
||||
|
||||
namespace VideoCommon::GPUThread {
|
||||
|
||||
@@ -75,63 +78,47 @@ using CommandData =
|
||||
struct CommandDataContainer {
|
||||
CommandDataContainer() = default;
|
||||
|
||||
CommandDataContainer(CommandData&& data) : data{std::move(data)} {}
|
||||
CommandDataContainer(CommandData&& data, u64 next_fence)
|
||||
: data{std::move(data)}, fence{next_fence} {}
|
||||
|
||||
CommandDataContainer& operator=(const CommandDataContainer& t) {
|
||||
data = std::move(t.data);
|
||||
fence = t.fence;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CommandData data;
|
||||
u64 fence{};
|
||||
};
|
||||
|
||||
/// Struct used to synchronize the GPU thread
|
||||
struct SynchState final {
|
||||
std::atomic_bool is_running{true};
|
||||
std::atomic_int queued_frame_count{};
|
||||
std::mutex frames_mutex;
|
||||
std::mutex synchronization_mutex;
|
||||
std::mutex commands_mutex;
|
||||
std::condition_variable commands_condition;
|
||||
std::condition_variable frames_condition;
|
||||
std::condition_variable synchronization_condition;
|
||||
|
||||
void IncrementFramesCounter() {
|
||||
std::lock_guard lock{frames_mutex};
|
||||
++queued_frame_count;
|
||||
/// Returns true if the gap in GPU commands is small enough that we can consider the CPU and GPU
|
||||
/// synchronized. This is entirely empirical.
|
||||
bool IsSynchronized() const {
|
||||
constexpr std::size_t max_queue_gap{5};
|
||||
return queue.Size() <= max_queue_gap;
|
||||
}
|
||||
|
||||
void DecrementFramesCounter() {
|
||||
{
|
||||
std::lock_guard lock{frames_mutex};
|
||||
--queued_frame_count;
|
||||
|
||||
if (queued_frame_count) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
frames_condition.notify_one();
|
||||
}
|
||||
|
||||
void WaitForFrames() {
|
||||
{
|
||||
std::lock_guard lock{frames_mutex};
|
||||
if (!queued_frame_count) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the GPU to be idle (all commands to be executed)
|
||||
{
|
||||
std::unique_lock lock{frames_mutex};
|
||||
frames_condition.wait(lock, [this] { return !queued_frame_count; });
|
||||
void TrySynchronize() {
|
||||
if (IsSynchronized()) {
|
||||
std::lock_guard<std::mutex> lock{synchronization_mutex};
|
||||
synchronization_condition.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void WaitForSynchronization(u64 fence);
|
||||
|
||||
void SignalCommands() {
|
||||
{
|
||||
std::unique_lock lock{commands_mutex};
|
||||
if (queue.Empty()) {
|
||||
return;
|
||||
}
|
||||
if (queue.Empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
commands_condition.notify_one();
|
||||
@@ -144,12 +131,15 @@ struct SynchState final {
|
||||
|
||||
using CommandQueue = Common::SPSCQueue<CommandDataContainer>;
|
||||
CommandQueue queue;
|
||||
u64 last_fence{};
|
||||
std::atomic<u64> signaled_fence{};
|
||||
};
|
||||
|
||||
/// Class used to manage the GPU thread
|
||||
class ThreadManager final {
|
||||
public:
|
||||
explicit ThreadManager(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher);
|
||||
explicit ThreadManager(Core::System& system, VideoCore::RendererBase& renderer,
|
||||
Tegra::DmaPusher& dma_pusher);
|
||||
~ThreadManager();
|
||||
|
||||
/// Push GPU command entries to be processed
|
||||
@@ -170,11 +160,12 @@ public:
|
||||
|
||||
private:
|
||||
/// Pushes a command to be executed by the GPU thread
|
||||
void PushCommand(CommandData&& command_data);
|
||||
u64 PushCommand(CommandData&& command_data);
|
||||
|
||||
private:
|
||||
SynchState state;
|
||||
VideoCore::RendererBase& renderer;
|
||||
Core::System& system;
|
||||
Core::Timing::EventType* synchronization_event{};
|
||||
std::thread thread;
|
||||
std::thread::id thread_id;
|
||||
};
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/renderer_opengl/gl_global_cache.h"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_primitive_assembler.h"
|
||||
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
@@ -26,7 +25,6 @@
|
||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
||||
#include "video_core/renderer_opengl/maxwell_to_gl.h"
|
||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
@@ -318,7 +316,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
const std::size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5
|
||||
|
||||
GLShader::MaxwellUniformData ubo{};
|
||||
ubo.SetFromRegs(gpu.state.shader_stages[stage]);
|
||||
ubo.SetFromRegs(gpu, stage);
|
||||
const GLintptr offset = buffer_cache.UploadHostMemory(
|
||||
&ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));
|
||||
|
||||
|
||||
@@ -12,15 +12,12 @@
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/icl/interval_map.hpp>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_cache.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_opengl/gl_buffer_cache.h"
|
||||
@@ -29,10 +26,8 @@
|
||||
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/morton.h"
|
||||
|
||||
@@ -5,10 +5,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "common/alignment.h"
|
||||
|
||||
@@ -6,13 +6,11 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/hash.h"
|
||||
#include "core/core.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
#include "video_core/renderer_opengl/utils.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
|
||||
@@ -5,21 +5,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/rasterizer_cache.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
|
||||
@@ -1196,11 +1196,12 @@ private:
|
||||
switch (meta->element) {
|
||||
case 0:
|
||||
case 1:
|
||||
return "textureSize(" + sampler + ", " + lod + ')' + GetSwizzle(meta->element);
|
||||
return "itof(int(textureSize(" + sampler + ", " + lod + ')' +
|
||||
GetSwizzle(meta->element) + "))";
|
||||
case 2:
|
||||
return "0";
|
||||
case 3:
|
||||
return "textureQueryLevels(" + sampler + ')';
|
||||
return "itof(textureQueryLevels(" + sampler + "))";
|
||||
}
|
||||
UNREACHABLE();
|
||||
return "0";
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <fmt/format.h>
|
||||
#include <lz4.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_paths.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/lz4_compression.h"
|
||||
#include "common/scm_rev.h"
|
||||
|
||||
#include "core/core.h"
|
||||
@@ -49,39 +49,6 @@ ShaderCacheVersionHash GetShaderCacheVersionHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<u8> CompressData(const T* source, std::size_t source_size) {
|
||||
if (source_size > LZ4_MAX_INPUT_SIZE) {
|
||||
// Source size exceeds LZ4 maximum input size
|
||||
return {};
|
||||
}
|
||||
const auto source_size_int = static_cast<int>(source_size);
|
||||
const int max_compressed_size = LZ4_compressBound(source_size_int);
|
||||
std::vector<u8> compressed(max_compressed_size);
|
||||
const int compressed_size = LZ4_compress_default(reinterpret_cast<const char*>(source),
|
||||
reinterpret_cast<char*>(compressed.data()),
|
||||
source_size_int, max_compressed_size);
|
||||
if (compressed_size <= 0) {
|
||||
// Compression failed
|
||||
return {};
|
||||
}
|
||||
compressed.resize(compressed_size);
|
||||
return compressed;
|
||||
}
|
||||
|
||||
std::vector<u8> DecompressData(const std::vector<u8>& compressed, std::size_t uncompressed_size) {
|
||||
std::vector<u8> uncompressed(uncompressed_size);
|
||||
const int size_check = LZ4_decompress_safe(reinterpret_cast<const char*>(compressed.data()),
|
||||
reinterpret_cast<char*>(uncompressed.data()),
|
||||
static_cast<int>(compressed.size()),
|
||||
static_cast<int>(uncompressed.size()));
|
||||
if (static_cast<int>(uncompressed_size) != size_check) {
|
||||
// Decompression failed
|
||||
return {};
|
||||
}
|
||||
return uncompressed;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ShaderDiskCacheRaw::ShaderDiskCacheRaw(u64 unique_identifier, Maxwell::ShaderProgram program_type,
|
||||
@@ -292,7 +259,7 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
|
||||
return {};
|
||||
}
|
||||
|
||||
dump.binary = DecompressData(compressed_binary, binary_length);
|
||||
dump.binary = Common::Compression::DecompressDataLZ4(compressed_binary, binary_length);
|
||||
if (dump.binary.empty()) {
|
||||
return {};
|
||||
}
|
||||
@@ -321,7 +288,7 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::vector<u8> code = DecompressData(compressed_code, code_size);
|
||||
const std::vector<u8> code = Common::Compression::DecompressDataLZ4(compressed_code, code_size);
|
||||
if (code.empty()) {
|
||||
return {};
|
||||
}
|
||||
@@ -507,7 +474,8 @@ void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::str
|
||||
if (!IsUsable())
|
||||
return;
|
||||
|
||||
const std::vector<u8> compressed_code{CompressData(code.data(), code.size())};
|
||||
const std::vector<u8> compressed_code{Common::Compression::CompressDataLZ4HC(
|
||||
reinterpret_cast<const u8*>(code.data()), code.size(), 9)};
|
||||
if (compressed_code.empty()) {
|
||||
LOG_ERROR(Render_OpenGL, "Failed to compress GLSL code - skipping shader {:016x}",
|
||||
unique_identifier);
|
||||
@@ -537,7 +505,9 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p
|
||||
std::vector<u8> binary(binary_length);
|
||||
glGetProgramBinary(program, binary_length, nullptr, &binary_format, binary.data());
|
||||
|
||||
const std::vector<u8> compressed_binary = CompressData(binary.data(), binary.size());
|
||||
const std::vector<u8> compressed_binary =
|
||||
Common::Compression::CompressDataLZ4HC(binary.data(), binary.size(), 9);
|
||||
|
||||
if (compressed_binary.empty()) {
|
||||
LOG_ERROR(Render_OpenGL, "Failed to compress binary program in shader={:016x}",
|
||||
usage.unique_identifier);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "common/assert.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_gen.h"
|
||||
|
||||
@@ -4,12 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/core.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
|
||||
namespace OpenGL::GLShader {
|
||||
|
||||
void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage) {
|
||||
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
const auto& state = gpu.state;
|
||||
using Tegra::Engines::Maxwell3D;
|
||||
|
||||
void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shader_stage) {
|
||||
const auto& regs = maxwell.regs;
|
||||
const auto& state = maxwell.state;
|
||||
|
||||
// TODO(bunnei): Support more than one viewport
|
||||
viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
|
||||
@@ -18,7 +18,7 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
|
||||
|
||||
u32 func = static_cast<u32>(regs.alpha_test_func);
|
||||
// Normalize the gl variants of opCompare to be the same as the normal variants
|
||||
u32 op_gl_variant_base = static_cast<u32>(Tegra::Engines::Maxwell3D::Regs::ComparisonOp::Never);
|
||||
const u32 op_gl_variant_base = static_cast<u32>(Maxwell3D::Regs::ComparisonOp::Never);
|
||||
if (func >= op_gl_variant_base) {
|
||||
func = func - op_gl_variant_base + 1U;
|
||||
}
|
||||
@@ -31,8 +31,9 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
|
||||
|
||||
// Assign in which stage the position has to be flipped
|
||||
// (the last stage before the fragment shader).
|
||||
if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) {
|
||||
flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
|
||||
constexpr u32 geometry_index = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
|
||||
if (maxwell.regs.shader_config[geometry_index].enable) {
|
||||
flip_stage = geometry_index;
|
||||
} else {
|
||||
flip_stage = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB);
|
||||
}
|
||||
|
||||
@@ -12,14 +12,13 @@
|
||||
|
||||
namespace OpenGL::GLShader {
|
||||
|
||||
using Tegra::Engines::Maxwell3D;
|
||||
|
||||
/// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned
|
||||
// NOTE: Always keep a vec4 at the end. The GL spec is not clear whether the alignment at
|
||||
// the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not.
|
||||
// Not following that rule will cause problems on some AMD drivers.
|
||||
/// @note Always keep a vec4 at the end. The GL spec is not clear whether the alignment at
|
||||
/// the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not.
|
||||
/// Not following that rule will cause problems on some AMD drivers.
|
||||
struct MaxwellUniformData {
|
||||
void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage);
|
||||
void SetFromRegs(const Tegra::Engines::Maxwell3D& maxwell, std::size_t shader_stage);
|
||||
|
||||
alignas(16) GLvec4 viewport_flip;
|
||||
struct alignas(16) {
|
||||
GLuint instance_id;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <glad/glad.h>
|
||||
#include "common/assert.h"
|
||||
|
||||
210
src/video_core/renderer_vulkan/vk_swapchain.cpp
Normal file
210
src/video_core/renderer_vulkan/vk_swapchain.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
#include "video_core/renderer_vulkan/vk_device.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
namespace {
|
||||
vk::SurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& formats) {
|
||||
if (formats.size() == 1 && formats[0].format == vk::Format::eUndefined) {
|
||||
return {vk::Format::eB8G8R8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear};
|
||||
}
|
||||
const auto& found = std::find_if(formats.begin(), formats.end(), [](const auto& format) {
|
||||
return format.format == vk::Format::eB8G8R8A8Unorm &&
|
||||
format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear;
|
||||
});
|
||||
return found != formats.end() ? *found : formats[0];
|
||||
}
|
||||
|
||||
vk::PresentModeKHR ChooseSwapPresentMode(const std::vector<vk::PresentModeKHR>& modes) {
|
||||
// Mailbox doesn't lock the application like fifo (vsync), prefer it
|
||||
const auto& found = std::find_if(modes.begin(), modes.end(), [](const auto& mode) {
|
||||
return mode == vk::PresentModeKHR::eMailbox;
|
||||
});
|
||||
return found != modes.end() ? *found : vk::PresentModeKHR::eFifo;
|
||||
}
|
||||
|
||||
vk::Extent2D ChooseSwapExtent(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width,
|
||||
u32 height) {
|
||||
constexpr auto undefined_size{std::numeric_limits<u32>::max()};
|
||||
if (capabilities.currentExtent.width != undefined_size) {
|
||||
return capabilities.currentExtent;
|
||||
}
|
||||
vk::Extent2D extent = {width, height};
|
||||
extent.width = std::max(capabilities.minImageExtent.width,
|
||||
std::min(capabilities.maxImageExtent.width, extent.width));
|
||||
extent.height = std::max(capabilities.minImageExtent.height,
|
||||
std::min(capabilities.maxImageExtent.height, extent.height));
|
||||
return extent;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
VKSwapchain::VKSwapchain(vk::SurfaceKHR surface, const VKDevice& device)
|
||||
: surface{surface}, device{device} {}
|
||||
|
||||
VKSwapchain::~VKSwapchain() = default;
|
||||
|
||||
void VKSwapchain::Create(u32 width, u32 height) {
|
||||
const auto dev = device.GetLogical();
|
||||
const auto& dld = device.GetDispatchLoader();
|
||||
const auto physical_device = device.GetPhysical();
|
||||
|
||||
const vk::SurfaceCapabilitiesKHR capabilities{
|
||||
physical_device.getSurfaceCapabilitiesKHR(surface, dld)};
|
||||
if (capabilities.maxImageExtent.width == 0 || capabilities.maxImageExtent.height == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
dev.waitIdle(dld);
|
||||
Destroy();
|
||||
|
||||
CreateSwapchain(capabilities, width, height);
|
||||
CreateSemaphores();
|
||||
CreateImageViews();
|
||||
|
||||
fences.resize(image_count, nullptr);
|
||||
}
|
||||
|
||||
void VKSwapchain::AcquireNextImage() {
|
||||
const auto dev{device.GetLogical()};
|
||||
const auto& dld{device.GetDispatchLoader()};
|
||||
dev.acquireNextImageKHR(*swapchain, std::numeric_limits<u64>::max(),
|
||||
*present_semaphores[frame_index], {}, &image_index, dld);
|
||||
|
||||
if (auto& fence = fences[image_index]; fence) {
|
||||
fence->Wait();
|
||||
fence->Release();
|
||||
fence = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool VKSwapchain::Present(vk::Semaphore render_semaphore, VKFence& fence) {
|
||||
const vk::Semaphore present_semaphore{*present_semaphores[frame_index]};
|
||||
const std::array<vk::Semaphore, 2> semaphores{present_semaphore, render_semaphore};
|
||||
const u32 wait_semaphore_count{render_semaphore ? 2U : 1U};
|
||||
const auto& dld{device.GetDispatchLoader()};
|
||||
const auto present_queue{device.GetPresentQueue()};
|
||||
bool recreated = false;
|
||||
|
||||
const vk::PresentInfoKHR present_info(wait_semaphore_count, semaphores.data(), 1,
|
||||
&swapchain.get(), &image_index, {});
|
||||
switch (const auto result = present_queue.presentKHR(&present_info, dld); result) {
|
||||
case vk::Result::eSuccess:
|
||||
break;
|
||||
case vk::Result::eErrorOutOfDateKHR:
|
||||
if (current_width > 0 && current_height > 0) {
|
||||
Create(current_width, current_height);
|
||||
recreated = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_CRITICAL(Render_Vulkan, "Vulkan failed to present swapchain due to {}!",
|
||||
vk::to_string(result));
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
ASSERT(fences[image_index] == nullptr);
|
||||
fences[image_index] = &fence;
|
||||
frame_index = (frame_index + 1) % image_count;
|
||||
return recreated;
|
||||
}
|
||||
|
||||
bool VKSwapchain::HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const {
|
||||
// TODO(Rodrigo): Handle framebuffer pixel format changes
|
||||
return framebuffer.width != current_width || framebuffer.height != current_height;
|
||||
}
|
||||
|
||||
void VKSwapchain::CreateSwapchain(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width,
|
||||
u32 height) {
|
||||
const auto dev{device.GetLogical()};
|
||||
const auto& dld{device.GetDispatchLoader()};
|
||||
const auto physical_device{device.GetPhysical()};
|
||||
|
||||
const std::vector<vk::SurfaceFormatKHR> formats{
|
||||
physical_device.getSurfaceFormatsKHR(surface, dld)};
|
||||
|
||||
const std::vector<vk::PresentModeKHR> present_modes{
|
||||
physical_device.getSurfacePresentModesKHR(surface, dld)};
|
||||
|
||||
const vk::SurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)};
|
||||
const vk::PresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)};
|
||||
extent = ChooseSwapExtent(capabilities, width, height);
|
||||
|
||||
current_width = extent.width;
|
||||
current_height = extent.height;
|
||||
|
||||
u32 requested_image_count{capabilities.minImageCount + 1};
|
||||
if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) {
|
||||
requested_image_count = capabilities.maxImageCount;
|
||||
}
|
||||
|
||||
vk::SwapchainCreateInfoKHR swapchain_ci(
|
||||
{}, surface, requested_image_count, surface_format.format, surface_format.colorSpace,
|
||||
extent, 1, vk::ImageUsageFlagBits::eColorAttachment, {}, {}, {},
|
||||
capabilities.currentTransform, vk::CompositeAlphaFlagBitsKHR::eOpaque, present_mode, false,
|
||||
{});
|
||||
|
||||
const u32 graphics_family{device.GetGraphicsFamily()};
|
||||
const u32 present_family{device.GetPresentFamily()};
|
||||
const std::array<u32, 2> queue_indices{graphics_family, present_family};
|
||||
if (graphics_family != present_family) {
|
||||
swapchain_ci.imageSharingMode = vk::SharingMode::eConcurrent;
|
||||
swapchain_ci.queueFamilyIndexCount = static_cast<u32>(queue_indices.size());
|
||||
swapchain_ci.pQueueFamilyIndices = queue_indices.data();
|
||||
} else {
|
||||
swapchain_ci.imageSharingMode = vk::SharingMode::eExclusive;
|
||||
}
|
||||
|
||||
swapchain = dev.createSwapchainKHRUnique(swapchain_ci, nullptr, dld);
|
||||
|
||||
images = dev.getSwapchainImagesKHR(*swapchain, dld);
|
||||
image_count = static_cast<u32>(images.size());
|
||||
image_format = surface_format.format;
|
||||
}
|
||||
|
||||
void VKSwapchain::CreateSemaphores() {
|
||||
const auto dev{device.GetLogical()};
|
||||
const auto& dld{device.GetDispatchLoader()};
|
||||
|
||||
present_semaphores.resize(image_count);
|
||||
for (std::size_t i = 0; i < image_count; i++) {
|
||||
present_semaphores[i] = dev.createSemaphoreUnique({}, nullptr, dld);
|
||||
}
|
||||
}
|
||||
|
||||
void VKSwapchain::CreateImageViews() {
|
||||
const auto dev{device.GetLogical()};
|
||||
const auto& dld{device.GetDispatchLoader()};
|
||||
|
||||
image_views.resize(image_count);
|
||||
for (std::size_t i = 0; i < image_count; i++) {
|
||||
const vk::ImageViewCreateInfo image_view_ci({}, images[i], vk::ImageViewType::e2D,
|
||||
image_format, {},
|
||||
{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1});
|
||||
image_views[i] = dev.createImageViewUnique(image_view_ci, nullptr, dld);
|
||||
}
|
||||
}
|
||||
|
||||
void VKSwapchain::Destroy() {
|
||||
frame_index = 0;
|
||||
present_semaphores.clear();
|
||||
framebuffers.clear();
|
||||
image_views.clear();
|
||||
swapchain.reset();
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
92
src/video_core/renderer_vulkan/vk_swapchain.h
Normal file
92
src/video_core/renderer_vulkan/vk_swapchain.h
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
|
||||
namespace Layout {
|
||||
struct FramebufferLayout;
|
||||
}
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class VKDevice;
|
||||
class VKFence;
|
||||
|
||||
class VKSwapchain {
|
||||
public:
|
||||
explicit VKSwapchain(vk::SurfaceKHR surface, const VKDevice& device);
|
||||
~VKSwapchain();
|
||||
|
||||
/// Creates (or recreates) the swapchain with a given size.
|
||||
void Create(u32 width, u32 height);
|
||||
|
||||
/// Acquires the next image in the swapchain, waits as needed.
|
||||
void AcquireNextImage();
|
||||
|
||||
/// Presents the rendered image to the swapchain. Returns true when the swapchains had to be
|
||||
/// recreated. Takes responsability for the ownership of fence.
|
||||
bool Present(vk::Semaphore render_semaphore, VKFence& fence);
|
||||
|
||||
/// Returns true when the framebuffer layout has changed.
|
||||
bool HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const;
|
||||
|
||||
const vk::Extent2D& GetSize() const {
|
||||
return extent;
|
||||
}
|
||||
|
||||
u32 GetImageCount() const {
|
||||
return image_count;
|
||||
}
|
||||
|
||||
u32 GetImageIndex() const {
|
||||
return image_index;
|
||||
}
|
||||
|
||||
vk::Image GetImageIndex(u32 index) const {
|
||||
return images[index];
|
||||
}
|
||||
|
||||
vk::ImageView GetImageViewIndex(u32 index) const {
|
||||
return *image_views[index];
|
||||
}
|
||||
|
||||
vk::Format GetImageFormat() const {
|
||||
return image_format;
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateSwapchain(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width, u32 height);
|
||||
void CreateSemaphores();
|
||||
void CreateImageViews();
|
||||
|
||||
void Destroy();
|
||||
|
||||
const vk::SurfaceKHR surface;
|
||||
const VKDevice& device;
|
||||
|
||||
UniqueSwapchainKHR swapchain;
|
||||
|
||||
u32 image_count{};
|
||||
std::vector<vk::Image> images;
|
||||
std::vector<UniqueImageView> image_views;
|
||||
std::vector<UniqueFramebuffer> framebuffers;
|
||||
std::vector<VKFence*> fences;
|
||||
std::vector<UniqueSemaphore> present_semaphores;
|
||||
|
||||
u32 image_index{};
|
||||
u32 frame_index{};
|
||||
|
||||
vk::Format image_format{};
|
||||
vk::Extent2D extent{};
|
||||
|
||||
u32 current_width{};
|
||||
u32 current_height{};
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
@@ -58,10 +58,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
|
||||
|
||||
scroll_area = new QScrollArea;
|
||||
|
||||
buttons = new QDialogButtonBox;
|
||||
buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
|
||||
buttons->addButton(tr("OK"), QDialogButtonBox::AcceptRole);
|
||||
|
||||
buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QtProfileSelectionDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QtProfileSelectionDialog::reject);
|
||||
|
||||
|
||||
@@ -75,13 +75,13 @@ QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
|
||||
length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length));
|
||||
});
|
||||
|
||||
buttons = new QDialogButtonBox;
|
||||
buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
|
||||
buttons->addButton(parameters.submit_text.empty()
|
||||
? tr("OK")
|
||||
: QString::fromStdU16String(parameters.submit_text),
|
||||
QDialogButtonBox::AcceptRole);
|
||||
|
||||
buttons = new QDialogButtonBox(QDialogButtonBox::Cancel);
|
||||
if (parameters.submit_text.empty()) {
|
||||
buttons->addButton(QDialogButtonBox::Ok);
|
||||
} else {
|
||||
buttons->addButton(QString::fromStdU16String(parameters.submit_text),
|
||||
QDialogButtonBox::AcceptRole);
|
||||
}
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::reject);
|
||||
layout->addWidget(header_label);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include "common/common_types.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "yuzu/debugger/profiler.h"
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QDockWidget>
|
||||
#include <QTimer>
|
||||
#include "common/microprofile.h"
|
||||
#include <QWidget>
|
||||
|
||||
class QAction;
|
||||
class QHideEvent;
|
||||
class QShowEvent;
|
||||
|
||||
class MicroProfileDialog : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -329,6 +329,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
|
||||
QMenu context_menu;
|
||||
QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
|
||||
QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location"));
|
||||
QAction* open_transferable_shader_cache =
|
||||
context_menu.addAction(tr("Open Transferable Shader Cache"));
|
||||
context_menu.addSeparator();
|
||||
QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
|
||||
QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
|
||||
@@ -344,6 +346,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
|
||||
[&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); });
|
||||
connect(open_lfs_location, &QAction::triggered,
|
||||
[&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); });
|
||||
connect(open_transferable_shader_cache, &QAction::triggered,
|
||||
[&]() { emit OpenTransferableShaderCacheRequested(program_id); });
|
||||
connect(dump_romfs, &QAction::triggered, [&]() { emit DumpRomFSRequested(program_id, path); });
|
||||
connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); });
|
||||
connect(navigate_to_gamedb_entry, &QAction::triggered,
|
||||
|
||||
@@ -66,6 +66,7 @@ signals:
|
||||
void GameChosen(QString game_path);
|
||||
void ShouldCancelWorker();
|
||||
void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
|
||||
void OpenTransferableShaderCacheRequested(u64 program_id);
|
||||
void DumpRomFSRequested(u64 program_id, const std::string& game_path);
|
||||
void CopyTIDRequested(u64 program_id);
|
||||
void NavigateToGamedbEntryRequested(u64 program_id,
|
||||
|
||||
@@ -37,14 +37,20 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include <glad/glad.h>
|
||||
|
||||
#define QT_NO_OPENGL
|
||||
#include <QClipboard>
|
||||
#include <QDesktopServices>
|
||||
#include <QDesktopWidget>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QProgressBar>
|
||||
#include <QProgressDialog>
|
||||
#include <QShortcut>
|
||||
#include <QStatusBar>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
#include <QtGui>
|
||||
#include <QtWidgets>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_paths.h"
|
||||
#include "common/detached_tasks.h"
|
||||
@@ -55,11 +61,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/telemetry.h"
|
||||
#include "core/core.h"
|
||||
#include "core/crypto/key_manager.h"
|
||||
#include "core/file_sys/bis_factory.h"
|
||||
#include "core/file_sys/card_image.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
@@ -71,7 +75,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include "core/frontend/applets/software_keyboard.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/hle/service/filesystem/fsp_ldr.h"
|
||||
#include "core/hle/service/nfp/nfp.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/loader/loader.h"
|
||||
@@ -648,6 +651,8 @@ void GMainWindow::RestoreUIState() {
|
||||
void GMainWindow::ConnectWidgetEvents() {
|
||||
connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile);
|
||||
connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);
|
||||
connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this,
|
||||
&GMainWindow::OnTransferableShaderCacheOpenFile);
|
||||
connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS);
|
||||
connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
|
||||
connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
|
||||
@@ -1082,6 +1087,39 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
|
||||
}
|
||||
|
||||
void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
|
||||
ASSERT(program_id != 0);
|
||||
|
||||
const QString tranferable_shader_cache_folder_path =
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) + "opengl" +
|
||||
DIR_SEP + "transferable";
|
||||
|
||||
const QString transferable_shader_cache_file_path =
|
||||
tranferable_shader_cache_folder_path + DIR_SEP +
|
||||
QString::fromStdString(fmt::format("{:016X}.bin", program_id));
|
||||
|
||||
if (!QFile::exists(transferable_shader_cache_file_path)) {
|
||||
QMessageBox::warning(this, tr("Error Opening Transferable Shader Cache"),
|
||||
tr("A shader cache for this title does not exist."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Windows supports opening a folder with selecting a specified file in explorer. On every other
|
||||
// OS we just open the transferable shader cache folder without preselecting the transferable
|
||||
// shader cache file for the selected game.
|
||||
#if defined(Q_OS_WIN)
|
||||
const QString explorer = QStringLiteral("explorer");
|
||||
QStringList param;
|
||||
if (!QFileInfo(transferable_shader_cache_file_path).isDir()) {
|
||||
param << QStringLiteral("/select,");
|
||||
}
|
||||
param << QDir::toNativeSeparators(transferable_shader_cache_file_path);
|
||||
QProcess::startDetached(explorer, param);
|
||||
#else
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(tranferable_shader_cache_folder_path));
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) {
|
||||
std::size_t out = 0;
|
||||
|
||||
|
||||
@@ -176,6 +176,7 @@ private slots:
|
||||
/// Called whenever a user selects a game in the game list widget.
|
||||
void OnGameListLoadFile(QString game_path);
|
||||
void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target);
|
||||
void OnTransferableShaderCacheOpenFile(u64 program_id);
|
||||
void OnGameListDumpRomFS(u64 program_id, const std::string& game_path);
|
||||
void OnGameListCopyTID(u64 program_id);
|
||||
void OnGameListNavigateToGamedbEntry(u64 program_id,
|
||||
|
||||
Reference in New Issue
Block a user