From 42d982c6fb58bb226209400f8691568e4de4e70e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=97=B1=20PixelyIon?= Date: Wed, 25 Mar 2020 01:47:31 +0530 Subject: [PATCH] Refactor Display Services and GPU This commit addresses the incorrect hierarchy of the GPU and refactors them at the same time. Now, the hierarchy much closely matches HOS. This commit also introduces a texture classes, albeit they're not complete and only partially implemented. --- app/CMakeLists.txt | 24 +- app/src/main/cpp/skyline/common.h | 6 +- app/src/main/cpp/skyline/gpu.cpp | 148 ++-------- app/src/main/cpp/skyline/gpu.h | 84 +----- app/src/main/cpp/skyline/gpu/display.cpp | 142 ---------- app/src/main/cpp/skyline/gpu/display.h | 171 ----------- app/src/main/cpp/skyline/gpu/texture.cpp | 81 ++++++ app/src/main/cpp/skyline/gpu/texture.h | 266 ++++++++++++++++++ .../am/controller/ISelfController.cpp | 11 +- .../cpp/skyline/services/audio/IAudioOut.cpp | 2 +- .../main/cpp/skyline/services/base_service.h | 22 +- .../{gpu => services/common}/parcel.cpp | 2 +- .../skyline/{gpu => services/common}/parcel.h | 2 +- .../services/hosbinder/IHOSBinderDriver.cpp | 237 ++++++++++++++++ .../IHOSBinderDriver.h} | 71 ++++- .../cpp/skyline/services/hosbinder/buffer.h | 54 ++++ .../cpp/skyline/services/hosbinder/display.h | 36 +++ .../skyline/services/nvdrv/INvDrvServices.cpp | 118 ++++++++ .../skyline/services/nvdrv/INvDrvServices.h | 91 ++++++ .../nvdrv}/devices/nvdevice.h | 2 +- .../nvdrv}/devices/nvhost_as_gpu.cpp | 2 +- .../nvdrv}/devices/nvhost_as_gpu.h | 2 +- .../nvdrv}/devices/nvhost_channel.cpp | 14 +- .../nvdrv}/devices/nvhost_channel.h | 2 +- .../nvdrv}/devices/nvhost_ctrl.cpp | 2 +- .../nvdrv}/devices/nvhost_ctrl.h | 2 +- .../nvdrv}/devices/nvhost_ctrl_gpu.cpp | 4 +- .../nvdrv}/devices/nvhost_ctrl_gpu.h | 2 +- .../{gpu => services/nvdrv}/devices/nvmap.cpp | 8 +- .../{gpu => services/nvdrv}/devices/nvmap.h | 2 +- .../main/cpp/skyline/services/nvdrv/nvdrv.cpp | 49 ---- .../main/cpp/skyline/services/nvdrv/nvdrv.h | 46 --- .../skyline/services/nvnflinger/dispdrv.cpp | 70 ----- .../main/cpp/skyline/services/serviceman.cpp | 29 +- .../main/cpp/skyline/services/serviceman.h | 17 +- app/src/main/cpp/skyline/services/vi/vi_m.cpp | 146 ---------- app/src/main/cpp/skyline/services/vi/vi_m.h | 142 ---------- .../visrv/IApplicationDisplayService.cpp | 95 +++++++ .../visrv/IApplicationDisplayService.h | 63 +++++ .../services/visrv/IDisplayService.cpp | 41 +++ .../skyline/services/visrv/IDisplayService.h | 39 +++ .../services/visrv/IManagerDisplayService.cpp | 35 +++ .../services/visrv/IManagerDisplayService.h | 28 ++ .../services/visrv/IManagerRootService.cpp | 13 + .../services/visrv/IManagerRootService.h | 20 ++ .../services/visrv/ISystemDisplayService.cpp | 10 + .../services/visrv/ISystemDisplayService.h | 18 ++ 47 files changed, 1424 insertions(+), 1047 deletions(-) delete mode 100644 app/src/main/cpp/skyline/gpu/display.cpp delete mode 100644 app/src/main/cpp/skyline/gpu/display.h create mode 100644 app/src/main/cpp/skyline/gpu/texture.cpp create mode 100644 app/src/main/cpp/skyline/gpu/texture.h rename app/src/main/cpp/skyline/{gpu => services/common}/parcel.cpp (98%) rename app/src/main/cpp/skyline/{gpu => services/common}/parcel.h (99%) create mode 100644 app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.cpp rename app/src/main/cpp/skyline/services/{nvnflinger/dispdrv.h => hosbinder/IHOSBinderDriver.h} (63%) create mode 100644 app/src/main/cpp/skyline/services/hosbinder/buffer.h create mode 100644 app/src/main/cpp/skyline/services/hosbinder/display.h create mode 100644 app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.cpp create mode 100644 app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h rename app/src/main/cpp/skyline/{gpu => services/nvdrv}/devices/nvdevice.h (99%) rename app/src/main/cpp/skyline/{gpu => services/nvdrv}/devices/nvhost_as_gpu.cpp (75%) rename app/src/main/cpp/skyline/{gpu => services/nvdrv}/devices/nvhost_as_gpu.h (87%) rename app/src/main/cpp/skyline/{gpu => services/nvdrv}/devices/nvhost_channel.cpp (67%) rename app/src/main/cpp/skyline/{gpu => services/nvdrv}/devices/nvhost_channel.h (97%) rename app/src/main/cpp/skyline/{gpu => services/nvdrv}/devices/nvhost_ctrl.cpp (75%) rename app/src/main/cpp/skyline/{gpu => services/nvdrv}/devices/nvhost_ctrl.h (87%) rename app/src/main/cpp/skyline/{gpu => services/nvdrv}/devices/nvhost_ctrl_gpu.cpp (98%) rename app/src/main/cpp/skyline/{gpu => services/nvdrv}/devices/nvhost_ctrl_gpu.h (96%) rename app/src/main/cpp/skyline/{gpu => services/nvdrv}/devices/nvmap.cpp (96%) rename app/src/main/cpp/skyline/{gpu => services/nvdrv}/devices/nvmap.h (98%) delete mode 100644 app/src/main/cpp/skyline/services/nvdrv/nvdrv.cpp delete mode 100644 app/src/main/cpp/skyline/services/nvdrv/nvdrv.h delete mode 100644 app/src/main/cpp/skyline/services/nvnflinger/dispdrv.cpp delete mode 100644 app/src/main/cpp/skyline/services/vi/vi_m.cpp delete mode 100644 app/src/main/cpp/skyline/services/vi/vi_m.h create mode 100644 app/src/main/cpp/skyline/services/visrv/IApplicationDisplayService.cpp create mode 100644 app/src/main/cpp/skyline/services/visrv/IApplicationDisplayService.h create mode 100644 app/src/main/cpp/skyline/services/visrv/IDisplayService.cpp create mode 100644 app/src/main/cpp/skyline/services/visrv/IDisplayService.h create mode 100644 app/src/main/cpp/skyline/services/visrv/IManagerDisplayService.cpp create mode 100644 app/src/main/cpp/skyline/services/visrv/IManagerDisplayService.h create mode 100644 app/src/main/cpp/skyline/services/visrv/IManagerRootService.cpp create mode 100644 app/src/main/cpp/skyline/services/visrv/IManagerRootService.h create mode 100644 app/src/main/cpp/skyline/services/visrv/ISystemDisplayService.cpp create mode 100644 app/src/main/cpp/skyline/services/visrv/ISystemDisplayService.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 3776c93c..d6ce0e64 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -32,13 +32,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/nce.cpp ${source_DIR}/skyline/jvm.cpp ${source_DIR}/skyline/gpu.cpp - ${source_DIR}/skyline/gpu/display.cpp - ${source_DIR}/skyline/gpu/parcel.cpp - ${source_DIR}/skyline/gpu/devices/nvhost_ctrl.cpp - ${source_DIR}/skyline/gpu/devices/nvhost_ctrl_gpu.cpp - ${source_DIR}/skyline/gpu/devices/nvhost_channel.cpp - ${source_DIR}/skyline/gpu/devices/nvmap.cpp - ${source_DIR}/skyline/gpu/devices/nvhost_as_gpu.cpp + ${source_DIR}/skyline/gpu/texture.cpp ${source_DIR}/skyline/os.cpp ${source_DIR}/skyline/loader/nro.cpp ${source_DIR}/skyline/kernel/memory.cpp @@ -50,6 +44,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/kernel/types/KTransferMemory.cpp ${source_DIR}/skyline/kernel/types/KPrivateMemory.cpp ${source_DIR}/skyline/services/serviceman.cpp + ${source_DIR}/skyline/services/common/parcel.cpp ${source_DIR}/skyline/services/sm/IUserInterface.cpp ${source_DIR}/skyline/services/fatalsrv/IService.cpp ${source_DIR}/skyline/services/audio/IAudioOutManager.cpp @@ -84,9 +79,18 @@ add_library(skyline SHARED ${source_DIR}/skyline/services/timesrv/ITimeZoneService.cpp ${source_DIR}/skyline/services/fssrv/IFileSystemProxy.cpp ${source_DIR}/skyline/services/fssrv/IFileSystem.cpp - ${source_DIR}/skyline/services/nvdrv/nvdrv.cpp - ${source_DIR}/skyline/services/nvnflinger/dispdrv.cpp - ${source_DIR}/skyline/services/vi/vi_m.cpp + ${source_DIR}/skyline/services/nvdrv/INvDrvServices.cpp + ${source_DIR}/skyline/services/nvdrv/devices/nvmap.cpp + ${source_DIR}/skyline/services/nvdrv/devices/nvhost_ctrl_gpu.cpp + ${source_DIR}/skyline/services/nvdrv/devices/nvhost_ctrl.cpp + ${source_DIR}/skyline/services/nvdrv/devices/nvhost_channel.cpp + ${source_DIR}/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp + ${source_DIR}/skyline/services/hosbinder/IHOSBinderDriver.cpp + ${source_DIR}/skyline/services/visrv/IDisplayService.cpp + ${source_DIR}/skyline/services/visrv/IApplicationDisplayService.cpp + ${source_DIR}/skyline/services/visrv/IManagerDisplayService.cpp + ${source_DIR}/skyline/services/visrv/IManagerRootService.cpp + ${source_DIR}/skyline/services/visrv/ISystemDisplayService.cpp ) target_link_libraries(skyline vulkan android fmt tinyxml2 oboe) diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index f102a65e..90e73d56 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -60,9 +60,6 @@ namespace skyline { constexpr u32 DockedResolutionW = 1920; //!< The width component of the docked resolution constexpr u32 DockedResolutionH = 1080; //!< The height component of the docked resolution constexpr u32 TokenLength = 0x50; //!< The length of the token on BufferQueue parcels - constexpr u32 GobHeight = 0x8; //!< The height of a blocklinear GOB - constexpr u32 GobStride = 0x40; //!< The stride of a blocklinear GOB - constexpr u32 GobSize = GobHeight * GobStride; //!< The size of a blocklinear GOB // Status codes namespace status { constexpr u32 Success = 0x0; //!< "Success" @@ -122,7 +119,8 @@ namespace skyline { template inline TypeVal AlignUp(TypeVal value, TypeMul multiple) { static_assert(std::is_integral() && std::is_integral()); - return (value + multiple) & ~(multiple - 1); + multiple--; + return (value + multiple) & ~(multiple); } /** diff --git a/app/src/main/cpp/skyline/gpu.cpp b/app/src/main/cpp/skyline/gpu.cpp index f0c1e84b..86be21e5 100644 --- a/app/src/main/cpp/skyline/gpu.cpp +++ b/app/src/main/cpp/skyline/gpu.cpp @@ -1,8 +1,4 @@ #include "gpu.h" -#include "gpu/devices/nvhost_ctrl.h" -#include "gpu/devices/nvhost_ctrl_gpu.h" -#include "gpu/devices/nvhost_channel.h" -#include "gpu/devices/nvhost_as_gpu.h" #include "jvm.h" #include #include @@ -11,7 +7,7 @@ extern bool Halt; extern jobject Surface; namespace skyline::gpu { - GPU::GPU(const DeviceState &state) : state(state), window(ANativeWindow_fromSurface(state.jvmManager->GetEnv(), Surface)), bufferQueue(state), vsyncEvent(std::make_shared(state)), bufferEvent(std::make_shared(state)) { + GPU::GPU(const DeviceState &state) : state(state), window(ANativeWindow_fromSurface(state.jvmManager->GetEnv(), Surface)), vsyncEvent(std::make_shared(state)), bufferEvent(std::make_shared(state)) { ANativeWindow_acquire(window); resolution.width = static_cast(ANativeWindow_getWidth(window)); resolution.height = static_cast(ANativeWindow_getHeight(window)); @@ -36,142 +32,34 @@ namespace skyline::gpu { surfaceUpdate = true; return; } - if (!bufferQueue.displayQueue.empty()) { - auto &buffer = bufferQueue.displayQueue.front(); - bufferQueue.displayQueue.pop(); - if (resolution != buffer->resolution || buffer->gbpBuffer.format != format) { - ANativeWindow_setBuffersGeometry(window, buffer->resolution.width, buffer->resolution.height, buffer->gbpBuffer.format); - resolution = buffer->resolution; - format = buffer->gbpBuffer.format; + + if (!presentationQueue.empty()) { + auto &texture = presentationQueue.front(); + presentationQueue.pop(); + + auto textureFormat = texture->GetAndroidFormat(); + if (resolution != texture->dimensions || textureFormat != format) { + ANativeWindow_setBuffersGeometry(window, texture->dimensions.width, texture->dimensions.height, textureFormat); + resolution = texture->dimensions; + format = textureFormat; } - u8 *inBuffer = buffer->GetAddress(); - madvise(inBuffer, buffer->gbpBuffer.size, MADV_SEQUENTIAL); + ANativeWindow_Buffer windowBuffer; ARect rect; + ANativeWindow_lock(window, &windowBuffer, &rect); - u8 *outBuffer = reinterpret_cast(windowBuffer.bits); - const u32 strideBytes = buffer->gbpBuffer.stride * buffer->bpp; - const u32 blockHeight = 1U << buffer->gbpBuffer.blockHeightLog2; - const u32 blockHeightPixels = 8U << buffer->gbpBuffer.blockHeightLog2; - const u32 widthBlocks = strideBytes >> 6U; - const u32 heightBlocks = ((resolution.height) + blockHeightPixels - 1) >> (3 + buffer->gbpBuffer.blockHeightLog2); - for (u32 blockY = 0; blockY < heightBlocks; blockY++) { - for (u32 blockX = 0; blockX < widthBlocks; blockX++) { - for (u32 gobY = 0; gobY < blockHeight; gobY++) { - const u32 x = blockX * constant::GobStride; - const u32 y = blockY * blockHeightPixels + gobY * constant::GobHeight; - if (y < resolution.height) { - u8 *inBlock = inBuffer; - u8 *outBlock = outBuffer + (y * strideBytes) + x; - for (u32 i = 0; i < 32; i++) { - const u32 yT = ((i >> 1) & 0x06) | (i & 0x01); - const u32 xT = ((i << 3) & 0x10) | ((i << 1) & 0x20); - std::memcpy(outBlock + (yT * strideBytes) + xT, inBlock, sizeof(u128)); - inBlock += sizeof(u128); - } - } - inBuffer += constant::GobSize; - } - } - } + memcpy(windowBuffer.bits, texture->backing.data(), texture->backing.size()); ANativeWindow_unlockAndPost(window); - bufferQueue.FreeBuffer(buffer->slot); + vsyncEvent->Signal(); + texture->releaseCallback(); + if (prevTime != 0) { auto now = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); state.logger->Error("{} ms, {} FPS", (now - prevTime) / 1000, 1000000 / (now - prevTime)); } + prevTime = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); } } - - u32 GPU::OpenDevice(const std::string &path) { - state.logger->Debug("Opening NVDRV device ({}): {}", fdIndex, path); - auto type = device::nvDeviceMap.at(path); - for (const auto &device : fdMap) { - if (device.second->deviceType == type) { - device.second->refCount++; - fdMap[fdIndex] = device.second; - return fdIndex++; - } - } - std::shared_ptr object; - switch (type) { - case (device::NvDeviceType::nvhost_ctrl): - object = std::make_shared(state); - break; - case (device::NvDeviceType::nvhost_gpu): - case (device::NvDeviceType::nvhost_vic): - case (device::NvDeviceType::nvhost_nvdec): - object = std::make_shared(state, type); - break; - case (device::NvDeviceType::nvhost_ctrl_gpu): - object = std::make_shared(state); - break; - case (device::NvDeviceType::nvmap): - object = std::make_shared(state); - break; - case (device::NvDeviceType::nvhost_as_gpu): - object = std::make_shared(state); - break; - default: - throw exception("Cannot find NVDRV device"); - } - deviceMap[type] = object; - fdMap[fdIndex] = object; - return fdIndex++; - } - - void GPU::CloseDevice(u32 fd) { - state.logger->Debug("Closing NVDRV device ({})", fd); - try { - auto device = fdMap.at(fd); - if (!--device->refCount) - deviceMap.erase(device->deviceType); - fdMap.erase(fd); - } catch (const std::out_of_range &) { - state.logger->Warn("Trying to close non-existent FD"); - } - } - - void GPU::Ioctl(u32 fd, u32 cmd, kernel::ipc::IpcRequest &request, kernel::ipc::IpcResponse &response) { - state.logger->Debug("IOCTL on device: 0x{:X}, cmd: 0x{:X}", fd, cmd); - try { - if (request.inputBuf.empty() || request.outputBuf.empty()) { - if (request.inputBuf.empty()) { - device::IoctlData data(request.outputBuf.at(0)); - fdMap.at(fd)->HandleIoctl(cmd, data); - response.Push(data.status); - } else { - device::IoctlData data(request.inputBuf.at(0)); - fdMap.at(fd)->HandleIoctl(cmd, data); - response.Push(data.status); - } - } else { - device::IoctlData data(request.inputBuf.at(0), request.outputBuf.at(0)); - fdMap.at(fd)->HandleIoctl(cmd, data); - response.Push(data.status); - } - } catch (const std::out_of_range &) { - throw exception("IOCTL was requested on an invalid file descriptor"); - } - } - - void GPU::SetDisplay(const std::string &name) { - try { - const auto type = displayTypeMap.at(name); - if (displayId == DisplayId::Null) - displayId = type; - else - throw exception("Trying to change display type from non-null type"); - } catch (const std::out_of_range &) { - throw exception("The display with name: '{}' doesn't exist", name); - } - } - - void GPU::CloseDisplay() { - if (displayId == DisplayId::Null) - state.logger->Warn("Trying to close uninitiated display"); - displayId = DisplayId::Null; - } } diff --git a/app/src/main/cpp/skyline/gpu.h b/app/src/main/cpp/skyline/gpu.h index 18964653..af46cc2a 100644 --- a/app/src/main/cpp/skyline/gpu.h +++ b/app/src/main/cpp/skyline/gpu.h @@ -1,10 +1,11 @@ #pragma once -#include "kernel/ipc.h" -#include "kernel/types/KEvent.h" -#include "gpu/display.h" -#include "gpu/devices/nvdevice.h" +#include #include +#include +#include +#include +#include "gpu/texture.h" namespace skyline::gpu { /** @@ -15,17 +16,12 @@ namespace skyline::gpu { private: ANativeWindow *window; //!< The ANativeWindow to render to const DeviceState &state; //!< The state of the device - u32 fdIndex{}; //!< Holds the index of a file descriptor - std::unordered_map> deviceMap; //!< A map from a NvDeviceType to the NvDevice object - std::unordered_map> fdMap; //!< A map from an FD to a shared pointer to it's NvDevice object bool surfaceUpdate{}; //!< If the surface needs to be updated double prevTime{}; //!< The time passed from the last frame public: - DisplayId displayId{DisplayId::Null}; //!< The ID of this display - LayerStatus layerStatus{LayerStatus::Uninitialized}; //!< This is the status of the single layer the display has - BufferQueue bufferQueue; //!< This holds all of the buffers to be pushed onto the display - Resolution resolution{}; //!< The resolution of the display window + std::queue> presentationQueue; //!< A queue of all the PresentationTextures to be posted to the display + texture::Dimensions resolution{}; //!< The resolution of the surface i32 format{}; //!< The format of the display window std::shared_ptr vsyncEvent; //!< This KEvent is triggered every time a frame is drawn std::shared_ptr bufferEvent; //!< This KEvent is triggered every time a buffer is freed @@ -44,71 +40,5 @@ namespace skyline::gpu { * @brief The loop that executes routine GPU functions */ void Loop(); - - /** - * @brief Open a specific device and return a FD - * @param path The path of the device to open an FD to - * @return The file descriptor to the device - */ - u32 OpenDevice(const std::string &path); - - /** - * @brief Close the specified FD - * @param fd The file descriptor to close - */ - void CloseDevice(u32 fd); - - /** - * @brief Returns a particular device with a specific FD - * @tparam objectClass The class of the device to return - * @param fd The file descriptor to retrieve - * @return A shared pointer to the device - */ - template - std::shared_ptr GetDevice(u32 fd) { - try { - auto item = fdMap.at(fd); - return std::static_pointer_cast(item); - } catch (std::out_of_range) { - throw exception("GetDevice was called with invalid file descriptor: 0x{:X}", fd); - } - } - - /** - * @brief Returns a particular device with a specific type - * @tparam objectClass The class of the device to return - * @param type The type of the device to return - * @return A shared pointer to the device - */ - template - std::shared_ptr GetDevice(device::NvDeviceType type) { - try { - auto item = deviceMap.at(type); - return std::static_pointer_cast(item); - } catch (std::out_of_range) { - throw exception("GetDevice was called with invalid type: 0x{:X}", type); - } - } - - /** - * @brief Process an Ioctl request to a device - * @param fd The file descriptor the Ioctl was issued to - * @param cmd The command of the Ioctl - * @param request The IPC request object - * @param response The IPC response object - */ - void Ioctl(u32 fd, u32 cmd, kernel::ipc::IpcRequest &request, kernel::ipc::IpcResponse &response); - - /** - * @brief This sets displayId to a specific display type - * @param name The name of the display - * @note displayId has to be DisplayId::Null or this will throw an exception - */ - void SetDisplay(const std::string &name); - - /** - * @brief This closes the display by setting displayId to DisplayId::Null - */ - void CloseDisplay(); }; } diff --git a/app/src/main/cpp/skyline/gpu/display.cpp b/app/src/main/cpp/skyline/gpu/display.cpp deleted file mode 100644 index 83a3f1b5..00000000 --- a/app/src/main/cpp/skyline/gpu/display.cpp +++ /dev/null @@ -1,142 +0,0 @@ -#include "display.h" -#include -#include - -namespace skyline::gpu { - Buffer::Buffer(const DeviceState &state, u32 slot, GbpBuffer &gbpBuffer) : state(state), slot(slot), gbpBuffer(gbpBuffer), resolution{gbpBuffer.width, gbpBuffer.height} { - if (gbpBuffer.nvmapHandle) - nvBuffer = state.gpu->GetDevice(device::NvDeviceType::nvmap)->handleTable.at(gbpBuffer.nvmapHandle); - else { - auto nvmap = state.gpu->GetDevice(device::NvDeviceType::nvmap); - for (const auto &object : nvmap->handleTable) { - if (object.second->id == gbpBuffer.nvmapId) { - nvBuffer = object.second; - break; - } - } - if (!nvBuffer) - throw exception("A QueueBuffer request has an invalid NVMap Handle ({}) and ID ({})", gbpBuffer.nvmapHandle, gbpBuffer.nvmapId); - } - switch (gbpBuffer.format) { - case WINDOW_FORMAT_RGBA_8888: - case WINDOW_FORMAT_RGBX_8888: - bpp = sizeof(u32); - break; - case WINDOW_FORMAT_RGB_565: - bpp = sizeof(u16); - break; - default: - throw exception("Unknown pixel format used for FB"); - } - } - - u8 *Buffer::GetAddress() { - return state.process->GetPointer(nvBuffer->address + gbpBuffer.offset); - } - - BufferQueue::BufferQueue(const DeviceState &state) : state(state) {} - - void BufferQueue::RequestBuffer(Parcel &in, Parcel &out) { - u32 slot = *reinterpret_cast(in.data.data() + constant::TokenLength); - out.WriteData(1); - out.WriteData(sizeof(GbpBuffer)); - out.WriteData(0); - out.WriteData(queue.at(slot)->gbpBuffer); - state.logger->Debug("RequestBuffer: Slot: {}", slot, sizeof(GbpBuffer)); - } - - void BufferQueue::DequeueBuffer(Parcel &in, Parcel &out) { - struct Data { - u32 format; - u32 width; - u32 height; - u32 timestamps; - u32 usage; - } *data = reinterpret_cast(in.data.data() + constant::TokenLength); - i64 slot{-1}; - while (slot == -1) { - for (auto &buffer : queue) { - if (buffer.second->status == BufferStatus::Free && buffer.second->resolution.width == data->width && buffer.second->resolution.height == data->height && buffer.second->gbpBuffer.usage == data->usage) { - slot = buffer.first; - buffer.second->status = BufferStatus::Dequeued; - break; - } - } - asm("yield"); - } - struct { - u32 slot; - u32 _unk_[13]; - } output{ - .slot = static_cast(slot) - }; - out.WriteData(output); - state.logger->Debug("DequeueBuffer: Width: {}, Height: {}, Format: {}, Usage: {}, Timestamps: {}, Slot: {}", data->width, data->height, data->format, data->usage, data->timestamps, slot); - } - - void BufferQueue::QueueBuffer(Parcel &in, Parcel &out) { - struct Data { - u32 slot; - u64 timestamp; - u32 autoTimestamp; - ARect crop; - u32 scalingMode; - u32 transform; - u32 stickyTransform; - u64 _unk0_; - u32 swapInterval; - Fence fence[4]; - } *data = reinterpret_cast(in.data.data() + constant::TokenLength); - auto buffer = queue.at(data->slot); - buffer->status = BufferStatus::Queued; - displayQueue.emplace(buffer); - state.gpu->bufferEvent->Signal(); - struct { - u32 width; - u32 height; - u32 _pad0_[3]; - } output{ - .width = buffer->gbpBuffer.width, - .height = buffer->gbpBuffer.height - }; - out.WriteData(output); - state.logger->Debug("QueueBuffer: Timestamp: {}, Auto Timestamp: {}, Crop: [T: {}, B: {}, L: {}, R: {}], Scaling Mode: {}, Transform: {}, Sticky Transform: {}, Swap Interval: {}, Slot: {}", data->timestamp, data->autoTimestamp, data->crop.top, data->crop.bottom, data->crop.left, data->crop.right, data->scalingMode, data->transform, data->stickyTransform, data->swapInterval, data->slot); - } - - void BufferQueue::CancelBuffer(Parcel &parcel) { - struct Data { - u32 slot; - Fence fence[4]; - } *data = reinterpret_cast(parcel.data.data() + constant::TokenLength); - FreeBuffer(data->slot); - state.logger->Debug("CancelBuffer: Slot: {}", data->slot); - } - - void BufferQueue::SetPreallocatedBuffer(Parcel &parcel) { - auto pointer = parcel.data.data() + constant::TokenLength; - struct Data { - u32 slot; - u32 _unk0_; - u32 length; - u32 _pad0_; - } *data = reinterpret_cast(pointer); - pointer += sizeof(Data); - auto gbpBuffer = reinterpret_cast(pointer); - queue[data->slot] = std::make_shared(state, data->slot, *gbpBuffer); - state.gpu->bufferEvent->Signal(); - state.logger->Debug("SetPreallocatedBuffer: Slot: {}, Magic: 0x{:X}, Width: {}, Height: {}, Stride: {}, Format: {}, Usage: {}, Index: {}, ID: {}, Handle: {}, Offset: 0x{:X}, Block Height: {}, Size: 0x{:X}", - data->slot, - gbpBuffer->magic, - gbpBuffer->width, - gbpBuffer->height, - gbpBuffer->stride, - gbpBuffer->format, - gbpBuffer->usage, - gbpBuffer->index, - gbpBuffer->nvmapId, - gbpBuffer->nvmapHandle, - gbpBuffer->offset, - (1U << gbpBuffer->blockHeightLog2), - gbpBuffer->size); - } -} diff --git a/app/src/main/cpp/skyline/gpu/display.h b/app/src/main/cpp/skyline/gpu/display.h deleted file mode 100644 index 2e153af5..00000000 --- a/app/src/main/cpp/skyline/gpu/display.h +++ /dev/null @@ -1,171 +0,0 @@ -#pragma once - -#include -#include -#include -#include "parcel.h" - -namespace skyline::gpu { - /** - * @brief A struct that encapsulates a resolution - */ - struct Resolution { - u32 width; //!< The width component of the resolution - u32 height; //!< The height component of the resolution - - inline bool operator==(const Resolution &r) { - return (width == r.width) && (height == r.height); - } - - inline bool operator!=(const Resolution &r) { - return !operator==(r); - } - }; - - /** - * @brief An enumeration of all the possible display IDs (https://switchbrew.org/wiki/Display_services#DisplayName) - */ - enum class DisplayId : u64 { - Default, - External, - Edid, - Internal, - Null - }; - - /** - * @brief A mapping from a display's name to it's displayType entry - */ - static const std::unordered_map displayTypeMap{ - {"Default", DisplayId::Default}, - {"External", DisplayId::External}, - {"Edid", DisplayId::Edid}, - {"Internal", DisplayId::Internal}, - {"Null", DisplayId::Null}, - }; - - /** - * @brief The status of a specific layer - */ - enum class LayerStatus { - Uninitialized, - Initialized - }; - - /** - * @brief The status of a specific buffer - */ - enum class BufferStatus { - Free, - Dequeued, - Queued - }; - - /** - * @brief This struct holds information about the graphics buffer (https://github.com/reswitched/libtransistor/blob/0f0c36227842c344d163922fc98ee76229e9f0ee/lib/display/graphic_buffer_queue.c#L66) - */ - struct GbpBuffer { - u32 magic; //!< The magic of the graphics buffer: 0x47424652 - u32 width; //!< The width of the buffer - u32 height; //!< The height of the buffer - u32 stride; //!< The stride of the buffer - u32 format; //!< The format of the buffer, this corresponds to AHardwareBuffer_Format - u32 usage; //!< The usage flags for the buffer - u32 _pad0_; - u32 index; //!< The index of the buffer - u32 _pad1_[3]; - u32 nvmapId; //!< The ID of the buffer in regards to /dev/nvmap - u32 _pad2_[8]; - u32 size; //!< The size of the buffer - u32 _pad3_[8]; - u32 nvmapHandle; //!< The handle of the buffer in regards to /dev/nvmap - u32 offset; //!< This is the offset of the pixel data in the GPU Buffer - u32 _pad4_; - u32 blockHeightLog2; //!< The log2 of the block height - u32 _pad5_[58]; - }; - - /** - * @brief This represents conditions for the completion of an asynchronous graphics operation - */ - struct Fence { - u32 syncptId; //!< The ID of the syncpoint - u32 syncptValue; //!< The value of the syncpoint - }; - - /** - * @brief This holds the state and describes a single Buffer - */ - class Buffer { - public: - const DeviceState &state; //!< The state of the device - u32 slot; //!< The slot the buffer is in - u32 bpp; //!< The amount of bytes per pixel - Resolution resolution; //!< The resolution of this buffer - GbpBuffer gbpBuffer; //!< The information about the underlying buffer - BufferStatus status{BufferStatus::Free}; //!< The status of this buffer - std::shared_ptr nvBuffer{}; //!< A shared pointer to the buffer's nvmap object - - /** - * @param state The state of the device - * @param slot The slot this buffer is in - * @param gpBuffer The GbpBuffer object for this buffer - */ - Buffer(const DeviceState &state, u32 slot, GbpBuffer &gbpBuffer); - - /** - * @return The address of the buffer on the kernel - */ - u8* GetAddress(); - }; - - /** - * @brief This is used to manage and queue up all display buffers to be shown - */ - class BufferQueue { - private: - const DeviceState &state; //!< The state of the device - - public: - std::unordered_map> queue; //!< A vector of shared pointers to all the queued buffers - std::queue> displayQueue; //!< A queue of all the buffers to be posted to the display - - /** - * @param state The state of the device - */ - BufferQueue(const DeviceState &state); - - /** - * @brief This the GbpBuffer struct of the specified buffer - */ - void RequestBuffer(Parcel &in, Parcel &out); - - /** - * @brief This returns the slot of a free buffer - */ - void DequeueBuffer(Parcel &in, Parcel &out); - - /** - * @brief This queues a buffer to be displayed - */ - void QueueBuffer(Parcel &in, Parcel &out); - - /** - * @brief This removes a previously queued buffer - */ - void CancelBuffer(Parcel &parcel); - - /** - * @brief This adds a pre-existing buffer to the queue - */ - void SetPreallocatedBuffer(Parcel &parcel); - - /** - * @brief This frees a buffer which is currently queued - * @param slotNo The slot of the buffer - */ - inline void FreeBuffer(u32 slotNo) { - queue.at(slotNo)->status = BufferStatus::Free; - } - }; -} diff --git a/app/src/main/cpp/skyline/gpu/texture.cpp b/app/src/main/cpp/skyline/gpu/texture.cpp new file mode 100644 index 00000000..15c4cdfa --- /dev/null +++ b/app/src/main/cpp/skyline/gpu/texture.cpp @@ -0,0 +1,81 @@ +#include +#include +#include "texture.h" + +namespace skyline::gpu { + GuestTexture::GuestTexture(const DeviceState &state, u64 address, texture::Dimensions dimensions, texture::Format format, texture::TileMode tiling, texture::TileConfig layout) : state(state), address(address), dimensions(dimensions), format(format), tileMode(tiling), tileConfig(layout) {} + + Texture::Texture(const DeviceState &state, std::shared_ptr guest, texture::Dimensions dimensions, texture::Format format, texture::Swizzle swizzle) : state(state), guest(guest), dimensions(dimensions), format(format), swizzle(swizzle) { + SynchronizeHost(); + } + + void Texture::SynchronizeHost() { + auto texture = state.process->GetPointer(guest->address); + auto size = format.GetSize(dimensions); + backing.resize(size); + auto output = reinterpret_cast(backing.data()); + + if (guest->tileMode == texture::TileMode::Block) { + // Reference on Block-linear tiling: https://gist.github.com/PixelyIon/d9c35050af0ef5690566ca9f0965bc32 + constexpr auto sectorWidth = 16; // The width of a sector in bytes + constexpr auto sectorHeight = 2; // The height of a sector in lines + constexpr auto gobWidth = 64; // The width of a GOB in bytes + constexpr auto gobHeight = 8; // The height of a GOB in lines + + auto robHeight = gobHeight * guest->tileConfig.blockHeight; // The height of a single ROB (Row of Blocks) in lines + auto surfaceHeightRobs = utils::AlignUp(dimensions.height / format.blockHeight, robHeight) / robHeight; // The height of the surface in ROBs (Row Of Blocks) + auto robWidthBytes = utils::AlignUp((guest->tileConfig.surfaceWidth / format.blockWidth) * format.bpb, gobWidth); // The width of a ROB in bytes + auto robWidthBlocks = robWidthBytes / gobWidth; // The width of a ROB in blocks (and GOBs because block width == 1 on the Tegra X1) + auto robBytes = robWidthBytes * robHeight; // The size of a ROB in bytes + auto gobYOffset = robWidthBytes * gobHeight; // The offset of the next Y-axis GOB from the current one in linear space + + auto inputSector = texture; // The address of the input sector + auto outputRob = output; // The address of the output block + + for (u32 rob = 0; rob < surfaceHeightRobs; rob++) { // Every Surface contains `surfaceHeightRobs` ROBs + auto outputBlock = outputRob; // We iterate through a block independently of the ROB + for (u32 block = 0; block < robWidthBlocks; block++) { // Every ROB contains `surfaceWidthBlocks` Blocks + auto outputGob = outputBlock; // We iterate through a GOB independently of the block + for (u32 gobY = 0; gobY < guest->tileConfig.blockHeight; gobY++) { // Every Block contains `blockHeight` Y-axis GOBs + for (u32 index = 0; index < sectorWidth * sectorHeight; index++) { // Every Y-axis GOB contains `sectorWidth * sectorHeight` sectors + const u32 xT = ((index << 3) & 0b10000) | ((index << 1) & 0b100000); // Morton-Swizzle on the X-axis + const u32 yT = ((index >> 1) & 0b110) | (index & 0b1); // Morton-Swizzle on the Y-axis + std::memcpy(outputGob + (yT * robWidthBytes) + xT, inputSector, sectorWidth); + inputSector += sectorWidth; // `sectorWidth` bytes are of sequential image data + } + outputGob += gobYOffset; // Increment the output GOB to the next Y-axis GOB + } + outputBlock += gobWidth; // Increment the output block to the next block (As Block Width = 1 GOB Width) + } + outputRob += robBytes; // Increment the output block to the next ROB + } + } else if (guest->tileMode == texture::TileMode::Pitch) { + auto sizeLine = guest->format.GetSize(dimensions.width, 1); // The size of a single line of pixel data + auto sizeStride = guest->format.GetSize(guest->tileConfig.pitch, 1); // The size of a single stride of pixel data + + auto inputLine = texture; // The address of the input line + auto outputLine = output; // The address of the output line + + for (auto line = 0; line < dimensions.height; line++) { + std::memcpy(outputLine, inputLine, sizeLine); + inputLine += sizeStride; + outputLine += sizeLine; + } + } else if (guest->tileMode == texture::TileMode::Linear) { + std::memcpy(output, texture, size); + } + } + + PresentationTexture::PresentationTexture(const DeviceState &state, const std::shared_ptr &guest, const texture::Dimensions &dimensions, const texture::Format &format, const std::function &releaseCallback) : releaseCallback(releaseCallback), Texture(state, guest, dimensions, format, {}) {} + + i32 PresentationTexture::GetAndroidFormat() { + switch (format.vkFormat) { + case vk::Format::eR8G8B8A8Unorm: + return WINDOW_FORMAT_RGBA_8888; + case vk::Format::eR5G6B5UnormPack16: + return WINDOW_FORMAT_RGB_565; + default: + throw exception("GetAndroidFormat: Cannot find corresponding Android surface format"); + } + } +} diff --git a/app/src/main/cpp/skyline/gpu/texture.h b/app/src/main/cpp/skyline/gpu/texture.h new file mode 100644 index 00000000..96ead5eb --- /dev/null +++ b/app/src/main/cpp/skyline/gpu/texture.h @@ -0,0 +1,266 @@ +#pragma once + +#include +#include + +namespace skyline { + namespace service::hosbinder { + class IHOSBinderDriver; + } + namespace gpu { + namespace texture { + /* + * @brief This is used to hold the dimensions of a surface + */ + struct Dimensions { + u32 width; //!< The width of the surface + u32 height; //!< The height of the surface + u32 depth; //!< The depth of the surface + + constexpr Dimensions() : width(0), height(0), depth(0) {} + + constexpr Dimensions(u32 width, u32 height) : width(width), height(height), depth(1) {} + + constexpr Dimensions(u32 width, u32 height, u32 depth) : width(width), height(height), depth(depth) {} + + /** + * @return If the specified dimension is equal to this one + */ + constexpr inline bool operator==(const Dimensions &dimensions) { + return (width == dimensions.width) && (height == dimensions.height) && (depth == dimensions.depth); + } + + /** + * @return If the specified dimension is not equal to this one + */ + constexpr inline bool operator!=(const Dimensions &dimensions) { + return (width != dimensions.width) || (height != dimensions.height) || (depth != dimensions.depth); + } + }; + + /** + * @brief This is used to hold the attributes of a texture format + */ + struct Format { + u8 bpb; //!< Bytes Per Block, this is to accommodate compressed formats + u16 blockHeight; //!< The height of a single block + u16 blockWidth; //!< The width of a single block + vk::Format vkFormat; //!< The underlying Vulkan type of the format + + /** + * @return If this is a compressed texture format or not + */ + inline constexpr bool IsCompressed() { + return (blockHeight != 1) || (blockWidth != 1); + } + + /** + * @param width The width of the texture in pixels + * @param height The height of the texture in pixels + * @param depth The depth of the texture in layers + * @return The size of the texture in bytes + */ + inline constexpr size_t GetSize(u32 width, u32 height, u32 depth = 1) { + return (((width / blockWidth) * (height / blockHeight)) * bpb) * depth; + } + + /** + * @param dimensions The dimensions of a texture + * @return The size of the texture in bytes + */ + inline constexpr size_t GetSize(Dimensions dimensions) { + return GetSize(dimensions.width, dimensions.height, dimensions.depth); + } + + /** + * @return If the specified format is equal to this one + */ + inline constexpr bool operator==(const Format &format) { + return vkFormat == format.vkFormat; + } + + /** + * @return If the specified format is not equal to this one + */ + inline constexpr bool operator!=(const Format &format) { + return vkFormat != format.vkFormat; + } + + /** + * @return If this format is actually valid or not + */ + inline constexpr operator bool() { + return bpb; + } + }; + + namespace format { + constexpr Format RGBA8888Unorm{sizeof(u8) * 4, 1, 1, vk::Format::eR8G8B8A8Unorm}; //!< 8-bits per channel 4-channel pixels + constexpr Format RGB565Unorm{sizeof(u8) * 2, 1, 1, vk::Format::eR5G6B5UnormPack16}; //!< Red channel: 5-bit, Green channel: 6-bit, Blue channel: 5-bit + } + + /** + * @brief This describes the linearity of a texture. Refer to Chapter 20.1 of the Tegra X1 TRM for information. + */ + enum class TileMode { + Linear, //!< This is a purely linear texture + Pitch, //!< This is a pitch-linear texture + Block, //!< This is a 16Bx2 block-linear texture + }; + + /** + * @brief This holds the parameters of the tiling mode, covered in Table 76 in the Tegra X1 TRM + */ + union TileConfig { + struct { + u8 blockHeight; //!< The height of the blocks in GOBs + u8 blockDepth; //!< The depth of the blocks in GOBs + u16 surfaceWidth; //!< The width of a surface in samples + }; + u32 pitch; //!< The pitch of the texture if it's pitch linear + }; + + /** + * @brief This enumerates all of the channel swizzle options + */ + enum class SwizzleChannel { + Zero, //!< Write 0 to the channel + One, //!< Write 1 to the channel + Red, //!< Red color channel + Green, //!< Green color channel + Blue, //!< Blue color channel + Alpha, //!< Alpha channel + }; + + /** + * @brief This holds all of the texture swizzles on each color channel + */ + struct Swizzle { + SwizzleChannel red{SwizzleChannel::Red}; //!< Swizzle for the red channel + SwizzleChannel green{SwizzleChannel::Green}; //!< Swizzle for the green channel + SwizzleChannel blue{SwizzleChannel::Blue}; //!< Swizzle for the blue channel + SwizzleChannel alpha{SwizzleChannel::Alpha}; //!< Swizzle for the alpha channel + }; + } + + class Texture; + class PresentationTexture; + + /** + * @brief This class is used to hold metadata about a guest texture and can be used to create a host Texture object + */ + class GuestTexture : public std::enable_shared_from_this { + private: + const DeviceState &state; //!< The state of the device + + public: + u64 address; //!< The address of the texture in guest memory + std::shared_ptr host; //!< The corresponding host texture object + texture::Dimensions dimensions; //!< The dimensions of the texture + texture::Format format; //!< The format of the texture + texture::TileMode tileMode; //!< The tiling mode of the texture + texture::TileConfig tileConfig; //!< The tiling configuration of the texture + + GuestTexture(const DeviceState &state, u64 address, texture::Dimensions dimensions, texture::Format format, texture::TileMode tileMode = texture::TileMode::Linear, texture::TileConfig tileConfig = {}); + + inline constexpr size_t Size() { + return format.GetSize(dimensions); + } + + /** + * @brief This creates a corresponding host texture object for this guest texture + * @param format The format of the host texture (Defaults to the format of the guest texture) + * @param dimensions The dimensions of the host texture (Defaults to the dimensions of the host texture) + * @param swizzle The channel swizzle of the host texture (Defaults to no channel swizzling) + * @return A shared pointer to the host texture object + * @note There can only be one host texture for a corresponding guest texture + */ + std::shared_ptr InitializeTexture(std::optional format = std::nullopt, std::optional dimensions = std::nullopt, texture::Swizzle swizzle = {}) { + if (host) + throw exception("Trying to create multiple Texture objects from a single GuestTexture"); + host = std::make_shared(state, shared_from_this(), dimensions ? *dimensions : this->dimensions, format ? *format : this->format, swizzle); + return host; + } + + protected: + std::shared_ptr InitializePresentationTexture() { + if (host) + throw exception("Trying to create multiple PresentationTexture objects from a single GuestTexture"); + auto presentation = std::make_shared(state, shared_from_this(), dimensions, format); + host = std::static_pointer_cast(presentation); + return presentation; + } + + friend service::hosbinder::IHOSBinderDriver; + }; + + /** + * @brief This class is used to store a texture which is backed by host objects + */ + class Texture { + private: + const DeviceState &state; //!< The state of the device + + public: + std::vector backing; //!< The object that holds a host copy of the guest texture (Will be replaced with a vk::Image) + std::shared_ptr guest; //!< The corresponding guest texture object + texture::Dimensions dimensions; //!< The dimensions of the texture + texture::Format format; //!< The format of the host texture + texture::Swizzle swizzle; //!< The swizzle of the host texture + + public: + Texture(const DeviceState &state, std::shared_ptr guest, texture::Dimensions dimensions, texture::Format format, texture::Swizzle swizzle); + + public: + /** + * @brief This convert this texture to the specified tiling mode + * @param tileMode The tiling mode to convert it to + * @param tileConfig The configuration for the tiling mode (Can be default argument for Linear) + */ + void ConvertTileMode(texture::TileMode tileMode, texture::TileConfig tileConfig = {}); + + /** + * @brief This sets the texture dimensions to the specified ones (As long as they are within the GuestTexture's range) + * @param dimensions The dimensions to adjust the texture to + */ + void SetDimensions(texture::Dimensions dimensions); + + /** + * @brief This sets the format to the specified one + * @param format The format to change the texture to + */ + void SetFormat(texture::Format format); + + /** + * @brief This sets the channel swizzle to the specified one + * @param swizzle The channel swizzle to the change the texture to + */ + void SetSwizzle(texture::Swizzle swizzle); + + /** + * @brief This synchronizes the host texture with the guest after it has been modified + */ + void SynchronizeHost(); + + /** + * @brief This synchronizes the guest texture with the host texture after it has been modified + */ + void SynchronizeGuest(); + }; + + /** + * @brief This class is used to hold a texture object alongside a release callback used for display presentation + */ + class PresentationTexture : public Texture { + public: + std::function releaseCallback; //!< The release callback after this texture has been displayed + + PresentationTexture(const DeviceState &state, const std::shared_ptr &guest, const texture::Dimensions &dimensions, const texture::Format &format, const std::function &releaseCallback = {}); + + /** + * @return The corresponding Android surface format for the current texture format + */ + i32 GetAndroidFormat(); + }; + } +} 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 d3129e26..c12cc851 100644 --- a/app/src/main/cpp/skyline/services/am/controller/ISelfController.cpp +++ b/app/src/main/cpp/skyline/services/am/controller/ISelfController.cpp @@ -1,4 +1,6 @@ -#include +#include +#include +#include #include "ISelfController.h" namespace skyline::service::am { @@ -19,10 +21,11 @@ namespace skyline::service::am { void ISelfController::SetOutOfFocusSuspendingEnabled(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {} void ISelfController::CreateManagedDisplayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - state.logger->Debug("Creating Managed Layer"); - if (state.gpu->layerStatus == gpu::LayerStatus::Initialized) + state.logger->Debug("Creating Managed Layer on Default Display"); + auto hosBinder = state.os->serviceManager.GetService(Service::hosbinder_IHOSBinderDriver); + if (hosBinder->layerStatus != hosbinder::LayerStatus::Uninitialized) throw exception("The application is creating more than one layer"); - state.gpu->layerStatus = gpu::LayerStatus::Initialized; + hosBinder->layerStatus = hosbinder::LayerStatus::Managed; response.Push(0); } } diff --git a/app/src/main/cpp/skyline/services/audio/IAudioOut.cpp b/app/src/main/cpp/skyline/services/audio/IAudioOut.cpp index 7e403e76..dd3f2f4f 100644 --- a/app/src/main/cpp/skyline/services/audio/IAudioOut.cpp +++ b/app/src/main/cpp/skyline/services/audio/IAudioOut.cpp @@ -52,7 +52,7 @@ namespace skyline::service::audio { void IAudioOut::RegisterBufferEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { auto handle = state.process->InsertItem(releaseEvent); - state.logger->Debug("Audout Buffer Release Event Handle: 0x{:X}", handle); + state.logger->Debug("IAudioOut: Buffer Release Event Handle: 0x{:X}", handle); response.copyHandles.push_back(handle); } diff --git a/app/src/main/cpp/skyline/services/base_service.h b/app/src/main/cpp/skyline/services/base_service.h index 7b5c9028..308ee83f 100644 --- a/app/src/main/cpp/skyline/services/base_service.h +++ b/app/src/main/cpp/skyline/services/base_service.h @@ -49,12 +49,12 @@ namespace skyline::service { timesrv_ITimeZoneService, fssrv_IFileSystemProxy, fssrv_IFileSystem, - nvdrv, - vi_m, - vi_IApplicationDisplayService, - vi_ISystemDisplayService, - vi_IManagerDisplayService, - nvnflinger_dispdrv, + nvdrv_INvDrvServices, + visrv_IManagerRootService, + visrv_IApplicationDisplayService, + visrv_ISystemDisplayService, + visrv_IManagerDisplayService, + hosbinder_IHOSBinderDriver, }; /** @@ -73,11 +73,11 @@ namespace skyline::service { {"time:a", Service::timesrv_IStaticService}, {"time:u", Service::timesrv_IStaticService}, {"fsp-srv", Service::fssrv_IFileSystemProxy}, - {"nvdrv", Service::nvdrv}, - {"nvdrv:a", Service::nvdrv}, - {"nvdrv:s", Service::nvdrv}, - {"nvdrv:t", Service::nvdrv}, - {"vi:m", Service::vi_m}, + {"nvdrv", Service::nvdrv_INvDrvServices}, + {"nvdrv:a", Service::nvdrv_INvDrvServices}, + {"nvdrv:s", Service::nvdrv_INvDrvServices}, + {"nvdrv:t", Service::nvdrv_INvDrvServices}, + {"vi:m", Service::visrv_IManagerRootService}, }; class ServiceManager; diff --git a/app/src/main/cpp/skyline/gpu/parcel.cpp b/app/src/main/cpp/skyline/services/common/parcel.cpp similarity index 98% rename from app/src/main/cpp/skyline/gpu/parcel.cpp rename to app/src/main/cpp/skyline/services/common/parcel.cpp index a624c583..1150fdc7 100644 --- a/app/src/main/cpp/skyline/gpu/parcel.cpp +++ b/app/src/main/cpp/skyline/services/common/parcel.cpp @@ -2,7 +2,7 @@ #include #include -namespace skyline::gpu { +namespace skyline::service { Parcel::Parcel(kernel::ipc::InputBuffer &buffer, const DeviceState &state) : Parcel(buffer.address, buffer.size, state) {} Parcel::Parcel(u64 address, u64 size, const DeviceState &state) : state(state) { diff --git a/app/src/main/cpp/skyline/gpu/parcel.h b/app/src/main/cpp/skyline/services/common/parcel.h similarity index 99% rename from app/src/main/cpp/skyline/gpu/parcel.h rename to app/src/main/cpp/skyline/services/common/parcel.h index 09de3265..c7ff5d5a 100644 --- a/app/src/main/cpp/skyline/gpu/parcel.h +++ b/app/src/main/cpp/skyline/services/common/parcel.h @@ -3,7 +3,7 @@ #include #include -namespace skyline::gpu { +namespace skyline::service { /** * @brief This class encapsulates a Parcel object (https://switchbrew.org/wiki/Display_services#Parcel) */ diff --git a/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.cpp b/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.cpp new file mode 100644 index 00000000..0e636a31 --- /dev/null +++ b/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.cpp @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include "IHOSBinderDriver.h" +#include "display.h" + +namespace skyline::service::hosbinder { + IHOSBinderDriver::IHOSBinderDriver(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::hosbinder_IHOSBinderDriver, "hosbinder_IHOSBinderDriver", { + {0x0, SFUNC(IHOSBinderDriver::TransactParcel)}, + {0x1, SFUNC(IHOSBinderDriver::AdjustRefcount)}, + {0x2, SFUNC(IHOSBinderDriver::GetNativeHandle)}, + {0x3, SFUNC(IHOSBinderDriver::TransactParcel)} + }) {} + + void IHOSBinderDriver::RequestBuffer(Parcel &in, Parcel &out) { + u32 slot = *reinterpret_cast(in.data.data() + constant::TokenLength); + out.WriteData(1); + out.WriteData(sizeof(GbpBuffer)); + out.WriteData(0); + out.WriteData(queue.at(slot)->gbpBuffer); + state.logger->Debug("RequestBuffer: Slot: {}", slot, sizeof(GbpBuffer)); + } + + void IHOSBinderDriver::DequeueBuffer(Parcel &in, Parcel &out) { + struct Data { + u32 format; + u32 width; + u32 height; + u32 timestamps; + u32 usage; + } *data = reinterpret_cast(in.data.data() + constant::TokenLength); + + i64 slot{-1}; + while (slot == -1) { + for (auto &buffer : queue) { + if (buffer.second->status == BufferStatus::Free && buffer.second->gbpBuffer.width == data->width && buffer.second->gbpBuffer.height == data->height && buffer.second->gbpBuffer.usage == data->usage) { + slot = buffer.first; + buffer.second->status = BufferStatus::Dequeued; + break; + } + } + asm("yield"); + } + + struct { + u32 slot; + u32 _unk_[13]; + } output{ + .slot = static_cast(slot) + }; + out.WriteData(output); + + state.logger->Debug("DequeueBuffer: Width: {}, Height: {}, Format: {}, Usage: {}, Timestamps: {}, Slot: {}", data->width, data->height, data->format, data->usage, data->timestamps, slot); + } + + void IHOSBinderDriver::QueueBuffer(Parcel &in, Parcel &out) { + struct Data { + u32 slot; + u64 timestamp; + u32 autoTimestamp; + ARect crop; + u32 scalingMode; + u32 transform; + u32 stickyTransform; + u64 _unk0_; + u32 swapInterval; + Fence fence[4]; + } *data = reinterpret_cast(in.data.data() + constant::TokenLength); + + auto buffer = queue.at(data->slot); + buffer->status = BufferStatus::Queued; + + auto slot = data->slot; + auto bufferEvent = state.gpu->bufferEvent; + buffer->texture->releaseCallback = [this, slot, bufferEvent]() { + FreeBuffer(slot); + bufferEvent->Signal(); + }; + + buffer->texture->SynchronizeHost(); + + state.gpu->presentationQueue.push(buffer->texture); + + struct { + u32 width; + u32 height; + u32 _pad0_[3]; + } output{ + .width = buffer->gbpBuffer.width, + .height = buffer->gbpBuffer.height + }; + + out.WriteData(output); + + state.logger->Debug("QueueBuffer: Timestamp: {}, Auto Timestamp: {}, Crop: [T: {}, B: {}, L: {}, R: {}], Scaling Mode: {}, Transform: {}, Sticky Transform: {}, Swap Interval: {}, Slot: {}", data->timestamp, data->autoTimestamp, data->crop.top, data->crop.bottom, data->crop.left, data->crop.right, data->scalingMode, data->transform, data->stickyTransform, data->swapInterval, data->slot); + } + + void IHOSBinderDriver::CancelBuffer(Parcel &parcel) { + struct Data { + u32 slot; + Fence fence[4]; + } *data = reinterpret_cast(parcel.data.data() + constant::TokenLength); + FreeBuffer(data->slot); + state.logger->Debug("CancelBuffer: Slot: {}", data->slot); + } + + void IHOSBinderDriver::SetPreallocatedBuffer(Parcel &parcel) { + auto pointer = parcel.data.data() + constant::TokenLength; + struct Data { + u32 slot; + u32 _unk0_; + u32 length; + u32 _pad0_; + } *data = reinterpret_cast(pointer); + + pointer += sizeof(Data); + auto gbpBuffer = reinterpret_cast(pointer); + + std::shared_ptr nvBuffer{}; + auto nvmap = state.os->serviceManager.GetService(Service::nvdrv_INvDrvServices)->GetDevice(nvdrv::device::NvDeviceType::nvmap); + + if (gbpBuffer->nvmapHandle) + nvBuffer = nvmap->handleTable.at(gbpBuffer->nvmapHandle); + else { + for (const auto &object : nvmap->handleTable) { + if (object.second->id == gbpBuffer->nvmapId) { + nvBuffer = object.second; + break; + } + } + if (!nvBuffer) + throw exception("A QueueBuffer request has an invalid NVMap Handle ({}) and ID ({})", gbpBuffer->nvmapHandle, gbpBuffer->nvmapId); + } + + gpu::texture::Format format; + switch (gbpBuffer->format) { + case WINDOW_FORMAT_RGBA_8888: + case WINDOW_FORMAT_RGBX_8888: + format = gpu::texture::format::RGBA8888Unorm; + break; + case WINDOW_FORMAT_RGB_565: + format = gpu::texture::format::RGB565Unorm; + break; + default: + throw exception("Unknown pixel format used for FB"); + } + + auto texture = std::make_shared(state, nvBuffer->address + gbpBuffer->offset, gpu::texture::Dimensions(gbpBuffer->width, gbpBuffer->height), format, gpu::texture::TileMode::Block, gpu::texture::TileConfig{.surfaceWidth = static_cast(gbpBuffer->stride), .blockHeight = static_cast(1U << gbpBuffer->blockHeightLog2), .blockDepth = 1}); + + queue[data->slot] = std::make_shared(data->slot, *gbpBuffer, texture->InitializePresentationTexture()); + state.gpu->bufferEvent->Signal(); + + state.logger->Debug("SetPreallocatedBuffer: Slot: {}, Magic: 0x{:X}, Width: {}, Height: {}, Stride: {}, Format: {}, Usage: {}, Index: {}, ID: {}, Handle: {}, Offset: 0x{:X}, Block Height: {}, Size: 0x{:X}", data->slot, gbpBuffer->magic, gbpBuffer->width, gbpBuffer->height, gbpBuffer->stride, gbpBuffer->format, gbpBuffer->usage, gbpBuffer->index, gbpBuffer->nvmapId, + gbpBuffer->nvmapHandle, gbpBuffer->offset, (1U << gbpBuffer->blockHeightLog2), gbpBuffer->size); + } + + void IHOSBinderDriver::TransactParcel(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto layerId = request.Pop(); + auto code = request.Pop(); + + Parcel in(request.inputBuf.at(0), state); + Parcel out(state); + + state.logger->Debug("TransactParcel: Layer ID: {}, Code: {}", layerId, code); + + switch (code) { + case TransactionCode::RequestBuffer: + RequestBuffer(in, out); + break; + case TransactionCode::DequeueBuffer: + DequeueBuffer(in, out); + break; + case TransactionCode::QueueBuffer: + QueueBuffer(in, out); + break; + case TransactionCode::CancelBuffer: + CancelBuffer(in); + break; + case TransactionCode::Query: + out.WriteData(0); + break; + case TransactionCode::Connect: { + ConnectParcel connect = { + .width = constant::HandheldResolutionW, + .height = constant::HandheldResolutionH, + .transformHint = 0, + .pendingBuffers = 0, + .status = constant::status::Success, + }; + out.WriteData(connect); + break; + } + case TransactionCode::Disconnect: + break; + case TransactionCode::SetPreallocatedBuffer: + SetPreallocatedBuffer(in); + break; + default: + throw exception("An unimplemented transaction was called: {}", static_cast(code)); + } + + out.WriteParcel(request.outputBuf.at(0)); + } + + void IHOSBinderDriver::AdjustRefcount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + request.Skip(); + auto addVal = request.Pop(); + auto type = request.Pop(); + state.logger->Debug("Reference Change: {} {} reference", addVal, type ? "strong" : "weak"); + } + + void IHOSBinderDriver::GetNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + handle_t handle = state.process->InsertItem(state.gpu->bufferEvent); + state.logger->Debug("Display Buffer Event Handle: 0x{:X}", handle); + response.copyHandles.push_back(handle); + response.Push(constant::status::Success); + } + + void IHOSBinderDriver::SetDisplay(const std::string &name) { + try { + const auto type = displayTypeMap.at(name); + if (displayId == DisplayId::Null) + displayId = type; + else + throw exception("Trying to change display type from non-null type"); + } catch (const std::out_of_range &) { + throw exception("The display with name: '{}' doesn't exist", name); + } + } + + void IHOSBinderDriver::CloseDisplay() { + if (displayId == DisplayId::Null) + state.logger->Warn("Trying to close uninitiated display"); + displayId = DisplayId::Null; + } +} diff --git a/app/src/main/cpp/skyline/services/nvnflinger/dispdrv.h b/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.h similarity index 63% rename from app/src/main/cpp/skyline/services/nvnflinger/dispdrv.h rename to app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.h index 098f0c1e..d1496b06 100644 --- a/app/src/main/cpp/skyline/services/nvnflinger/dispdrv.h +++ b/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.h @@ -1,10 +1,12 @@ #pragma once -#include "services/base_service.h" -#include "services/serviceman.h" -#include +#include +#include +#include +#include "buffer.h" +#include "display.h" -namespace skyline::service::nvnflinger { +namespace skyline::service::hosbinder { /** * @brief This enumerates the functions called by TransactParcel for android.gui.IGraphicBufferProducer * @refitem https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#35 @@ -26,10 +28,18 @@ namespace skyline::service::nvnflinger { SetPreallocatedBuffer = 14, //!< No source on this but it's used to set a existing buffer according to libtransistor and libNX }; + /** + * @brief This represents conditions for the completion of an asynchronous graphics operation + */ + struct Fence { + u32 syncptId; //!< The ID of the syncpoint + u32 syncptValue; //!< The value of the syncpoint + }; + /** * @brief nvnflinger:dispdrv or nns::hosbinder::IHOSBinderDriver is responsible for writing buffers to the display */ - class dispdrv : public BaseService { + class IHOSBinderDriver : public BaseService { private: /** * @brief This is the structure of the parcel used for TransactionCode::Connect @@ -42,8 +52,45 @@ namespace skyline::service::nvnflinger { u32 status; //!< The status of the buffer queue }; + std::unordered_map> queue; //!< A vector of shared pointers to all the queued buffers + /** + * @brief This the GbpBuffer struct of the specified buffer + */ + void RequestBuffer(Parcel &in, Parcel &out); + + /** + * @brief This returns the slot of a free buffer + */ + void DequeueBuffer(Parcel &in, Parcel &out); + + /** + * @brief This queues a buffer to be displayed + */ + void QueueBuffer(Parcel &in, Parcel &out); + + /** + * @brief This removes a previously queued buffer + */ + void CancelBuffer(Parcel &parcel); + + /** + * @brief This adds a pre-existing buffer to the queue + */ + void SetPreallocatedBuffer(Parcel &parcel); + + /** + * @brief This frees a buffer which is currently queued + * @param slotNo The slot of the buffer + */ + inline void FreeBuffer(u32 slotNo) { + queue.at(slotNo)->status = BufferStatus::Free; + } + public: - dispdrv(const DeviceState &state, ServiceManager &manager); + DisplayId displayId{DisplayId::Null}; //!< The ID of this display + LayerStatus layerStatus{LayerStatus::Uninitialized}; //!< This is the status of the single layer the display has + + IHOSBinderDriver(const DeviceState &state, ServiceManager &manager); /** * @brief This emulates the transaction of parcels between a IGraphicBufferProducer and the application (https://switchbrew.org/wiki/Nvnflinger_services#TransactParcel) @@ -59,5 +106,17 @@ namespace skyline::service::nvnflinger { * @brief This adjusts the reference counts to the underlying binder, it is stubbed as we aren't using the real symbols (https://switchbrew.org/wiki/Nvnflinger_services#GetNativeHandle) */ void GetNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This sets displayId to a specific display type + * @param name The name of the display + * @note displayId has to be DisplayId::Null or this will throw an exception + */ + void SetDisplay(const std::string &name); + + /** + * @brief This closes the display by setting displayId to DisplayId::Null + */ + void CloseDisplay(); }; } diff --git a/app/src/main/cpp/skyline/services/hosbinder/buffer.h b/app/src/main/cpp/skyline/services/hosbinder/buffer.h new file mode 100644 index 00000000..522704b7 --- /dev/null +++ b/app/src/main/cpp/skyline/services/hosbinder/buffer.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include + +namespace skyline::service::hosbinder { + /** + * @brief This struct holds information about the graphics buffer (https://github.com/reswitched/libtransistor/blob/0f0c36227842c344d163922fc98ee76229e9f0ee/lib/display/graphic_buffer_queue.c#L66) + */ + struct GbpBuffer { + u32 magic; //!< The magic of the graphics buffer: 0x47424652 + u32 width; //!< The width of the buffer + u32 height; //!< The height of the buffer + u32 stride; //!< The stride of the buffer + u32 format; //!< The format of the buffer, this corresponds to AHardwareBuffer_Format + u32 usage; //!< The usage flags for the buffer + u32 _pad0_; + u32 index; //!< The index of the buffer + u32 _pad1_[3]; + u32 nvmapId; //!< The ID of the buffer in regards to /dev/nvmap + u32 _pad2_[8]; + u32 size; //!< The size of the buffer + u32 _pad3_[8]; + u32 nvmapHandle; //!< The handle of the buffer in regards to /dev/nvmap + u32 offset; //!< This is the offset of the pixel data in the GPU Buffer + u32 _pad4_; + u32 blockHeightLog2; //!< The log2 of the block height + u32 _pad5_[58]; + }; + + /** + * @brief The current status of a buffer + */ + enum class BufferStatus { + Free, //!< The buffer is free + Dequeued, //!< The buffer has been dequeued from the display + Queued, //!< The buffer is queued to be displayed + }; + + /** + * @brief This holds all relevant objects for a specific buffer + */ + class Buffer { + public: + u32 slot; //!< The slot the buffer is in + BufferStatus status{BufferStatus::Free}; //!< The status of this buffer + std::shared_ptr texture{}; //!< The underlying PresentationTexture of this buffer + GbpBuffer gbpBuffer; //!< The GbpBuffer object for this buffer + + Buffer(u32 slot, const GbpBuffer &gbpBuffer, const std::shared_ptr &texture) : slot(slot), gbpBuffer(gbpBuffer), texture(texture) {}; + }; +} diff --git a/app/src/main/cpp/skyline/services/hosbinder/display.h b/app/src/main/cpp/skyline/services/hosbinder/display.h new file mode 100644 index 00000000..e99ee5ea --- /dev/null +++ b/app/src/main/cpp/skyline/services/hosbinder/display.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +namespace skyline::service::hosbinder { + /** + * @brief An enumeration of all the possible display IDs (https://switchbrew.org/wiki/Display_services#DisplayName) + */ + enum class DisplayId : u64 { + Default, //!< Refers to the default display used by most applications + External, //!< Refers to an external display + Edid, //!< Refers to an external display with EDID capabilities + Internal, //!< Refers to the the internal display + Null, //!< Refers to the null display which is used for discarding data + }; + + /** + * @brief A mapping from a display's name to it's displayType entry + */ + static const std::unordered_map displayTypeMap{ + {"Default", DisplayId::Default}, + {"External", DisplayId::External}, + {"Edid", DisplayId::Edid}, + {"Internal", DisplayId::Internal}, + {"Null", DisplayId::Null}, + }; + + /** + * @brief The status of a display layer + */ + enum class LayerStatus { + Uninitialized, //!< The layer hasn't been initialized + Stray, //!< The layer has been initialized as a stray layer + Managed, //!< The layer has been initialized as a managed layer + }; +} diff --git a/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.cpp b/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.cpp new file mode 100644 index 00000000..b97dc1f4 --- /dev/null +++ b/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.cpp @@ -0,0 +1,118 @@ +#include "INvDrvServices.h" +#include +#include "devices/nvhost_ctrl.h" +#include "devices/nvhost_ctrl_gpu.h" +#include "devices/nvhost_channel.h" +#include "devices/nvhost_as_gpu.h" + +namespace skyline::service::nvdrv { + u32 INvDrvServices::OpenDevice(const std::string &path) { + state.logger->Debug("Opening NVDRV device ({}): {}", fdIndex, path); + auto type = device::nvDeviceMap.at(path); + for (const auto &device : fdMap) { + if (device.second->deviceType == type) { + device.second->refCount++; + fdMap[fdIndex] = device.second; + return fdIndex++; + } + } + + std::shared_ptr object; + switch (type) { + case (device::NvDeviceType::nvhost_ctrl): + object = std::make_shared(state); + break; + case (device::NvDeviceType::nvhost_gpu): + case (device::NvDeviceType::nvhost_vic): + case (device::NvDeviceType::nvhost_nvdec): + object = std::make_shared(state, type); + break; + case (device::NvDeviceType::nvhost_ctrl_gpu): + object = std::make_shared(state); + break; + case (device::NvDeviceType::nvmap): + object = std::make_shared(state); + break; + case (device::NvDeviceType::nvhost_as_gpu): + object = std::make_shared(state); + break; + default: + throw exception("Cannot find NVDRV device"); + } + deviceMap[type] = object; + fdMap[fdIndex] = object; + return fdIndex++; + } + + INvDrvServices::INvDrvServices(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::nvdrv_INvDrvServices, "INvDrvServices", { + {0x0, SFUNC(INvDrvServices::Open)}, + {0x1, SFUNC(INvDrvServices::Ioctl)}, + {0x2, SFUNC(INvDrvServices::Close)}, + {0x3, SFUNC(INvDrvServices::Initialize)}, + {0x4, SFUNC(INvDrvServices::QueryEvent)}, + {0x8, SFUNC(INvDrvServices::SetAruidByPID)} + }) {} + + void INvDrvServices::Open(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto buffer = request.inputBuf.at(0); + auto path = state.process->GetString(buffer.address, buffer.size); + response.Push(OpenDevice(path)); + response.Push(constant::status::Success); + } + + void INvDrvServices::Ioctl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto fd = request.Pop(); + auto cmd = request.Pop(); + state.logger->Debug("IOCTL on device: 0x{:X}, cmd: 0x{:X}", fd, cmd); + try { + if (request.inputBuf.empty() || request.outputBuf.empty()) { + if (request.inputBuf.empty()) { + device::IoctlData data(request.outputBuf.at(0)); + fdMap.at(fd)->HandleIoctl(cmd, data); + response.Push(data.status); + } else { + device::IoctlData data(request.inputBuf.at(0)); + fdMap.at(fd)->HandleIoctl(cmd, data); + response.Push(data.status); + } + } else { + device::IoctlData data(request.inputBuf.at(0), request.outputBuf.at(0)); + fdMap.at(fd)->HandleIoctl(cmd, data); + response.Push(data.status); + } + } catch (const std::out_of_range &) { + throw exception("IOCTL was requested on an invalid file descriptor"); + } + } + + void INvDrvServices::Close(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + u32 fd = *reinterpret_cast(request.cmdArg); + state.logger->Debug("Closing NVDRV device ({})", fd); + try { + auto device = fdMap.at(fd); + if (!--device->refCount) + deviceMap.erase(device->deviceType); + fdMap.erase(fd); + } catch (const std::out_of_range &) { + state.logger->Warn("Trying to close non-existent FD"); + } + response.Push(constant::status::Success); + } + + void INvDrvServices::Initialize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + response.Push(constant::status::Success); + } + + void INvDrvServices::QueryEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto fd = request.Pop(); + auto eventId = request.Pop(); + auto event = std::make_shared(state); + auto handle = state.process->InsertItem(event); + state.logger->Debug("QueryEvent: FD: {}, Event ID: {}, Handle: {}", fd, eventId, handle); + response.copyHandles.push_back(handle); + } + + void INvDrvServices::SetAruidByPID(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + response.Push(constant::status::Success); + } +} diff --git a/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h b/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h new file mode 100644 index 00000000..d50a7b47 --- /dev/null +++ b/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h @@ -0,0 +1,91 @@ +#pragma once + +#include +#include +#include +#include +#include "devices/nvdevice.h" + +namespace skyline::service::nvdrv { + /** + * @brief nvdrv or INvDrvServices is used to access the Nvidia GPU inside the Switch (https://switchbrew.org/wiki/NV_services#nvdrv.2C_nvdrv:a.2C_nvdrv:s.2C_nvdrv:t) + */ + class INvDrvServices : public BaseService { + private: + std::unordered_map> deviceMap; //!< A map from a NvDeviceType to the NvDevice object + std::unordered_map> fdMap; //!< A map from an FD to a shared pointer to it's NvDevice object + u32 fdIndex{}; //!< Holds the index of a file descriptor + + /** + * @brief Open a specific device and return a FD + * @param path The path of the device to open an FD to + * @return The file descriptor to the device + */ + u32 OpenDevice(const std::string &path); + + /** + * @brief Returns a particular device with a specific FD + * @tparam objectClass The class of the device to return + * @param fd The file descriptor to retrieve + * @return A shared pointer to the device + */ + template + std::shared_ptr GetDevice(u32 fd) { + try { + auto item = fdMap.at(fd); + return std::static_pointer_cast(item); + } catch (std::out_of_range) { + throw exception("GetDevice was called with invalid file descriptor: 0x{:X}", fd); + } + } + + public: + /** + * @brief Returns a particular device with a specific type + * @tparam objectClass The class of the device to return + * @param type The type of the device to return + * @return A shared pointer to the device + */ + template + std::shared_ptr GetDevice(device::NvDeviceType type) { + try { + auto item = deviceMap.at(type); + return std::static_pointer_cast(item); + } catch (std::out_of_range) { + throw exception("GetDevice was called with invalid type: 0x{:X}", type); + } + } + + INvDrvServices(const DeviceState &state, ServiceManager &manager); + + /** + * @brief Open a specific device and return a FD (https://switchbrew.org/wiki/NV_services#Open) + */ + void Open(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Close the specified FD (https://switchbrew.org/wiki/NV_services#Close) + */ + void Ioctl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Close the specified FD (https://switchbrew.org/wiki/NV_services#Close) + */ + void Close(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This initializes the driver (https://switchbrew.org/wiki/NV_services#Initialize) + */ + void Initialize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This returns a specific event from a device (https://switchbrew.org/wiki/NV_services#QueryEvent) + */ + void QueryEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This sets the AppletResourceUserId which matches the PID (https://switchbrew.org/wiki/NV_services#SetAruidByPID) + */ + void SetAruidByPID(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + }; +} diff --git a/app/src/main/cpp/skyline/gpu/devices/nvdevice.h b/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.h similarity index 99% rename from app/src/main/cpp/skyline/gpu/devices/nvdevice.h rename to app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.h index 0a6428ac..079fb7ed 100644 --- a/app/src/main/cpp/skyline/gpu/devices/nvdevice.h +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.h @@ -5,7 +5,7 @@ #define NFUNC(function) std::bind(&function, this, std::placeholders::_1) -namespace skyline::gpu::device { +namespace skyline::service::nvdrv::device { /** * @brief An enumeration of all the devices that can be opened by nvdrv */ diff --git a/app/src/main/cpp/skyline/gpu/devices/nvhost_as_gpu.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp similarity index 75% rename from app/src/main/cpp/skyline/gpu/devices/nvhost_as_gpu.cpp rename to app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp index de427864..5b61cdd8 100644 --- a/app/src/main/cpp/skyline/gpu/devices/nvhost_as_gpu.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp @@ -1,5 +1,5 @@ #include "nvhost_as_gpu.h" -namespace skyline::gpu::device { +namespace skyline::service::nvdrv::device { NvHostAsGpu::NvHostAsGpu(const DeviceState &state) : NvDevice(state, NvDeviceType::nvhost_as_gpu, {}) {} } diff --git a/app/src/main/cpp/skyline/gpu/devices/nvhost_as_gpu.h b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.h similarity index 87% rename from app/src/main/cpp/skyline/gpu/devices/nvhost_as_gpu.h rename to app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.h index 8e596da1..5f41275e 100644 --- a/app/src/main/cpp/skyline/gpu/devices/nvhost_as_gpu.h +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.h @@ -2,7 +2,7 @@ #include "nvdevice.h" -namespace skyline::gpu::device { +namespace skyline::service::nvdrv::device { /** * @brief NvHostAsGpu (/dev/nvhost-as-gpu) is used to access GPU virtual address spaces (https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-as-gpu) */ diff --git a/app/src/main/cpp/skyline/gpu/devices/nvhost_channel.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.cpp similarity index 67% rename from app/src/main/cpp/skyline/gpu/devices/nvhost_channel.cpp rename to app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.cpp index 48c3f482..4cd4bc43 100644 --- a/app/src/main/cpp/skyline/gpu/devices/nvhost_channel.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.cpp @@ -1,7 +1,7 @@ #include "nvhost_channel.h" #include -namespace skyline::gpu::device { +namespace skyline::service::nvdrv::device { NvHostChannel::NvHostChannel(const DeviceState &state, NvDeviceType type) : NvDevice(state, type, { {0x40044801, NFUNC(NvHostChannel::SetNvmapFd)}, {0xC0104809, NFUNC(NvHostChannel::AllocObjCtx)}, @@ -12,15 +12,15 @@ namespace skyline::gpu::device { {0x40084714, NFUNC(NvHostChannel::SetUserData)} }) {} - void NvHostChannel::SetNvmapFd(skyline::gpu::device::IoctlData &buffer) {} + void NvHostChannel::SetNvmapFd(IoctlData &buffer) {} - void NvHostChannel::AllocObjCtx(skyline::gpu::device::IoctlData &buffer) {} + void NvHostChannel::AllocObjCtx(IoctlData &buffer) {} void NvHostChannel::ZcullBind(IoctlData &buffer) {} - void NvHostChannel::SetErrorNotifier(skyline::gpu::device::IoctlData &buffer) {} + void NvHostChannel::SetErrorNotifier(IoctlData &buffer) {} - void NvHostChannel::SetPriority(skyline::gpu::device::IoctlData &buffer) { + void NvHostChannel::SetPriority(IoctlData &buffer) { auto priority = state.process->GetObject(buffer.input[0].address); switch (priority) { case NvChannelPriority::Low: @@ -35,8 +35,8 @@ namespace skyline::gpu::device { } } - void NvHostChannel::AllocGpfifoEx2(skyline::gpu::device::IoctlData &buffer) {} + void NvHostChannel::AllocGpfifoEx2(IoctlData &buffer) {} - void NvHostChannel::SetUserData(skyline::gpu::device::IoctlData &buffer) {} + void NvHostChannel::SetUserData(IoctlData &buffer) {} } diff --git a/app/src/main/cpp/skyline/gpu/devices/nvhost_channel.h b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.h similarity index 97% rename from app/src/main/cpp/skyline/gpu/devices/nvhost_channel.h rename to app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.h index f9939264..671ba2e5 100644 --- a/app/src/main/cpp/skyline/gpu/devices/nvhost_channel.h +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.h @@ -2,7 +2,7 @@ #include "nvdevice.h" -namespace skyline::gpu::device { +namespace skyline::service::nvdrv::device { /** * @brief NvHostChannel is used as a common interface for all Channel devices (https://switchbrew.org/wiki/NV_services#Channels) */ diff --git a/app/src/main/cpp/skyline/gpu/devices/nvhost_ctrl.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.cpp similarity index 75% rename from app/src/main/cpp/skyline/gpu/devices/nvhost_ctrl.cpp rename to app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.cpp index e38a04a9..a0f9db8a 100644 --- a/app/src/main/cpp/skyline/gpu/devices/nvhost_ctrl.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.cpp @@ -1,5 +1,5 @@ #include "nvhost_ctrl.h" -namespace skyline::gpu::device { +namespace skyline::service::nvdrv::device { NvHostCtrl::NvHostCtrl(const DeviceState &state) : NvDevice(state, NvDeviceType::nvhost_ctrl, {}) {} } diff --git a/app/src/main/cpp/skyline/gpu/devices/nvhost_ctrl.h b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.h similarity index 87% rename from app/src/main/cpp/skyline/gpu/devices/nvhost_ctrl.h rename to app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.h index e7422905..0ba838b3 100644 --- a/app/src/main/cpp/skyline/gpu/devices/nvhost_ctrl.h +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.h @@ -2,7 +2,7 @@ #include "nvdevice.h" -namespace skyline::gpu::device { +namespace skyline::service::nvdrv::device { /** * @brief NvHostCtrl (/dev/nvhost-ctrl) is used for GPU synchronization (https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-ctrl) */ diff --git a/app/src/main/cpp/skyline/gpu/devices/nvhost_ctrl_gpu.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl_gpu.cpp similarity index 98% rename from app/src/main/cpp/skyline/gpu/devices/nvhost_ctrl_gpu.cpp rename to app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl_gpu.cpp index a2660621..b82ef629 100644 --- a/app/src/main/cpp/skyline/gpu/devices/nvhost_ctrl_gpu.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -1,7 +1,7 @@ #include "nvhost_ctrl_gpu.h" #include -namespace skyline::gpu::device { +namespace skyline::service::nvdrv::device { NvHostCtrlGpu::NvHostCtrlGpu(const DeviceState &state) : NvDevice(state, NvDeviceType::nvhost_ctrl_gpu, { {0x80044701, NFUNC(NvHostCtrlGpu::ZCullGetCtxSize)}, {0x80284702, NFUNC(NvHostCtrlGpu::ZCullGetInfo)}, @@ -15,7 +15,7 @@ namespace skyline::gpu::device { state.process->WriteMemory(size, buffer.output[0].address); } - void NvHostCtrlGpu::ZCullGetInfo(skyline::gpu::device::IoctlData &buffer) { + void NvHostCtrlGpu::ZCullGetInfo(IoctlData &buffer) { struct { u32 widthAlignPixels{0x20}; u32 heightAlignPixels{0x20}; diff --git a/app/src/main/cpp/skyline/gpu/devices/nvhost_ctrl_gpu.h b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl_gpu.h similarity index 96% rename from app/src/main/cpp/skyline/gpu/devices/nvhost_ctrl_gpu.h rename to app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl_gpu.h index c5fafe61..cf76f452 100644 --- a/app/src/main/cpp/skyline/gpu/devices/nvhost_ctrl_gpu.h +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl_gpu.h @@ -2,7 +2,7 @@ #include "nvdevice.h" -namespace skyline::gpu::device { +namespace skyline::service::nvdrv::device { /** * @brief NvHostCtrlGpu (/dev/nvhost-ctrl-gpu) is used for context independent operations on the underlying GPU (https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-ctrl-gpu) */ diff --git a/app/src/main/cpp/skyline/gpu/devices/nvmap.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvmap.cpp similarity index 96% rename from app/src/main/cpp/skyline/gpu/devices/nvmap.cpp rename to app/src/main/cpp/skyline/services/nvdrv/devices/nvmap.cpp index 08421c0b..1baa702a 100644 --- a/app/src/main/cpp/skyline/gpu/devices/nvmap.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvmap.cpp @@ -1,7 +1,7 @@ #include "nvmap.h" #include -namespace skyline::gpu::device { +namespace skyline::service::nvdrv::device { NvMap::NvMapObject::NvMapObject(u32 id, u32 size) : id(id), size(size) {} NvMap::NvMap(const DeviceState &state) : NvDevice(state, NvDeviceType::nvmap, { @@ -24,7 +24,7 @@ namespace skyline::gpu::device { state.logger->Debug("Create: Input: Size: 0x{:X}, Output: Handle: 0x{:X}, Status: {}", data.size, data.handle, buffer.status); } - void NvMap::FromId(skyline::gpu::device::IoctlData &buffer) { + void NvMap::FromId(IoctlData &buffer) { struct Data { u32 id; // In u32 handle; // Out @@ -64,7 +64,7 @@ namespace skyline::gpu::device { state.logger->Debug("Alloc: Input: Handle: 0x{:X}, HeapMask: 0x{:X}, Flags: {}, Align: 0x{:X}, Kind: {}, Address: 0x{:X}, Output: Status: {}", data.handle, data.heapMask, data.flags, data.align, data.kind, data.address, buffer.status); } - void NvMap::Free(skyline::gpu::device::IoctlData &buffer) { + void NvMap::Free(IoctlData &buffer) { struct Data { u32 handle; // In u32 _pad0_; @@ -128,7 +128,7 @@ namespace skyline::gpu::device { state.logger->Debug("Param: Input: Handle: 0x{:X}, Parameter: {}, Output: Result: 0x{:X}, Status: {}", data.handle, data.parameter, data.result, buffer.status); } - void NvMap::GetId(skyline::gpu::device::IoctlData &buffer) { + void NvMap::GetId(IoctlData &buffer) { struct Data { u32 id; // Out u32 handle; // In diff --git a/app/src/main/cpp/skyline/gpu/devices/nvmap.h b/app/src/main/cpp/skyline/services/nvdrv/devices/nvmap.h similarity index 98% rename from app/src/main/cpp/skyline/gpu/devices/nvmap.h rename to app/src/main/cpp/skyline/services/nvdrv/devices/nvmap.h index 9e104d64..8e0d8870 100644 --- a/app/src/main/cpp/skyline/gpu/devices/nvmap.h +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvmap.h @@ -2,7 +2,7 @@ #include "nvdevice.h" -namespace skyline::gpu::device { +namespace skyline::service::nvdrv::device { /** * @brief NvMap (/dev/nvmap) is used to map certain CPU memory as GPU memory (https://switchbrew.org/wiki/NV_services) (https://android.googlesource.com/kernel/tegra/+/refs/heads/android-tegra-flounder-3.10-marshmallow/include/linux/nvmap.h) */ diff --git a/app/src/main/cpp/skyline/services/nvdrv/nvdrv.cpp b/app/src/main/cpp/skyline/services/nvdrv/nvdrv.cpp deleted file mode 100644 index 54449adf..00000000 --- a/app/src/main/cpp/skyline/services/nvdrv/nvdrv.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "nvdrv.h" -#include - -namespace skyline::service::nvdrv { - nvdrv::nvdrv(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::nvdrv, "nvdrv", { - {0x0, SFUNC(nvdrv::Open)}, - {0x1, SFUNC(nvdrv::Ioctl)}, - {0x2, SFUNC(nvdrv::Close)}, - {0x3, SFUNC(nvdrv::Initialize)}, - {0x4, SFUNC(nvdrv::QueryEvent)}, - {0x8, SFUNC(nvdrv::SetAruidByPID)} - }) {} - - void nvdrv::Open(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto buffer = request.inputBuf.at(0); - auto path = state.process->GetString(buffer.address, buffer.size); - response.Push(state.gpu->OpenDevice(path)); - response.Push(constant::status::Success); - } - - void nvdrv::Ioctl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto fd = request.Pop(); - auto cmd = request.Pop(); - state.gpu->Ioctl(fd, cmd, request, response); - } - - void nvdrv::Close(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - u32 fd = *reinterpret_cast(request.cmdArg); - state.gpu->CloseDevice(fd); - response.Push(constant::status::Success); - } - - void nvdrv::Initialize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - response.Push(constant::status::Success); - } - - void nvdrv::QueryEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto fd = request.Pop(); - auto eventId = request.Pop(); - auto event = std::make_shared(state); - auto handle = state.process->InsertItem(event); - state.logger->Debug("QueryEvent: FD: {}, Event ID: {}, Handle: {}", fd, eventId, handle); - response.copyHandles.push_back(handle); - } - - void nvdrv::SetAruidByPID(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - response.Push(constant::status::Success); - } -} diff --git a/app/src/main/cpp/skyline/services/nvdrv/nvdrv.h b/app/src/main/cpp/skyline/services/nvdrv/nvdrv.h deleted file mode 100644 index 38f74456..00000000 --- a/app/src/main/cpp/skyline/services/nvdrv/nvdrv.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace skyline::service::nvdrv { - /** - * @brief nvdrv or INvDrvServices is used to access the Nvidia GPU inside the Switch (https://switchbrew.org/wiki/NV_services#nvdrv.2C_nvdrv:a.2C_nvdrv:s.2C_nvdrv:t) - */ - class nvdrv : public BaseService { - public: - nvdrv(const DeviceState &state, ServiceManager &manager); - - /** - * @brief Open a specific device and return a FD (https://switchbrew.org/wiki/NV_services#Open) - */ - void Open(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief Close the specified FD (https://switchbrew.org/wiki/NV_services#Close) - */ - void Ioctl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief Close the specified FD (https://switchbrew.org/wiki/NV_services#Close) - */ - void Close(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief This initializes the driver (https://switchbrew.org/wiki/NV_services#Initialize) - */ - void Initialize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief This returns a specific event from a device (https://switchbrew.org/wiki/NV_services#QueryEvent) - */ - void QueryEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief This sets the AppletResourceUserId which matches the PID (https://switchbrew.org/wiki/NV_services#SetAruidByPID) - */ - void SetAruidByPID(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - }; -} diff --git a/app/src/main/cpp/skyline/services/nvnflinger/dispdrv.cpp b/app/src/main/cpp/skyline/services/nvnflinger/dispdrv.cpp deleted file mode 100644 index b1aba2af..00000000 --- a/app/src/main/cpp/skyline/services/nvnflinger/dispdrv.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "dispdrv.h" -#include -#include - -namespace skyline::service::nvnflinger { - dispdrv::dispdrv(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::nvnflinger_dispdrv, "nvnflinger_dispdrv", { - {0x0, SFUNC(dispdrv::TransactParcel)}, - {0x1, SFUNC(dispdrv::AdjustRefcount)}, - {0x2, SFUNC(dispdrv::GetNativeHandle)}, - {0x3, SFUNC(dispdrv::TransactParcel)} - }) {} - - void dispdrv::TransactParcel(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto layerId = request.Pop(); - auto code = request.Pop(); - gpu::Parcel in(request.inputBuf.at(0), state); - gpu::Parcel out(state); - state.logger->Debug("TransactParcel: Layer ID: {}, Code: {}", layerId, code); - switch (code) { - case TransactionCode::RequestBuffer: - state.gpu->bufferQueue.RequestBuffer(in, out); - break; - case TransactionCode::DequeueBuffer: - state.gpu->bufferQueue.DequeueBuffer(in, out); - break; - case TransactionCode::QueueBuffer: - state.gpu->bufferQueue.QueueBuffer(in, out); - break; - case TransactionCode::CancelBuffer: - state.gpu->bufferQueue.CancelBuffer(in); - break; - case TransactionCode::Query: - out.WriteData(0); - break; - case TransactionCode::Connect: { - ConnectParcel connect = { - .width = constant::HandheldResolutionW, - .height = constant::HandheldResolutionH, - .transformHint = 0, - .pendingBuffers = 0, - .status = constant::status::Success, - }; - out.WriteData(connect); - break; - } - case TransactionCode::Disconnect: - break; - case TransactionCode::SetPreallocatedBuffer: - state.gpu->bufferQueue.SetPreallocatedBuffer(in); - break; - default: - throw exception("An unimplemented transaction was called: {}", static_cast(code)); - } - out.WriteParcel(request.outputBuf.at(0)); - } - - void dispdrv::AdjustRefcount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - request.Skip(); - auto addVal = request.Pop(); - auto type = request.Pop(); - state.logger->Debug("Reference Change: {} {} reference", addVal, type ? "strong" : "weak"); - } - - void dispdrv::GetNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - handle_t handle = state.process->InsertItem(state.gpu->bufferEvent); - state.logger->Debug("Display Buffer Event Handle: 0x{:X}", handle); - response.copyHandles.push_back(handle); - response.Push(constant::status::Success); - } -} diff --git a/app/src/main/cpp/skyline/services/serviceman.cpp b/app/src/main/cpp/skyline/services/serviceman.cpp index adf17b6a..7aeda422 100644 --- a/app/src/main/cpp/skyline/services/serviceman.cpp +++ b/app/src/main/cpp/skyline/services/serviceman.cpp @@ -10,16 +10,17 @@ #include "hid/IHidServer.h" #include "timesrv/IStaticService.h" #include "fssrv/IFileSystemProxy.h" -#include "nvdrv/nvdrv.h" -#include "vi/vi_m.h" +#include "services/nvdrv/INvDrvServices.h" +#include "visrv/IManagerRootService.h" #include "serviceman.h" namespace skyline::service { ServiceManager::ServiceManager(const DeviceState &state) : state(state) {} - std::shared_ptr ServiceManager::GetService(const Service serviceType) { - if (serviceMap.count(serviceType)) - return serviceMap.at(serviceType); + std::shared_ptr ServiceManager::CreateService(const Service serviceType) { + auto serviceIter = serviceMap.find(serviceType); + if (serviceIter != serviceMap.end()) + return (*serviceIter).second; std::shared_ptr serviceObj; switch (serviceType) { @@ -56,14 +57,14 @@ namespace skyline::service { case Service::fssrv_IFileSystemProxy: serviceObj = std::make_shared(state, *this); break; - case Service::nvdrv: - serviceObj = std::make_shared(state, *this); + case Service::nvdrv_INvDrvServices: + serviceObj = std::make_shared(state, *this); break; - case Service::vi_m: - serviceObj = std::make_shared(state, *this); + case Service::visrv_IManagerRootService: + serviceObj = std::make_shared(state, *this); break; default: - throw exception("GetService called on missing object, type: {}", serviceType); + throw exception("CreateService called on missing object, type: {}", serviceType); } serviceMap[serviceType] = serviceObj; return serviceObj; @@ -71,12 +72,12 @@ namespace skyline::service { handle_t ServiceManager::NewSession(const Service serviceType) { std::lock_guard serviceGuard(mutex); - return state.process->NewHandle(GetService(serviceType)).handle; + return state.process->NewHandle(CreateService(serviceType)).handle; } std::shared_ptr ServiceManager::NewService(const std::string &serviceName, type::KSession &session, ipc::IpcResponse &response) { std::lock_guard serviceGuard(mutex); - auto serviceObject = GetService(ServiceString.at(serviceName)); + auto serviceObject = CreateService(ServiceString.at(serviceName)); handle_t handle{}; if (response.isDomain) { session.domainTable[++session.handleIndex] = serviceObject; @@ -90,7 +91,7 @@ namespace skyline::service { return serviceObject; } - void ServiceManager::RegisterService(std::shared_ptr serviceObject, type::KSession &session, ipc::IpcResponse &response) { // NOLINT(performance-unnecessary-value-param) + void ServiceManager::RegisterService(std::shared_ptr serviceObject, type::KSession &session, ipc::IpcResponse &response, bool submodule) { // NOLINT(performance-unnecessary-value-param) std::lock_guard serviceGuard(mutex); handle_t handle{}; if (response.isDomain) { @@ -101,6 +102,8 @@ namespace skyline::service { handle = state.process->NewHandle(serviceObject).handle; response.moveHandles.push_back(handle); } + if(!submodule) + serviceMap[serviceObject->serviceType] = serviceObject; state.logger->Debug("Service has been registered: \"{}\" (0x{:X})", serviceObject->serviceName, handle); } diff --git a/app/src/main/cpp/skyline/services/serviceman.h b/app/src/main/cpp/skyline/services/serviceman.h index c839d3d9..2cbaa1d8 100644 --- a/app/src/main/cpp/skyline/services/serviceman.h +++ b/app/src/main/cpp/skyline/services/serviceman.h @@ -15,10 +15,11 @@ namespace skyline::service { skyline::Mutex mutex; //!< This mutex is used to ensure concurrent access to services doesn't cause crashes /** + * @brief Creates an instance of the service if it doesn't already exist, otherwise returns an existing instance * @param serviceType The type of service requested * @return A shared pointer to an instance of the service */ - std::shared_ptr GetService(const Service serviceType); + std::shared_ptr CreateService(const Service serviceType); public: /** @@ -46,8 +47,20 @@ namespace skyline::service { * @param serviceObject An instance of the service * @param session The session object of the command * @param response The response object to write the handle or virtual handle to + * @param submodule If the registered service is a submodule or not */ - void RegisterService(std::shared_ptr serviceObject, type::KSession &session, ipc::IpcResponse &response); + void RegisterService(std::shared_ptr serviceObject, type::KSession &session, ipc::IpcResponse &response, bool submodule = true); + + /** + * @param serviceType The type of the service + * @tparam The class of the service + * @return A shared pointer to an instance of the service + * @note This only works for services created with `NewService` as sub-interfaces used with `RegisterService` can have multiple instances + */ + template + inline std::shared_ptr GetService(const Service serviceType) { + return std::static_pointer_cast(serviceMap.at(serviceType)); + } /** * @brief Closes an existing session to a service diff --git a/app/src/main/cpp/skyline/services/vi/vi_m.cpp b/app/src/main/cpp/skyline/services/vi/vi_m.cpp deleted file mode 100644 index 292274ca..00000000 --- a/app/src/main/cpp/skyline/services/vi/vi_m.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include "vi_m.h" -#include -#include -#include - -namespace skyline::service::vi { - vi_m::vi_m(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::nvdrv, "nvdrv", { - {0x2, SFUNC(vi_m::GetDisplayService)} - }) {} - - void vi_m::GetDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - manager.RegisterService(SRVREG(IApplicationDisplayService), session, response); - } - - IDisplayService::IDisplayService(const DeviceState &state, ServiceManager &manager, const Service serviceType, const std::string &serviceName, const std::unordered_map> &vTable) : BaseService(state, manager, serviceType, serviceName, vTable) {} - - void IDisplayService::CreateStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - state.logger->Debug("Creating Stray Layer"); - response.Push(0); // There's only one layer - gpu::Parcel parcel(state); - LayerParcel data{ - .type = 0x20, - .pid = 0, - .bufferId = 0, // As we only have one layer and buffer - .string = "dispdrv" - }; - parcel.WriteData(data); - response.Push(parcel.WriteParcel(request.outputBuf.at(0))); - } - - IApplicationDisplayService::IApplicationDisplayService(const DeviceState &state, ServiceManager &manager) : IDisplayService(state, manager, Service::vi_IApplicationDisplayService, "vi:IApplicationDisplayService", { - {0x64, SFUNC(IApplicationDisplayService::GetRelayService)}, - {0x65, SFUNC(IApplicationDisplayService::GetSystemDisplayService)}, - {0x66, SFUNC(IApplicationDisplayService::GetManagerDisplayService)}, - {0x67, SFUNC(IApplicationDisplayService::GetIndirectDisplayTransactionService)}, - {0x3F2, SFUNC(IApplicationDisplayService::OpenDisplay)}, - {0x3FC, SFUNC(IApplicationDisplayService::CloseDisplay)}, - {0x7E4, SFUNC(IApplicationDisplayService::OpenLayer)}, - {0x7E5, SFUNC(IApplicationDisplayService::CloseLayer)}, - {0x835, SFUNC(IApplicationDisplayService::SetLayerScalingMode)}, - {0x1452, SFUNC(IApplicationDisplayService::GetDisplayVsyncEvent)}, - }) {} - - void IApplicationDisplayService::GetRelayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - manager.RegisterService(SRVREG(nvnflinger::dispdrv), session, response); - } - - void IApplicationDisplayService::GetIndirectDisplayTransactionService(skyline::kernel::type::KSession &session, skyline::kernel::ipc::IpcRequest &request, skyline::kernel::ipc::IpcResponse &response) { - manager.RegisterService(SRVREG(nvnflinger::dispdrv), session, response); - } - - void IApplicationDisplayService::GetSystemDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - manager.RegisterService(SRVREG(ISystemDisplayService), session, response); - } - - void IApplicationDisplayService::GetManagerDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - manager.RegisterService(SRVREG(IManagerDisplayService), session, response); - } - - void IApplicationDisplayService::OpenDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - std::string displayName(reinterpret_cast(request.cmdArg)); - state.logger->Debug("Setting display as: {}", displayName); - state.gpu->SetDisplay(displayName); - response.Push(0); // There's only one display - } - - void IApplicationDisplayService::CloseDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - state.logger->Debug("Closing the display"); - state.gpu->CloseDisplay(); - } - - void IApplicationDisplayService::OpenLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - struct InputStruct { - char displayName[0x40]; - u64 layerId; - u64 userId; - } input = request.Pop(); - state.logger->Debug("Opening Layer: Display Name: {}, Layer ID: {}, User ID: {}", input.displayName, input.layerId, input.userId); - std::string name(input.displayName); - gpu::Parcel parcel(state); - LayerParcel data{ - .type = 0x20, - .pid = 0, - .bufferId = 0, // As we only have one layer and buffer - .string = "dispdrv" - }; - parcel.WriteData(data); - parcel.objects.resize(4); - response.Push(parcel.WriteParcel(request.outputBuf.at(0))); - } - - void IApplicationDisplayService::CloseLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - u64 layerId = request.Pop(); - state.logger->Debug("Closing Layer: {}", layerId); - if (state.gpu->layerStatus == gpu::LayerStatus::Uninitialized) - state.logger->Warn("The application is destroying an uninitialized layer"); - state.gpu->layerStatus = gpu::LayerStatus::Uninitialized; - response.Push(constant::status::Success); - } - - void IApplicationDisplayService::SetLayerScalingMode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - auto scalingMode = request.Pop(); - auto layerId = request.Pop(); - state.logger->Debug("Setting Layer Scaling mode to '{}' for layer {}", scalingMode, layerId); - } - - void IApplicationDisplayService::GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - handle_t handle = state.process->InsertItem(state.gpu->vsyncEvent); - state.logger->Debug("VSync Event Handle: 0x{:X}", handle); - response.copyHandles.push_back(handle); - } - - ISystemDisplayService::ISystemDisplayService(const DeviceState &state, ServiceManager &manager) : IDisplayService(state, manager, Service::vi_ISystemDisplayService, "vi:ISystemDisplayService", { - {0x89D, SFUNC(ISystemDisplayService::SetLayerZ)}, - {0x908, SFUNC(IDisplayService::CreateStrayLayer)} - }) {} - - void ISystemDisplayService::SetLayerZ(skyline::kernel::type::KSession &session, skyline::kernel::ipc::IpcRequest &request, skyline::kernel::ipc::IpcResponse &response) {} - - IManagerDisplayService::IManagerDisplayService(const DeviceState &state, ServiceManager &manager) : IDisplayService(state, manager, Service::vi_IManagerDisplayService, "vi:IManagerDisplayService", { - {0x7DA, SFUNC(IManagerDisplayService::CreateManagedLayer)}, - {0x7DB, SFUNC(IManagerDisplayService::DestroyManagedLayer)}, - {0x7DC, SFUNC(IDisplayService::CreateStrayLayer)}, - {0x1770, SFUNC(IManagerDisplayService::AddToLayerStack)} - }) {} - - void IManagerDisplayService::CreateManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - request.Skip(); - u64 displayId = request.Pop(); - state.logger->Debug("Creating Managed Layer: {}", displayId); - if (state.gpu->layerStatus == gpu::LayerStatus::Initialized) - throw exception("The application is creating more than one layer"); - state.gpu->layerStatus = gpu::LayerStatus::Initialized; - response.Push(0); // There's only one layer - } - - void IManagerDisplayService::DestroyManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - state.logger->Debug("Destroying managed layer"); - if (state.gpu->layerStatus == gpu::LayerStatus::Uninitialized) - state.logger->Warn("The application is destroying an uninitialized layer"); - state.gpu->layerStatus = gpu::LayerStatus::Uninitialized; - response.Push(constant::status::Success); - } - - void IManagerDisplayService::AddToLayerStack(skyline::kernel::type::KSession &session, skyline::kernel::ipc::IpcRequest &request, skyline::kernel::ipc::IpcResponse &response) {} -} diff --git a/app/src/main/cpp/skyline/services/vi/vi_m.h b/app/src/main/cpp/skyline/services/vi/vi_m.h deleted file mode 100644 index c22a17ae..00000000 --- a/app/src/main/cpp/skyline/services/vi/vi_m.h +++ /dev/null @@ -1,142 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace skyline::service::vi { - /** - * @brief This service is used to get an handle to #IApplicationDisplayService (https://switchbrew.org/wiki/Display_services#vi:m) - */ - class vi_m : public BaseService { - public: - vi_m(const DeviceState &state, ServiceManager &manager); - - /** - * @brief This returns an handle to #IApplicationDisplayService (https://switchbrew.org/wiki/Display_services#GetDisplayService) - */ - void GetDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - }; - - /** - * @brief This is the base class for all IDisplayService variants with common functions - */ - class IDisplayService : public BaseService { - protected: - /** - * @brief This is the format of the parcel used in OpenLayer/CreateStrayLayer - */ - struct LayerParcel { - u32 type; //!< The type of the layer - u32 pid; //!< The PID that the layer belongs to - u32 bufferId; //!< The buffer ID of the layer - u32 _pad0_[3]; - u8 string[0x8]; //!< "dispdrv" - u64 _pad1_; - }; - static_assert(sizeof(LayerParcel) == 0x28); - - public: - IDisplayService(const DeviceState &state, ServiceManager &manager, const Service serviceType, const std::string &serviceName, const std::unordered_map> &vTable); - - /** - * @brief This takes a display's ID and returns a layer ID and the corresponding buffer ID - */ - void CreateStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - }; - - /** - * @brief This service is used to access the display (https://switchbrew.org/wiki/Display_services#IApplicationDisplayService) - */ - class IApplicationDisplayService : public IDisplayService { - public: - IApplicationDisplayService(const DeviceState &state, ServiceManager &manager); - - /** - * @brief Returns an handle to the 'nvnflinger' service (https://switchbrew.org/wiki/Display_services#GetRelayService) - */ - void GetRelayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief Returns an handle to the 'nvnflinger' service (https://switchbrew.org/wiki/Display_services#GetIndirectDisplayTransactionService) - */ - void GetIndirectDisplayTransactionService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief Returns an handle to #ISystemDisplayService (https://switchbrew.org/wiki/Display_services#GetSystemDisplayService) - */ - void GetSystemDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief Returns an handle to #IManagerDisplayService (https://switchbrew.org/wiki/Display_services#GetManagerDisplayService) - */ - void GetManagerDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief Opens up a display using it's name as the input (https://switchbrew.org/wiki/Display_services#OpenDisplay) - */ - void OpenDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief Closes an open display using it's ID (https://switchbrew.org/wiki/Display_services#CloseDisplay) - */ - void CloseDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief Opens a specific layer on a display (https://switchbrew.org/wiki/Display_services#OpenLayer) - */ - void OpenLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief Closes a specific layer on a display (https://switchbrew.org/wiki/Display_services#CloseLayer) - */ - void CloseLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief Sets the scaling mode for a window, this is not required by emulators (https://switchbrew.org/wiki/Display_services#SetLayerScalingMode) - */ - void SetLayerScalingMode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief Returns a handle to a KEvent which is triggered every time a frame is drawn (https://switchbrew.org/wiki/Display_services#GetDisplayVsyncEvent) - */ - void GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - }; - - /** - * @brief This service retrieves information about a display in context of the entire system (https://switchbrew.org/wiki/Display_services#ISystemDisplayService) - */ - class ISystemDisplayService : public IDisplayService { - public: - ISystemDisplayService(const DeviceState &state, ServiceManager &manager); - - /** - * @brief Sets the Z index of a layer - */ - void SetLayerZ(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - }; - - /** - * @brief This service retrieves information about a display in context of the entire system (https://switchbrew.org/wiki/Display_services#IManagerDisplayService) - */ - class IManagerDisplayService : public IDisplayService { - public: - IManagerDisplayService(const DeviceState &state, ServiceManager &manager); - - /** - * @brief Creates a managed layer on a specific display - */ - void CreateManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief Destroys a managed layer created on a specific display - */ - void DestroyManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief This takes a layer's ID and adds it to the layer stack - */ - void AddToLayerStack(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - }; -} diff --git a/app/src/main/cpp/skyline/services/visrv/IApplicationDisplayService.cpp b/app/src/main/cpp/skyline/services/visrv/IApplicationDisplayService.cpp new file mode 100644 index 00000000..a570ee00 --- /dev/null +++ b/app/src/main/cpp/skyline/services/visrv/IApplicationDisplayService.cpp @@ -0,0 +1,95 @@ +#include +#include +#include +#include "IApplicationDisplayService.h" +#include "ISystemDisplayService.h" +#include "IManagerDisplayService.h" + +namespace skyline::service::visrv { + IApplicationDisplayService::IApplicationDisplayService(const DeviceState &state, ServiceManager &manager) : IDisplayService(state, manager, Service::visrv_IApplicationDisplayService, "visrv:IApplicationDisplayService", { + {0x64, SFUNC(IApplicationDisplayService::GetRelayService)}, + {0x65, SFUNC(IApplicationDisplayService::GetSystemDisplayService)}, + {0x66, SFUNC(IApplicationDisplayService::GetManagerDisplayService)}, + {0x67, SFUNC(IApplicationDisplayService::GetIndirectDisplayTransactionService)}, + {0x3F2, SFUNC(IApplicationDisplayService::OpenDisplay)}, + {0x3FC, SFUNC(IApplicationDisplayService::CloseDisplay)}, + {0x7E4, SFUNC(IApplicationDisplayService::OpenLayer)}, + {0x7E5, SFUNC(IApplicationDisplayService::CloseLayer)}, + {0x7EE, SFUNC(IDisplayService::CreateStrayLayer)}, + {0x7EF, SFUNC(IDisplayService::DestroyStrayLayer)}, + {0x835, SFUNC(IApplicationDisplayService::SetLayerScalingMode)}, + {0x1452, SFUNC(IApplicationDisplayService::GetDisplayVsyncEvent)}, + }) {} + + void IApplicationDisplayService::GetRelayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + manager.RegisterService(SRVREG(hosbinder::IHOSBinderDriver), session, response, false); + } + + void IApplicationDisplayService::GetIndirectDisplayTransactionService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + manager.RegisterService(SRVREG(hosbinder::IHOSBinderDriver), session, response, false); + } + + void IApplicationDisplayService::GetSystemDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + manager.RegisterService(SRVREG(ISystemDisplayService), session, response); + } + + void IApplicationDisplayService::GetManagerDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + manager.RegisterService(SRVREG(IManagerDisplayService), session, response); + } + + void IApplicationDisplayService::OpenDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + std::string displayName(reinterpret_cast(request.cmdArg)); + state.logger->Debug("Setting display as: {}", displayName); + state.os->serviceManager.GetService(Service::hosbinder_IHOSBinderDriver)->SetDisplay(displayName); + response.Push(0); // There's only one display + } + + void IApplicationDisplayService::CloseDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + state.logger->Debug("Closing the display"); + state.os->serviceManager.GetService(Service::hosbinder_IHOSBinderDriver)->CloseDisplay(); + } + + void IApplicationDisplayService::OpenLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + struct InputStruct { + char displayName[0x40]; + u64 layerId; + u64 userId; + } input = request.Pop(); + state.logger->Debug("Opening Layer: Display Name: {}, Layer ID: {}, User ID: {}", input.displayName, input.layerId, input.userId); + std::string name(input.displayName); + Parcel parcel(state); + LayerParcel data{ + .type = 0x20, + .pid = 0, + .bufferId = 0, // As we only have one layer and buffer + .string = "dispdrv" + }; + parcel.WriteData(data); + parcel.objects.resize(4); + response.Push(parcel.WriteParcel(request.outputBuf.at(0))); + } + + void IApplicationDisplayService::CloseLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + u64 layerId = request.Pop(); + state.logger->Debug("Closing Layer: {}", layerId); + + auto hosBinder = state.os->serviceManager.GetService(Service::hosbinder_IHOSBinderDriver); + if (hosBinder->layerStatus == hosbinder::LayerStatus::Uninitialized) + state.logger->Warn("The application is destroying an uninitialized layer"); + hosBinder->layerStatus = hosbinder::LayerStatus::Uninitialized; + + response.Push(constant::status::Success); + } + + void IApplicationDisplayService::SetLayerScalingMode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto scalingMode = request.Pop(); + auto layerId = request.Pop(); + state.logger->Debug("Setting Layer Scaling mode to '{}' for layer {}", scalingMode, layerId); + } + + void IApplicationDisplayService::GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + handle_t handle = state.process->InsertItem(state.gpu->vsyncEvent); + state.logger->Debug("VSync Event Handle: 0x{:X}", handle); + response.copyHandles.push_back(handle); + } +} diff --git a/app/src/main/cpp/skyline/services/visrv/IApplicationDisplayService.h b/app/src/main/cpp/skyline/services/visrv/IApplicationDisplayService.h new file mode 100644 index 00000000..ae5fdde3 --- /dev/null +++ b/app/src/main/cpp/skyline/services/visrv/IApplicationDisplayService.h @@ -0,0 +1,63 @@ +#pragma once + +#include "IDisplayService.h" + +namespace skyline::service::visrv { + /** + * @brief This service is used to access the display (https://switchbrew.org/wiki/Display_services#IApplicationDisplayService) + */ + class IApplicationDisplayService : public IDisplayService { + public: + IApplicationDisplayService(const DeviceState &state, ServiceManager &manager); + + /** + * @brief Returns an handle to the 'nvnflinger' service (https://switchbrew.org/wiki/Display_services#GetRelayService) + */ + void GetRelayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Returns an handle to the 'nvnflinger' service (https://switchbrew.org/wiki/Display_services#GetIndirectDisplayTransactionService) + */ + void GetIndirectDisplayTransactionService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Returns an handle to #ISystemDisplayService (https://switchbrew.org/wiki/Display_services#GetSystemDisplayService) + */ + void GetSystemDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Returns an handle to #IManagerDisplayService (https://switchbrew.org/wiki/Display_services#GetManagerDisplayService) + */ + void GetManagerDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Opens up a display using it's name as the input (https://switchbrew.org/wiki/Display_services#OpenDisplay) + */ + void OpenDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Closes an open display using it's ID (https://switchbrew.org/wiki/Display_services#CloseDisplay) + */ + void CloseDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Opens a specific layer on a display (https://switchbrew.org/wiki/Display_services#OpenLayer) + */ + void OpenLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Closes a specific layer on a display (https://switchbrew.org/wiki/Display_services#CloseLayer) + */ + void CloseLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Sets the scaling mode for a window, this is not required by emulators (https://switchbrew.org/wiki/Display_services#SetLayerScalingMode) + */ + void SetLayerScalingMode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Returns a handle to a KEvent which is triggered every time a frame is drawn (https://switchbrew.org/wiki/Display_services#GetDisplayVsyncEvent) + */ + void GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + }; +} diff --git a/app/src/main/cpp/skyline/services/visrv/IDisplayService.cpp b/app/src/main/cpp/skyline/services/visrv/IDisplayService.cpp new file mode 100644 index 00000000..0db7c05d --- /dev/null +++ b/app/src/main/cpp/skyline/services/visrv/IDisplayService.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include "IDisplayService.h" + +namespace skyline::service::visrv { + IDisplayService::IDisplayService(const DeviceState &state, ServiceManager &manager, const Service serviceType, const std::string &serviceName, const std::unordered_map> &vTable) : BaseService(state, manager, serviceType, serviceName, vTable) {} + + void IDisplayService::CreateStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + request.Skip(); + auto displayId = request.Pop(); + + state.logger->Debug("Creating Stray Layer on Display: {}", displayId); + + auto hosBinder = state.os->serviceManager.GetService(Service::hosbinder_IHOSBinderDriver); + if (hosBinder->layerStatus == hosbinder::LayerStatus::Stray) + throw exception("The application is creating more than one stray layer"); + hosBinder->layerStatus = hosbinder::LayerStatus::Stray; + response.Push(0); // There's only one layer + + Parcel parcel(state); + LayerParcel data{ + .type = 0x20, + .pid = 0, + .bufferId = 0, // As we only have one layer and buffer + .string = "dispdrv" + }; + parcel.WriteData(data); + response.Push(parcel.WriteParcel(request.outputBuf.at(0))); + } + + void IDisplayService::DestroyStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto layerId = request.Pop(); + state.logger->Debug("Destroying Stray Layer: {}", layerId); + + auto hosBinder = state.os->serviceManager.GetService(Service::hosbinder_IHOSBinderDriver); + if (hosBinder->layerStatus == hosbinder::LayerStatus::Uninitialized) + state.logger->Warn("The application is destroying an uninitialized layer"); + hosBinder->layerStatus = hosbinder::LayerStatus::Uninitialized; + } +} diff --git a/app/src/main/cpp/skyline/services/visrv/IDisplayService.h b/app/src/main/cpp/skyline/services/visrv/IDisplayService.h new file mode 100644 index 00000000..4a825b29 --- /dev/null +++ b/app/src/main/cpp/skyline/services/visrv/IDisplayService.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include + +namespace skyline::service::visrv { +/** + * @brief This is the base class for all IDisplayService variants with common functions + */ + class IDisplayService : public BaseService { + protected: + /** + * @brief This is the format of the parcel used in OpenLayer/CreateStrayLayer + */ + struct LayerParcel { + u32 type; //!< The type of the layer + u32 pid; //!< The PID that the layer belongs to + u32 bufferId; //!< The buffer ID of the layer + u32 _pad0_[3]; + u8 string[0x8]; //!< "dispdrv" + u64 _pad1_; + }; + static_assert(sizeof(LayerParcel) == 0x28); + + public: + IDisplayService(const DeviceState &state, ServiceManager &manager, const Service serviceType, const std::string &serviceName, const std::unordered_map > &vTable); + + /** + * @brief This creates a stray layer using a display's ID and returns a layer ID and the corresponding buffer ID + */ + void CreateStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This takes a layer ID and destroys the corresponding stray layer + */ + void DestroyStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + }; +} diff --git a/app/src/main/cpp/skyline/services/visrv/IManagerDisplayService.cpp b/app/src/main/cpp/skyline/services/visrv/IManagerDisplayService.cpp new file mode 100644 index 00000000..3b789c44 --- /dev/null +++ b/app/src/main/cpp/skyline/services/visrv/IManagerDisplayService.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include "IManagerDisplayService.h" + +namespace skyline::service::visrv { + IManagerDisplayService::IManagerDisplayService(const DeviceState &state, ServiceManager &manager) : IDisplayService(state, manager, Service::visrv_IManagerDisplayService, "visrv:IManagerDisplayService", { + {0x7DA, SFUNC(IManagerDisplayService::CreateManagedLayer)}, + {0x7DB, SFUNC(IManagerDisplayService::DestroyManagedLayer)}, + {0x7DC, SFUNC(IDisplayService::CreateStrayLayer)}, + {0x1770, SFUNC(IManagerDisplayService::AddToLayerStack)} + }) {} + + void IManagerDisplayService::CreateManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + request.Skip(); + auto displayId = request.Pop(); + state.logger->Debug("Creating Managed Layer on Display: {}", displayId); + auto hosBinder = state.os->serviceManager.GetService(Service::hosbinder_IHOSBinderDriver); + if (hosBinder->layerStatus != hosbinder::LayerStatus::Uninitialized) + throw exception("The application is creating more than one layer"); + hosBinder->layerStatus = hosbinder::LayerStatus::Managed; + response.Push(0); // There's only one layer + } + + void IManagerDisplayService::DestroyManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto layerId = request.Pop(); + state.logger->Debug("Destroying Managed Layer: {}", layerId); + auto hosBinder = state.os->serviceManager.GetService(Service::hosbinder_IHOSBinderDriver); + if (hosBinder->layerStatus == hosbinder::LayerStatus::Uninitialized) + state.logger->Warn("The application is destroying an uninitialized layer"); + hosBinder->layerStatus = hosbinder::LayerStatus::Uninitialized; + } + + void IManagerDisplayService::AddToLayerStack(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {} +} diff --git a/app/src/main/cpp/skyline/services/visrv/IManagerDisplayService.h b/app/src/main/cpp/skyline/services/visrv/IManagerDisplayService.h new file mode 100644 index 00000000..32a0e014 --- /dev/null +++ b/app/src/main/cpp/skyline/services/visrv/IManagerDisplayService.h @@ -0,0 +1,28 @@ +#pragma once + +#include "IDisplayService.h" + +namespace skyline::service::visrv { + /** + * @brief This service retrieves information about a display in context of the entire system (https://switchbrew.org/wiki/Display_services#IManagerDisplayService) + */ + class IManagerDisplayService : public IDisplayService { + public: + IManagerDisplayService(const DeviceState &state, ServiceManager &manager); + + /** + * @brief Creates a managed layer on a specific display + */ + void CreateManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief Destroys a managed layer created on a specific display + */ + void DestroyManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + + /** + * @brief This takes a layer's ID and adds it to the layer stack + */ + void AddToLayerStack(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + }; +} diff --git a/app/src/main/cpp/skyline/services/visrv/IManagerRootService.cpp b/app/src/main/cpp/skyline/services/visrv/IManagerRootService.cpp new file mode 100644 index 00000000..e962a7ad --- /dev/null +++ b/app/src/main/cpp/skyline/services/visrv/IManagerRootService.cpp @@ -0,0 +1,13 @@ +#include "IManagerRootService.h" +#include +#include "IApplicationDisplayService.h" + +namespace skyline::service::visrv { + IManagerRootService::IManagerRootService(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::visrv_IManagerRootService, "visrv:IManagerRootService", { + {0x2, SFUNC(IManagerRootService::GetDisplayService)} + }) {} + + void IManagerRootService::GetDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + manager.RegisterService(SRVREG(IApplicationDisplayService), session, response); + } +} diff --git a/app/src/main/cpp/skyline/services/visrv/IManagerRootService.h b/app/src/main/cpp/skyline/services/visrv/IManagerRootService.h new file mode 100644 index 00000000..07dc9397 --- /dev/null +++ b/app/src/main/cpp/skyline/services/visrv/IManagerRootService.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +namespace skyline::service::visrv { + /** + * @brief This service is used to get an handle to #IApplicationDisplayService (https://switchbrew.org/wiki/Display_services#vi:m) + */ + class IManagerRootService : public BaseService { + public: + IManagerRootService(const DeviceState &state, ServiceManager &manager); + + /** + * @brief This returns an handle to #IApplicationDisplayService (https://switchbrew.org/wiki/Display_services#GetDisplayService) + */ + void GetDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + }; +} diff --git a/app/src/main/cpp/skyline/services/visrv/ISystemDisplayService.cpp b/app/src/main/cpp/skyline/services/visrv/ISystemDisplayService.cpp new file mode 100644 index 00000000..08c1278d --- /dev/null +++ b/app/src/main/cpp/skyline/services/visrv/ISystemDisplayService.cpp @@ -0,0 +1,10 @@ +#include "ISystemDisplayService.h" + +namespace skyline::service::visrv { + ISystemDisplayService::ISystemDisplayService(const DeviceState &state, ServiceManager &manager) : IDisplayService(state, manager, Service::visrv_ISystemDisplayService, "visrv:ISystemDisplayService", { + {0x89D, SFUNC(ISystemDisplayService::SetLayerZ)}, + {0x908, SFUNC(IDisplayService::CreateStrayLayer)} + }) {} + + void ISystemDisplayService::SetLayerZ(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {} +} diff --git a/app/src/main/cpp/skyline/services/visrv/ISystemDisplayService.h b/app/src/main/cpp/skyline/services/visrv/ISystemDisplayService.h new file mode 100644 index 00000000..3ba0a07f --- /dev/null +++ b/app/src/main/cpp/skyline/services/visrv/ISystemDisplayService.h @@ -0,0 +1,18 @@ +#pragma once + +#include "IDisplayService.h" + +namespace skyline::service::visrv { + /** + * @brief This service retrieves information about a display in context of the entire system (https://switchbrew.org/wiki/Display_services#ISystemDisplayService) + */ + class ISystemDisplayService : public IDisplayService { + public: + ISystemDisplayService(const DeviceState &state, ServiceManager &manager); + + /** + * @brief Sets the Z index of a layer + */ + void SetLayerZ(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + }; +}