Refactor Display Services and GPU

This commit addresses the incorrect hierarchy of the GPU and refactors them at the same time. Now, the hierarchy much closely matches HOS. This commit also introduces a texture classes, albeit they're not complete and only partially implemented.
This commit is contained in:
◱ PixelyIon 2020-03-25 01:47:31 +05:30 committed by ◱ PixelyIon
parent 500b49d329
commit 42d982c6fb
47 changed files with 1424 additions and 1047 deletions

View File

@ -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)

View File

@ -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<typename TypeVal, typename TypeMul>
inline TypeVal AlignUp(TypeVal value, TypeMul multiple) {
static_assert(std::is_integral<TypeVal>() && std::is_integral<TypeMul>());
return (value + multiple) & ~(multiple - 1);
multiple--;
return (value + multiple) & ~(multiple);
}
/**

View File

@ -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 <kernel/types/KProcess.h>
#include <android/native_window_jni.h>
@ -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<kernel::type::KEvent>(state)), bufferEvent(std::make_shared<kernel::type::KEvent>(state)) {
GPU::GPU(const DeviceState &state) : state(state), window(ANativeWindow_fromSurface(state.jvmManager->GetEnv(), Surface)), vsyncEvent(std::make_shared<kernel::type::KEvent>(state)), bufferEvent(std::make_shared<kernel::type::KEvent>(state)) {
ANativeWindow_acquire(window);
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
resolution.height = static_cast<u32>(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<u8 *>(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::microseconds>(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::microseconds>(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<device::NvDevice> object;
switch (type) {
case (device::NvDeviceType::nvhost_ctrl):
object = std::make_shared<device::NvHostCtrl>(state);
break;
case (device::NvDeviceType::nvhost_gpu):
case (device::NvDeviceType::nvhost_vic):
case (device::NvDeviceType::nvhost_nvdec):
object = std::make_shared<device::NvHostChannel>(state, type);
break;
case (device::NvDeviceType::nvhost_ctrl_gpu):
object = std::make_shared<device::NvHostCtrlGpu>(state);
break;
case (device::NvDeviceType::nvmap):
object = std::make_shared<device::NvMap>(state);
break;
case (device::NvDeviceType::nvhost_as_gpu):
object = std::make_shared<device::NvHostAsGpu>(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<u32>(data.status);
} else {
device::IoctlData data(request.inputBuf.at(0));
fdMap.at(fd)->HandleIoctl(cmd, data);
response.Push<u32>(data.status);
}
} else {
device::IoctlData data(request.inputBuf.at(0), request.outputBuf.at(0));
fdMap.at(fd)->HandleIoctl(cmd, data);
response.Push<u32>(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;
}
}

View File

@ -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 <queue>
#include <android/native_window.h>
#include <kernel/ipc.h>
#include <kernel/types/KEvent.h>
#include <services/nvdrv/devices/nvmap.h>
#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<device::NvDeviceType, std::shared_ptr<device::NvDevice>> deviceMap; //!< A map from a NvDeviceType to the NvDevice object
std::unordered_map<u32, std::shared_ptr<device::NvDevice>> 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<std::shared_ptr<PresentationTexture>> 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<kernel::type::KEvent> vsyncEvent; //!< This KEvent is triggered every time a frame is drawn
std::shared_ptr<kernel::type::KEvent> 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<typename objectClass>
std::shared_ptr<objectClass> GetDevice(u32 fd) {
try {
auto item = fdMap.at(fd);
return std::static_pointer_cast<objectClass>(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<typename objectClass>
std::shared_ptr<objectClass> GetDevice(device::NvDeviceType type) {
try {
auto item = deviceMap.at(type);
return std::static_pointer_cast<objectClass>(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();
};
}

View File

@ -1,142 +0,0 @@
#include "display.h"
#include <kernel/types/KProcess.h>
#include <gpu.h>
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::NvMap>(device::NvDeviceType::nvmap)->handleTable.at(gbpBuffer.nvmapHandle);
else {
auto nvmap = state.gpu->GetDevice<device::NvMap>(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<u8>(nvBuffer->address + gbpBuffer.offset);
}
BufferQueue::BufferQueue(const DeviceState &state) : state(state) {}
void BufferQueue::RequestBuffer(Parcel &in, Parcel &out) {
u32 slot = *reinterpret_cast<u32 *>(in.data.data() + constant::TokenLength);
out.WriteData<u32>(1);
out.WriteData<u32>(sizeof(GbpBuffer));
out.WriteData<u32>(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<Data *>(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<u32>(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<Data *>(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<Data *>(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<Data *>(pointer);
pointer += sizeof(Data);
auto gbpBuffer = reinterpret_cast<GbpBuffer *>(pointer);
queue[data->slot] = std::make_shared<Buffer>(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);
}
}

View File

@ -1,171 +0,0 @@
#pragma once
#include <common.h>
#include <queue>
#include <gpu/devices/nvmap.h>
#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<std::string, DisplayId> 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<device::NvMap::NvMapObject> 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<u32, std::shared_ptr<Buffer>> queue; //!< A vector of shared pointers to all the queued buffers
std::queue<std::shared_ptr<Buffer>> 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;
}
};
}

View File

@ -0,0 +1,81 @@
#include <android/native_window.h>
#include <kernel/types/KProcess.h>
#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<GuestTexture> 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<u8>(guest->address);
auto size = format.GetSize(dimensions);
backing.resize(size);
auto output = reinterpret_cast<u8 *>(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<GuestTexture> &guest, const texture::Dimensions &dimensions, const texture::Format &format, const std::function<void()> &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");
}
}
}

View File

@ -0,0 +1,266 @@
#pragma once
#include <common.h>
#include <vulkan/vulkan.hpp>
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<GuestTexture> {
private:
const DeviceState &state; //!< The state of the device
public:
u64 address; //!< The address of the texture in guest memory
std::shared_ptr<Texture> 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<Texture> InitializeTexture(std::optional<texture::Format> format = std::nullopt, std::optional<texture::Dimensions> dimensions = std::nullopt, texture::Swizzle swizzle = {}) {
if (host)
throw exception("Trying to create multiple Texture objects from a single GuestTexture");
host = std::make_shared<Texture>(state, shared_from_this(), dimensions ? *dimensions : this->dimensions, format ? *format : this->format, swizzle);
return host;
}
protected:
std::shared_ptr<PresentationTexture> InitializePresentationTexture() {
if (host)
throw exception("Trying to create multiple PresentationTexture objects from a single GuestTexture");
auto presentation = std::make_shared<PresentationTexture>(state, shared_from_this(), dimensions, format);
host = std::static_pointer_cast<Texture>(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<u8> backing; //!< The object that holds a host copy of the guest texture (Will be replaced with a vk::Image)
std::shared_ptr<GuestTexture> 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<GuestTexture> 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<void()> releaseCallback; //!< The release callback after this texture has been displayed
PresentationTexture(const DeviceState &state, const std::shared_ptr<GuestTexture> &guest, const texture::Dimensions &dimensions, const texture::Format &format, const std::function<void()> &releaseCallback = {});
/**
* @return The corresponding Android surface format for the current texture format
*/
i32 GetAndroidFormat();
};
}
}

View File

@ -1,4 +1,6 @@
#include <gpu.h>
#include <os.h>
#include <services/hosbinder/IHOSBinderDriver.h>
#include <services/hosbinder/display.h>
#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<hosbinder::IHOSBinderDriver>(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<u64>(0);
}
}

View File

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

View File

@ -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;

View File

@ -2,7 +2,7 @@
#include <os.h>
#include <kernel/types/KProcess.h>
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) {

View File

@ -3,7 +3,7 @@
#include <common.h>
#include <kernel/ipc.h>
namespace skyline::gpu {
namespace skyline::service {
/**
* @brief This class encapsulates a Parcel object (https://switchbrew.org/wiki/Display_services#Parcel)
*/

View File

@ -0,0 +1,237 @@
#include <gpu.h>
#include <os.h>
#include <kernel/types/KProcess.h>
#include <services/nvdrv/INvDrvServices.h>
#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<u32 *>(in.data.data() + constant::TokenLength);
out.WriteData<u32>(1);
out.WriteData<u32>(sizeof(GbpBuffer));
out.WriteData<u32>(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<Data *>(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<u32>(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<Data *>(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<Data *>(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<Data *>(pointer);
pointer += sizeof(Data);
auto gbpBuffer = reinterpret_cast<GbpBuffer *>(pointer);
std::shared_ptr<nvdrv::device::NvMap::NvMapObject> nvBuffer{};
auto nvmap = state.os->serviceManager.GetService<nvdrv::INvDrvServices>(Service::nvdrv_INvDrvServices)->GetDevice<nvdrv::device::NvMap>(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<gpu::GuestTexture>(state, nvBuffer->address + gbpBuffer->offset, gpu::texture::Dimensions(gbpBuffer->width, gbpBuffer->height), format, gpu::texture::TileMode::Block, gpu::texture::TileConfig{.surfaceWidth = static_cast<u16>(gbpBuffer->stride), .blockHeight = static_cast<u8>(1U << gbpBuffer->blockHeightLog2), .blockDepth = 1});
queue[data->slot] = std::make_shared<Buffer>(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<u32>();
auto code = request.Pop<TransactionCode>();
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<u64>(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<u32>(code));
}
out.WriteParcel(request.outputBuf.at(0));
}
void IHOSBinderDriver::AdjustRefcount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
request.Skip<u32>();
auto addVal = request.Pop<i32>();
auto type = request.Pop<i32>();
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<u32>(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;
}
}

View File

@ -1,10 +1,12 @@
#pragma once
#include "services/base_service.h"
#include "services/serviceman.h"
#include <gpu/parcel.h>
#include <services/base_service.h>
#include <services/serviceman.h>
#include <services/common/parcel.h>
#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<u32, std::shared_ptr<Buffer>> 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();
};
}

View File

@ -0,0 +1,54 @@
#pragma once
#include <common.h>
#include <services/common/parcel.h>
#include <services/nvdrv/devices/nvmap.h>
#include <gpu.h>
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<gpu::PresentationTexture> 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<gpu::PresentationTexture> &texture) : slot(slot), gbpBuffer(gbpBuffer), texture(texture) {};
};
}

View File

@ -0,0 +1,36 @@
#pragma once
#include <common.h>
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 <std::string, DisplayId> 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
};
}

View File

@ -0,0 +1,118 @@
#include "INvDrvServices.h"
#include <kernel/types/KProcess.h>
#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<device::NvDevice> object;
switch (type) {
case (device::NvDeviceType::nvhost_ctrl):
object = std::make_shared<device::NvHostCtrl>(state);
break;
case (device::NvDeviceType::nvhost_gpu):
case (device::NvDeviceType::nvhost_vic):
case (device::NvDeviceType::nvhost_nvdec):
object = std::make_shared<device::NvHostChannel>(state, type);
break;
case (device::NvDeviceType::nvhost_ctrl_gpu):
object = std::make_shared<device::NvHostCtrlGpu>(state);
break;
case (device::NvDeviceType::nvmap):
object = std::make_shared<device::NvMap>(state);
break;
case (device::NvDeviceType::nvhost_as_gpu):
object = std::make_shared<device::NvHostAsGpu>(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<u32>(OpenDevice(path));
response.Push<u32>(constant::status::Success);
}
void INvDrvServices::Ioctl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto fd = request.Pop<u32>();
auto cmd = request.Pop<u32>();
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<u32>(data.status);
} else {
device::IoctlData data(request.inputBuf.at(0));
fdMap.at(fd)->HandleIoctl(cmd, data);
response.Push<u32>(data.status);
}
} else {
device::IoctlData data(request.inputBuf.at(0), request.outputBuf.at(0));
fdMap.at(fd)->HandleIoctl(cmd, data);
response.Push<u32>(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<u32 *>(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<u32>(constant::status::Success);
}
void INvDrvServices::Initialize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
response.Push<u32>(constant::status::Success);
}
void INvDrvServices::QueryEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto fd = request.Pop<u32>();
auto eventId = request.Pop<u32>();
auto event = std::make_shared<type::KEvent>(state);
auto handle = state.process->InsertItem<type::KEvent>(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<u32>(constant::status::Success);
}
}

View File

@ -0,0 +1,91 @@
#pragma once
#include <services/base_service.h>
#include <services/serviceman.h>
#include <kernel/types/KTransferMemory.h>
#include <gpu.h>
#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<device::NvDeviceType, std::shared_ptr<device::NvDevice>> deviceMap; //!< A map from a NvDeviceType to the NvDevice object
std::unordered_map<u32, std::shared_ptr<device::NvDevice>> 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<typename objectClass>
std::shared_ptr<objectClass> GetDevice(u32 fd) {
try {
auto item = fdMap.at(fd);
return std::static_pointer_cast<objectClass>(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<typename objectClass>
std::shared_ptr<objectClass> GetDevice(device::NvDeviceType type) {
try {
auto item = deviceMap.at(type);
return std::static_pointer_cast<objectClass>(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);
};
}

View File

@ -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
*/

View File

@ -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, {}) {}
}

View File

@ -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)
*/

View File

@ -1,7 +1,7 @@
#include "nvhost_channel.h"
#include <kernel/types/KProcess.h>
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<NvChannelPriority>(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) {}
}

View File

@ -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)
*/

View File

@ -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, {}) {}
}

View File

@ -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)
*/

View File

@ -1,7 +1,7 @@
#include "nvhost_ctrl_gpu.h"
#include <kernel/types/KProcess.h>
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};

View File

@ -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)
*/

View File

@ -1,7 +1,7 @@
#include "nvmap.h"
#include <kernel/types/KProcess.h>
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

View File

@ -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)
*/

View File

@ -1,49 +0,0 @@
#include "nvdrv.h"
#include <kernel/types/KProcess.h>
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<u32>(state.gpu->OpenDevice(path));
response.Push<u32>(constant::status::Success);
}
void nvdrv::Ioctl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto fd = request.Pop<u32>();
auto cmd = request.Pop<u32>();
state.gpu->Ioctl(fd, cmd, request, response);
}
void nvdrv::Close(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
u32 fd = *reinterpret_cast<u32 *>(request.cmdArg);
state.gpu->CloseDevice(fd);
response.Push<u32>(constant::status::Success);
}
void nvdrv::Initialize(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
response.Push<u32>(constant::status::Success);
}
void nvdrv::QueryEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto fd = request.Pop<u32>();
auto eventId = request.Pop<u32>();
auto event = std::make_shared<type::KEvent>(state);
auto handle = state.process->InsertItem<type::KEvent>(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<u32>(constant::status::Success);
}
}

View File

@ -1,46 +0,0 @@
#pragma once
#include <services/base_service.h>
#include <services/serviceman.h>
#include <kernel/types/KTransferMemory.h>
#include <gpu.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 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);
};
}

View File

@ -1,70 +0,0 @@
#include "dispdrv.h"
#include <kernel/types/KProcess.h>
#include <gpu.h>
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<u32>();
auto code = request.Pop<TransactionCode>();
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<u64>(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<u32>(code));
}
out.WriteParcel(request.outputBuf.at(0));
}
void dispdrv::AdjustRefcount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
request.Skip<u32>();
auto addVal = request.Pop<i32>();
auto type = request.Pop<i32>();
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<u32>(constant::status::Success);
}
}

View File

@ -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<BaseService> ServiceManager::GetService(const Service serviceType) {
if (serviceMap.count(serviceType))
return serviceMap.at(serviceType);
std::shared_ptr<BaseService> ServiceManager::CreateService(const Service serviceType) {
auto serviceIter = serviceMap.find(serviceType);
if (serviceIter != serviceMap.end())
return (*serviceIter).second;
std::shared_ptr<BaseService> serviceObj;
switch (serviceType) {
@ -56,14 +57,14 @@ namespace skyline::service {
case Service::fssrv_IFileSystemProxy:
serviceObj = std::make_shared<fssrv::IFileSystemProxy>(state, *this);
break;
case Service::nvdrv:
serviceObj = std::make_shared<nvdrv::nvdrv>(state, *this);
case Service::nvdrv_INvDrvServices:
serviceObj = std::make_shared<nvdrv::INvDrvServices>(state, *this);
break;
case Service::vi_m:
serviceObj = std::make_shared<vi::vi_m>(state, *this);
case Service::visrv_IManagerRootService:
serviceObj = std::make_shared<visrv::IManagerRootService>(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<type::KSession>(GetService(serviceType)).handle;
return state.process->NewHandle<type::KSession>(CreateService(serviceType)).handle;
}
std::shared_ptr<BaseService> 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<BaseService> serviceObject, type::KSession &session, ipc::IpcResponse &response) { // NOLINT(performance-unnecessary-value-param)
void ServiceManager::RegisterService(std::shared_ptr<BaseService> 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<type::KSession>(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);
}

View File

@ -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<BaseService> GetService(const Service serviceType);
std::shared_ptr<BaseService> 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<BaseService> serviceObject, type::KSession &session, ipc::IpcResponse &response);
void RegisterService(std::shared_ptr<BaseService> 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<typename Type>
inline std::shared_ptr<Type> GetService(const Service serviceType) {
return std::static_pointer_cast<Type>(serviceMap.at(serviceType));
}
/**
* @brief Closes an existing session to a service

View File

@ -1,146 +0,0 @@
#include "vi_m.h"
#include <kernel/types/KProcess.h>
#include <services/nvnflinger/dispdrv.h>
#include <gpu/display.h>
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<u32, std::function<void(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>> &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<u64>(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<u64>(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<char *>(request.cmdArg));
state.logger->Debug("Setting display as: {}", displayName);
state.gpu->SetDisplay(displayName);
response.Push<u64>(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<InputStruct>();
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<u64>();
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<u32>(constant::status::Success);
}
void IApplicationDisplayService::SetLayerScalingMode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto scalingMode = request.Pop<u64>();
auto layerId = request.Pop<u64>();
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<u32>();
u64 displayId = request.Pop<u64>();
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<u64>(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<u32>(constant::status::Success);
}
void IManagerDisplayService::AddToLayerStack(skyline::kernel::type::KSession &session, skyline::kernel::ipc::IpcRequest &request, skyline::kernel::ipc::IpcResponse &response) {}
}

View File

@ -1,142 +0,0 @@
#pragma once
#include <services/base_service.h>
#include <services/serviceman.h>
#include <gpu.h>
#include <gpu/parcel.h>
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<u32, std::function<void(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>> &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);
};
}

View File

@ -0,0 +1,95 @@
#include <os.h>
#include <kernel/types/KProcess.h>
#include <services/hosbinder/IHOSBinderDriver.h>
#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<char *>(request.cmdArg));
state.logger->Debug("Setting display as: {}", displayName);
state.os->serviceManager.GetService<hosbinder::IHOSBinderDriver>(Service::hosbinder_IHOSBinderDriver)->SetDisplay(displayName);
response.Push<u64>(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<hosbinder::IHOSBinderDriver>(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<InputStruct>();
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<u64>();
state.logger->Debug("Closing Layer: {}", layerId);
auto hosBinder = state.os->serviceManager.GetService<hosbinder::IHOSBinderDriver>(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<u32>(constant::status::Success);
}
void IApplicationDisplayService::SetLayerScalingMode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto scalingMode = request.Pop<u64>();
auto layerId = request.Pop<u64>();
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);
}
}

View File

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

View File

@ -0,0 +1,41 @@
#include <os.h>
#include <services/hosbinder/IHOSBinderDriver.h>
#include <services/hosbinder/display.h>
#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<u32, std::function<void(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>> &vTable) : BaseService(state, manager, serviceType, serviceName, vTable) {}
void IDisplayService::CreateStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
request.Skip<u32>();
auto displayId = request.Pop<u64>();
state.logger->Debug("Creating Stray Layer on Display: {}", displayId);
auto hosBinder = state.os->serviceManager.GetService<hosbinder::IHOSBinderDriver>(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<u64>(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<u64>(parcel.WriteParcel(request.outputBuf.at(0)));
}
void IDisplayService::DestroyStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto layerId = request.Pop<u64>();
state.logger->Debug("Destroying Stray Layer: {}", layerId);
auto hosBinder = state.os->serviceManager.GetService<hosbinder::IHOSBinderDriver>(Service::hosbinder_IHOSBinderDriver);
if (hosBinder->layerStatus == hosbinder::LayerStatus::Uninitialized)
state.logger->Warn("The application is destroying an uninitialized layer");
hosBinder->layerStatus = hosbinder::LayerStatus::Uninitialized;
}
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <services/base_service.h>
#include <services/serviceman.h>
#include <services/common/parcel.h>
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 <u32, std::function<void(type::KSession & , ipc::IpcRequest & , ipc::IpcResponse & )>> &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);
};
}

View File

@ -0,0 +1,35 @@
#include <os.h>
#include <services/hosbinder/IHOSBinderDriver.h>
#include <services/hosbinder/display.h>
#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<u32>();
auto displayId = request.Pop<u64>();
state.logger->Debug("Creating Managed Layer on Display: {}", displayId);
auto hosBinder = state.os->serviceManager.GetService<hosbinder::IHOSBinderDriver>(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<u64>(0); // There's only one layer
}
void IManagerDisplayService::DestroyManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto layerId = request.Pop<u64>();
state.logger->Debug("Destroying Managed Layer: {}", layerId);
auto hosBinder = state.os->serviceManager.GetService<hosbinder::IHOSBinderDriver>(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) {}
}

View File

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

View File

@ -0,0 +1,13 @@
#include "IManagerRootService.h"
#include <kernel/types/KProcess.h>
#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);
}
}

View File

@ -0,0 +1,20 @@
#pragma once
#include <services/base_service.h>
#include <services/serviceman.h>
#include <gpu.h>
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);
};
}

View File

@ -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) {}
}

View File

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