Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
8f054c47f6 | |||
a8d869c347 | |||
9da78f6126 | |||
0842ee6d7b | |||
6ec079ede8 | |||
83b329f6e1 |
@ -51,6 +51,10 @@ if (MSVC)
|
||||
/Zc:throwingNew
|
||||
/GT
|
||||
|
||||
# Some flags for more deterministic builds, to aid caching.
|
||||
/experimental:deterministic
|
||||
/d1trimfile:"${CMAKE_SOURCE_DIR}"
|
||||
|
||||
# External headers diagnostics
|
||||
/experimental:external # Enables the external headers options. This option isn't required in Visual Studio 2019 version 16.10 and later
|
||||
/external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers
|
||||
@ -87,7 +91,8 @@ if (MSVC)
|
||||
|
||||
# Since MSVC's debugging information is not very deterministic, so we have to disable it
|
||||
# when using ccache or other caching tools
|
||||
if (CITRA_USE_CCACHE OR CITRA_USE_PRECOMPILED_HEADERS)
|
||||
if (CMAKE_C_COMPILER_LAUNCHER STREQUAL "ccache" OR CMAKE_CXX_COMPILER_LAUNCHER STREQUAL "ccache"
|
||||
OR CITRA_USE_PRECOMPILED_HEADERS)
|
||||
# Precompiled headers are deleted if not using /Z7. See https://github.com/nanoant/CMakePCHCompiler/issues/21
|
||||
add_compile_options(/Z7)
|
||||
else()
|
||||
@ -98,7 +103,7 @@ if (MSVC)
|
||||
add_compile_options("$<$<CONFIG:Release>:/GS->")
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG /MANIFEST:NO" CACHE STRING "" FORCE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF /PDBALTPATH:%_PDB%" CACHE STRING "" FORCE)
|
||||
else()
|
||||
add_compile_options(
|
||||
-Wall
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <QString>
|
||||
#include <QStyleOption>
|
||||
#include <QTime>
|
||||
#include <fmt/format.h>
|
||||
#include "citra_qt/loading_screen.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/loader/loader.h"
|
||||
@ -129,11 +130,17 @@ void LoadingScreen::Prepare(Loader::AppLoader& loader) {
|
||||
if (loader.ReadIcon(buffer) == Loader::ResultStatus::Success) {
|
||||
QPixmap icon = GetQPixmapFromSMDH(buffer);
|
||||
ui->icon->setPixmap(icon);
|
||||
} else {
|
||||
ui->icon->clear();
|
||||
}
|
||||
std::string title;
|
||||
if (loader.ReadTitle(title) == Loader::ResultStatus::Success) {
|
||||
ui->title->setText(tr("Now Loading\n%1").arg(QString::fromStdString(title)));
|
||||
if (loader.ReadTitle(title) != Loader::ResultStatus::Success) {
|
||||
u64 program_id;
|
||||
if (loader.ReadProgramId(program_id) == Loader::ResultStatus::Success) {
|
||||
title = fmt::format("{:016x}", program_id);
|
||||
}
|
||||
}
|
||||
ui->title->setText(tr("Now Loading\n%1").arg(QString::fromStdString(title)));
|
||||
eta_shown = false;
|
||||
OnLoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ static inline u64 ComputeStructHash64(const T& data) noexcept {
|
||||
* Combines the seed parameter with the provided hash, producing a new unique hash
|
||||
* Implementation from: http://boost.sourceforge.net/doc/html/boost/hash_combine.html
|
||||
*/
|
||||
inline u64 HashCombine(std::size_t seed, const u64 hash) {
|
||||
inline u64 HashCombine(const u64 seed, const u64 hash) {
|
||||
return seed ^ (hash + 0x9e3779b9 + (seed << 6) + (seed >> 2));
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@ -68,13 +69,16 @@ bool AddressArbiter::ResumeHighestPriorityThread(VAddr address) {
|
||||
|
||||
AddressArbiter::AddressArbiter(KernelSystem& kernel)
|
||||
: Object(kernel), kernel(kernel), timeout_callback(std::make_shared<Callback>(*this)) {}
|
||||
AddressArbiter::~AddressArbiter() {}
|
||||
|
||||
AddressArbiter::~AddressArbiter() {
|
||||
if (resource_limit) {
|
||||
resource_limit->Release(ResourceLimitType::AddressArbiter, 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<AddressArbiter> KernelSystem::CreateAddressArbiter(std::string name) {
|
||||
auto address_arbiter{std::make_shared<AddressArbiter>(*this)};
|
||||
|
||||
auto address_arbiter = std::make_shared<AddressArbiter>(*this);
|
||||
address_arbiter->name = std::move(name);
|
||||
|
||||
return address_arbiter;
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
namespace Kernel {
|
||||
|
||||
class Thread;
|
||||
class ResourceLimit;
|
||||
|
||||
enum class ArbitrationType : u32 {
|
||||
Signal,
|
||||
@ -51,6 +52,7 @@ public:
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
std::shared_ptr<ResourceLimit> resource_limit;
|
||||
std::string name; ///< Name of address arbiter object (optional)
|
||||
|
||||
ResultCode ArbitrateAddress(std::shared_ptr<Thread> thread, ArbitrationType type, VAddr address,
|
||||
@ -86,6 +88,7 @@ private:
|
||||
ar& name;
|
||||
ar& waiting_threads;
|
||||
ar& timeout_callback;
|
||||
ar& resource_limit;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -9,6 +9,12 @@
|
||||
namespace Kernel {
|
||||
namespace ErrCodes {
|
||||
enum {
|
||||
OutOfSharedMems = 11,
|
||||
OutOfThreads = 12,
|
||||
OutOfMutexes = 13,
|
||||
OutOfSemaphores = 14,
|
||||
OutOfEvents = 15,
|
||||
OutOfTimers = 16,
|
||||
OutOfHandles = 19,
|
||||
SessionClosedByRemote = 26,
|
||||
PortNameTooLong = 30,
|
||||
@ -16,6 +22,7 @@ enum {
|
||||
NoPendingSessions = 35,
|
||||
WrongPermission = 46,
|
||||
InvalidBufferDescriptor = 48,
|
||||
OutOfAddressArbiters = 51,
|
||||
MaxConnectionsReached = 52,
|
||||
CommandTooLarge = 54,
|
||||
};
|
||||
|
@ -2,12 +2,11 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include "common/archives.h"
|
||||
#include "common/assert.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Kernel::Event)
|
||||
@ -15,16 +14,19 @@ SERIALIZE_EXPORT_IMPL(Kernel::Event)
|
||||
namespace Kernel {
|
||||
|
||||
Event::Event(KernelSystem& kernel) : WaitObject(kernel) {}
|
||||
Event::~Event() {}
|
||||
|
||||
Event::~Event() {
|
||||
if (resource_limit) {
|
||||
resource_limit->Release(ResourceLimitType::Event, 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Event> KernelSystem::CreateEvent(ResetType reset_type, std::string name) {
|
||||
auto evt{std::make_shared<Event>(*this)};
|
||||
|
||||
evt->signaled = false;
|
||||
evt->reset_type = reset_type;
|
||||
evt->name = std::move(name);
|
||||
|
||||
return evt;
|
||||
auto event = std::make_shared<Event>(*this);
|
||||
event->signaled = false;
|
||||
event->reset_type = reset_type;
|
||||
event->name = std::move(name);
|
||||
return event;
|
||||
}
|
||||
|
||||
bool Event::ShouldWait(const Thread* thread) const {
|
||||
|
@ -7,8 +7,8 @@
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
|
||||
namespace Kernel {
|
||||
@ -45,6 +45,8 @@ public:
|
||||
void Signal();
|
||||
void Clear();
|
||||
|
||||
std::shared_ptr<ResourceLimit> resource_limit;
|
||||
|
||||
private:
|
||||
ResetType reset_type; ///< Current ResetType
|
||||
|
||||
@ -60,6 +62,7 @@ private:
|
||||
ar& reset_type;
|
||||
ar& signaled;
|
||||
ar& name;
|
||||
ar& resource_limit;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -137,6 +137,10 @@ const SharedPage::Handler& KernelSystem::GetSharedPageHandler() const {
|
||||
return *shared_page_handler;
|
||||
}
|
||||
|
||||
ConfigMem::Handler& KernelSystem::GetConfigMemHandler() {
|
||||
return *config_mem_handler;
|
||||
}
|
||||
|
||||
IPCDebugger::Recorder& KernelSystem::GetIPCRecorder() {
|
||||
return *ipc_recorder;
|
||||
}
|
||||
|
@ -295,6 +295,8 @@ public:
|
||||
SharedPage::Handler& GetSharedPageHandler();
|
||||
const SharedPage::Handler& GetSharedPageHandler() const;
|
||||
|
||||
ConfigMem::Handler& GetConfigMemHandler();
|
||||
|
||||
IPCDebugger::Recorder& GetIPCRecorder();
|
||||
const IPCDebugger::Recorder& GetIPCRecorder() const;
|
||||
|
||||
|
@ -2,15 +2,14 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <vector>
|
||||
#include "common/archives.h"
|
||||
#include "common/assert.h"
|
||||
#include "core/core.h"
|
||||
#include "core/global.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/mutex.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Kernel::Mutex)
|
||||
@ -27,18 +26,23 @@ void ReleaseThreadMutexes(Thread* thread) {
|
||||
}
|
||||
|
||||
Mutex::Mutex(KernelSystem& kernel) : WaitObject(kernel), kernel(kernel) {}
|
||||
Mutex::~Mutex() {}
|
||||
|
||||
Mutex::~Mutex() {
|
||||
if (resource_limit) {
|
||||
resource_limit->Release(ResourceLimitType::Mutex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Mutex> KernelSystem::CreateMutex(bool initial_locked, std::string name) {
|
||||
auto mutex{std::make_shared<Mutex>(*this)};
|
||||
|
||||
auto mutex = std::make_shared<Mutex>(*this);
|
||||
mutex->lock_count = 0;
|
||||
mutex->name = std::move(name);
|
||||
mutex->holding_thread = nullptr;
|
||||
|
||||
// Acquire mutex with current thread if initialized as locked
|
||||
if (initial_locked)
|
||||
mutex->Acquire(thread_managers[current_cpu->GetID()]->GetCurrentThread());
|
||||
if (initial_locked) {
|
||||
mutex->Acquire(GetCurrentThreadManager().GetCurrentThread());
|
||||
}
|
||||
|
||||
return mutex;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
@ -36,6 +37,7 @@ public:
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
std::shared_ptr<ResourceLimit> resource_limit;
|
||||
int lock_count; ///< Number of times the mutex has been acquired
|
||||
u32 priority; ///< The priority of the mutex, used for priority inheritance.
|
||||
std::string name; ///< Name of mutex (optional)
|
||||
@ -71,6 +73,7 @@ private:
|
||||
ar& priority;
|
||||
ar& name;
|
||||
ar& holding_thread;
|
||||
ar& resource_limit;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -185,6 +185,11 @@ void Process::Set3dsxKernelCaps() {
|
||||
void Process::Run(s32 main_thread_priority, u32 stack_size) {
|
||||
memory_region = kernel.GetMemoryRegion(flags.memory_region);
|
||||
|
||||
// Ensure we can reserve a thread. Real kernel returns 0xC860180C if this fails.
|
||||
if (!resource_limit->Reserve(ResourceLimitType::Thread, 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
|
||||
MemoryState memory_state) {
|
||||
HeapAllocate(segment.addr, segment.size, permissions, memory_state, true);
|
||||
@ -281,7 +286,7 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u32 size, VMAPermission per
|
||||
|
||||
holding_memory += allocated_fcram;
|
||||
memory_used += size;
|
||||
resource_limit->current_commit += size;
|
||||
resource_limit->Reserve(ResourceLimitType::Commit, size);
|
||||
|
||||
return target;
|
||||
}
|
||||
@ -310,7 +315,7 @@ ResultCode Process::HeapFree(VAddr target, u32 size) {
|
||||
ASSERT(result.IsSuccess());
|
||||
|
||||
memory_used -= size;
|
||||
resource_limit->current_commit -= size;
|
||||
resource_limit->Release(ResourceLimitType::Commit, size);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@ -356,7 +361,7 @@ ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission p
|
||||
|
||||
holding_memory += MemoryRegionInfo::Interval(physical_offset, physical_offset + size);
|
||||
memory_used += size;
|
||||
resource_limit->current_commit += size;
|
||||
resource_limit->Reserve(ResourceLimitType::Commit, size);
|
||||
|
||||
LOG_DEBUG(Kernel, "Allocated at target={:08X}", target);
|
||||
return target;
|
||||
@ -385,7 +390,7 @@ ResultCode Process::LinearFree(VAddr target, u32 size) {
|
||||
|
||||
holding_memory -= MemoryRegionInfo::Interval(physical_offset, physical_offset + size);
|
||||
memory_used -= size;
|
||||
resource_limit->current_commit -= size;
|
||||
resource_limit->Release(ResourceLimitType::Commit, size);
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@ -569,7 +574,7 @@ void Process::FreeAllMemory() {
|
||||
auto size = entry.upper() - entry.lower();
|
||||
memory_region->Free(entry.lower(), size);
|
||||
memory_used -= size;
|
||||
resource_limit->current_commit -= size;
|
||||
resource_limit->Release(ResourceLimitType::Commit, size);
|
||||
}
|
||||
holding_memory.clear();
|
||||
|
||||
@ -590,7 +595,7 @@ void Process::FreeAllMemory() {
|
||||
// leaks in these values still.
|
||||
LOG_DEBUG(Kernel, "Remaining memory used after process cleanup: 0x{:08X}", memory_used);
|
||||
LOG_DEBUG(Kernel, "Remaining memory resource commit after process cleanup: 0x{:08X}",
|
||||
resource_limit->current_commit);
|
||||
resource_limit->GetCurrentValue(ResourceLimitType::Commit));
|
||||
}
|
||||
|
||||
Kernel::Process::Process(KernelSystem& kernel)
|
||||
|
@ -2,10 +2,8 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "common/archives.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
|
||||
@ -14,145 +12,130 @@ SERIALIZE_EXPORT_IMPL(Kernel::ResourceLimit)
|
||||
namespace Kernel {
|
||||
|
||||
ResourceLimit::ResourceLimit(KernelSystem& kernel) : Object(kernel) {}
|
||||
ResourceLimit::~ResourceLimit() {}
|
||||
|
||||
ResourceLimit::~ResourceLimit() = default;
|
||||
|
||||
std::shared_ptr<ResourceLimit> ResourceLimit::Create(KernelSystem& kernel, std::string name) {
|
||||
auto resource_limit{std::make_shared<ResourceLimit>(kernel)};
|
||||
|
||||
resource_limit->name = std::move(name);
|
||||
auto resource_limit = std::make_shared<ResourceLimit>(kernel);
|
||||
resource_limit->m_name = std::move(name);
|
||||
return resource_limit;
|
||||
}
|
||||
|
||||
std::shared_ptr<ResourceLimit> ResourceLimitList::GetForCategory(ResourceLimitCategory category) {
|
||||
switch (category) {
|
||||
case ResourceLimitCategory::APPLICATION:
|
||||
case ResourceLimitCategory::SYS_APPLET:
|
||||
case ResourceLimitCategory::LIB_APPLET:
|
||||
case ResourceLimitCategory::OTHER:
|
||||
return resource_limits[static_cast<u8>(category)];
|
||||
default:
|
||||
LOG_CRITICAL(Kernel, "Unknown resource limit category");
|
||||
UNREACHABLE();
|
||||
}
|
||||
s32 ResourceLimit::GetCurrentValue(ResourceLimitType type) const {
|
||||
const auto index = static_cast<size_t>(type);
|
||||
return m_current_values[index];
|
||||
}
|
||||
|
||||
s32 ResourceLimit::GetCurrentResourceValue(u32 resource) const {
|
||||
switch (resource) {
|
||||
case COMMIT:
|
||||
return current_commit;
|
||||
case THREAD:
|
||||
return current_threads;
|
||||
case EVENT:
|
||||
return current_events;
|
||||
case MUTEX:
|
||||
return current_mutexes;
|
||||
case SEMAPHORE:
|
||||
return current_semaphores;
|
||||
case TIMER:
|
||||
return current_timers;
|
||||
case SHARED_MEMORY:
|
||||
return current_shared_mems;
|
||||
case ADDRESS_ARBITER:
|
||||
return current_address_arbiters;
|
||||
case CPU_TIME:
|
||||
return current_cpu_time;
|
||||
default:
|
||||
LOG_ERROR(Kernel, "Unknown resource type={:08X}", resource);
|
||||
UNIMPLEMENTED();
|
||||
return 0;
|
||||
}
|
||||
s32 ResourceLimit::GetLimitValue(ResourceLimitType type) const {
|
||||
const auto index = static_cast<size_t>(type);
|
||||
return m_limit_values[index];
|
||||
}
|
||||
|
||||
u32 ResourceLimit::GetMaxResourceValue(u32 resource) const {
|
||||
switch (resource) {
|
||||
case PRIORITY:
|
||||
return max_priority;
|
||||
case COMMIT:
|
||||
return max_commit;
|
||||
case THREAD:
|
||||
return max_threads;
|
||||
case EVENT:
|
||||
return max_events;
|
||||
case MUTEX:
|
||||
return max_mutexes;
|
||||
case SEMAPHORE:
|
||||
return max_semaphores;
|
||||
case TIMER:
|
||||
return max_timers;
|
||||
case SHARED_MEMORY:
|
||||
return max_shared_mems;
|
||||
case ADDRESS_ARBITER:
|
||||
return max_address_arbiters;
|
||||
case CPU_TIME:
|
||||
return max_cpu_time;
|
||||
default:
|
||||
LOG_ERROR(Kernel, "Unknown resource type={:08X}", resource);
|
||||
UNIMPLEMENTED();
|
||||
return 0;
|
||||
void ResourceLimit::SetLimitValue(ResourceLimitType type, s32 value) {
|
||||
const auto index = static_cast<size_t>(type);
|
||||
m_limit_values[index] = value;
|
||||
}
|
||||
|
||||
bool ResourceLimit::Reserve(ResourceLimitType type, s32 amount) {
|
||||
const auto index = static_cast<size_t>(type);
|
||||
const s32 limit = m_limit_values[index];
|
||||
const s32 new_value = m_current_values[index] + amount;
|
||||
if (new_value > limit) {
|
||||
LOG_ERROR(Kernel, "New value {} exceeds limit {} for resource type {}", new_value, limit,
|
||||
type);
|
||||
return false;
|
||||
}
|
||||
m_current_values[index] = new_value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourceLimit::Release(ResourceLimitType type, s32 amount) {
|
||||
const auto index = static_cast<size_t>(type);
|
||||
const s32 value = m_current_values[index];
|
||||
if (amount > value) {
|
||||
LOG_ERROR(Kernel, "Amount {} exceeds current value {} for resource type {}", amount, value,
|
||||
type);
|
||||
return false;
|
||||
}
|
||||
m_current_values[index] = value - amount;
|
||||
return true;
|
||||
}
|
||||
|
||||
ResourceLimitList::ResourceLimitList(KernelSystem& kernel) {
|
||||
// Create the four resource limits that the system uses
|
||||
// Create the APPLICATION resource limit
|
||||
// PM makes APPMEMALLOC always match app RESLIMIT_COMMIT.
|
||||
// See: https://github.com/LumaTeam/Luma3DS/blob/e2778a45/sysmodules/pm/source/reslimit.c#L275
|
||||
const bool is_new_3ds = Settings::values.is_new_3ds.GetValue();
|
||||
const auto& appmemalloc = kernel.GetMemoryRegion(MemoryRegion::APPLICATION);
|
||||
|
||||
std::shared_ptr<ResourceLimit> resource_limit = ResourceLimit::Create(kernel, "Applications");
|
||||
resource_limit->max_priority = 0x18;
|
||||
resource_limit->max_commit = is_new_3ds ? 0x7C00000 : 0x4000000;
|
||||
resource_limit->max_threads = 0x20;
|
||||
resource_limit->max_events = 0x20;
|
||||
resource_limit->max_mutexes = 0x20;
|
||||
resource_limit->max_semaphores = 0x8;
|
||||
resource_limit->max_timers = 0x8;
|
||||
resource_limit->max_shared_mems = 0x10;
|
||||
resource_limit->max_address_arbiters = 0x2;
|
||||
resource_limit->max_cpu_time = 0x0;
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::APPLICATION)] = resource_limit;
|
||||
// Create the Application resource limit
|
||||
auto resource_limit = ResourceLimit::Create(kernel, "Applications");
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x18);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Commit, appmemalloc->size);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Thread, 0x20);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Event, 0x20);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Mutex, 0x20);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Semaphore, 0x8);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Timer, 0x8);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, 0x10);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, 0x2);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x0);
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::Application)] = resource_limit;
|
||||
|
||||
// Create the SYS_APPLET resource limit
|
||||
// Create the SysApplet resource limit
|
||||
resource_limit = ResourceLimit::Create(kernel, "System Applets");
|
||||
resource_limit->max_priority = 0x4;
|
||||
resource_limit->max_commit = is_new_3ds ? 0x5E06000 : 0x2606000;
|
||||
resource_limit->max_threads = is_new_3ds ? 0x1D : 0xE;
|
||||
resource_limit->max_events = is_new_3ds ? 0xB : 0x8;
|
||||
resource_limit->max_mutexes = 0x8;
|
||||
resource_limit->max_semaphores = 0x4;
|
||||
resource_limit->max_timers = 0x4;
|
||||
resource_limit->max_shared_mems = 0x8;
|
||||
resource_limit->max_address_arbiters = 0x3;
|
||||
resource_limit->max_cpu_time = 0x2710;
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::SYS_APPLET)] = resource_limit;
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x4);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Commit, is_new_3ds ? 0x5E06000 : 0x2606000);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Thread, is_new_3ds ? 0x1D : 0xE);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Event, is_new_3ds ? 0xB : 0x8);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Mutex, 0x8);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Semaphore, 0x4);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Timer, 0x4);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, 0x8);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, 0x3);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x2710);
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::SysApplet)] = resource_limit;
|
||||
|
||||
// Create the LIB_APPLET resource limit
|
||||
// Create the LibApplet resource limit
|
||||
resource_limit = ResourceLimit::Create(kernel, "Library Applets");
|
||||
resource_limit->max_priority = 0x4;
|
||||
resource_limit->max_commit = 0x602000;
|
||||
resource_limit->max_threads = 0xE;
|
||||
resource_limit->max_events = 0x8;
|
||||
resource_limit->max_mutexes = 0x8;
|
||||
resource_limit->max_semaphores = 0x4;
|
||||
resource_limit->max_timers = 0x4;
|
||||
resource_limit->max_shared_mems = 0x8;
|
||||
resource_limit->max_address_arbiters = 0x1;
|
||||
resource_limit->max_cpu_time = 0x2710;
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::LIB_APPLET)] = resource_limit;
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x4);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Commit, 0x602000);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Thread, 0xE);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Event, 0x8);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Mutex, 0x8);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Semaphore, 0x4);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Timer, 0x4);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, 0x8);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, 0x1);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x2710);
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::LibApplet)] = resource_limit;
|
||||
|
||||
// Create the OTHER resource limit
|
||||
// Create the Other resource limit
|
||||
resource_limit = ResourceLimit::Create(kernel, "Others");
|
||||
resource_limit->max_priority = 0x4;
|
||||
resource_limit->max_commit = is_new_3ds ? 0x2182000 : 0x1682000;
|
||||
resource_limit->max_threads = is_new_3ds ? 0xE1 : 0xCA;
|
||||
resource_limit->max_events = is_new_3ds ? 0x108 : 0xF8;
|
||||
resource_limit->max_mutexes = is_new_3ds ? 0x25 : 0x23;
|
||||
resource_limit->max_semaphores = is_new_3ds ? 0x43 : 0x40;
|
||||
resource_limit->max_timers = is_new_3ds ? 0x2C : 0x2B;
|
||||
resource_limit->max_shared_mems = is_new_3ds ? 0x1F : 0x1E;
|
||||
resource_limit->max_address_arbiters = is_new_3ds ? 0x2D : 0x2B;
|
||||
resource_limit->max_cpu_time = 0x3E8;
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::OTHER)] = resource_limit;
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Priority, 0x4);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Commit, is_new_3ds ? 0x2182000 : 0x1682000);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Thread, is_new_3ds ? 0xE1 : 0xCA);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Event, is_new_3ds ? 0x108 : 0xF8);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Mutex, is_new_3ds ? 0x25 : 0x23);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Semaphore, is_new_3ds ? 0x43 : 0x40);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::Timer, is_new_3ds ? 0x2C : 0x2B);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::SharedMemory, is_new_3ds ? 0x1F : 0x1E);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::AddressArbiter, is_new_3ds ? 0x2D : 0x2B);
|
||||
resource_limit->SetLimitValue(ResourceLimitType::CpuTime, 0x3E8);
|
||||
resource_limits[static_cast<u8>(ResourceLimitCategory::Other)] = resource_limit;
|
||||
}
|
||||
|
||||
ResourceLimitList::~ResourceLimitList() = default;
|
||||
|
||||
std::shared_ptr<ResourceLimit> ResourceLimitList::GetForCategory(ResourceLimitCategory category) {
|
||||
switch (category) {
|
||||
case ResourceLimitCategory::Application:
|
||||
case ResourceLimitCategory::SysApplet:
|
||||
case ResourceLimitCategory::LibApplet:
|
||||
case ResourceLimitCategory::Other:
|
||||
return resource_limits[static_cast<u8>(category)];
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown resource limit category");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
@ -16,23 +16,24 @@
|
||||
namespace Kernel {
|
||||
|
||||
enum class ResourceLimitCategory : u8 {
|
||||
APPLICATION = 0,
|
||||
SYS_APPLET = 1,
|
||||
LIB_APPLET = 2,
|
||||
OTHER = 3
|
||||
Application = 0,
|
||||
SysApplet = 1,
|
||||
LibApplet = 2,
|
||||
Other = 3,
|
||||
};
|
||||
|
||||
enum ResourceTypes {
|
||||
PRIORITY = 0,
|
||||
COMMIT = 1,
|
||||
THREAD = 2,
|
||||
EVENT = 3,
|
||||
MUTEX = 4,
|
||||
SEMAPHORE = 5,
|
||||
TIMER = 6,
|
||||
SHARED_MEMORY = 7,
|
||||
ADDRESS_ARBITER = 8,
|
||||
CPU_TIME = 9,
|
||||
enum class ResourceLimitType : u32 {
|
||||
Priority = 0,
|
||||
Commit = 1,
|
||||
Thread = 2,
|
||||
Event = 3,
|
||||
Mutex = 4,
|
||||
Semaphore = 5,
|
||||
Timer = 6,
|
||||
SharedMemory = 7,
|
||||
AddressArbiter = 8,
|
||||
CpuTime = 9,
|
||||
Max = 10,
|
||||
};
|
||||
|
||||
class ResourceLimit final : public Object {
|
||||
@ -50,7 +51,7 @@ public:
|
||||
return "ResourceLimit";
|
||||
}
|
||||
std::string GetName() const override {
|
||||
return name;
|
||||
return m_name;
|
||||
}
|
||||
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit;
|
||||
@ -58,90 +59,28 @@ public:
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value for the specified resource.
|
||||
* @param resource Requested resource type
|
||||
* @returns The current value of the resource type
|
||||
*/
|
||||
s32 GetCurrentResourceValue(u32 resource) const;
|
||||
s32 GetCurrentValue(ResourceLimitType type) const;
|
||||
s32 GetLimitValue(ResourceLimitType type) const;
|
||||
|
||||
/**
|
||||
* Gets the max value for the specified resource.
|
||||
* @param resource Requested resource type
|
||||
* @returns The max value of the resource type
|
||||
*/
|
||||
u32 GetMaxResourceValue(u32 resource) const;
|
||||
void SetLimitValue(ResourceLimitType name, s32 value);
|
||||
|
||||
/// Name of resource limit object.
|
||||
std::string name;
|
||||
bool Reserve(ResourceLimitType type, s32 amount);
|
||||
bool Release(ResourceLimitType type, s32 amount);
|
||||
|
||||
/// Max thread priority that a process in this category can create
|
||||
s32 max_priority = 0;
|
||||
|
||||
/// Max memory that processes in this category can use
|
||||
s32 max_commit = 0;
|
||||
|
||||
///< Max number of objects that can be collectively created by the processes in this category
|
||||
s32 max_threads = 0;
|
||||
s32 max_events = 0;
|
||||
s32 max_mutexes = 0;
|
||||
s32 max_semaphores = 0;
|
||||
s32 max_timers = 0;
|
||||
s32 max_shared_mems = 0;
|
||||
s32 max_address_arbiters = 0;
|
||||
|
||||
/// Max CPU time that the processes in this category can utilize
|
||||
s32 max_cpu_time = 0;
|
||||
|
||||
// TODO(Subv): Increment these in their respective Kernel::T::Create functions, keeping in mind
|
||||
// that APPLICATION resource limits should not be affected by the objects created by service
|
||||
// modules.
|
||||
// Currently we have no way of distinguishing if a Create was called by the running application,
|
||||
// or by a service module. Approach this once we have separated the service modules into their
|
||||
// own processes
|
||||
|
||||
/// Current memory that the processes in this category are using
|
||||
s32 current_commit = 0;
|
||||
|
||||
///< Current number of objects among all processes in this category
|
||||
s32 current_threads = 0;
|
||||
s32 current_events = 0;
|
||||
s32 current_mutexes = 0;
|
||||
s32 current_semaphores = 0;
|
||||
s32 current_timers = 0;
|
||||
s32 current_shared_mems = 0;
|
||||
s32 current_address_arbiters = 0;
|
||||
|
||||
/// Current CPU time that the processes in this category are utilizing
|
||||
s32 current_cpu_time = 0;
|
||||
private:
|
||||
using ResourceArray = std::array<s32, static_cast<size_t>(ResourceLimitType::Max)>;
|
||||
ResourceArray m_limit_values{};
|
||||
ResourceArray m_current_values{};
|
||||
std::string m_name;
|
||||
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& boost::serialization::base_object<Object>(*this);
|
||||
// NB most of these aren't used at all currently, but we're adding them here for forwards
|
||||
// compatibility
|
||||
ar& name;
|
||||
ar& max_priority;
|
||||
ar& max_commit;
|
||||
ar& max_threads;
|
||||
ar& max_events;
|
||||
ar& max_mutexes;
|
||||
ar& max_semaphores;
|
||||
ar& max_timers;
|
||||
ar& max_shared_mems;
|
||||
ar& max_address_arbiters;
|
||||
ar& max_cpu_time;
|
||||
ar& current_commit;
|
||||
ar& current_threads;
|
||||
ar& current_events;
|
||||
ar& current_mutexes;
|
||||
ar& current_semaphores;
|
||||
ar& current_timers;
|
||||
ar& current_shared_mems;
|
||||
ar& current_address_arbiters;
|
||||
ar& current_cpu_time;
|
||||
ar& m_name;
|
||||
ar& m_limit_values;
|
||||
ar& m_current_values;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3,9 +3,10 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/archives.h"
|
||||
#include "common/assert.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/semaphore.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
@ -14,23 +15,27 @@ SERIALIZE_EXPORT_IMPL(Kernel::Semaphore)
|
||||
namespace Kernel {
|
||||
|
||||
Semaphore::Semaphore(KernelSystem& kernel) : WaitObject(kernel) {}
|
||||
Semaphore::~Semaphore() {}
|
||||
|
||||
Semaphore::~Semaphore() {
|
||||
if (resource_limit) {
|
||||
resource_limit->Release(ResourceLimitType::Semaphore, 1);
|
||||
}
|
||||
}
|
||||
|
||||
ResultVal<std::shared_ptr<Semaphore>> KernelSystem::CreateSemaphore(s32 initial_count,
|
||||
s32 max_count,
|
||||
std::string name) {
|
||||
|
||||
if (initial_count > max_count)
|
||||
if (initial_count > max_count) {
|
||||
return ERR_INVALID_COMBINATION_KERNEL;
|
||||
|
||||
auto semaphore{std::make_shared<Semaphore>(*this)};
|
||||
}
|
||||
|
||||
// When the semaphore is created, some slots are reserved for other threads,
|
||||
// and the rest is reserved for the caller thread
|
||||
auto semaphore = std::make_shared<Semaphore>(*this);
|
||||
semaphore->max_count = max_count;
|
||||
semaphore->available_count = initial_count;
|
||||
semaphore->name = std::move(name);
|
||||
|
||||
return semaphore;
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include <boost/serialization/export.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include <queue>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
@ -16,6 +15,8 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class ResourceLimit;
|
||||
|
||||
class Semaphore final : public WaitObject {
|
||||
public:
|
||||
explicit Semaphore(KernelSystem& kernel);
|
||||
@ -33,6 +34,7 @@ public:
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
std::shared_ptr<ResourceLimit> resource_limit;
|
||||
s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have
|
||||
s32 available_count; ///< Number of free slots left in the semaphore
|
||||
std::string name; ///< Name of semaphore (optional)
|
||||
@ -55,6 +57,7 @@ private:
|
||||
ar& max_count;
|
||||
ar& available_count;
|
||||
ar& name;
|
||||
ar& resource_limit;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/memory.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@ -14,6 +15,7 @@ SERIALIZE_EXPORT_IMPL(Kernel::SharedMemory)
|
||||
namespace Kernel {
|
||||
|
||||
SharedMemory::SharedMemory(KernelSystem& kernel) : Object(kernel), kernel(kernel) {}
|
||||
|
||||
SharedMemory::~SharedMemory() {
|
||||
for (const auto& interval : holding_memory) {
|
||||
kernel.GetMemoryRegion(MemoryRegion::SYSTEM)
|
||||
@ -22,6 +24,7 @@ SharedMemory::~SharedMemory() {
|
||||
|
||||
auto process = owner_process.lock();
|
||||
if (process) {
|
||||
process->resource_limit->Release(ResourceLimitType::SharedMemory, 1);
|
||||
if (base_address != 0) {
|
||||
process->vm_manager.ChangeMemoryState(base_address, size, MemoryState::Locked,
|
||||
VMAPermission::None, MemoryState::Private,
|
||||
@ -35,8 +38,8 @@ SharedMemory::~SharedMemory() {
|
||||
ResultVal<std::shared_ptr<SharedMemory>> KernelSystem::CreateSharedMemory(
|
||||
std::shared_ptr<Process> owner_process, u32 size, MemoryPermission permissions,
|
||||
MemoryPermission other_permissions, VAddr address, MemoryRegion region, std::string name) {
|
||||
auto shared_memory{std::make_shared<SharedMemory>(*this)};
|
||||
|
||||
auto shared_memory = std::make_shared<SharedMemory>(*this);
|
||||
shared_memory->owner_process = owner_process;
|
||||
shared_memory->name = std::move(name);
|
||||
shared_memory->size = size;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/kernel/config_mem.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
@ -390,6 +391,8 @@ private:
|
||||
VAddr names, u32 name_count);
|
||||
ResultCode GetResourceLimitLimitValues(VAddr values, Handle resource_limit_handle, VAddr names,
|
||||
u32 name_count);
|
||||
ResultCode SetResourceLimitLimitValues(Handle res_limit, VAddr names, VAddr resource_list,
|
||||
u32 name_count);
|
||||
ResultCode CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr stack_top,
|
||||
u32 priority, s32 processor_id);
|
||||
void ExitThread();
|
||||
@ -1070,9 +1073,18 @@ ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_co
|
||||
|
||||
/// Create an address arbiter (to allocate access to shared resources)
|
||||
ResultCode SVC::CreateAddressArbiter(Handle* out_handle) {
|
||||
std::shared_ptr<AddressArbiter> arbiter = kernel.CreateAddressArbiter();
|
||||
CASCADE_RESULT(*out_handle,
|
||||
kernel.GetCurrentProcess()->handle_table.Create(std::move(arbiter)));
|
||||
// Update address arbiter count in resource limit.
|
||||
const auto current_process = kernel.GetCurrentProcess();
|
||||
const auto& resource_limit = current_process->resource_limit;
|
||||
if (!resource_limit->Reserve(ResourceLimitType::AddressArbiter, 1)) {
|
||||
return ResultCode(ErrCodes::OutOfAddressArbiters, ErrorModule::OS,
|
||||
ErrorSummary::OutOfResource, ErrorLevel::Status);
|
||||
}
|
||||
|
||||
// Create address arbiter.
|
||||
const auto arbiter = kernel.CreateAddressArbiter();
|
||||
arbiter->resource_limit = resource_limit;
|
||||
CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(arbiter)));
|
||||
LOG_TRACE(Kernel_SVC, "returned handle=0x{:08X}", *out_handle);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@ -1161,14 +1173,15 @@ ResultCode SVC::GetResourceLimitCurrentValues(VAddr values, Handle resource_limi
|
||||
LOG_TRACE(Kernel_SVC, "called resource_limit={:08X}, names={:08X}, name_count={}",
|
||||
resource_limit_handle, names, name_count);
|
||||
|
||||
std::shared_ptr<ResourceLimit> resource_limit =
|
||||
const auto resource_limit =
|
||||
kernel.GetCurrentProcess()->handle_table.Get<ResourceLimit>(resource_limit_handle);
|
||||
if (resource_limit == nullptr)
|
||||
if (!resource_limit) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < name_count; ++i) {
|
||||
u32 name = memory.Read32(names + i * sizeof(u32));
|
||||
s64 value = resource_limit->GetCurrentResourceValue(name);
|
||||
for (u32 i = 0; i < name_count; ++i) {
|
||||
const u32 name = memory.Read32(names + i * sizeof(u32));
|
||||
const s64 value = resource_limit->GetCurrentValue(static_cast<ResourceLimitType>(name));
|
||||
memory.Write64(values + i * sizeof(u64), value);
|
||||
}
|
||||
|
||||
@ -1181,20 +1194,55 @@ ResultCode SVC::GetResourceLimitLimitValues(VAddr values, Handle resource_limit_
|
||||
LOG_TRACE(Kernel_SVC, "called resource_limit={:08X}, names={:08X}, name_count={}",
|
||||
resource_limit_handle, names, name_count);
|
||||
|
||||
std::shared_ptr<ResourceLimit> resource_limit =
|
||||
const auto resource_limit =
|
||||
kernel.GetCurrentProcess()->handle_table.Get<ResourceLimit>(resource_limit_handle);
|
||||
if (resource_limit == nullptr)
|
||||
if (!resource_limit) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < name_count; ++i) {
|
||||
u32 name = memory.Read32(names + i * sizeof(u32));
|
||||
s64 value = resource_limit->GetMaxResourceValue(name);
|
||||
for (u32 i = 0; i < name_count; ++i) {
|
||||
const auto name = static_cast<ResourceLimitType>(memory.Read32(names + i * sizeof(u32)));
|
||||
if (name >= ResourceLimitType::Max) {
|
||||
return ERR_INVALID_ENUM_VALUE;
|
||||
}
|
||||
const s64 value = resource_limit->GetLimitValue(name);
|
||||
memory.Write64(values + i * sizeof(u64), value);
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode SVC::SetResourceLimitLimitValues(Handle res_limit, VAddr names, VAddr resource_list,
|
||||
u32 name_count) {
|
||||
LOG_TRACE(Kernel_SVC, "called resource_limit={:08X}, names={:08X}, name_count={}", res_limit,
|
||||
names, name_count);
|
||||
|
||||
const auto resource_limit =
|
||||
kernel.GetCurrentProcess()->handle_table.Get<ResourceLimit>(res_limit);
|
||||
if (!resource_limit) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < name_count; ++i) {
|
||||
const auto name = static_cast<ResourceLimitType>(memory.Read32(names + i * sizeof(u32)));
|
||||
if (name >= ResourceLimitType::Max) {
|
||||
return ERR_INVALID_ENUM_VALUE;
|
||||
}
|
||||
const s64 value = memory.Read64(resource_list + i * sizeof(u64));
|
||||
const s32 value_high = value >> 32;
|
||||
if (value_high < 0) {
|
||||
return ERR_OUT_OF_RANGE_KERNEL;
|
||||
}
|
||||
if (name == ResourceLimitType::Commit && value_high != 0) {
|
||||
auto& config_mem = kernel.GetConfigMemHandler().GetConfigMem();
|
||||
config_mem.app_mem_alloc = value_high;
|
||||
}
|
||||
resource_limit->SetLimitValue(name, static_cast<s32>(value));
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
/// Creates a new thread
|
||||
ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr stack_top,
|
||||
u32 priority, s32 processor_id) {
|
||||
@ -1204,11 +1252,10 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
std::shared_ptr<Process> current_process = kernel.GetCurrentProcess();
|
||||
|
||||
std::shared_ptr<ResourceLimit>& resource_limit = current_process->resource_limit;
|
||||
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority &&
|
||||
!current_process->no_thread_restrictions) {
|
||||
const auto current_process = kernel.GetCurrentProcess();
|
||||
const auto& resource_limit = current_process->resource_limit;
|
||||
const u32 max_priority = resource_limit->GetLimitValue(ResourceLimitType::Priority);
|
||||
if (max_priority > priority && !current_process->no_thread_restrictions) {
|
||||
return ERR_NOT_AUTHORIZED;
|
||||
}
|
||||
|
||||
@ -1240,6 +1287,13 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr
|
||||
break;
|
||||
}
|
||||
|
||||
// Update thread count in resource limit.
|
||||
if (!resource_limit->Reserve(ResourceLimitType::Thread, 1)) {
|
||||
return ResultCode(ErrCodes::OutOfThreads, ErrorModule::OS, ErrorSummary::OutOfResource,
|
||||
ErrorLevel::Status);
|
||||
}
|
||||
|
||||
// Create thread.
|
||||
CASCADE_RESULT(std::shared_ptr<Thread> thread,
|
||||
kernel.CreateThread(name, entry_point, priority, arg, processor_id, stack_top,
|
||||
current_process));
|
||||
@ -1284,14 +1338,16 @@ ResultCode SVC::SetThreadPriority(Handle handle, u32 priority) {
|
||||
return ERR_OUT_OF_RANGE;
|
||||
}
|
||||
|
||||
std::shared_ptr<Thread> thread = kernel.GetCurrentProcess()->handle_table.Get<Thread>(handle);
|
||||
if (thread == nullptr)
|
||||
const auto thread = kernel.GetCurrentProcess()->handle_table.Get<Thread>(handle);
|
||||
if (!thread) {
|
||||
return ERR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
// Note: The kernel uses the current process's resource limit instead of
|
||||
// the one from the thread owner's resource limit.
|
||||
std::shared_ptr<ResourceLimit>& resource_limit = kernel.GetCurrentProcess()->resource_limit;
|
||||
if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) {
|
||||
const auto& resource_limit = kernel.GetCurrentProcess()->resource_limit;
|
||||
const u32 max_priority = resource_limit->GetLimitValue(ResourceLimitType::Priority);
|
||||
if (max_priority > priority) {
|
||||
return ERR_NOT_AUTHORIZED;
|
||||
}
|
||||
|
||||
@ -1299,8 +1355,9 @@ ResultCode SVC::SetThreadPriority(Handle handle, u32 priority) {
|
||||
thread->UpdatePriority();
|
||||
|
||||
// Update the mutexes that this thread is waiting for
|
||||
for (auto& mutex : thread->pending_mutexes)
|
||||
for (auto& mutex : thread->pending_mutexes) {
|
||||
mutex->UpdatePriority();
|
||||
}
|
||||
|
||||
system.PrepareReschedule();
|
||||
return RESULT_SUCCESS;
|
||||
@ -1308,9 +1365,19 @@ ResultCode SVC::SetThreadPriority(Handle handle, u32 priority) {
|
||||
|
||||
/// Create a mutex
|
||||
ResultCode SVC::CreateMutex(Handle* out_handle, u32 initial_locked) {
|
||||
std::shared_ptr<Mutex> mutex = kernel.CreateMutex(initial_locked != 0);
|
||||
// Update mutex count in resource limit.
|
||||
const auto current_process = kernel.GetCurrentProcess();
|
||||
const auto& resource_limit = current_process->resource_limit;
|
||||
if (!resource_limit->Reserve(ResourceLimitType::Mutex, 1)) {
|
||||
return ResultCode(ErrCodes::OutOfMutexes, ErrorModule::OS, ErrorSummary::OutOfResource,
|
||||
ErrorLevel::Status);
|
||||
}
|
||||
|
||||
// Create mutex.
|
||||
const auto mutex = kernel.CreateMutex(initial_locked != 0);
|
||||
mutex->name = fmt::format("mutex-{:08x}", system.GetRunningCore().GetReg(14));
|
||||
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(mutex)));
|
||||
mutex->resource_limit = resource_limit;
|
||||
CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(mutex)));
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called initial_locked={} : created handle=0x{:08X}",
|
||||
initial_locked ? "true" : "false", *out_handle);
|
||||
@ -1373,11 +1440,20 @@ ResultCode SVC::GetThreadId(u32* thread_id, Handle handle) {
|
||||
|
||||
/// Creates a semaphore
|
||||
ResultCode SVC::CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count) {
|
||||
// Update semaphore count in resource limit.
|
||||
const auto current_process = kernel.GetCurrentProcess();
|
||||
const auto& resource_limit = current_process->resource_limit;
|
||||
if (!resource_limit->Reserve(ResourceLimitType::Semaphore, 1)) {
|
||||
return ResultCode(ErrCodes::OutOfSemaphores, ErrorModule::OS, ErrorSummary::OutOfResource,
|
||||
ErrorLevel::Status);
|
||||
}
|
||||
|
||||
// Create semaphore
|
||||
CASCADE_RESULT(std::shared_ptr<Semaphore> semaphore,
|
||||
kernel.CreateSemaphore(initial_count, max_count));
|
||||
semaphore->name = fmt::format("semaphore-{:08x}", system.GetRunningCore().GetReg(14));
|
||||
CASCADE_RESULT(*out_handle,
|
||||
kernel.GetCurrentProcess()->handle_table.Create(std::move(semaphore)));
|
||||
semaphore->resource_limit = resource_limit;
|
||||
CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(semaphore)));
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called initial_count={}, max_count={}, created handle=0x{:08X}",
|
||||
initial_count, max_count, *out_handle);
|
||||
@ -1461,10 +1537,19 @@ ResultCode SVC::QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 ad
|
||||
|
||||
/// Create an event
|
||||
ResultCode SVC::CreateEvent(Handle* out_handle, u32 reset_type) {
|
||||
std::shared_ptr<Event> evt =
|
||||
kernel.CreateEvent(static_cast<ResetType>(reset_type),
|
||||
fmt::format("event-{:08x}", system.GetRunningCore().GetReg(14)));
|
||||
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(evt)));
|
||||
// Update event count in resource limit.
|
||||
const auto current_process = kernel.GetCurrentProcess();
|
||||
const auto& resource_limit = current_process->resource_limit;
|
||||
if (!resource_limit->Reserve(ResourceLimitType::Event, 1)) {
|
||||
return ResultCode(ErrCodes::OutOfEvents, ErrorModule::OS, ErrorSummary::OutOfResource,
|
||||
ErrorLevel::Status);
|
||||
}
|
||||
|
||||
// Create event.
|
||||
const auto name = fmt::format("event-{:08x}", system.GetRunningCore().GetReg(14));
|
||||
const auto event = kernel.CreateEvent(static_cast<ResetType>(reset_type), name);
|
||||
event->resource_limit = resource_limit;
|
||||
CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(event)));
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type,
|
||||
*out_handle);
|
||||
@ -1505,10 +1590,19 @@ ResultCode SVC::ClearEvent(Handle handle) {
|
||||
|
||||
/// Creates a timer
|
||||
ResultCode SVC::CreateTimer(Handle* out_handle, u32 reset_type) {
|
||||
std::shared_ptr<Timer> timer =
|
||||
kernel.CreateTimer(static_cast<ResetType>(reset_type),
|
||||
fmt ::format("timer-{:08x}", system.GetRunningCore().GetReg(14)));
|
||||
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(timer)));
|
||||
// Update timer count in resource limit.
|
||||
const auto current_process = kernel.GetCurrentProcess();
|
||||
const auto& resource_limit = current_process->resource_limit;
|
||||
if (!resource_limit->Reserve(ResourceLimitType::Timer, 1)) {
|
||||
return ResultCode(ErrCodes::OutOfTimers, ErrorModule::OS, ErrorSummary::OutOfResource,
|
||||
ErrorLevel::Status);
|
||||
}
|
||||
|
||||
// Create timer.
|
||||
const auto name = fmt::format("timer-{:08x}", system.GetRunningCore().GetReg(14));
|
||||
const auto timer = kernel.CreateTimer(static_cast<ResetType>(reset_type), name);
|
||||
timer->resource_limit = resource_limit;
|
||||
CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(timer)));
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type,
|
||||
*out_handle);
|
||||
@ -1655,15 +1749,22 @@ ResultCode SVC::CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my
|
||||
return ERR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
std::shared_ptr<Process> current_process = kernel.GetCurrentProcess();
|
||||
// Update shared memory count in resource limit.
|
||||
const auto current_process = kernel.GetCurrentProcess();
|
||||
const auto& resource_limit = current_process->resource_limit;
|
||||
if (!resource_limit->Reserve(ResourceLimitType::SharedMemory, 1)) {
|
||||
return ResultCode(ErrCodes::OutOfSharedMems, ErrorModule::OS, ErrorSummary::OutOfResource,
|
||||
ErrorLevel::Status);
|
||||
}
|
||||
|
||||
// When trying to create a memory block with address = 0,
|
||||
// if the process has the Shared Device Memory flag in the exheader,
|
||||
// then we have to allocate from the same region as the caller process instead of the BASE
|
||||
// region.
|
||||
MemoryRegion region = MemoryRegion::BASE;
|
||||
if (addr == 0 && current_process->flags.shared_device_mem)
|
||||
if (addr == 0 && current_process->flags.shared_device_mem) {
|
||||
region = current_process->flags.memory_region;
|
||||
}
|
||||
|
||||
CASCADE_RESULT(shared_memory,
|
||||
kernel.CreateSharedMemory(
|
||||
@ -2224,7 +2325,7 @@ const std::array<SVC::FunctionDef, 180> SVC::SVC_Table{{
|
||||
{0x76, &SVC::Wrap<&SVC::TerminateProcess>, "TerminateProcess"},
|
||||
{0x77, nullptr, "SetProcessResourceLimits"},
|
||||
{0x78, nullptr, "CreateResourceLimit"},
|
||||
{0x79, nullptr, "SetResourceLimitValues"},
|
||||
{0x79, &SVC::Wrap<&SVC::SetResourceLimitLimitValues>, "SetResourceLimitLimitValues"},
|
||||
{0x7A, nullptr, "AddCodeSegment"},
|
||||
{0x7B, nullptr, "Backdoor"},
|
||||
{0x7C, &SVC::Wrap<&SVC::KernelSetState>, "KernelSetState"},
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/mutex.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
@ -59,7 +60,12 @@ void Thread::Acquire(Thread* thread) {
|
||||
Thread::Thread(KernelSystem& kernel, u32 core_id)
|
||||
: WaitObject(kernel), core_id(core_id), thread_manager(kernel.GetThreadManager(core_id)) {}
|
||||
|
||||
Thread::~Thread() = default;
|
||||
Thread::~Thread() {
|
||||
auto process = owner_process.lock();
|
||||
if (process) {
|
||||
process->resource_limit->Release(ResourceLimitType::Thread, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Thread* ThreadManager::GetCurrentThread() const {
|
||||
return current_thread.get();
|
||||
@ -342,7 +348,7 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(
|
||||
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
|
||||
}
|
||||
|
||||
auto thread{std::make_shared<Thread>(*this, processor_id)};
|
||||
auto thread = std::make_shared<Thread>(*this, processor_id);
|
||||
|
||||
thread_managers[processor_id]->thread_list.push_back(thread);
|
||||
thread_managers[processor_id]->ready_queue.prepare(priority);
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/kernel/timer.h"
|
||||
|
||||
@ -17,14 +19,17 @@ namespace Kernel {
|
||||
|
||||
Timer::Timer(KernelSystem& kernel)
|
||||
: WaitObject(kernel), kernel(kernel), timer_manager(kernel.GetTimerManager()) {}
|
||||
|
||||
Timer::~Timer() {
|
||||
Cancel();
|
||||
timer_manager.timer_callback_table.erase(callback_id);
|
||||
if (resource_limit) {
|
||||
resource_limit->Release(ResourceLimitType::Timer, 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Timer> KernelSystem::CreateTimer(ResetType reset_type, std::string name) {
|
||||
auto timer{std::make_shared<Timer>(*this)};
|
||||
|
||||
auto timer = std::make_shared<Timer>(*this);
|
||||
timer->reset_type = reset_type;
|
||||
timer->signaled = false;
|
||||
timer->name = std::move(name);
|
||||
@ -32,7 +37,6 @@ std::shared_ptr<Timer> KernelSystem::CreateTimer(ResetType reset_type, std::stri
|
||||
timer->interval_delay = 0;
|
||||
timer->callback_id = ++timer_manager->next_timer_callback_id;
|
||||
timer_manager->timer_callback_table[timer->callback_id] = timer.get();
|
||||
|
||||
return timer;
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,8 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class ResourceLimit;
|
||||
|
||||
class Timer final : public WaitObject {
|
||||
public:
|
||||
explicit Timer(KernelSystem& kernel);
|
||||
@ -96,6 +98,8 @@ public:
|
||||
*/
|
||||
void Signal(s64 cycles_late);
|
||||
|
||||
std::shared_ptr<ResourceLimit> resource_limit;
|
||||
|
||||
private:
|
||||
ResetType reset_type; ///< The ResetType of this timer
|
||||
|
||||
@ -123,6 +127,7 @@ private:
|
||||
ar& signaled;
|
||||
ar& name;
|
||||
ar& callback_id;
|
||||
ar& resource_limit;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/ac/ac.h"
|
||||
#include "core/hle/service/ac/ac_i.h"
|
||||
|
@ -34,6 +34,10 @@ namespace Service::FS {
|
||||
enum class MediaType : u32;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class Mutex;
|
||||
}
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
namespace ErrCodes {
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include "core/global.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "common/archives.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/csnd/csnd_snd.h"
|
||||
|
||||
|
@ -16,7 +16,7 @@ static const ResultCode ERROR_BUFFER_TOO_SMALL = // 0xE0E12C1F
|
||||
ResultCode(static_cast<ErrorDescription>(31), ErrorModule::RO, ErrorSummary::InvalidArgument,
|
||||
ErrorLevel::Usage);
|
||||
|
||||
static ResultCode CROFormatError(u32 description) {
|
||||
static constexpr ResultCode CROFormatError(u32 description) {
|
||||
return ResultCode(static_cast<ErrorDescription>(description), ErrorModule::RO,
|
||||
ErrorSummary::WrongArgument, ErrorLevel::Permanent);
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/resource_limit.h"
|
||||
#include "core/hle/kernel/semaphore.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
#include "core/hle/kernel/server_session.h"
|
||||
|
@ -277,7 +277,7 @@ ResultStatus AppLoader_THREEDSX::Load(std::shared_ptr<Kernel::Process>& process)
|
||||
|
||||
// Attach the default resource limit (APPLICATION) to the process
|
||||
process->resource_limit = Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory(
|
||||
Kernel::ResourceLimitCategory::APPLICATION);
|
||||
Kernel::ResourceLimitCategory::Application);
|
||||
|
||||
// On real HW this is done with FS:Reg, but we can be lazy
|
||||
auto fs_user =
|
||||
|
@ -388,7 +388,7 @@ ResultStatus AppLoader_ELF::Load(std::shared_ptr<Kernel::Process>& process) {
|
||||
|
||||
// Attach the default resource limit (APPLICATION) to the process
|
||||
process->resource_limit = Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory(
|
||||
Kernel::ResourceLimitCategory::APPLICATION);
|
||||
Kernel::ResourceLimitCategory::Application);
|
||||
|
||||
process->Run(48, Kernel::DEFAULT_STACK_SIZE);
|
||||
|
||||
|
@ -3,12 +3,11 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <codecvt>
|
||||
#include <cstring>
|
||||
#include <locale>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <fmt/format.h>
|
||||
#include "common/literals.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/string_util.h"
|
||||
@ -32,6 +31,7 @@
|
||||
|
||||
namespace Loader {
|
||||
|
||||
using namespace Common::Literals;
|
||||
static const u64 UPDATE_MASK = 0x0000000e00000000;
|
||||
|
||||
FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
|
||||
@ -148,13 +148,41 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process)
|
||||
codeset->entrypoint = codeset->CodeSegment().addr;
|
||||
codeset->memory = std::move(code);
|
||||
|
||||
process = Core::System::GetInstance().Kernel().CreateProcess(std::move(codeset));
|
||||
auto& system = Core::System::GetInstance();
|
||||
process = system.Kernel().CreateProcess(std::move(codeset));
|
||||
|
||||
// Attach a resource limit to the process based on the resource limit category
|
||||
process->resource_limit =
|
||||
Core::System::GetInstance().Kernel().ResourceLimit().GetForCategory(
|
||||
static_cast<Kernel::ResourceLimitCategory>(
|
||||
overlay_ncch->exheader_header.arm11_system_local_caps.resource_limit_category));
|
||||
const auto category = static_cast<Kernel::ResourceLimitCategory>(
|
||||
overlay_ncch->exheader_header.arm11_system_local_caps.resource_limit_category);
|
||||
process->resource_limit = system.Kernel().ResourceLimit().GetForCategory(category);
|
||||
|
||||
// When running N3DS-unaware titles pm will lie about the amount of memory available.
|
||||
// This means RESLIMIT_COMMIT = APPMEMALLOC doesn't correspond to the actual size of
|
||||
// APPLICATION. See:
|
||||
// https://github.com/LumaTeam/Luma3DS/blob/e2778a45/sysmodules/pm/source/launch.c#L237
|
||||
auto& ncch_caps = overlay_ncch->exheader_header.arm11_system_local_caps;
|
||||
const auto o3ds_mode = static_cast<Kernel::MemoryMode>(ncch_caps.system_mode.Value());
|
||||
const auto n3ds_mode = static_cast<Kernel::New3dsMemoryMode>(ncch_caps.n3ds_mode);
|
||||
const bool is_new_3ds = Settings::values.is_new_3ds.GetValue();
|
||||
if (is_new_3ds && n3ds_mode == Kernel::New3dsMemoryMode::Legacy &&
|
||||
category == Kernel::ResourceLimitCategory::Application) {
|
||||
u64 new_limit = 0;
|
||||
switch (o3ds_mode) {
|
||||
case Kernel::MemoryMode::Prod:
|
||||
new_limit = 64_MiB;
|
||||
break;
|
||||
case Kernel::MemoryMode::Dev1:
|
||||
new_limit = 96_MiB;
|
||||
break;
|
||||
case Kernel::MemoryMode::Dev2:
|
||||
new_limit = 80_MiB;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
process->resource_limit->SetLimitValue(Kernel::ResourceLimitType::Commit,
|
||||
static_cast<s32>(new_limit));
|
||||
}
|
||||
|
||||
// Set the default CPU core for this process
|
||||
process->ideal_processor =
|
||||
@ -171,9 +199,7 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process)
|
||||
u32 stack_size = overlay_ncch->exheader_header.codeset_info.stack_size;
|
||||
|
||||
// On real HW this is done with FS:Reg, but we can be lazy
|
||||
auto fs_user =
|
||||
Core::System::GetInstance().ServiceManager().GetService<Service::FS::FS_USER>(
|
||||
"fs:USER");
|
||||
auto fs_user = system.ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
|
||||
fs_user->RegisterProgramInfo(process->process_id, process->codeset->program_id, filepath);
|
||||
|
||||
Service::FS::FS_USER::ProductInfo product_info{};
|
||||
|
@ -156,13 +156,11 @@ add_library(video_core STATIC
|
||||
shader/shader.h
|
||||
shader/shader_interpreter.cpp
|
||||
shader/shader_interpreter.h
|
||||
shader/shader_jit_a64.cpp
|
||||
shader/shader_jit.cpp
|
||||
shader/shader_jit.h
|
||||
shader/shader_jit_a64_compiler.cpp
|
||||
shader/shader_jit_a64.h
|
||||
shader/shader_jit_a64_compiler.h
|
||||
shader/shader_jit_x64.cpp
|
||||
shader/shader_jit_x64_compiler.cpp
|
||||
shader/shader_jit_x64.h
|
||||
shader/shader_jit_x64_compiler.h
|
||||
texture/etc1.cpp
|
||||
texture/etc1.h
|
||||
|
@ -13,17 +13,15 @@
|
||||
#include "video_core/regs_shader.h"
|
||||
#include "video_core/shader/shader.h"
|
||||
#include "video_core/shader/shader_interpreter.h"
|
||||
#if CITRA_ARCH(x86_64)
|
||||
#include "video_core/shader/shader_jit_x64.h"
|
||||
#elif CITRA_ARCH(arm64)
|
||||
#include "video_core/shader/shader_jit_a64.h"
|
||||
#endif
|
||||
#if CITRA_ARCH(x86_64) || CITRA_ARCH(arm64)
|
||||
#include "video_core/shader/shader_jit.h"
|
||||
#endif // CITRA_ARCH(x86_64) || CITRA_ARCH(arm64)
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace Pica::Shader {
|
||||
|
||||
void OutputVertex::ValidateSemantics(const RasterizerRegs& regs) {
|
||||
unsigned int num_attributes = regs.vs_output_total;
|
||||
u32 num_attributes = regs.vs_output_total;
|
||||
ASSERT(num_attributes <= 7);
|
||||
for (std::size_t attrib = 0; attrib < num_attributes; ++attrib) {
|
||||
u32 output_register_map = regs.vs_output_attributes[attrib].raw;
|
||||
@ -54,7 +52,7 @@ OutputVertex OutputVertex::FromAttributeBuffer(const RasterizerRegs& regs,
|
||||
static_assert(sizeof(std::array<f24, 24>) == sizeof(ret),
|
||||
"Struct and array have different sizes.");
|
||||
|
||||
unsigned int num_attributes = regs.vs_output_total & 7;
|
||||
u32 num_attributes = regs.vs_output_total & 7;
|
||||
for (std::size_t attrib = 0; attrib < num_attributes; ++attrib) {
|
||||
const auto output_register_map = regs.vs_output_attributes[attrib];
|
||||
vertex_slots_overflow[output_register_map.map_x] = input.attr[attrib][0];
|
||||
@ -65,7 +63,7 @@ OutputVertex OutputVertex::FromAttributeBuffer(const RasterizerRegs& regs,
|
||||
|
||||
// The hardware takes the absolute and saturates vertex colors like this, *before* doing
|
||||
// interpolation
|
||||
for (unsigned i = 0; i < 4; ++i) {
|
||||
for (u32 i = 0; i < 4; ++i) {
|
||||
float c = std::fabs(ret.color[i].ToFloat32());
|
||||
ret.color[i] = f24::FromFloat32(c < 1.0f ? c : 1.0f);
|
||||
}
|
||||
@ -84,10 +82,10 @@ OutputVertex OutputVertex::FromAttributeBuffer(const RasterizerRegs& regs,
|
||||
}
|
||||
|
||||
void UnitState::LoadInput(const ShaderRegs& config, const AttributeBuffer& input) {
|
||||
const unsigned max_attribute = config.max_input_attribute_index;
|
||||
const u32 max_attribute = config.max_input_attribute_index;
|
||||
|
||||
for (unsigned attr = 0; attr <= max_attribute; ++attr) {
|
||||
unsigned reg = config.GetRegisterForAttribute(attr);
|
||||
for (u32 attr = 0; attr <= max_attribute; ++attr) {
|
||||
u32 reg = config.GetRegisterForAttribute(attr);
|
||||
registers.input[reg] = input.attr[attr];
|
||||
}
|
||||
}
|
||||
@ -141,11 +139,9 @@ void GSUnitState::ConfigOutput(const ShaderRegs& config) {
|
||||
|
||||
MICROPROFILE_DEFINE(GPU_Shader, "GPU", "Shader", MP_RGB(50, 50, 240));
|
||||
|
||||
#if CITRA_ARCH(x86_64)
|
||||
static std::unique_ptr<JitX64Engine> jit_engine;
|
||||
#elif CITRA_ARCH(arm64)
|
||||
static std::unique_ptr<JitA64Engine> jit_engine;
|
||||
#endif
|
||||
#if CITRA_ARCH(x86_64) || CITRA_ARCH(arm64)
|
||||
static std::unique_ptr<JitEngine> jit_engine;
|
||||
#endif // CITRA_ARCH(x86_64) || CITRA_ARCH(arm64)
|
||||
static InterpreterEngine interpreter_engine;
|
||||
|
||||
ShaderEngine* GetEngine() {
|
||||
@ -153,7 +149,7 @@ ShaderEngine* GetEngine() {
|
||||
// TODO(yuriks): Re-initialize on each change rather than being persistent
|
||||
if (VideoCore::g_shader_jit_enabled) {
|
||||
if (jit_engine == nullptr) {
|
||||
jit_engine = std::make_unique<decltype(jit_engine)::element_type>();
|
||||
jit_engine = std::make_unique<JitEngine>();
|
||||
}
|
||||
return jit_engine.get();
|
||||
}
|
||||
@ -164,7 +160,7 @@ ShaderEngine* GetEngine() {
|
||||
|
||||
void Shutdown() {
|
||||
#if CITRA_ARCH(x86_64) || CITRA_ARCH(arm64)
|
||||
jit_engine = nullptr;
|
||||
jit_engine.reset();
|
||||
#endif // CITRA_ARCH(x86_64) || CITRA_ARCH(arm64)
|
||||
}
|
||||
|
||||
|
@ -22,8 +22,8 @@
|
||||
|
||||
namespace Pica::Shader {
|
||||
|
||||
constexpr unsigned MAX_PROGRAM_CODE_LENGTH = 4096;
|
||||
constexpr unsigned MAX_SWIZZLE_DATA_LENGTH = 4096;
|
||||
constexpr u32 MAX_PROGRAM_CODE_LENGTH = 4096;
|
||||
constexpr u32 MAX_SWIZZLE_DATA_LENGTH = 4096;
|
||||
using ProgramCode = std::array<u32, MAX_PROGRAM_CODE_LENGTH>;
|
||||
using SwizzleData = std::array<u32, MAX_SWIZZLE_DATA_LENGTH>;
|
||||
|
||||
@ -33,7 +33,7 @@ struct AttributeBuffer {
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
void serialize(Archive& ar, const u32 file_version) {
|
||||
ar& attr;
|
||||
}
|
||||
};
|
||||
@ -62,7 +62,7 @@ struct OutputVertex {
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
void serialize(Archive& ar, const u32) {
|
||||
ar& pos;
|
||||
ar& quat;
|
||||
ar& color;
|
||||
@ -113,7 +113,7 @@ struct GSEmitter {
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
void serialize(Archive& ar, const u32 file_version) {
|
||||
ar& buffer;
|
||||
ar& vertex_id;
|
||||
ar& prim_emit;
|
||||
@ -142,7 +142,7 @@ struct UnitState {
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
void serialize(Archive& ar, const u32 file_version) {
|
||||
ar& input;
|
||||
ar& temporary;
|
||||
ar& output;
|
||||
@ -158,15 +158,15 @@ struct UnitState {
|
||||
|
||||
GSEmitter* emitter_ptr;
|
||||
|
||||
static std::size_t InputOffset(int register_index) {
|
||||
static std::size_t InputOffset(s32 register_index) {
|
||||
return offsetof(UnitState, registers.input) + register_index * sizeof(Common::Vec4<f24>);
|
||||
}
|
||||
|
||||
static std::size_t OutputOffset(int register_index) {
|
||||
static std::size_t OutputOffset(s32 register_index) {
|
||||
return offsetof(UnitState, registers.output) + register_index * sizeof(Common::Vec4<f24>);
|
||||
}
|
||||
|
||||
static std::size_t TemporaryOffset(int register_index) {
|
||||
static std::size_t TemporaryOffset(s32 register_index) {
|
||||
return offsetof(UnitState, registers.temporary) +
|
||||
register_index * sizeof(Common::Vec4<f24>);
|
||||
}
|
||||
@ -184,7 +184,7 @@ struct UnitState {
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
void serialize(Archive& ar, const u32 file_version) {
|
||||
ar& registers;
|
||||
ar& conditional_code;
|
||||
ar& address_registers;
|
||||
@ -207,7 +207,7 @@ struct GSUnitState : public UnitState {
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
void serialize(Archive& ar, const u32 file_version) {
|
||||
ar& boost::serialization::base_object<UnitState>(*this);
|
||||
ar& emitter;
|
||||
}
|
||||
@ -221,22 +221,22 @@ struct Uniforms {
|
||||
std::array<bool, 16> b;
|
||||
std::array<Common::Vec4<u8>, 4> i;
|
||||
|
||||
static std::size_t GetFloatUniformOffset(unsigned index) {
|
||||
static std::size_t GetFloatUniformOffset(u32 index) {
|
||||
return offsetof(Uniforms, f) + index * sizeof(Common::Vec4<f24>);
|
||||
}
|
||||
|
||||
static std::size_t GetBoolUniformOffset(unsigned index) {
|
||||
static std::size_t GetBoolUniformOffset(u32 index) {
|
||||
return offsetof(Uniforms, b) + index * sizeof(bool);
|
||||
}
|
||||
|
||||
static std::size_t GetIntUniformOffset(unsigned index) {
|
||||
static std::size_t GetIntUniformOffset(u32 index) {
|
||||
return offsetof(Uniforms, i) + index * sizeof(Common::Vec4<u8>);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
void serialize(Archive& ar, const u32 file_version) {
|
||||
ar& f;
|
||||
ar& b;
|
||||
ar& i;
|
||||
@ -251,7 +251,7 @@ struct ShaderSetup {
|
||||
|
||||
/// Data private to ShaderEngines
|
||||
struct EngineData {
|
||||
unsigned int entry_point;
|
||||
u32 entry_point;
|
||||
/// Used by the JIT, points to a compiled shader object.
|
||||
const void* cached_shader = nullptr;
|
||||
} engine_data;
|
||||
@ -288,7 +288,7 @@ private:
|
||||
|
||||
friend class boost::serialization::access;
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
void serialize(Archive& ar, const u32 file_version) {
|
||||
ar& uniforms;
|
||||
ar& program_code;
|
||||
ar& swizzle_data;
|
||||
@ -307,7 +307,7 @@ public:
|
||||
* Performs any shader unit setup that only needs to happen once per shader (as opposed to once
|
||||
* per vertex, which would happen within the `Run` function).
|
||||
*/
|
||||
virtual void SetupBatch(ShaderSetup& setup, unsigned int entry_point) = 0;
|
||||
virtual void SetupBatch(ShaderSetup& setup, u32 entry_point) = 0;
|
||||
|
||||
/**
|
||||
* Runs the currently setup shader.
|
||||
|
@ -3,27 +3,32 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/arch.h"
|
||||
#if CITRA_ARCH(x86_64)
|
||||
#if CITRA_ARCH(x86_64) || CITRA_ARCH(arm64)
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "video_core/shader/shader.h"
|
||||
#include "video_core/shader/shader_jit_x64.h"
|
||||
#include "video_core/shader/shader_jit.h"
|
||||
#if CITRA_ARCH(arm64)
|
||||
#include "video_core/shader/shader_jit_a64_compiler.h"
|
||||
#endif
|
||||
#if CITRA_ARCH(x86_64)
|
||||
#include "video_core/shader/shader_jit_x64_compiler.h"
|
||||
#endif
|
||||
|
||||
namespace Pica::Shader {
|
||||
|
||||
JitX64Engine::JitX64Engine() = default;
|
||||
JitX64Engine::~JitX64Engine() = default;
|
||||
JitEngine::JitEngine() = default;
|
||||
JitEngine::~JitEngine() = default;
|
||||
|
||||
void JitX64Engine::SetupBatch(ShaderSetup& setup, unsigned int entry_point) {
|
||||
void JitEngine::SetupBatch(ShaderSetup& setup, u32 entry_point) {
|
||||
ASSERT(entry_point < MAX_PROGRAM_CODE_LENGTH);
|
||||
setup.engine_data.entry_point = entry_point;
|
||||
|
||||
u64 code_hash = setup.GetProgramCodeHash();
|
||||
u64 swizzle_hash = setup.GetSwizzleDataHash();
|
||||
const u64 code_hash = setup.GetProgramCodeHash();
|
||||
const u64 swizzle_hash = setup.GetSwizzleDataHash();
|
||||
|
||||
u64 cache_key = code_hash ^ swizzle_hash;
|
||||
const u64 cache_key = Common::HashCombine(code_hash, swizzle_hash);
|
||||
auto iter = cache.find(cache_key);
|
||||
if (iter != cache.end()) {
|
||||
setup.engine_data.cached_shader = iter->second.get();
|
||||
@ -37,7 +42,7 @@ void JitX64Engine::SetupBatch(ShaderSetup& setup, unsigned int entry_point) {
|
||||
|
||||
MICROPROFILE_DECLARE(GPU_Shader);
|
||||
|
||||
void JitX64Engine::Run(const ShaderSetup& setup, UnitState& state) const {
|
||||
void JitEngine::Run(const ShaderSetup& setup, UnitState& state) const {
|
||||
ASSERT(setup.engine_data.cached_shader != nullptr);
|
||||
|
||||
MICROPROFILE_SCOPE(GPU_Shader);
|
||||
@ -48,4 +53,4 @@ void JitX64Engine::Run(const ShaderSetup& setup, UnitState& state) const {
|
||||
|
||||
} // namespace Pica::Shader
|
||||
|
||||
#endif // CITRA_ARCH(x86_64)
|
||||
#endif // CITRA_ARCH(x86_64) || CITRA_ARCH(arm64)
|
@ -5,7 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/arch.h"
|
||||
#if CITRA_ARCH(x86_64)
|
||||
#if CITRA_ARCH(x86_64) || CITRA_ARCH(arm64)
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
@ -16,12 +16,12 @@ namespace Pica::Shader {
|
||||
|
||||
class JitShader;
|
||||
|
||||
class JitX64Engine final : public ShaderEngine {
|
||||
class JitEngine final : public ShaderEngine {
|
||||
public:
|
||||
JitX64Engine();
|
||||
~JitX64Engine() override;
|
||||
JitEngine();
|
||||
~JitEngine() override;
|
||||
|
||||
void SetupBatch(ShaderSetup& setup, unsigned int entry_point) override;
|
||||
void SetupBatch(ShaderSetup& setup, u32 entry_point) override;
|
||||
void Run(const ShaderSetup& setup, UnitState& state) const override;
|
||||
|
||||
private:
|
||||
@ -30,4 +30,4 @@ private:
|
||||
|
||||
} // namespace Pica::Shader
|
||||
|
||||
#endif // CITRA_ARCH(x86_64)
|
||||
#endif // CITRA_ARCH(x86_64) || CITRA_ARCH(arm64)
|
@ -1,51 +0,0 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/arch.h"
|
||||
#if CITRA_ARCH(arm64)
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "video_core/shader/shader.h"
|
||||
#include "video_core/shader/shader_jit_a64.h"
|
||||
#include "video_core/shader/shader_jit_a64_compiler.h"
|
||||
|
||||
namespace Pica::Shader {
|
||||
|
||||
JitA64Engine::JitA64Engine() = default;
|
||||
JitA64Engine::~JitA64Engine() = default;
|
||||
|
||||
void JitA64Engine::SetupBatch(ShaderSetup& setup, unsigned int entry_point) {
|
||||
ASSERT(entry_point < MAX_PROGRAM_CODE_LENGTH);
|
||||
setup.engine_data.entry_point = entry_point;
|
||||
|
||||
u64 code_hash = setup.GetProgramCodeHash();
|
||||
u64 swizzle_hash = setup.GetSwizzleDataHash();
|
||||
|
||||
u64 cache_key = code_hash ^ swizzle_hash;
|
||||
auto iter = cache.find(cache_key);
|
||||
if (iter != cache.end()) {
|
||||
setup.engine_data.cached_shader = iter->second.get();
|
||||
} else {
|
||||
auto shader = std::make_unique<JitShader>();
|
||||
shader->Compile(&setup.program_code, &setup.swizzle_data);
|
||||
setup.engine_data.cached_shader = shader.get();
|
||||
cache.emplace_hint(iter, cache_key, std::move(shader));
|
||||
}
|
||||
}
|
||||
|
||||
MICROPROFILE_DECLARE(GPU_Shader);
|
||||
|
||||
void JitA64Engine::Run(const ShaderSetup& setup, UnitState& state) const {
|
||||
ASSERT(setup.engine_data.cached_shader != nullptr);
|
||||
|
||||
MICROPROFILE_SCOPE(GPU_Shader);
|
||||
|
||||
const JitShader* shader = static_cast<const JitShader*>(setup.engine_data.cached_shader);
|
||||
shader->Run(setup, state, setup.engine_data.entry_point);
|
||||
}
|
||||
|
||||
} // namespace Pica::Shader
|
||||
|
||||
#endif // CITRA_ARCH(arm64)
|
@ -1,33 +0,0 @@
|
||||
// Copyright 2023 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/arch.h"
|
||||
#if CITRA_ARCH(arm64)
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/shader/shader.h"
|
||||
|
||||
namespace Pica::Shader {
|
||||
|
||||
class JitShader;
|
||||
|
||||
class JitA64Engine final : public ShaderEngine {
|
||||
public:
|
||||
JitA64Engine();
|
||||
~JitA64Engine() override;
|
||||
|
||||
void SetupBatch(ShaderSetup& setup, unsigned int entry_point) override;
|
||||
void Run(const ShaderSetup& setup, UnitState& state) const override;
|
||||
|
||||
private:
|
||||
std::unordered_map<u64, std::unique_ptr<JitShader>> cache;
|
||||
};
|
||||
|
||||
} // namespace Pica::Shader
|
||||
|
||||
#endif // CITRA_ARCH(arm64)
|
@ -163,7 +163,7 @@ void JitShader::Compile_Assert(bool condition, const char* msg) {}
|
||||
* @param src_reg SourceRegister object corresponding to the source register to load
|
||||
* @param dest Destination QReg register to store the loaded, swizzled source register
|
||||
*/
|
||||
void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg,
|
||||
void JitShader::Compile_SwizzleSrc(Instruction instr, u32 src_num, SourceRegister src_reg,
|
||||
QReg dest) {
|
||||
XReg src_ptr = XZR;
|
||||
std::size_t src_offset;
|
||||
@ -855,7 +855,7 @@ void JitShader::Compile_SETE(Instruction instr) {
|
||||
l(end);
|
||||
}
|
||||
|
||||
void JitShader::Compile_Block(unsigned end) {
|
||||
void JitShader::Compile_Block(u32 end) {
|
||||
while (program_counter < end) {
|
||||
Compile_NextInstr();
|
||||
}
|
||||
@ -957,7 +957,7 @@ void JitShader::Compile(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>* program_
|
||||
BR(ABI_PARAM3);
|
||||
|
||||
// Compile entire program
|
||||
Compile_Block(static_cast<unsigned>(program_code->size()));
|
||||
Compile_Block(static_cast<u32>(program_code->size()));
|
||||
|
||||
// Free memory that's no longer needed
|
||||
program_code = nullptr;
|
||||
|
@ -37,7 +37,7 @@ class JitShader : private oaknut::CodeBlock, public oaknut::CodeGenerator {
|
||||
public:
|
||||
JitShader();
|
||||
|
||||
void Run(const ShaderSetup& setup, UnitState& state, unsigned offset) const {
|
||||
void Run(const ShaderSetup& setup, UnitState& state, u32 offset) const {
|
||||
program(&setup.uniforms, &state, instruction_labels[offset].ptr<const std::byte*>());
|
||||
}
|
||||
|
||||
@ -75,10 +75,10 @@ public:
|
||||
void Compile_SETE(Instruction instr);
|
||||
|
||||
private:
|
||||
void Compile_Block(unsigned end);
|
||||
void Compile_Block(u32 end);
|
||||
void Compile_NextInstr();
|
||||
|
||||
void Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg,
|
||||
void Compile_SwizzleSrc(Instruction instr, u32 src_num, SourceRegister src_reg,
|
||||
oaknut::QReg dest);
|
||||
void Compile_DestEnable(Instruction instr, oaknut::QReg dest);
|
||||
|
||||
@ -129,10 +129,10 @@ private:
|
||||
std::vector<oaknut::Label> loop_break_labels;
|
||||
|
||||
/// Offsets in code where a return needs to be inserted
|
||||
std::vector<unsigned> return_offsets;
|
||||
std::vector<u32> return_offsets;
|
||||
|
||||
unsigned program_counter = 0; ///< Offset of the next instruction to decode
|
||||
u8 loop_depth = 0; ///< Depth of the (nested) loops currently compiled
|
||||
u32 program_counter = 0; ///< Offset of the next instruction to decode
|
||||
u8 loop_depth = 0; ///< Depth of the (nested) loops currently compiled
|
||||
|
||||
using CompiledShader = void(const void* setup, void* state, const std::byte* start_addr);
|
||||
CompiledShader* program = nullptr;
|
||||
|
@ -187,7 +187,7 @@ void JitShader::Compile_Assert(bool condition, const char* msg) {
|
||||
* @param src_reg SourceRegister object corresponding to the source register to load
|
||||
* @param dest Destination XMM register to store the loaded, swizzled source register
|
||||
*/
|
||||
void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg,
|
||||
void JitShader::Compile_SwizzleSrc(Instruction instr, u32 src_num, SourceRegister src_reg,
|
||||
Xmm dest) {
|
||||
Reg64 src_ptr;
|
||||
std::size_t src_offset;
|
||||
@ -213,13 +213,13 @@ void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRe
|
||||
ASSERT_MSG(src_offset == static_cast<std::size_t>(src_offset_disp),
|
||||
"Source register offset too large for int type");
|
||||
|
||||
unsigned operand_desc_id;
|
||||
u32 operand_desc_id;
|
||||
|
||||
const bool is_inverted =
|
||||
(0 != (instr.opcode.Value().GetInfo().subtype & OpCode::Info::SrcInversed));
|
||||
|
||||
unsigned address_register_index;
|
||||
unsigned offset_src;
|
||||
u32 address_register_index;
|
||||
u32 offset_src;
|
||||
|
||||
if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MAD ||
|
||||
instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MADI) {
|
||||
@ -254,7 +254,7 @@ void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRe
|
||||
|
||||
// First we add 128 to address_reg so the first comparison is turned to
|
||||
// address_reg >= 0 && address_reg < 256 which can be performed with
|
||||
// a single unsigned comparison (cmovb)
|
||||
// a single u32 comparison (cmovb)
|
||||
lea(eax, ptr[address_reg + 128]);
|
||||
mov(ebx, src_reg.GetIndex());
|
||||
mov(ecx, address_reg.cvt32());
|
||||
@ -297,7 +297,7 @@ void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRe
|
||||
|
||||
void JitShader::Compile_DestEnable(Instruction instr, Xmm src) {
|
||||
DestRegister dest;
|
||||
unsigned operand_desc_id;
|
||||
u32 operand_desc_id;
|
||||
if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MAD ||
|
||||
instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MADI) {
|
||||
operand_desc_id = instr.mad.operand_desc_id;
|
||||
@ -915,7 +915,7 @@ void JitShader::Compile_SETE(Instruction instr) {
|
||||
L(end);
|
||||
}
|
||||
|
||||
void JitShader::Compile_Block(unsigned end) {
|
||||
void JitShader::Compile_Block(u32 end) {
|
||||
while (program_counter < end) {
|
||||
Compile_NextInstr();
|
||||
}
|
||||
@ -943,7 +943,7 @@ void JitShader::Compile_NextInstr() {
|
||||
Instruction instr = {(*program_code)[program_counter++]};
|
||||
|
||||
OpCode::Id opcode = instr.opcode.Value();
|
||||
auto instr_func = instr_table[static_cast<unsigned>(opcode)];
|
||||
auto instr_func = instr_table[static_cast<u32>(opcode)];
|
||||
|
||||
if (instr_func) {
|
||||
// JIT the instruction!
|
||||
@ -1023,7 +1023,7 @@ void JitShader::Compile(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>* program_
|
||||
jmp(ABI_PARAM3);
|
||||
|
||||
// Compile entire program
|
||||
Compile_Block(static_cast<unsigned>(program_code->size()));
|
||||
Compile_Block(static_cast<u32>(program_code->size()));
|
||||
|
||||
// Free memory that's no longer needed
|
||||
program_code = nullptr;
|
||||
|
@ -36,7 +36,7 @@ class JitShader : public Xbyak::CodeGenerator {
|
||||
public:
|
||||
JitShader();
|
||||
|
||||
void Run(const ShaderSetup& setup, UnitState& state, unsigned offset) const {
|
||||
void Run(const ShaderSetup& setup, UnitState& state, u32 offset) const {
|
||||
program(&setup.uniforms, &state, instruction_labels[offset].getAddress());
|
||||
}
|
||||
|
||||
@ -74,10 +74,10 @@ public:
|
||||
void Compile_SETE(Instruction instr);
|
||||
|
||||
private:
|
||||
void Compile_Block(unsigned end);
|
||||
void Compile_Block(u32 end);
|
||||
void Compile_NextInstr();
|
||||
|
||||
void Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg,
|
||||
void Compile_SwizzleSrc(Instruction instr, u32 src_num, SourceRegister src_reg,
|
||||
Xbyak::Xmm dest);
|
||||
void Compile_DestEnable(Instruction instr, Xbyak::Xmm dest);
|
||||
|
||||
@ -128,10 +128,10 @@ private:
|
||||
std::vector<Xbyak::Label> loop_break_labels;
|
||||
|
||||
/// Offsets in code where a return needs to be inserted
|
||||
std::vector<unsigned> return_offsets;
|
||||
std::vector<u32> return_offsets;
|
||||
|
||||
unsigned program_counter = 0; ///< Offset of the next instruction to decode
|
||||
u8 loop_depth = 0; ///< Depth of the (nested) loops currently compiled
|
||||
u32 program_counter = 0; ///< Offset of the next instruction to decode
|
||||
u8 loop_depth = 0; ///< Depth of the (nested) loops currently compiled
|
||||
|
||||
using CompiledShader = void(const void* setup, void* state, const u8* start_addr);
|
||||
CompiledShader* program = nullptr;
|
||||
|
Reference in New Issue
Block a user