From 9b9bf8d3009f56fa1120e67c35d97c586399dac3 Mon Sep 17 00:00:00 2001 From: PixelyIon Date: Mon, 11 Oct 2021 09:13:25 +0530 Subject: [PATCH] Introduce `ThreadLocal` Class + Fix Several GPU Bugs * Fix `AddClearColorSubpass` bug where it would not generate a `VkCmdNextSubpass` when an attachment clear was utilized * Fix `AddSubpass` bug where the Depth Stencil texture would not be synced * Respect `VkCommandPool` external synchronization requirements by making it thread-local with a custom RAII wrapper * Fix linear RT width calculation as it's provided in terms of bytes rather than format units * Fix `AllocateStagingBuffer` bug where it would not supply `eTransferDst` as a usage flag * Fix `AllocateMappedImage` where `VkMemoryPropertyFlags` were not respected resulting in non-`eHostVisible` memory being utilized * Change feature requirement in `AndroidManifest.xml` to Vulkan 1.1 from OGL 3.1 as this was incorrect --- .gitignore | 3 + app/src/main/AndroidManifest.xml | 8 +- app/src/main/cpp/skyline/common.cpp | 7 +- app/src/main/cpp/skyline/common.h | 8 +- .../main/cpp/skyline/common/thread_local.h | 129 ++++++++++++++++++ .../cpp/skyline/gpu/command_scheduler.cpp | 13 +- .../main/cpp/skyline/gpu/command_scheduler.h | 17 ++- .../gpu/interconnect/command_executor.cpp | 26 ++-- .../skyline/gpu/interconnect/command_nodes.h | 13 +- .../gpu/interconnect/graphics_context.h | 17 +++ .../main/cpp/skyline/gpu/memory_manager.cpp | 4 +- .../main/cpp/skyline/gpu/texture/texture.cpp | 4 + .../main/cpp/skyline/gpu/texture/texture.h | 2 + .../cpp/skyline/kernel/types/KProcess.cpp | 15 +- .../main/cpp/skyline/kernel/types/KProcess.h | 6 + .../am/controller/ISelfController.cpp | 7 + .../services/am/controller/ISelfController.h | 7 + 17 files changed, 248 insertions(+), 38 deletions(-) create mode 100644 app/src/main/cpp/skyline/common/thread_local.h diff --git a/.gitignore b/.gitignore index 85e631d3..502ed50a 100644 --- a/.gitignore +++ b/.gitignore @@ -92,3 +92,6 @@ lint/reports/ # Discord plugin for IntelliJ IDEA .idea/discord.xml + +# Adreno Validation Layer +libVkLayer_adreno.so diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 48396fd9..9104301e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,11 +4,13 @@ package="emu.skyline"> + - + android:name="android.hardware.vulkan.version" + android:required="true" + android:version="0x401000" /> + (*this); input = std::make_shared(*this); } + + DeviceState::~DeviceState() { + if (process) + process->ClearHandleTable(); + } } diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index 9d59ebee..2294c060 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -711,19 +711,21 @@ namespace skyline { struct DeviceState { DeviceState(kernel::OS *os, std::shared_ptr jvmManager, std::shared_ptr settings, std::shared_ptr logger); + ~DeviceState(); + kernel::OS *os; std::shared_ptr jvm; std::shared_ptr settings; std::shared_ptr logger; std::shared_ptr loader; + std::shared_ptr process{}; + static thread_local inline std::shared_ptr thread{}; //!< The KThread of the thread which accesses this object + static thread_local inline nce::ThreadContext *ctx{}; //!< The context of the guest thread for the corresponding host thread std::shared_ptr gpu; std::shared_ptr soc; std::shared_ptr audio; std::shared_ptr nce; std::shared_ptr scheduler; - std::shared_ptr process; - static thread_local inline std::shared_ptr thread{}; //!< The KThread of the thread which accesses this object - static thread_local inline nce::ThreadContext *ctx{}; //!< The context of the guest thread for the corresponding host thread std::shared_ptr input; }; } diff --git a/app/src/main/cpp/skyline/common/thread_local.h b/app/src/main/cpp/skyline/common/thread_local.h new file mode 100644 index 00000000..ffe604ba --- /dev/null +++ b/app/src/main/cpp/skyline/common/thread_local.h @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include + +namespace skyline { + /** + * @brief A thread-local RAII-bound wrapper class which unlike `thread_local` doesn't require the member to be static + * @note Caller must ensure any arguments passed into the constructor remain valid throughout its lifetime + * @note Caller must ensure the destructors of the object doesn't have any thread-local dependencies as they might be called from another thread + * @note RAII-bound means that *all* thread-local instances of the object will be destroyed after this class is destroyed but can also be destroyed when a thread owning an instance dies + */ + template> + class ThreadLocal; + + template + class ThreadLocal { + private: + pthread_key_t key; + std::function constructor; + + public: + template + ThreadLocal(Args &&... args) : constructor([args...]() { return new Type(args...); }) { + int result; + if ((result = pthread_key_create(&key, nullptr))) + throw exception("Cannot create pthread_key: {}", strerror(result)); + } + + Type *operator->() { + auto pointer{pthread_getspecific(key)}; + if (pointer) + return static_cast(pointer); + + int result; + Type *object{constructor(*this)}; + if ((result = pthread_setspecific(key, object))) + throw exception("Cannot set pthread_key to constructed type: {}", strerror(result)); + + return object; + } + + Type &operator*() { + return *operator->(); + } + + ~ThreadLocal() { + pthread_key_delete(key); + } + }; + + template + class ThreadLocal { + private: + struct IntrustiveTypeNode { + Type object; + ThreadLocal &threadLocal; + IntrustiveTypeNode *next{}; + + template + IntrustiveTypeNode(ThreadLocal &threadLocal, Args &&... args) : object(std::forward(args)...), threadLocal(threadLocal) {} + + ~IntrustiveTypeNode() { + auto current{threadLocal.list.load(std::memory_order_acquire)}; + while (current == this) + if (threadLocal.list.compare_exchange_strong(current, next, std::memory_order_release, std::memory_order_consume)) + return; + + while (current) { + if (current->next == this) { + current->next = next; + return; + } + current = current->next; + } + } + }; + + pthread_key_t key; + std::function constructor; + std::atomic list; //!< An atomic instrusive linked list of all instances of the object to call non-trivial destructors for the objects + + public: + template + ThreadLocal(Args &&... args) : constructor([args...](ThreadLocal &threadLocal) { return new IntrustiveTypeNode(threadLocal, args...); }) { + auto destructor{[](void *object) { + static_cast(object)->~IntrustiveTypeNode(); + }}; + + int result; + if ((result = pthread_key_create(&key, destructor))) + throw exception("Cannot create pthread_key: {}", strerror(result)); + } + + Type *operator->() { + auto pointer{pthread_getspecific(key)}; + if (pointer) + return &static_cast(pointer)->object; + + int result; + IntrustiveTypeNode *node{constructor(*this)}; + if ((result = pthread_setspecific(key, node))) + throw exception("Cannot set pthread_key to constructed type: {}", strerror(result)); + + auto next{list.load(std::memory_order_acquire)}; + do { + node->next = next; + } while (!list.compare_exchange_strong(next, node, std::memory_order_release, std::memory_order_consume)); + + return &node->object; + } + + Type &operator*() { + return *operator->(); + } + + ~ThreadLocal() { + auto current{list.exchange(nullptr, std::memory_order_acquire)}; + while (current) { + current->object.~Type(); + current = current->next; + } + + pthread_key_delete(key); + } + }; +} diff --git a/app/src/main/cpp/skyline/gpu/command_scheduler.cpp b/app/src/main/cpp/skyline/gpu/command_scheduler.cpp index 10dacb02..5d9f9e73 100644 --- a/app/src/main/cpp/skyline/gpu/command_scheduler.cpp +++ b/app/src/main/cpp/skyline/gpu/command_scheduler.cpp @@ -20,21 +20,20 @@ namespace skyline::gpu { return false; } - CommandScheduler::CommandScheduler(GPU &pGpu) : gpu(pGpu), vkCommandPool(pGpu.vkDevice, vk::CommandPoolCreateInfo{ + CommandScheduler::CommandScheduler(GPU &pGpu) : gpu(pGpu), pool(std::ref(pGpu.vkDevice), vk::CommandPoolCreateInfo{ .flags = vk::CommandPoolCreateFlagBits::eTransient | vk::CommandPoolCreateFlagBits::eResetCommandBuffer, .queueFamilyIndex = pGpu.vkQueueFamilyIndex, }) {} CommandScheduler::ActiveCommandBuffer CommandScheduler::AllocateCommandBuffer() { - std::scoped_lock lock(mutex); - auto slot{std::find_if(commandBuffers.begin(), commandBuffers.end(), CommandBufferSlot::AllocateIfFree)}; - auto slotId{std::distance(commandBuffers.begin(), slot)}; - if (slot != commandBuffers.end()) + auto slot{std::find_if(pool->buffers.begin(), pool->buffers.end(), CommandBufferSlot::AllocateIfFree)}; + auto slotId{std::distance(pool->buffers.begin(), slot)}; + if (slot != pool->buffers.end()) return ActiveCommandBuffer(*slot); vk::CommandBuffer commandBuffer; vk::CommandBufferAllocateInfo commandBufferAllocateInfo{ - .commandPool = *vkCommandPool, + .commandPool = *pool->vkCommandPool, .level = vk::CommandBufferLevel::ePrimary, .commandBufferCount = 1, }; @@ -42,7 +41,7 @@ namespace skyline::gpu { auto result{(*gpu.vkDevice).allocateCommandBuffers(&commandBufferAllocateInfo, &commandBuffer, *gpu.vkDevice.getDispatcher())}; if (result != vk::Result::eSuccess) vk::throwResultException(result, __builtin_FUNCTION()); - return ActiveCommandBuffer(commandBuffers.emplace_back(gpu.vkDevice, commandBuffer, vkCommandPool)); + return ActiveCommandBuffer(pool->buffers.emplace_back(gpu.vkDevice, commandBuffer, pool->vkCommandPool)); } void CommandScheduler::SubmitCommandBuffer(const vk::raii::CommandBuffer &commandBuffer, vk::Fence fence) { diff --git a/app/src/main/cpp/skyline/gpu/command_scheduler.h b/app/src/main/cpp/skyline/gpu/command_scheduler.h index 259d0be3..6afcabc2 100644 --- a/app/src/main/cpp/skyline/gpu/command_scheduler.h +++ b/app/src/main/cpp/skyline/gpu/command_scheduler.h @@ -3,6 +3,7 @@ #pragma once +#include #include "fence_cycle.h" namespace skyline::gpu { @@ -62,9 +63,19 @@ namespace skyline::gpu { }; GPU &gpu; - std::mutex mutex; //!< Synchronizes mutations to the command pool due to allocations - vk::raii::CommandPool vkCommandPool; - std::list commandBuffers; + + /** + * @brief A command pool designed to be thread-local to respect external synchronization for all command buffers and the associated pool + * @note If we utilized a single global pool there would need to be a mutex around command buffer recording which would incur significant costs + */ + struct CommandPool { + vk::raii::CommandPool vkCommandPool; + std::list buffers; + + template + constexpr CommandPool(Args &&... args) : vkCommandPool(std::forward(args)...) {} + }; + ThreadLocal pool; /** * @brief Allocates an existing or new primary command buffer from the pool diff --git a/app/src/main/cpp/skyline/gpu/interconnect/command_executor.cpp b/app/src/main/cpp/skyline/gpu/interconnect/command_executor.cpp index c46c8c09..06bf7c9e 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/command_executor.cpp +++ b/app/src/main/cpp/skyline/gpu/interconnect/command_executor.cpp @@ -20,16 +20,18 @@ namespace skyline::gpu::interconnect { } void CommandExecutor::AddSubpass(const std::function &, GPU &)> &function, vk::Rect2D renderArea, std::vector inputAttachments, std::vector colorAttachments, std::optional depthStencilAttachment) { - for (const auto& attachments : {inputAttachments, colorAttachments}) - for (const auto& attachment : attachments) + for (const auto &attachments : {inputAttachments, colorAttachments}) + for (const auto &attachment : attachments) syncTextures.emplace(attachment.backing.get()); + if (depthStencilAttachment) + syncTextures.emplace(depthStencilAttachment->backing.get()); bool newRenderpass{CreateRenderpass(renderArea)}; renderpass->AddSubpass(inputAttachments, colorAttachments, depthStencilAttachment ? &*depthStencilAttachment : nullptr); if (newRenderpass) nodes.emplace_back(std::in_place_type_t(), function); else - nodes.emplace_back(std::in_place_type_t(), function); + nodes.emplace_back(std::in_place_type_t(), function); } void CommandExecutor::AddClearColorSubpass(TextureView attachment, const vk::ClearColorValue &value) { @@ -38,7 +40,10 @@ namespace skyline::gpu::interconnect { })}; renderpass->AddSubpass({}, attachment, nullptr); - if (!renderpass->ClearColorAttachment(0, value)) { + if (renderpass->ClearColorAttachment(0, value)) { + if (!newRenderpass) + nodes.emplace_back(std::in_place_type_t()); + } else { auto function{[scissor = attachment.backing->dimensions, value](vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr &, GPU &) { commandBuffer.clearAttachments(vk::ClearAttachment{ .aspectMask = vk::ImageAspectFlagBits::eColor, @@ -54,7 +59,7 @@ namespace skyline::gpu::interconnect { if (newRenderpass) nodes.emplace_back(std::in_place_type_t(), function); else - nodes.emplace_back(std::in_place_type_t(), function); + nodes.emplace_back(std::in_place_type_t(), function); } } @@ -73,12 +78,15 @@ namespace skyline::gpu::interconnect { using namespace node; for (NodeVariant &node : nodes) { + #define NODE(name) [&](name& node) { node(commandBuffer, cycle, gpu); } std::visit(VariantVisitor{ - [&](FunctionNode &node) { node(commandBuffer, cycle, gpu); }, - [&](RenderpassNode &node) { node(commandBuffer, cycle, gpu); }, - [&](NextSubpassNode &node) { node(commandBuffer, cycle, gpu); }, - [&](RenderpassEndNode &node) { node(commandBuffer, cycle, gpu); }, + NODE(FunctionNode), + NODE(RenderpassNode), + NODE(NextSubpassNode), + NODE(NextSubpassFunctionNode), + NODE(RenderpassEndNode), }, node); + #undef NODE } for (auto texture : syncTextures) diff --git a/app/src/main/cpp/skyline/gpu/interconnect/command_nodes.h b/app/src/main/cpp/skyline/gpu/interconnect/command_nodes.h index 9dc30f23..adee94a1 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/command_nodes.h +++ b/app/src/main/cpp/skyline/gpu/interconnect/command_nodes.h @@ -286,10 +286,19 @@ namespace skyline::gpu::interconnect::node { } }; + /** + * @brief A node which progresses to the next subpass during a renderpass + */ + struct NextSubpassNode { + void operator()(vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr &cycle, GPU &gpu) { + commandBuffer.nextSubpass(vk::SubpassContents::eInline); + } + }; + /** * @brief A FunctionNode which progresses to the next subpass prior to calling the function */ - struct NextSubpassNode : private FunctionNode { + struct NextSubpassFunctionNode : private FunctionNode { using FunctionNode::FunctionNode; void operator()(vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr &cycle, GPU &gpu) { @@ -307,5 +316,5 @@ namespace skyline::gpu::interconnect::node { } }; - using NodeVariant = std::variant; //!< A variant encompassing all command nodes types + using NodeVariant = std::variant; //!< A variant encompassing all command nodes types } diff --git a/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h b/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h index 923c027a..613c3f09 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h +++ b/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h @@ -32,6 +32,7 @@ namespace skyline::gpu::interconnect { u32 gpuAddressHigh; }; }; + u32 widthBytes; //!< The width in bytes for linear textures GuestTexture guest; std::optional view; @@ -74,6 +75,9 @@ namespace skyline::gpu::interconnect { void SetRenderTargetWidth(size_t index, u32 value) { auto &renderTarget{renderTargets.at(index)}; + renderTarget.widthBytes = value; + if (renderTarget.guest.tileConfig.mode == texture::TileMode::Linear && renderTarget.guest.format) + value /= renderTarget.guest.format->bpb; // Width is in bytes rather than format units for linear textures renderTarget.guest.dimensions.width = value; renderTarget.view.reset(); } @@ -134,6 +138,10 @@ namespace skyline::gpu::interconnect { throw exception("Cannot translate the supplied RT format: 0x{:X}", static_cast(format)); } }(); + + if (renderTarget.guest.tileConfig.mode == texture::TileMode::Linear && renderTarget.guest.format) + renderTarget.guest.dimensions.width = renderTarget.widthBytes / renderTarget.guest.format->bpb; + renderTarget.disabled = !renderTarget.guest.format; renderTarget.view.reset(); } @@ -142,8 +150,17 @@ namespace skyline::gpu::interconnect { auto &renderTarget{renderTargets.at(index)}; auto &config{renderTarget.guest.tileConfig}; if (mode.isLinear) { + if (config.mode != texture::TileMode::Linear && renderTarget.guest.format) { + // Width is provided in bytes rather than format units for linear textures + renderTarget.widthBytes = renderTarget.guest.dimensions.width; + renderTarget.guest.dimensions.width /= renderTarget.guest.format->bpb; + } + config.mode = texture::TileMode::Linear; } else [[likely]] { + if (config.mode == texture::TileMode::Linear && renderTarget.guest.format) + renderTarget.guest.dimensions.width = renderTarget.widthBytes; + config = texture::TileConfig{ .mode = texture::TileMode::Block, .blockHeight = static_cast(1U << mode.blockHeightLog2), diff --git a/app/src/main/cpp/skyline/gpu/memory_manager.cpp b/app/src/main/cpp/skyline/gpu/memory_manager.cpp index 56df83f1..46021d0a 100644 --- a/app/src/main/cpp/skyline/gpu/memory_manager.cpp +++ b/app/src/main/cpp/skyline/gpu/memory_manager.cpp @@ -78,7 +78,7 @@ namespace skyline::gpu::memory { std::shared_ptr MemoryManager::AllocateStagingBuffer(vk::DeviceSize size) { vk::BufferCreateInfo bufferCreateInfo{ .size = size, - .usage = vk::BufferUsageFlagBits::eTransferSrc, + .usage = vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst, .sharingMode = vk::SharingMode::eExclusive, .queueFamilyIndexCount = 1, .pQueueFamilyIndices = &gpu.vkQueueFamilyIndex, @@ -112,7 +112,7 @@ namespace skyline::gpu::memory { Image MemoryManager::AllocateMappedImage(const vk::ImageCreateInfo &createInfo) { VmaAllocationCreateInfo allocationCreateInfo{ .usage = VMA_MEMORY_USAGE_UNKNOWN, - .memoryTypeBits = static_cast(vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eDeviceLocal), + .requiredFlags = static_cast(vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eDeviceLocal), }; VkImage image; diff --git a/app/src/main/cpp/skyline/gpu/texture/texture.cpp b/app/src/main/cpp/skyline/gpu/texture/texture.cpp index a2374853..99a9ea88 100644 --- a/app/src/main/cpp/skyline/gpu/texture/texture.cpp +++ b/app/src/main/cpp/skyline/gpu/texture/texture.cpp @@ -484,6 +484,10 @@ namespace skyline::gpu { cycle = lCycle; } + Texture::~Texture() { + WaitOnFence(); + } + TextureView::TextureView(std::shared_ptr backing, vk::ImageViewType type, vk::ImageSubresourceRange range, texture::Format format, vk::ComponentMapping mapping) : backing(std::move(backing)), type(type), format(format), mapping(mapping), range(range) {} vk::ImageView TextureView::GetView() { diff --git a/app/src/main/cpp/skyline/gpu/texture/texture.h b/app/src/main/cpp/skyline/gpu/texture/texture.h index 97b59ccd..14f4fca4 100644 --- a/app/src/main/cpp/skyline/gpu/texture/texture.h +++ b/app/src/main/cpp/skyline/gpu/texture/texture.h @@ -371,6 +371,8 @@ namespace skyline::gpu { */ Texture(GPU &gpu, texture::Dimensions dimensions, texture::Format format, vk::ImageLayout initialLayout = vk::ImageLayout::eGeneral, vk::ImageUsageFlags usage = {}, vk::ImageTiling tiling = vk::ImageTiling::eOptimal, u32 mipLevels = 1, u32 layerCount = 1, vk::SampleCountFlagBits sampleCount = vk::SampleCountFlagBits::e1); + ~Texture(); + /** * @note The handle returned is nullable and the appropriate precautions should be taken */ diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp index f9ebdc1a..5c7938a3 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.cpp @@ -32,14 +32,8 @@ namespace skyline::kernel::type { if (all) { for (const auto &thread : threads) thread->Kill(join); - } else { - std::shared_ptr thread; - try { - thread = threads.at(0); - } catch (const std::out_of_range &) { - return; - } - thread->Kill(join); + } else if (!threads.empty()) { + threads[0]->Kill(join); } } @@ -101,6 +95,11 @@ namespace skyline::kernel::type { return std::nullopt; } + void KProcess::ClearHandleTable() { + std::shared_lock lock(handleMutex); + handles.clear(); + } + constexpr u32 HandleWaitersBit{1UL << 30}; //!< A bit which denotes if a mutex psuedo-handle has waiters or not Result KProcess::MutexLock(u32 *mutex, KHandle ownerHandle, KHandle tag) { diff --git a/app/src/main/cpp/skyline/kernel/types/KProcess.h b/app/src/main/cpp/skyline/kernel/types/KProcess.h index 327f5732..d65ee223 100644 --- a/app/src/main/cpp/skyline/kernel/types/KProcess.h +++ b/app/src/main/cpp/skyline/kernel/types/KProcess.h @@ -201,6 +201,12 @@ namespace skyline { handles.at(handle - constant::BaseHandleIndex) = nullptr; } + /** + * @brief Clear the process handle table + * @note A handle created prior to clearing must not be retrieved after this is run + */ + void ClearHandleTable(); + /** * @brief Locks the mutex at the specified address * @param ownerHandle The psuedo-handle of the current mutex owner diff --git a/app/src/main/cpp/skyline/services/am/controller/ISelfController.cpp b/app/src/main/cpp/skyline/services/am/controller/ISelfController.cpp index 7f829c33..f95f2375 100644 --- a/app/src/main/cpp/skyline/services/am/controller/ISelfController.cpp +++ b/app/src/main/cpp/skyline/services/am/controller/ISelfController.cpp @@ -8,6 +8,13 @@ namespace skyline::service::am { ISelfController::ISelfController(const DeviceState &state, ServiceManager &manager) : libraryAppletLaunchableEvent(std::make_shared(state, false)), accumulatedSuspendedTickChangedEvent(std::make_shared(state, false)), hosbinder(manager.CreateOrGetService("dispdrv")), BaseService(state, manager) {} + Result ISelfController::Exit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + if (state.thread->id) + state.process->Kill(false); + std::longjmp(state.thread->originalCtx, true); + return {}; + } + Result ISelfController::LockExit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { return {}; } diff --git a/app/src/main/cpp/skyline/services/am/controller/ISelfController.h b/app/src/main/cpp/skyline/services/am/controller/ISelfController.h index fc7f200e..ba972403 100644 --- a/app/src/main/cpp/skyline/services/am/controller/ISelfController.h +++ b/app/src/main/cpp/skyline/services/am/controller/ISelfController.h @@ -23,6 +23,12 @@ namespace skyline::service::am { public: ISelfController(const DeviceState &state, ServiceManager &manager); + /** + * @brief Exits the current applet + * @url https://switchbrew.org/wiki/Applet_Manager_services#Exit + */ + Result Exit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + /** * @brief Function prevents the running application from being quit via the home button * @url https://switchbrew.org/wiki/Applet_Manager_services#LockExit @@ -90,6 +96,7 @@ namespace skyline::service::am { Result GetAccumulatedSuspendedTickChangedEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); SERVICE_DECL( + SFUNC(0x0, ISelfController, Exit), SFUNC(0x1, ISelfController, LockExit), SFUNC(0x2, ISelfController, UnlockExit), SFUNC(0x9, ISelfController, GetLibraryAppletLaunchableEvent),