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); + }; +}