diff --git a/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.cpp b/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.cpp index 0fec3d7d..cbe103e3 100644 --- a/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.cpp +++ b/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.cpp @@ -6,13 +6,18 @@ #include #include #include -#include #include #include #include "GraphicBufferProducer.h" namespace skyline::service::hosbinder { - GraphicBufferProducer::GraphicBufferProducer(const DeviceState &state) : state(state), bufferEvent(std::make_shared(state, true)) {} + GraphicBufferProducer::GraphicBufferProducer(const DeviceState &state, nvdrv::core::NvMap &nvMap) : state(state), bufferEvent(std::make_shared(state, true)), nvMap(nvMap) {} + + void GraphicBufferProducer::FreeGraphicBufferNvMap(GraphicBuffer &buffer) { + auto surface{buffer.graphicHandle.surfaces.at(0)}; + u32 nvMapHandleId{surface.nvmapHandle ? surface.nvmapHandle : buffer.graphicHandle.nvmapId}; + nvMap.FreeHandle(nvMapHandleId, true); + } u32 GraphicBufferProducer::GetPendingBufferCount() { u32 count{}; @@ -64,6 +69,12 @@ namespace skyline::service::hosbinder { for (auto &slot : queue) { slot.state = BufferState::Free; slot.frameNumber = std::numeric_limits::max(); + + if (slot.texture) { + slot.texture = {}; + FreeGraphicBufferNvMap(*slot.graphicBuffer); + } + slot.graphicBuffer = nullptr; } } else if (preallocatedBufferCount < count) { @@ -160,6 +171,12 @@ namespace skyline::service::hosbinder { bufferSlot.state = BufferState::Free; bufferSlot.frameNumber = std::numeric_limits::max(); + + if (bufferSlot.texture) { + bufferSlot.texture = {}; + FreeGraphicBufferNvMap(*bufferSlot.graphicBuffer); + } + bufferSlot.graphicBuffer = nullptr; bufferEvent->Signal(); @@ -183,6 +200,12 @@ namespace skyline::service::hosbinder { bufferSlot->state = BufferState::Free; bufferSlot->frameNumber = std::numeric_limits::max(); + + if (bufferSlot->texture) { + bufferSlot->texture = {}; + FreeGraphicBufferNvMap(*bufferSlot->graphicBuffer); + } + graphicBuffer = *std::exchange(bufferSlot->graphicBuffer, nullptr); fence = AndroidFence{}; @@ -202,6 +225,11 @@ namespace skyline::service::hosbinder { } } + if (bufferSlot->texture) { + bufferSlot->texture = {}; + FreeGraphicBufferNvMap(*bufferSlot->graphicBuffer); + } + if (bufferSlot == queue.end()) { state.logger->Warn("Could not find any free slots to attach the graphic buffer to"); return AndroidStatus::NoMemory; @@ -304,27 +332,13 @@ namespace skyline::service::hosbinder { if (surface.scanFormat != NvDisplayScanFormat::Progressive) throw exception("Non-Progressive surfaces are not supported: {}", ToString(surface.scanFormat)); - std::shared_ptr nvBuffer{}; - { - auto driver{nvdrv::driver.lock()}; - auto nvmap{driver->nvMap.lock()}; - if (surface.nvmapHandle) { - nvBuffer = nvmap->GetObject(surface.nvmapHandle); - } else { - std::shared_lock nvmapLock(nvmap->mapMutex); - for (const auto &object : nvmap->maps) { - if (object->id == handle.nvmapId) { - nvBuffer = object; - break; - } - } - if (!nvBuffer) - throw exception("A QueueBuffer request has an invalid NvMap Handle ({}) and ID ({})", surface.nvmapHandle, handle.nvmapId); - } - } + // Duplicate the handle so it can't be freed by the guest + auto nvMapHandleObj{nvMap.GetHandle(surface.nvmapHandle ? surface.nvmapHandle : handle.nvmapId)}; + if (auto err{nvMapHandleObj->Duplicate(true)}; err != PosixResult::Success) + throw exception("Failed to duplicate graphic buffer NvMap handle: {}!", static_cast(err)); - if (surface.size > (nvBuffer->size - surface.offset)) - throw exception("Surface doesn't fit into NvMap mapping of size 0x{:X} when mapped at 0x{:X} -> 0x{:X}", nvBuffer->size, surface.offset, surface.offset + surface.size); + if (surface.size > (nvMapHandleObj->origSize - surface.offset)) + throw exception("Surface doesn't fit into NvMap mapping of size 0x{:X} when mapped at 0x{:X} -> 0x{:X}", nvMapHandleObj->origSize, surface.offset, surface.offset + surface.size); gpu::texture::TileMode tileMode; gpu::texture::TileConfig tileConfig{}; @@ -344,7 +358,7 @@ namespace skyline::service::hosbinder { throw exception("Legacy 16Bx16 tiled surfaces are not supported"); } - auto guestTexture{std::make_shared(state, nvBuffer->ptr + surface.offset, gpu::texture::Dimensions(surface.width, surface.height), format, tileMode, tileConfig)}; + auto guestTexture{std::make_shared(state, nvMapHandleObj->GetPointer() + surface.offset, gpu::texture::Dimensions(surface.width, surface.height), format, tileMode, tileConfig)}; buffer.texture = guestTexture->CreateTexture({}, vk::ImageTiling::eLinear); } @@ -529,6 +543,12 @@ namespace skyline::service::hosbinder { for (auto &slot : queue) { slot.state = BufferState::Free; slot.frameNumber = std::numeric_limits::max(); + + if (slot.texture) { + slot.texture = {}; + FreeGraphicBufferNvMap(*slot.graphicBuffer); + } + slot.graphicBuffer = nullptr; } @@ -544,12 +564,16 @@ namespace skyline::service::hosbinder { } auto &buffer{queue[slot]}; + if (buffer.texture) { + buffer.texture = {}; + FreeGraphicBufferNvMap(*buffer.graphicBuffer); + } + buffer.state = BufferState::Free; buffer.frameNumber = 0; buffer.wasBufferRequested = false; buffer.isPreallocated = graphicBuffer != nullptr; buffer.graphicBuffer = graphicBuffer ? std::make_unique(*graphicBuffer) : nullptr; - buffer.texture = {}; if (graphicBuffer) { if (graphicBuffer->magic != GraphicBuffer::Magic) diff --git a/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.h b/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.h index 8bd3bb00..2fd6591a 100644 --- a/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.h +++ b/app/src/main/cpp/skyline/services/hosbinder/GraphicBufferProducer.h @@ -14,6 +14,10 @@ namespace skyline::gpu { class Texture; } +namespace skyline::service::nvdrv::core { + class NvMap; +} + namespace skyline::service::hosbinder { /** * @url https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h;l=52-91 @@ -66,6 +70,9 @@ namespace skyline::service::hosbinder { AndroidPixelFormat defaultFormat{AndroidPixelFormat::RGBA8888}; //!< The assumed format of a buffer if none is supplied in DequeueBuffer NativeWindowApi connectedApi{NativeWindowApi::None}; //!< The API that the producer is currently connected to u64 frameNumber{}; //!< The amount of frames that have been presented so far + nvdrv::core::NvMap &nvMap; + + void FreeGraphicBufferNvMap(GraphicBuffer &buffer); /** * @return The amount of buffers which have been queued onto the consumer @@ -180,7 +187,7 @@ namespace skyline::service::hosbinder { SetPreallocatedBuffer = 14, //!< A transaction specific to HOS, see the implementation for a description of its functionality }; - GraphicBufferProducer(const DeviceState &state); + GraphicBufferProducer(const DeviceState &state, nvdrv::core::NvMap &nvmap); /** * @brief The handler for Binder IPC transactions with IGraphicBufferProducer diff --git a/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.cpp b/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.cpp index 237c9844..7489fda6 100644 --- a/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.cpp +++ b/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.cpp @@ -7,7 +7,7 @@ #include "GraphicBufferProducer.h" namespace skyline::service::hosbinder { - IHOSBinderDriver::IHOSBinderDriver(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {} + IHOSBinderDriver::IHOSBinderDriver(const DeviceState &state, ServiceManager &manager, nvdrv::core::NvMap &nvMap) : BaseService(state, manager), nvMap(nvMap) {} Result IHOSBinderDriver::TransactParcel(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { // We opted for just supporting a single layer and display as it's what basically all games use and wasting cycles on it is pointless @@ -122,7 +122,7 @@ namespace skyline::service::hosbinder { layerStrongReferenceCount = InitialStrongReferenceCount; layerWeakReferenceCount = 0; - layer.emplace(state); + layer.emplace(state, nvMap); return DefaultLayerId; } diff --git a/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.h b/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.h index 3ec8df52..80fcc6a1 100644 --- a/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.h +++ b/app/src/main/cpp/skyline/services/hosbinder/IHOSBinderDriver.h @@ -42,8 +42,10 @@ namespace skyline::service::hosbinder { constexpr static u32 DefaultBinderLayerHandle{1}; //!< The handle as assigned by SurfaceFlinger of the default layer std::optional layer; //!< The IGraphicBufferProducer backing the layer (NativeWindow) + nvdrv::core::NvMap &nvMap; + public: - IHOSBinderDriver(const DeviceState &state, ServiceManager &manager); + IHOSBinderDriver(const DeviceState &state, ServiceManager &manager, nvdrv::core::NvMap &nvMap); /** * @brief Emulates the transaction of parcels between a IGraphicBufferProducer and the application