diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be84dc12..52aa6c5b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,6 @@ name: CI -on: push +on: [push, pull_request] jobs: build: diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index ef8e679c..c7473c2c 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -113,6 +113,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/services/fssrv/IFile.cpp ${source_DIR}/skyline/services/fssrv/IStorage.cpp ${source_DIR}/skyline/services/nvdrv/INvDrvServices.cpp + ${source_DIR}/skyline/services/nvdrv/driver.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 @@ -120,6 +121,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp ${source_DIR}/skyline/services/nvdrv/devices/nvhost_syncpoint.cpp ${source_DIR}/skyline/services/hosbinder/IHOSBinderDriver.cpp + ${source_DIR}/skyline/services/hosbinder/GraphicBufferProducer.cpp ${source_DIR}/skyline/services/visrv/IDisplayService.cpp ${source_DIR}/skyline/services/visrv/IApplicationDisplayService.cpp ${source_DIR}/skyline/services/visrv/IManagerDisplayService.cpp diff --git a/app/src/main/cpp/skyline/gpu/texture.h b/app/src/main/cpp/skyline/gpu/texture.h index a7bb9c0b..794538c6 100644 --- a/app/src/main/cpp/skyline/gpu/texture.h +++ b/app/src/main/cpp/skyline/gpu/texture.h @@ -8,7 +8,7 @@ namespace skyline { namespace service::hosbinder { - class IHOSBinderDriver; + class GraphicBufferProducer; } namespace gpu { namespace texture { @@ -189,7 +189,7 @@ namespace skyline { return presentation; } - friend service::hosbinder::IHOSBinderDriver; + friend service::hosbinder::GraphicBufferProducer; }; /** diff --git a/app/src/main/cpp/skyline/services/am/controller/ISelfController.cpp b/app/src/main/cpp/skyline/services/am/controller/ISelfController.cpp index 1f01d0f5..8d5192f0 100644 --- a/app/src/main/cpp/skyline/services/am/controller/ISelfController.cpp +++ b/app/src/main/cpp/skyline/services/am/controller/ISelfController.cpp @@ -2,7 +2,7 @@ // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #include -#include +#include #include #include "ISelfController.h" @@ -61,10 +61,10 @@ namespace skyline::service::am { Result ISelfController::CreateManagedDisplayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { state.logger->Debug("Creating Managed Layer on Default Display"); - auto hosBinder = state.os->serviceManager.GetService("dispdrv"); - if (hosBinder->layerStatus != hosbinder::LayerStatus::Uninitialized) + auto producer = hosbinder::producer.lock(); + if (producer->layerStatus != hosbinder::LayerStatus::Uninitialized) throw exception("The application is creating more than one layer"); - hosBinder->layerStatus = hosbinder::LayerStatus::Managed; + producer->layerStatus = hosbinder::LayerStatus::Managed; response.Push(0); return {}; diff --git a/app/src/main/cpp/skyline/services/common/parcel.h b/app/src/main/cpp/skyline/services/common/parcel.h index 84eb6435..a9709b98 100644 --- a/app/src/main/cpp/skyline/services/common/parcel.h +++ b/app/src/main/cpp/skyline/services/common/parcel.h @@ -28,6 +28,7 @@ namespace skyline::service { public: std::vector data; //!< A vector filled with data in the parcel std::vector objects; //!< A vector filled with objects in the parcel + size_t dataOffset{}; //!< This is the offset of the data read from the parcel /** * @brief This constructor fills in the Parcel object with data from a IPC buffer @@ -52,16 +53,26 @@ namespace skyline::service { */ Parcel(const DeviceState &state); + /** + * @return A reference to an item from the top of data + */ + template + inline ValueType &Pop() { + ValueType &value = *reinterpret_cast(data.data() + dataOffset); + dataOffset += sizeof(ValueType); + return value; + } + /** * @brief Writes some data to the Parcel * @tparam ValueType The type of the object to write * @param value The object to be written */ template - void WriteData(const ValueType &value) { + void Push(const ValueType &value) { data.reserve(data.size() + sizeof(ValueType)); auto item = reinterpret_cast(&value); - for (uint index = 0; sizeof(ValueType) > index; index++) { + for (size_t index{}; sizeof(ValueType) > index; index++) { data.push_back(*item); item++; } @@ -73,10 +84,10 @@ namespace skyline::service { * @param value The object to be written */ template - void WriteObject(const ValueType &value) { + void PushObject(const ValueType &value) { objects.reserve(objects.size() + sizeof(ValueType)); auto item = reinterpret_cast(&value); - for (uint index = 0; sizeof(ValueType) > index; index++) { + for (size_t index{}; sizeof(ValueType) > index; index++) { objects.push_back(*item); item++; } diff --git a/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.cpp b/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.cpp new file mode 100644 index 00000000..0d7de4ca --- /dev/null +++ b/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.cpp @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include +#include +#include +#include +#include +#include +#include "GraphicBufferProducer.h" +#include "display.h" + +namespace skyline::service::hosbinder { + Buffer::Buffer(const GbpBuffer &gbpBuffer, const std::shared_ptr &texture) : gbpBuffer(gbpBuffer), texture(texture) {} + + GraphicBufferProducer::GraphicBufferProducer(const DeviceState &state) : state(state) {} + + void GraphicBufferProducer::RequestBuffer(Parcel &in, Parcel &out) { + u32 slot{in.Pop()}; + + out.Push(1); + out.Push(sizeof(GbpBuffer)); + out.Push(0); + out.Push(queue.at(slot)->gbpBuffer); + + state.logger->Debug("RequestBuffer: Slot: {}", slot, sizeof(GbpBuffer)); + } + + void GraphicBufferProducer::DequeueBuffer(Parcel &in, Parcel &out) { + u32 format{in.Pop()}; + u32 width{in.Pop()}; + u32 height{in.Pop()}; + u32 timestamp{in.Pop()}; + u32 usage{in.Pop()}; + + std::optional slot{std::nullopt}; + while (!slot) { + for (auto &buffer : queue) { + if (buffer.second->status == BufferStatus::Free && buffer.second->gbpBuffer.format == format && buffer.second->gbpBuffer.width == width && buffer.second->gbpBuffer.height == height && (buffer.second->gbpBuffer.usage & usage) == usage) { + slot = buffer.first; + buffer.second->status = BufferStatus::Dequeued; + break; + } + } + } + + out.Push(*slot); + out.Push(std::array{1, 0x24}); // Unknown + + state.logger->Debug("DequeueBuffer: Width: {}, Height: {}, Format: {}, Usage: {}, Timestamp: {}, Slot: {}", width, height, format, usage, timestamp, *slot); + } + + void GraphicBufferProducer::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; + nvdrv::Fence fence[4]; + } &data = in.Pop(); + + 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]() { + queue.at(slot)->status = BufferStatus::Free; + bufferEvent->Signal(); + }; + + buffer->texture->SynchronizeHost(); + state.gpu->presentationQueue.push(buffer->texture); + + struct { + u32 width; + u32 height; + u32 _pad_[3]; + } output{ + .width = buffer->gbpBuffer.width, + .height = buffer->gbpBuffer.height, + }; + out.Push(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 GraphicBufferProducer::CancelBuffer(Parcel &in) { + u32 slot{in.Pop()}; + //auto fences{in.Pop>()}; + + queue.at(slot)->status = BufferStatus::Free; + + state.logger->Debug("CancelBuffer: Slot: {}", slot); + } + + void GraphicBufferProducer::Connect(Parcel &out) { + struct { + u32 width{constant::HandheldResolutionW}; //!< The width of the display + u32 height{constant::HandheldResolutionH}; //!< The height of the display + u32 transformHint{}; //!< A hint of the transformation of the display + u32 pendingBuffers{}; //!< The number of pending buffers + u32 status{}; //!< The status of the buffer queue + } data{}; + out.Push(data); + state.logger->Debug("Connect"); + } + + void GraphicBufferProducer::SetPreallocatedBuffer(Parcel &in) { + struct Data { + u32 slot; + u32 _unk0_; + u32 length; + u32 _pad0_; + } &data = in.Pop(); + + auto& gbpBuffer = in.Pop(); + + std::shared_ptr nvBuffer{}; + + auto driver = nvdrv::driver.lock(); + auto nvmap = driver->GetDevice(nvdrv::device::NvDeviceType::nvmap); + + if (gbpBuffer.nvmapHandle) { + nvBuffer = nvmap->handleTable.at(gbpBuffer.nvmapHandle); + } else { + for (const auto &object : nvmap->handleTable) { + if (object.second->id == gbpBuffer.nvmapId) { + nvBuffer = object.second; + break; + } + } + if (!nvBuffer) + throw exception("A QueueBuffer request has an invalid NVMap Handle ({}) and ID ({})", gbpBuffer.nvmapHandle, gbpBuffer.nvmapId); + } + + gpu::texture::Format format; + switch (gbpBuffer.format) { + case WINDOW_FORMAT_RGBA_8888: + case WINDOW_FORMAT_RGBX_8888: + format = gpu::format::RGBA8888Unorm; + break; + case WINDOW_FORMAT_RGB_565: + format = gpu::format::RGB565Unorm; + break; + default: + throw exception("Unknown pixel format used for FB"); + } + + auto texture = std::make_shared(state, nvBuffer->address + gbpBuffer.offset, gpu::texture::Dimensions(gbpBuffer.width, gbpBuffer.height), format, gpu::texture::TileMode::Block, gpu::texture::TileConfig{.surfaceWidth = static_cast(gbpBuffer.stride), .blockHeight = static_cast(1U << gbpBuffer.blockHeightLog2), .blockDepth = 1}); + + queue[data.slot] = std::make_shared(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 GraphicBufferProducer::OnTransact(TransactionCode code, Parcel &in, Parcel &out) { + 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.Push(0); + break; + case TransactionCode::Connect: + Connect(out); + break; + case TransactionCode::Disconnect: + break; + case TransactionCode::SetPreallocatedBuffer: + SetPreallocatedBuffer(in); + break; + default: + throw exception("An unimplemented transaction was called: {}", static_cast(code)); + } + } + + void GraphicBufferProducer::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 GraphicBufferProducer::CloseDisplay() { + if (displayId == DisplayId::Null) + state.logger->Warn("Trying to close uninitiated display"); + displayId = DisplayId::Null; + } + + std::weak_ptr producer{}; +} diff --git a/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.h b/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.h new file mode 100644 index 00000000..337bc4be --- /dev/null +++ b/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.h @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include +#include +#include "display.h" + +namespace skyline::service::hosbinder { + /** + * @brief This internal struct contains all data 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]; + }; + + 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 A wrapper over GbpBuffer which contains additional state that we track for a buffer + */ + class Buffer { + public: + BufferStatus status{BufferStatus::Free}; + std::shared_ptr texture; + GbpBuffer gbpBuffer; + + Buffer(const GbpBuffer &gbpBuffer, const std::shared_ptr &texture); + }; + + /** + * @brief nvnflinger:dispdrv or nns::hosbinder::IHOSBinderDriver is responsible for writing buffers to the display + */ + class GraphicBufferProducer { + private: + const DeviceState &state; + std::unordered_map> queue; //!< A vector of shared pointers to all the queued buffers + + /** + * @brief Request for the GbpBuffer of a buffer + */ + void RequestBuffer(Parcel &in, Parcel &out); + + /** + * @brief Try to dequeue a free graphics buffer that has been consumed + */ + void DequeueBuffer(Parcel &in, Parcel &out); + + /** + * @brief Queue a buffer to be presented + */ + void QueueBuffer(Parcel &in, Parcel &out); + + /** + * @brief Remove a previously queued buffer + */ + void CancelBuffer(Parcel &in); + + /** + * @brief Query a few attributes of the graphic buffers + */ + void Connect(Parcel &out); + + /** + * @brief Attach a GPU buffer to a graphics buffer + */ + void SetPreallocatedBuffer(Parcel &in); + + 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 + + /** + * @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 + */ + enum class TransactionCode : u32 { + RequestBuffer = 1, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#281 + SetBufferCount = 2, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#293 + DequeueBuffer = 3, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#300 + DetachBuffer = 4, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#318 + DetachNextBuffer = 5, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#325 + AttachBuffer = 6, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#343 + QueueBuffer = 7, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#353 + CancelBuffer = 8, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#364 + Query = 9, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#372 + Connect = 10, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#381 + Disconnect = 11, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#396 + SetSidebandStream = 12, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#403 + AllocateBuffers = 13, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#413 + SetPreallocatedBuffer = 14, //!< No source on this but it's used to set a existing buffer according to libtransistor and libNX + }; + + GraphicBufferProducer(const DeviceState &state); + + /** + * @brief The handler for Binder IPC transactions with IGraphicBufferProducer (https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#277) + */ + void OnTransact(TransactionCode code, Parcel &in, Parcel &out); + + /** + * @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(); + }; + + extern std::weak_ptr producer; //!< A globally shared instance of the GraphicsBufferProducer +} diff --git a/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.cpp b/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.cpp index 6a68715c..92367304 100644 --- a/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.cpp +++ b/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.cpp @@ -1,210 +1,30 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) -#include -#include #include -#include -#include -#include #include "IHOSBinderDriver.h" -#include "display.h" +#include "GraphicBufferProducer.h" namespace skyline::service::hosbinder { - IHOSBinderDriver::IHOSBinderDriver(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, { + IHOSBinderDriver::IHOSBinderDriver(const DeviceState &state, ServiceManager &manager) : producer(hosbinder::producer.expired() ? std::make_shared(state) : hosbinder::producer.lock()), BaseService(state, manager, { {0x0, SFUNC(IHOSBinderDriver::TransactParcel)}, {0x1, SFUNC(IHOSBinderDriver::AdjustRefcount)}, {0x2, SFUNC(IHOSBinderDriver::GetNativeHandle)}, {0x3, SFUNC(IHOSBinderDriver::TransactParcel)} - }) {} - - void IHOSBinderDriver::RequestBuffer(Parcel &in, Parcel &out) { - u32 slot = *reinterpret_cast(in.data.data()); - out.WriteData(1); - out.WriteData(sizeof(GbpBuffer)); - out.WriteData(0); - out.WriteData(queue.at(slot)->gbpBuffer); - state.logger->Debug("RequestBuffer: Slot: {}", slot, sizeof(GbpBuffer)); - } - - void IHOSBinderDriver::DequeueBuffer(Parcel &in, Parcel &out) { - struct Data { - u32 format; - u32 width; - u32 height; - u32 timestamps; - u32 usage; - } *data = reinterpret_cast(in.data.data()); - - 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) == data->usage) { - slot = buffer.first; - buffer.second->status = BufferStatus::Dequeued; - break; - } - } - asm("yield"); - } - - struct { - u32 slot; - u32 _unk_[13]; - } output{ - .slot = static_cast(slot), - ._unk_ = {1, 0x24} - }; - 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; - nvdrv::Fence fence[4]; - } *data = reinterpret_cast(in.data.data()); - - 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; - nvdrv::Fence fence[4]; - } *data = reinterpret_cast(parcel.data.data()); - FreeBuffer(data->slot); - state.logger->Debug("CancelBuffer: Slot: {}", data->slot); - } - - void IHOSBinderDriver::SetPreallocatedBuffer(Parcel &parcel) { - auto pointer = parcel.data.data(); - struct Data { - u32 slot; - u32 _unk0_; - u32 length; - u32 _pad0_; - } *data = reinterpret_cast(pointer); - - pointer += sizeof(Data); - auto gbpBuffer = reinterpret_cast(pointer); - - std::shared_ptr nvBuffer{}; - auto nvmap = state.os->serviceManager.GetService("nvdrv:a")->GetDevice(nvdrv::device::NvDeviceType::nvmap); - - if (gbpBuffer->nvmapHandle) { - nvBuffer = nvmap->handleTable.at(gbpBuffer->nvmapHandle); - } else { - for (const auto &object : nvmap->handleTable) { - if (object.second->id == gbpBuffer->nvmapId) { - nvBuffer = object.second; - break; - } - } - if (!nvBuffer) - throw exception("A QueueBuffer request has an invalid NVMap Handle ({}) and ID ({})", gbpBuffer->nvmapHandle, gbpBuffer->nvmapId); - } - - gpu::texture::Format format; - switch (gbpBuffer->format) { - case WINDOW_FORMAT_RGBA_8888: - case WINDOW_FORMAT_RGBX_8888: - format = gpu::format::RGBA8888Unorm; - break; - case WINDOW_FORMAT_RGB_565: - format = gpu::format::RGB565Unorm; - break; - default: - throw exception("Unknown pixel format used for FB"); - } - - auto texture = std::make_shared(state, nvBuffer->address + gbpBuffer->offset, gpu::texture::Dimensions(gbpBuffer->width, gbpBuffer->height), format, gpu::texture::TileMode::Block, gpu::texture::TileConfig{.surfaceWidth = static_cast(gbpBuffer->stride), .blockHeight = static_cast(1U << gbpBuffer->blockHeightLog2), .blockDepth = 1}); - - queue[data->slot] = std::make_shared(data->slot, *gbpBuffer, texture->InitializePresentationTexture()); - state.gpu->bufferEvent->Signal(); - - state.logger->Debug("SetPreallocatedBuffer: Slot: {}, Magic: 0x{:X}, Width: {}, Height: {}, Stride: {}, Format: {}, Usage: {}, Index: {}, ID: {}, Handle: {}, Offset: 0x{:X}, Block Height: {}, Size: 0x{:X}", data->slot, gbpBuffer->magic, gbpBuffer->width, gbpBuffer->height, gbpBuffer->stride, gbpBuffer->format, gbpBuffer->usage, gbpBuffer->index, gbpBuffer->nvmapId, - gbpBuffer->nvmapHandle, gbpBuffer->offset, (1U << gbpBuffer->blockHeightLog2), gbpBuffer->size); + }) { + if (hosbinder::producer.expired()) + hosbinder::producer = producer; } Result IHOSBinderDriver::TransactParcel(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { auto layerId = request.Pop(); - auto code = request.Pop(); + auto code = request.Pop(); Parcel in(request.inputBuf.at(0), state, true); Parcel out(state); state.logger->Debug("TransactParcel: Layer ID: {}, Code: {}", layerId, code); - - switch (code) { - case TransactionCode::RequestBuffer: - RequestBuffer(in, out); - break; - case TransactionCode::DequeueBuffer: - DequeueBuffer(in, out); - break; - case TransactionCode::QueueBuffer: - QueueBuffer(in, out); - break; - case TransactionCode::CancelBuffer: - CancelBuffer(in); - break; - case TransactionCode::Query: - out.WriteData(0); - break; - case TransactionCode::Connect: { - ConnectParcel connect = { - .width = constant::HandheldResolutionW, - .height = constant::HandheldResolutionH, - .transformHint = 0, - .pendingBuffers = 0, - .status = 0, - }; - out.WriteData(connect); - break; - } - case TransactionCode::Disconnect: - break; - case TransactionCode::SetPreallocatedBuffer: - SetPreallocatedBuffer(in); - break; - default: - throw exception("An unimplemented transaction was called: {}", static_cast(code)); - } + producer->OnTransact(code, in, out); out.WriteParcel(request.outputBuf.at(0)); return {}; @@ -226,22 +46,4 @@ namespace skyline::service::hosbinder { return {}; } - - void IHOSBinderDriver::SetDisplay(const std::string &name) { - try { - const auto type = DisplayTypeMap.at(name); - if (displayId == DisplayId::Null) - displayId = type; - else - throw exception("Trying to change display type from non-null type"); - } catch (const std::out_of_range &) { - throw exception("The display with name: '{}' doesn't exist", name); - } - } - - void IHOSBinderDriver::CloseDisplay() { - if (displayId == DisplayId::Null) - state.logger->Warn("Trying to close uninitiated display"); - displayId = DisplayId::Null; - } } diff --git a/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.h b/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.h index bd247076..441dfe8a 100644 --- a/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.h +++ b/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.h @@ -5,87 +5,18 @@ #include #include -#include -#include "buffer.h" -#include "display.h" 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 - */ - enum class TransactionCode : u32 { - RequestBuffer = 1, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#281 - SetBufferCount = 2, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#293 - DequeueBuffer = 3, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#300 - DetachBuffer = 4, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#318 - DetachNextBuffer = 5, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#325 - AttachBuffer = 6, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#343 - QueueBuffer = 7, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#353 - CancelBuffer = 8, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#364 - Query = 9, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#372 - Connect = 10, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#381 - Disconnect = 11, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#396 - SetSidebandStream = 12, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#403 - AllocateBuffers = 13, //!< https://android.googlesource.com/platform/frameworks/native/+/8dc5539/libs/gui/IGraphicBufferProducer.cpp#413 - SetPreallocatedBuffer = 14, //!< No source on this but it's used to set a existing buffer according to libtransistor and libNX - }; + class GraphicBufferProducer; /** * @brief nvnflinger:dispdrv or nns::hosbinder::IHOSBinderDriver is responsible for writing buffers to the display */ class IHOSBinderDriver : public BaseService { private: - /** - * @brief This is the structure of the parcel used for TransactionCode::Connect - */ - struct ConnectParcel { - u32 width; //!< The width of the display - u32 height; //!< The height of the display - u32 transformHint; //!< A hint of the transformation of the display - u32 pendingBuffers; //!< The number of pending buffers - u32 status; //!< The status of the buffer queue - }; - - std::unordered_map> queue; //!< A vector of shared pointers to all the queued buffers - - /** - * @brief This the GbpBuffer struct of the specified buffer - */ - void RequestBuffer(Parcel &in, Parcel &out); - - /** - * @brief This returns the slot of a free buffer - */ - void DequeueBuffer(Parcel &in, Parcel &out); - - /** - * @brief This queues a buffer to be displayed - */ - void QueueBuffer(Parcel &in, Parcel &out); - - /** - * @brief This removes a previously queued buffer - */ - void CancelBuffer(Parcel &parcel); - - /** - * @brief This adds a pre-existing buffer to the queue - */ - void SetPreallocatedBuffer(Parcel &parcel); - - /** - * @brief This frees a buffer which is currently queued - * @param slotNo The slot of the buffer - */ - inline void FreeBuffer(u32 slotNo) { - queue.at(slotNo)->status = BufferStatus::Free; - } + std::shared_ptr producer; 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 - IHOSBinderDriver(const DeviceState &state, ServiceManager &manager); /** @@ -102,17 +33,5 @@ namespace skyline::service::hosbinder { * @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) */ Result GetNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); - - /** - * @brief This sets displayId to a specific display type - * @param name The name of the display - * @note displayId has to be DisplayId::Null or this will throw an exception - */ - void SetDisplay(const std::string &name); - - /** - * @brief This closes the display by setting displayId to DisplayId::Null - */ - void CloseDisplay(); }; } diff --git a/app/src/main/cpp/skyline/services/hosbinder/buffer.h b/app/src/main/cpp/skyline/services/hosbinder/buffer.h index 49b93563..bdc6d858 100644 --- a/app/src/main/cpp/skyline/services/hosbinder/buffer.h +++ b/app/src/main/cpp/skyline/services/hosbinder/buffer.h @@ -9,49 +9,4 @@ #include namespace skyline::service::hosbinder { - /** - * @brief This struct holds information about the graphics buffer (https://github.com/reswitched/libtransistor/blob/0f0c36227842c344d163922fc98ee76229e9f0ee/lib/display/graphic_buffer_queue.c#L66) - */ - struct GbpBuffer { - u32 magic; //!< The magic of the graphics buffer: 0x47424652 - u32 width; //!< The width of the buffer - u32 height; //!< The height of the buffer - u32 stride; //!< The stride of the buffer - u32 format; //!< The format of the buffer, this corresponds to AHardwareBuffer_Format - u32 usage; //!< The usage flags for the buffer - u32 _pad0_; - u32 index; //!< The index of the buffer - u32 _pad1_[3]; - u32 nvmapId; //!< The ID of the buffer in regards to /dev/nvmap - u32 _pad2_[8]; - u32 size; //!< The size of the buffer - u32 _pad3_[8]; - u32 nvmapHandle; //!< The handle of the buffer in regards to /dev/nvmap - u32 offset; //!< This is the offset of the pixel data in the GPU Buffer - u32 _pad4_; - u32 blockHeightLog2; //!< The log2 of the block height - u32 _pad5_[58]; - }; - - /** - * @brief The current status of a buffer - */ - enum class BufferStatus { - Free, //!< The buffer is free - Dequeued, //!< The buffer has been dequeued from the display - Queued, //!< The buffer is queued to be displayed - }; - - /** - * @brief This holds all relevant objects for a specific buffer - */ - class Buffer { - public: - u32 slot; //!< The slot the buffer is in - BufferStatus status{BufferStatus::Free}; //!< The status of this buffer - std::shared_ptr texture{}; //!< The underlying PresentationTexture of this buffer - GbpBuffer gbpBuffer; //!< The GbpBuffer object for this buffer - - Buffer(u32 slot, const GbpBuffer &gbpBuffer, const std::shared_ptr &texture) : slot(slot), gbpBuffer(gbpBuffer), texture(texture) {}; - }; } diff --git a/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.cpp b/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.cpp index 5e48e671..42fae047 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.cpp @@ -2,59 +2,12 @@ // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #include -#include "devices/nvhost_ctrl.h" -#include "devices/nvhost_ctrl_gpu.h" -#include "devices/nvhost_channel.h" -#include "devices/nvhost_as_gpu.h" #include "INvDrvServices.h" +#include "driver.h" +#include "devices/nvdevice.h" namespace skyline::service::nvdrv { - u32 INvDrvServices::OpenDevice(const std::string &path) { - state.logger->Debug("Opening NVDRV device ({}): {}", fdIndex, path); - auto type = device::nvDeviceMap.at(path); - for (const auto &device : fdMap) { - if (device.second->deviceType == type) { - device.second->refCount++; - fdMap[fdIndex] = device.second; - return fdIndex++; - } - } - - std::shared_ptr object; - switch (type) { - case device::NvDeviceType::nvhost_ctrl: - object = std::make_shared(state); - break; - - case device::NvDeviceType::nvhost_gpu: - case device::NvDeviceType::nvhost_vic: - case device::NvDeviceType::nvhost_nvdec: - object = std::make_shared(state, type); - break; - - case device::NvDeviceType::nvhost_ctrl_gpu: - object = std::make_shared(state); - break; - - case device::NvDeviceType::nvmap: - object = std::make_shared(state); - break; - - case device::NvDeviceType::nvhost_as_gpu: - object = std::make_shared(state); - break; - - default: - throw exception("Cannot find NVDRV device"); - } - - deviceMap[type] = object; - fdMap[fdIndex] = object; - - return fdIndex++; - } - - INvDrvServices::INvDrvServices(const DeviceState &state, ServiceManager &manager) : hostSyncpoint(state), BaseService(state, manager, { + INvDrvServices::INvDrvServices(const DeviceState &state, ServiceManager &manager) : driver(nvdrv::driver.expired() ? std::make_shared(state) : nvdrv::driver.lock()), BaseService(state, manager, { {0x0, SFUNC(INvDrvServices::Open)}, {0x1, SFUNC(INvDrvServices::Ioctl)}, {0x2, SFUNC(INvDrvServices::Close)}, @@ -62,13 +15,16 @@ namespace skyline::service::nvdrv { {0x4, SFUNC(INvDrvServices::QueryEvent)}, {0x8, SFUNC(INvDrvServices::SetAruidByPID)}, {0xD, SFUNC(INvDrvServices::SetGraphicsFirmwareMemoryMarginEnabled)} - }) {} + }) { + if (nvdrv::driver.expired()) + nvdrv::driver = driver; + } Result INvDrvServices::Open(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { auto buffer = request.inputBuf.at(0); auto path = state.process->GetString(buffer.address, buffer.size); - response.Push(OpenDevice(path)); + response.Push(driver->OpenDevice(path)); response.Push(device::NvStatus::Success); return {}; @@ -77,9 +33,10 @@ namespace skyline::service::nvdrv { Result INvDrvServices::Ioctl(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { auto fd = request.Pop(); auto cmd = request.Pop(); - state.logger->Debug("IOCTL on device: 0x{:X}, cmd: 0x{:X}", fd, cmd); + auto device = driver->GetDevice(fd); + // Strip the permissions from the command leaving only the ID cmd &= 0xFFFF; @@ -88,18 +45,18 @@ namespace skyline::service::nvdrv { if (request.inputBuf.empty()) { device::IoctlData data(request.outputBuf.at(0)); - fdMap.at(fd)->HandleIoctl(cmd, data); + device->HandleIoctl(cmd, data); response.Push(data.status); } else { device::IoctlData data(request.inputBuf.at(0)); - fdMap.at(fd)->HandleIoctl(cmd, data); + device->HandleIoctl(cmd, data); response.Push(data.status); } } else { device::IoctlData data(request.inputBuf.at(0), request.outputBuf.at(0)); - fdMap.at(fd)->HandleIoctl(cmd, data); + device->HandleIoctl(cmd, data); response.Push(data.status); } } catch (const std::out_of_range &) { @@ -113,15 +70,7 @@ namespace skyline::service::nvdrv { auto fd = request.Pop(); 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"); - } + driver->CloseDevice(fd); response.Push(device::NvStatus::Success); return {}; @@ -135,7 +84,8 @@ namespace skyline::service::nvdrv { Result INvDrvServices::QueryEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { auto fd = request.Pop(); auto eventId = request.Pop(); - auto device = fdMap.at(fd); + + auto device = driver->GetDevice(fd); auto event = device->QueryEvent(eventId); if (event != nullptr) { diff --git a/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h b/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h index 48621b6a..b7a4ac6e 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h +++ b/app/src/main/cpp/skyline/services/nvdrv/INvDrvServices.h @@ -5,63 +5,18 @@ #include #include -#include -#include -#include "devices/nvdevice.h" -#include "devices/nvhost_syncpoint.h" namespace skyline::service::nvdrv { + class Driver; + /** * @brief nvdrv or INvDrvServices is used to access the Nvidia GPU inside the Switch (https://switchbrew.org/wiki/NV_services#nvdrv.2C_nvdrv:a.2C_nvdrv:s.2C_nvdrv:t) */ class INvDrvServices : public BaseService { private: - std::unordered_map> deviceMap; //!< A map from a NvDeviceType to the NvDevice object - std::unordered_map> fdMap; //!< A map from an FD to a shared pointer to it's NvDevice object - u32 fdIndex{}; //!< Holds the index of a file descriptor - - /** - * @brief Open a specific device and return a FD - * @param path The path of the device to open an FD to - * @return The file descriptor to the device - */ - u32 OpenDevice(const std::string &path); - - /** - * @brief Returns a particular device with a specific FD - * @tparam objectClass The class of the device to return - * @param fd The file descriptor to retrieve - * @return A shared pointer to the device - */ - template - std::shared_ptr GetDevice(u32 fd) { - try { - auto item = fdMap.at(fd); - return std::static_pointer_cast(item); - } catch (std::out_of_range) { - throw exception("GetDevice was called with invalid file descriptor: 0x{:X}", fd); - } - } + std::shared_ptr driver; public: - NvHostSyncpoint hostSyncpoint; - - /** - * @brief Returns a particular device with a specific type - * @tparam objectClass The class of the device to return - * @param type The type of the device to return - * @return A shared pointer to the device - */ - template - std::shared_ptr GetDevice(device::NvDeviceType type) { - try { - auto item = deviceMap.at(type); - return std::static_pointer_cast(item); - } catch (std::out_of_range) { - throw exception("GetDevice was called with invalid type: 0x{:X}", type); - } - } - INvDrvServices(const DeviceState &state, ServiceManager &manager); /** diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.h b/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.h index 0748ef0b..3f3f5f4e 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.h +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvdevice.h @@ -10,6 +10,8 @@ #define NFUNC(function) std::bind(&function, this, std::placeholders::_1) namespace skyline::service::nvdrv::device { + using namespace kernel; + /** * @brief An enumeration of all the devices that can be opened by nvdrv */ diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp index 547c9dfb..48ba5853 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include "nvmap.h" #include "nvhost_as_gpu.h" @@ -70,7 +70,8 @@ namespace skyline::service::nvdrv::device { if (!region.nvmapHandle) return; - auto nvmap = state.os->serviceManager.GetService("nvdrv")->GetDevice(nvdrv::device::NvDeviceType::nvmap)->handleTable.at(region.nvmapHandle); + auto driver = nvdrv::driver.lock(); + auto nvmap = driver->GetDevice(nvdrv::device::NvDeviceType::nvmap)->handleTable.at(region.nvmapHandle); u64 mapPhysicalAddress = region.bufferOffset + nvmap->address; u64 mapSize = region.mappingSize ? region.mappingSize : nvmap->size; @@ -133,7 +134,8 @@ namespace skyline::service::nvdrv::device { for (auto entry : entries) { try { - auto nvmap = state.os->serviceManager.GetService("nvdrv")->GetDevice(nvdrv::device::NvDeviceType::nvmap)->handleTable.at(entry.nvmapHandle); + auto driver = nvdrv::driver.lock(); + auto nvmap = driver->GetDevice(nvdrv::device::NvDeviceType::nvmap)->handleTable.at(entry.nvmapHandle); u64 mapAddress = static_cast(entry.gpuOffset) << MinAlignmentShift; u64 mapPhysicalAddress = nvmap->address + (static_cast(entry.mapOffset) << MinAlignmentShift); diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.cpp index 73ec9aa8..73be2424 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_channel.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include "nvhost_channel.h" namespace skyline::service::nvdrv::device { @@ -19,7 +19,8 @@ namespace skyline::service::nvdrv::device { {0x481A, NFUNC(NvHostChannel::AllocGpfifoEx2)}, {0x4714, NFUNC(NvHostChannel::SetUserData)}, }) { - auto &hostSyncpoint = state.os->serviceManager.GetService("nvdrv")->hostSyncpoint; + auto driver = nvdrv::driver.lock(); + auto &hostSyncpoint = driver->hostSyncpoint; channelFence.id = hostSyncpoint.AllocateSyncpoint(false); channelFence.UpdateValue(hostSyncpoint); @@ -48,7 +49,8 @@ namespace skyline::service::nvdrv::device { Fence fence; } &args = state.process->GetReference(buffer.output.at(0).address); - auto &hostSyncpoint = state.os->serviceManager.GetService("nvdrv")->hostSyncpoint; + auto driver = nvdrv::driver.lock(); + auto &hostSyncpoint = driver->hostSyncpoint; if (args.flags.fenceWait) { if (args.flags.incrementWithValue) { @@ -104,7 +106,8 @@ namespace skyline::service::nvdrv::device { u32 reserved[3]; } &args = state.process->GetReference(buffer.input.at(0).address); - channelFence.UpdateValue(state.os->serviceManager.GetService("nvdrv")->hostSyncpoint); + auto driver = nvdrv::driver.lock(); + channelFence.UpdateValue(driver->hostSyncpoint); args.fence = channelFence; } diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.cpp index f0a7a6e9..9d155205 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.cpp +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include "nvhost_ctrl.h" namespace skyline::service::nvdrv::device { @@ -95,7 +95,8 @@ namespace skyline::service::nvdrv::device { return; } - auto &hostSyncpoint = state.os->serviceManager.GetService("nvdrv")->hostSyncpoint; + auto driver = nvdrv::driver.lock(); + auto &hostSyncpoint = driver->hostSyncpoint; // Check if the syncpoint has already expired using the last known values if (hostSyncpoint.HasSyncpointExpired(args.fence.id, args.fence.value)) { @@ -173,7 +174,8 @@ namespace skyline::service::nvdrv::device { event.state = NvHostEvent::State::Cancelled; - auto &hostSyncpoint = state.os->serviceManager.GetService("nvdrv")->hostSyncpoint; + auto driver = nvdrv::driver.lock(); + auto &hostSyncpoint = driver->hostSyncpoint; hostSyncpoint.UpdateMin(event.fence.id); } diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.h b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.h index b2c58954..86dc2767 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.h +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_ctrl.h @@ -31,7 +31,7 @@ namespace skyline { Cancelling = 2, Signaling = 3, Signaled = 4, - Cancelled = 5 + Cancelled = 5, }; NvHostEvent(const DeviceState &state); diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_syncpoint.h b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_syncpoint.h index b30769e6..d77816c5 100644 --- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_syncpoint.h +++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost_syncpoint.h @@ -3,8 +3,6 @@ #pragma once -#include -#include #include namespace skyline::service::nvdrv { diff --git a/app/src/main/cpp/skyline/services/nvdrv/driver.cpp b/app/src/main/cpp/skyline/services/nvdrv/driver.cpp new file mode 100644 index 00000000..35861dc2 --- /dev/null +++ b/app/src/main/cpp/skyline/services/nvdrv/driver.cpp @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include "driver.h" +#include "devices/nvhost_ctrl.h" +#include "devices/nvhost_ctrl_gpu.h" +#include "devices/nvmap.h" +#include "devices/nvhost_channel.h" +#include "devices/nvhost_as_gpu.h" + +namespace skyline::service::nvdrv { + Driver::Driver(const DeviceState &state) : state(state), hostSyncpoint(state) {} + + u32 Driver::OpenDevice(const std::string &path) { + state.logger->Debug("Opening NVDRV device ({}): {}", fdIndex, path); + auto type = device::nvDeviceMap.at(path); + for (const auto &device : fdMap) { + if (device.second->deviceType == type) { + device.second->refCount++; + fdMap[fdIndex] = device.second; + return fdIndex++; + } + } + + std::shared_ptr object; + switch (type) { + case device::NvDeviceType::nvhost_ctrl: + object = std::make_shared(state); + break; + + case device::NvDeviceType::nvhost_gpu: + case device::NvDeviceType::nvhost_vic: + case device::NvDeviceType::nvhost_nvdec: + object = std::make_shared(state, type); + break; + + case device::NvDeviceType::nvhost_ctrl_gpu: + object = std::make_shared(state); + break; + + case device::NvDeviceType::nvmap: + object = std::make_shared(state); + break; + + case device::NvDeviceType::nvhost_as_gpu: + object = std::make_shared(state); + break; + + default: + throw exception("Cannot find NVDRV device"); + } + + deviceMap[type] = object; + fdMap[fdIndex] = object; + + return fdIndex++; + } + + void Driver::CloseDevice(skyline::u32 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"); + } + } + + std::weak_ptr driver{}; +} diff --git a/app/src/main/cpp/skyline/services/nvdrv/driver.h b/app/src/main/cpp/skyline/services/nvdrv/driver.h new file mode 100644 index 00000000..bd669161 --- /dev/null +++ b/app/src/main/cpp/skyline/services/nvdrv/driver.h @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include "devices/nvhost_syncpoint.h" + +namespace skyline::service::nvdrv { + namespace device { + class NvDevice; + enum class NvDeviceType; + } + + /** + * @brief nvnflinger:dispdrv or nns::hosbinder::IHOSBinderDriver is responsible for writing buffers to the display + */ + class Driver { + private: + const DeviceState &state; + std::unordered_map> deviceMap; //!< A map from a NvDeviceType to the NvDevice object + std::unordered_map> fdMap; //!< A map from an FD to a shared pointer to it's NvDevice object + u32 fdIndex{}; //!< The index of a file descriptor + + public: + NvHostSyncpoint hostSyncpoint; + + Driver(const DeviceState &state); + + /** + * @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 Closes the specified device with it's file descriptor + */ + void CloseDevice(u32 fd); + + /** + * @brief Returns a particular device with a specific FD + * @tparam objectClass The class of the device to return + * @param fd The file descriptor to retrieve + * @return A shared pointer to the device + */ + template + std::shared_ptr GetDevice(u32 fd) { + try { + auto item = fdMap.at(fd); + return std::static_pointer_cast(item); + } catch (std::out_of_range) { + throw exception("GetDevice was called with invalid file descriptor: 0x{:X}", fd); + } + } + + /** + * @brief Returns a particular device with a specific type + * @tparam objectClass The class of the device to return + * @param type The type of the device to return + * @return A shared pointer to the device + */ + template + std::shared_ptr GetDevice(device::NvDeviceType type) { + try { + auto item = deviceMap.at(type); + return std::static_pointer_cast(item); + } catch (std::out_of_range) { + throw exception("GetDevice was called with invalid type: 0x{:X}", type); + } + } + }; + + extern std::weak_ptr driver; //!< A globally shared instance of the Driver +} diff --git a/app/src/main/cpp/skyline/services/serviceman.cpp b/app/src/main/cpp/skyline/services/serviceman.cpp index 7450032c..eaa80c72 100644 --- a/app/src/main/cpp/skyline/services/serviceman.cpp +++ b/app/src/main/cpp/skyline/services/serviceman.cpp @@ -100,7 +100,7 @@ namespace skyline::service { return serviceObject; } - void ServiceManager::RegisterService(std::shared_ptr serviceObject, type::KSession &session, ipc::IpcResponse &response, bool submodule, ServiceName name) { // NOLINT(performance-unnecessary-value-param) + void ServiceManager::RegisterService(std::shared_ptr serviceObject, type::KSession &session, ipc::IpcResponse &response) { // NOLINT(performance-unnecessary-value-param) std::lock_guard serviceGuard(mutex); KHandle handle{}; @@ -113,9 +113,6 @@ namespace skyline::service { response.moveHandles.push_back(handle); } - if (!submodule) - serviceMap[name] = serviceObject; - state.logger->Debug("Service has been registered: \"{}\" (0x{:X})", serviceObject->GetName(), handle); } diff --git a/app/src/main/cpp/skyline/services/serviceman.h b/app/src/main/cpp/skyline/services/serviceman.h index fddb9fcc..7d271f29 100644 --- a/app/src/main/cpp/skyline/services/serviceman.h +++ b/app/src/main/cpp/skyline/services/serviceman.h @@ -49,7 +49,7 @@ namespace skyline::service { * @param submodule If the registered service is a submodule or not * @param name The name of the service to register if it's not a submodule - it will be added to the service map */ - void RegisterService(std::shared_ptr serviceObject, type::KSession &session, ipc::IpcResponse &response, bool submodule = true, ServiceName name = {}); + void RegisterService(std::shared_ptr serviceObject, type::KSession &session, ipc::IpcResponse &response); /** * @param serviceType The type of the service diff --git a/app/src/main/cpp/skyline/services/visrv/IApplicationDisplayService.cpp b/app/src/main/cpp/skyline/services/visrv/IApplicationDisplayService.cpp index f53531cb..bb3ba2b9 100644 --- a/app/src/main/cpp/skyline/services/visrv/IApplicationDisplayService.cpp +++ b/app/src/main/cpp/skyline/services/visrv/IApplicationDisplayService.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "IApplicationDisplayService.h" #include "ISystemDisplayService.h" #include "IManagerDisplayService.h" @@ -25,12 +26,12 @@ namespace skyline::service::visrv { }) {} Result IApplicationDisplayService::GetRelayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - manager.RegisterService(SRVREG(hosbinder::IHOSBinderDriver), session, response, false, util::MakeMagic("dispdrv")); + manager.RegisterService(SRVREG(hosbinder::IHOSBinderDriver), session, response); return {}; } Result IApplicationDisplayService::GetIndirectDisplayTransactionService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { - manager.RegisterService(SRVREG(hosbinder::IHOSBinderDriver), session, response, false, util::MakeMagic("dispdrv")); + manager.RegisterService(SRVREG(hosbinder::IHOSBinderDriver), session, response); return {}; } @@ -47,7 +48,9 @@ namespace skyline::service::visrv { Result IApplicationDisplayService::OpenDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { std::string displayName(request.PopString()); state.logger->Debug("Setting display as: {}", displayName); - state.os->serviceManager.GetService("dispdrv")->SetDisplay(displayName); + + auto producer = hosbinder::producer.lock(); + producer->SetDisplay(displayName); response.Push(0); // There's only one display return {}; @@ -55,7 +58,8 @@ namespace skyline::service::visrv { Result IApplicationDisplayService::CloseDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { state.logger->Debug("Closing the display"); - state.os->serviceManager.GetService("dispdrv")->CloseDisplay(); + auto producer = hosbinder::producer.lock(); + producer->CloseDisplay(); return {}; } @@ -76,7 +80,7 @@ namespace skyline::service::visrv { .bufferId = 0, // As we only have one layer and buffer .string = "dispdrv" }; - parcel.WriteData(data); + parcel.Push(data); parcel.objects.resize(4); response.Push(parcel.WriteParcel(request.outputBuf.at(0))); @@ -87,10 +91,11 @@ namespace skyline::service::visrv { u64 layerId = request.Pop(); state.logger->Debug("Closing Layer: {}", layerId); - auto hosBinder = state.os->serviceManager.GetService("dispdrv"); - if (hosBinder->layerStatus == hosbinder::LayerStatus::Uninitialized) + auto producer = hosbinder::producer.lock(); + if (producer->layerStatus == hosbinder::LayerStatus::Uninitialized) state.logger->Warn("The application is destroying an uninitialized layer"); - hosBinder->layerStatus = hosbinder::LayerStatus::Uninitialized; + producer->layerStatus = hosbinder::LayerStatus::Uninitialized; + return {}; } diff --git a/app/src/main/cpp/skyline/services/visrv/IDisplayService.cpp b/app/src/main/cpp/skyline/services/visrv/IDisplayService.cpp index 7250e6c4..88d360e8 100644 --- a/app/src/main/cpp/skyline/services/visrv/IDisplayService.cpp +++ b/app/src/main/cpp/skyline/services/visrv/IDisplayService.cpp @@ -2,7 +2,7 @@ // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #include -#include +#include #include #include "IDisplayService.h" @@ -15,10 +15,11 @@ namespace skyline::service::visrv { state.logger->Debug("Creating Stray Layer on Display: {}", displayId); - auto hosBinder = state.os->serviceManager.GetService("dispdrv"); - if (hosBinder->layerStatus == hosbinder::LayerStatus::Stray) + auto producer = hosbinder::producer.lock(); + if (producer->layerStatus == hosbinder::LayerStatus::Stray) throw exception("The application is creating more than one stray layer"); - hosBinder->layerStatus = hosbinder::LayerStatus::Stray; + producer->layerStatus = hosbinder::LayerStatus::Stray; + response.Push(0); // There's only one layer Parcel parcel(state); @@ -28,7 +29,7 @@ namespace skyline::service::visrv { .bufferId = 0, // As we only have one layer and buffer .string = "dispdrv" }; - parcel.WriteData(data); + parcel.Push(data); response.Push(parcel.WriteParcel(request.outputBuf.at(0))); return {}; @@ -38,10 +39,11 @@ namespace skyline::service::visrv { auto layerId = request.Pop(); state.logger->Debug("Destroying Stray Layer: {}", layerId); - auto hosBinder = state.os->serviceManager.GetService("dispdrv"); - if (hosBinder->layerStatus == hosbinder::LayerStatus::Uninitialized) + auto producer = hosbinder::producer.lock(); + if (producer->layerStatus == hosbinder::LayerStatus::Uninitialized) state.logger->Warn("The application is destroying an uninitialized layer"); - hosBinder->layerStatus = hosbinder::LayerStatus::Uninitialized; + producer->layerStatus = hosbinder::LayerStatus::Uninitialized; + return {}; } } diff --git a/app/src/main/cpp/skyline/services/visrv/IManagerDisplayService.cpp b/app/src/main/cpp/skyline/services/visrv/IManagerDisplayService.cpp index 4f123dad..ff1f7ab9 100644 --- a/app/src/main/cpp/skyline/services/visrv/IManagerDisplayService.cpp +++ b/app/src/main/cpp/skyline/services/visrv/IManagerDisplayService.cpp @@ -2,7 +2,7 @@ // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) #include -#include +#include #include #include "IManagerDisplayService.h" @@ -19,10 +19,10 @@ namespace skyline::service::visrv { auto displayId = request.Pop(); state.logger->Debug("Creating Managed Layer on Display: {}", displayId); - auto hosBinder = state.os->serviceManager.GetService("dispdrv"); - if (hosBinder->layerStatus != hosbinder::LayerStatus::Uninitialized) + auto producer = hosbinder::producer.lock(); + if (producer->layerStatus != hosbinder::LayerStatus::Uninitialized) throw exception("The application is creating more than one layer"); - hosBinder->layerStatus = hosbinder::LayerStatus::Managed; + producer->layerStatus = hosbinder::LayerStatus::Managed; response.Push(0); // There's only one layer return {}; @@ -32,11 +32,10 @@ namespace skyline::service::visrv { auto layerId = request.Pop(); state.logger->Debug("Destroying Managed Layer: {}", layerId); - auto hosBinder = state.os->serviceManager.GetService("dispdrv"); - if (hosBinder->layerStatus == hosbinder::LayerStatus::Uninitialized) + auto producer = hosbinder::producer.lock(); + if (producer->layerStatus == hosbinder::LayerStatus::Uninitialized) state.logger->Warn("The application is destroying an uninitialized layer"); - - hosBinder->layerStatus = hosbinder::LayerStatus::Uninitialized; + producer->layerStatus = hosbinder::LayerStatus::Uninitialized; return {}; } diff --git a/app/src/main/java/emu/skyline/AppDialog.kt b/app/src/main/java/emu/skyline/AppDialog.kt index 56b052f3..28f240a6 100644 --- a/app/src/main/java/emu/skyline/AppDialog.kt +++ b/app/src/main/java/emu/skyline/AppDialog.kt @@ -53,7 +53,7 @@ class AppDialog : BottomSheetDialogFragment() { override fun onCreate(savedInstanceState : Bundle?) { super.onCreate(savedInstanceState) - item = arguments!!.getSerializable("item") as AppItem + item = requireArguments().getSerializable("item") as AppItem } /**