Compare commits
21 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea8f633dc0 | ||
|
|
140cd5e209 | ||
|
|
7931a68d4e | ||
|
|
580e3564c9 | ||
|
|
74a4a50470 | ||
|
|
774fa0b828 | ||
|
|
65ae1ac4e5 | ||
|
|
a504bad3fb | ||
|
|
e796351a0d | ||
|
|
57279e1981 | ||
|
|
28719ee3b4 | ||
|
|
cb2bce8006 | ||
|
|
628153cccd | ||
|
|
4366241739 | ||
|
|
20cc0b8d3c | ||
|
|
2d70c30fb2 | ||
|
|
26d0381161 | ||
|
|
d09e98f566 | ||
|
|
108be41316 | ||
|
|
c6147a439d | ||
|
|
746dab407e |
@@ -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);
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -907,19 +907,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};
|
||||
|
||||
@@ -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)
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user