Make GraphicBufferProducer use the new nvmap API

This commit is contained in:
Billy Laws 2021-07-17 17:44:48 +01:00
parent 5123be6604
commit 054a7aa91e
4 changed files with 61 additions and 28 deletions

View File

@ -6,13 +6,18 @@
#include <gpu.h>
#include <gpu/texture/format.h>
#include <soc.h>
#include <services/nvdrv/driver.h>
#include <services/nvdrv/devices/nvmap.h>
#include <services/common/fence.h>
#include "GraphicBufferProducer.h"
namespace skyline::service::hosbinder {
GraphicBufferProducer::GraphicBufferProducer(const DeviceState &state) : state(state), bufferEvent(std::make_shared<kernel::type::KEvent>(state, true)) {}
GraphicBufferProducer::GraphicBufferProducer(const DeviceState &state, nvdrv::core::NvMap &nvMap) : state(state), bufferEvent(std::make_shared<kernel::type::KEvent>(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<u32>::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<u32>::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<u32>::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<nvdrv::device::NvMap::NvMapObject> 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<i32>(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<gpu::GuestTexture>(state, nvBuffer->ptr + surface.offset, gpu::texture::Dimensions(surface.width, surface.height), format, tileMode, tileConfig)};
auto guestTexture{std::make_shared<gpu::GuestTexture>(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<u32>::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>(*graphicBuffer) : nullptr;
buffer.texture = {};
if (graphicBuffer) {
if (graphicBuffer->magic != GraphicBuffer::Magic)

View File

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

View File

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

View File

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