mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2024-11-14 09:54:58 +00:00
service: nvnflinger: Implement shared buffer
Co-authored-by: Liam <byteslice@airmail.cc>
This commit is contained in:
parent
f1f3d490ef
commit
35f25882e0
12 changed files with 572 additions and 22 deletions
|
@ -698,6 +698,8 @@ add_library(core STATIC
|
||||||
hle/service/nvnflinger/consumer_base.cpp
|
hle/service/nvnflinger/consumer_base.cpp
|
||||||
hle/service/nvnflinger/consumer_base.h
|
hle/service/nvnflinger/consumer_base.h
|
||||||
hle/service/nvnflinger/consumer_listener.h
|
hle/service/nvnflinger/consumer_listener.h
|
||||||
|
hle/service/nvnflinger/fb_share_buffer_manager.cpp
|
||||||
|
hle/service/nvnflinger/fb_share_buffer_manager.h
|
||||||
hle/service/nvnflinger/graphic_buffer_producer.cpp
|
hle/service/nvnflinger/graphic_buffer_producer.cpp
|
||||||
hle/service/nvnflinger/graphic_buffer_producer.h
|
hle/service/nvnflinger/graphic_buffer_producer.h
|
||||||
hle/service/nvnflinger/hos_binder_driver_server.cpp
|
hle/service/nvnflinger/hos_binder_driver_server.cpp
|
||||||
|
|
|
@ -45,13 +45,6 @@ public:
|
||||||
IsSharedMemMapped = 6
|
IsSharedMemMapped = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
|
||||||
/// Id to use for the next handle that is created.
|
|
||||||
u32 next_handle = 0;
|
|
||||||
|
|
||||||
/// Id to use for the next object that is created.
|
|
||||||
u32 next_id = 0;
|
|
||||||
|
|
||||||
struct IocCreateParams {
|
struct IocCreateParams {
|
||||||
// Input
|
// Input
|
||||||
u32_le size{};
|
u32_le size{};
|
||||||
|
@ -113,6 +106,13 @@ private:
|
||||||
NvResult IocParam(std::span<const u8> input, std::span<u8> output);
|
NvResult IocParam(std::span<const u8> input, std::span<u8> output);
|
||||||
NvResult IocFree(std::span<const u8> input, std::span<u8> output);
|
NvResult IocFree(std::span<const u8> input, std::span<u8> output);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Id to use for the next handle that is created.
|
||||||
|
u32 next_handle = 0;
|
||||||
|
|
||||||
|
/// Id to use for the next object that is created.
|
||||||
|
u32 next_id = 0;
|
||||||
|
|
||||||
NvCore::Container& container;
|
NvCore::Container& container;
|
||||||
NvCore::NvMap& file;
|
NvCore::NvMap& file;
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
namespace Service::android {
|
namespace Service::android {
|
||||||
|
|
||||||
class GraphicBuffer;
|
struct GraphicBuffer;
|
||||||
|
|
||||||
class BufferItem final {
|
class BufferItem final {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
namespace Service::android {
|
namespace Service::android {
|
||||||
|
|
||||||
class GraphicBuffer;
|
struct GraphicBuffer;
|
||||||
|
|
||||||
enum class BufferState : u32 {
|
enum class BufferState : u32 {
|
||||||
Free = 0,
|
Free = 0,
|
||||||
|
|
351
src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
Normal file
351
src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/hle/kernel/k_process.h"
|
||||||
|
#include "core/hle/kernel/k_system_resource.h"
|
||||||
|
#include "core/hle/service/nvdrv/devices/nvmap.h"
|
||||||
|
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||||
|
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
|
||||||
|
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
|
||||||
|
#include "core/hle/service/nvnflinger/pixel_format.h"
|
||||||
|
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
|
||||||
|
#include "core/hle/service/vi/layer/vi_layer.h"
|
||||||
|
#include "core/hle/service/vi/vi_results.h"
|
||||||
|
|
||||||
|
namespace Service::Nvnflinger {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
|
||||||
|
std::unique_ptr<Kernel::KPageGroup>* out_page_group,
|
||||||
|
Core::System& system, u32 size) {
|
||||||
|
using Core::Memory::YUZU_PAGESIZE;
|
||||||
|
|
||||||
|
// Allocate memory for the system shared buffer.
|
||||||
|
// FIXME: Because the gmmu can only point to cpu addresses, we need
|
||||||
|
// to map this in the application space to allow it to be used.
|
||||||
|
// FIXME: Add proper smmu emulation.
|
||||||
|
// FIXME: This memory belongs to vi's .data section.
|
||||||
|
auto& kernel = system.Kernel();
|
||||||
|
auto* process = system.ApplicationProcess();
|
||||||
|
auto& page_table = process->GetPageTable();
|
||||||
|
|
||||||
|
// Hold a temporary page group reference while we try to map it.
|
||||||
|
auto pg = std::make_unique<Kernel::KPageGroup>(
|
||||||
|
kernel, std::addressof(kernel.GetSystemSystemResource().GetBlockInfoManager()));
|
||||||
|
|
||||||
|
// Allocate memory from secure pool.
|
||||||
|
R_TRY(kernel.MemoryManager().AllocateAndOpen(
|
||||||
|
pg.get(), size / YUZU_PAGESIZE,
|
||||||
|
Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure,
|
||||||
|
Kernel::KMemoryManager::Direction::FromBack)));
|
||||||
|
|
||||||
|
// Get bounds of where mapping is possible.
|
||||||
|
const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart());
|
||||||
|
const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE;
|
||||||
|
const auto state = Kernel::KMemoryState::Io;
|
||||||
|
const auto perm = Kernel::KMemoryPermission::UserReadWrite;
|
||||||
|
std::mt19937_64 rng{process->GetRandomEntropy(0)};
|
||||||
|
|
||||||
|
// Retry up to 64 times to map into alias code range.
|
||||||
|
Result res = ResultSuccess;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 64; i++) {
|
||||||
|
*out_map_address = alias_code_begin + ((rng() % alias_code_size) * YUZU_PAGESIZE);
|
||||||
|
res = page_table.MapPageGroup(*out_map_address, *pg, state, perm);
|
||||||
|
if (R_SUCCEEDED(res)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return failure, if necessary
|
||||||
|
R_UNLESS(i < 64, res);
|
||||||
|
|
||||||
|
// Return the mapped page group.
|
||||||
|
*out_page_group = std::move(pg);
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::span<u8> SerializeIoc(T& params) {
|
||||||
|
return std::span(reinterpret_cast<u8*>(std::addressof(params)), sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) {
|
||||||
|
// Create a handle.
|
||||||
|
Nvidia::Devices::nvmap::IocCreateParams create_in_params{
|
||||||
|
.size = size,
|
||||||
|
.handle = 0,
|
||||||
|
};
|
||||||
|
Nvidia::Devices::nvmap::IocCreateParams create_out_params{};
|
||||||
|
R_UNLESS(nvmap.IocCreate(SerializeIoc(create_in_params), SerializeIoc(create_out_params)) ==
|
||||||
|
Nvidia::NvResult::Success,
|
||||||
|
VI::ResultOperationFailed);
|
||||||
|
|
||||||
|
// Assign the output handle.
|
||||||
|
*out_nv_map_handle = create_out_params.handle;
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) {
|
||||||
|
// Free the handle.
|
||||||
|
Nvidia::Devices::nvmap::IocFreeParams free_in_params{
|
||||||
|
.handle = handle,
|
||||||
|
};
|
||||||
|
Nvidia::Devices::nvmap::IocFreeParams free_out_params{};
|
||||||
|
R_UNLESS(nvmap.IocFree(SerializeIoc(free_in_params), SerializeIoc(free_out_params)) ==
|
||||||
|
Nvidia::NvResult::Success,
|
||||||
|
VI::ResultOperationFailed);
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer,
|
||||||
|
u32 size) {
|
||||||
|
// Assign the allocated memory to the handle.
|
||||||
|
Nvidia::Devices::nvmap::IocAllocParams alloc_in_params{
|
||||||
|
.handle = handle,
|
||||||
|
.heap_mask = 0,
|
||||||
|
.flags = {},
|
||||||
|
.align = 0,
|
||||||
|
.kind = 0,
|
||||||
|
.address = GetInteger(buffer),
|
||||||
|
};
|
||||||
|
Nvidia::Devices::nvmap::IocAllocParams alloc_out_params{};
|
||||||
|
R_UNLESS(nvmap.IocAlloc(SerializeIoc(alloc_in_params), SerializeIoc(alloc_out_params)) ==
|
||||||
|
Nvidia::NvResult::Success,
|
||||||
|
VI::ResultOperationFailed);
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv,
|
||||||
|
Common::ProcessAddress buffer, u32 size) {
|
||||||
|
// Get the nvmap device.
|
||||||
|
auto nvmap_fd = nvdrv.Open("/dev/nvmap");
|
||||||
|
auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd);
|
||||||
|
ASSERT(nvmap != nullptr);
|
||||||
|
|
||||||
|
// Create a handle.
|
||||||
|
R_TRY(CreateNvMapHandle(out_handle, *nvmap, size));
|
||||||
|
|
||||||
|
// Ensure we maintain a clean state on failure.
|
||||||
|
ON_RESULT_FAILURE {
|
||||||
|
ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle)));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assign the allocated memory to the handle.
|
||||||
|
R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888;
|
||||||
|
constexpr u32 SharedBufferBlockLinearBpp = 4;
|
||||||
|
|
||||||
|
constexpr u32 SharedBufferBlockLinearWidth = 1280;
|
||||||
|
constexpr u32 SharedBufferBlockLinearHeight = 768;
|
||||||
|
constexpr u32 SharedBufferBlockLinearStride =
|
||||||
|
SharedBufferBlockLinearWidth * SharedBufferBlockLinearBpp;
|
||||||
|
constexpr u32 SharedBufferNumSlots = 7;
|
||||||
|
|
||||||
|
constexpr u32 SharedBufferWidth = 1280;
|
||||||
|
constexpr u32 SharedBufferHeight = 720;
|
||||||
|
constexpr u32 SharedBufferAsync = false;
|
||||||
|
|
||||||
|
constexpr u32 SharedBufferSlotSize =
|
||||||
|
SharedBufferBlockLinearWidth * SharedBufferBlockLinearHeight * SharedBufferBlockLinearBpp;
|
||||||
|
constexpr u32 SharedBufferSize = SharedBufferSlotSize * SharedBufferNumSlots;
|
||||||
|
|
||||||
|
constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] {
|
||||||
|
SharedMemoryPoolLayout layout{};
|
||||||
|
layout.num_slots = SharedBufferNumSlots;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < SharedBufferNumSlots; i++) {
|
||||||
|
layout.slots[i].buffer_offset = i * SharedBufferSlotSize;
|
||||||
|
layout.slots[i].size = SharedBufferSlotSize;
|
||||||
|
layout.slots[i].width = SharedBufferWidth;
|
||||||
|
layout.slots[i].height = SharedBufferHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return layout;
|
||||||
|
}();
|
||||||
|
|
||||||
|
void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) {
|
||||||
|
auto buffer = std::make_shared<android::GraphicBuffer>();
|
||||||
|
buffer->width = SharedBufferWidth;
|
||||||
|
buffer->height = SharedBufferHeight;
|
||||||
|
buffer->stride = SharedBufferBlockLinearStride;
|
||||||
|
buffer->format = SharedBufferBlockLinearFormat;
|
||||||
|
buffer->buffer_id = handle;
|
||||||
|
buffer->offset = slot * SharedBufferSlotSize;
|
||||||
|
ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
FbShareBufferManager::FbShareBufferManager(Core::System& system, Nvnflinger& flinger,
|
||||||
|
std::shared_ptr<Nvidia::Module> nvdrv)
|
||||||
|
: m_system(system), m_flinger(flinger), m_nvdrv(std::move(nvdrv)) {}
|
||||||
|
|
||||||
|
FbShareBufferManager::~FbShareBufferManager() = default;
|
||||||
|
|
||||||
|
Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u64 display_id) {
|
||||||
|
std::scoped_lock lk{m_guard};
|
||||||
|
|
||||||
|
// Ensure we have not already created a buffer.
|
||||||
|
R_UNLESS(m_buffer_id == 0, VI::ResultOperationFailed);
|
||||||
|
|
||||||
|
// Allocate memory and space for the shared buffer.
|
||||||
|
Common::ProcessAddress map_address;
|
||||||
|
R_TRY(AllocateIoForProcessAddressSpace(std::addressof(map_address),
|
||||||
|
std::addressof(m_buffer_page_group), m_system,
|
||||||
|
SharedBufferSize));
|
||||||
|
|
||||||
|
// Create an nvmap handle for the buffer and assign the memory to it.
|
||||||
|
R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, map_address,
|
||||||
|
SharedBufferSize));
|
||||||
|
|
||||||
|
// Record the display id.
|
||||||
|
m_display_id = display_id;
|
||||||
|
|
||||||
|
// Create a layer for the display.
|
||||||
|
m_layer_id = m_flinger.CreateLayer(m_display_id).value();
|
||||||
|
|
||||||
|
// Set up the buffer.
|
||||||
|
m_buffer_id = m_next_buffer_id++;
|
||||||
|
|
||||||
|
// Get the layer.
|
||||||
|
VI::Layer* layer = m_flinger.FindLayer(m_display_id, m_layer_id);
|
||||||
|
ASSERT(layer != nullptr);
|
||||||
|
|
||||||
|
// Get the producer and set preallocated buffers.
|
||||||
|
auto& producer = layer->GetBufferQueue();
|
||||||
|
MakeGraphicBuffer(producer, 0, m_buffer_nvmap_handle);
|
||||||
|
MakeGraphicBuffer(producer, 1, m_buffer_nvmap_handle);
|
||||||
|
|
||||||
|
// Assign outputs.
|
||||||
|
*out_buffer_id = m_buffer_id;
|
||||||
|
*out_layer_id = m_layer_id;
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
|
||||||
|
s32* out_nvmap_handle,
|
||||||
|
SharedMemoryPoolLayout* out_pool_layout,
|
||||||
|
u64 buffer_id,
|
||||||
|
u64 applet_resource_user_id) {
|
||||||
|
std::scoped_lock lk{m_guard};
|
||||||
|
|
||||||
|
R_UNLESS(m_buffer_id > 0, VI::ResultNotFound);
|
||||||
|
R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound);
|
||||||
|
|
||||||
|
*out_pool_layout = SharedBufferPoolLayout;
|
||||||
|
*out_buffer_size = SharedBufferSize;
|
||||||
|
*out_nvmap_handle = m_buffer_nvmap_handle;
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) {
|
||||||
|
// Ensure the layer id is valid.
|
||||||
|
R_UNLESS(m_layer_id > 0 && layer_id == m_layer_id, VI::ResultNotFound);
|
||||||
|
|
||||||
|
// Get the layer.
|
||||||
|
VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id);
|
||||||
|
R_UNLESS(layer != nullptr, VI::ResultNotFound);
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
|
*out_layer = layer;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FbShareBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence,
|
||||||
|
std::array<s32, 4>& out_slot_indexes,
|
||||||
|
s64* out_target_slot, u64 layer_id) {
|
||||||
|
std::scoped_lock lk{m_guard};
|
||||||
|
|
||||||
|
// Get the layer.
|
||||||
|
VI::Layer* layer;
|
||||||
|
R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id));
|
||||||
|
|
||||||
|
// Get the producer.
|
||||||
|
auto& producer = layer->GetBufferQueue();
|
||||||
|
|
||||||
|
// Get the next buffer from the producer.
|
||||||
|
s32 slot;
|
||||||
|
R_UNLESS(producer.DequeueBuffer(std::addressof(slot), out_fence, SharedBufferAsync != 0,
|
||||||
|
SharedBufferWidth, SharedBufferHeight,
|
||||||
|
SharedBufferBlockLinearFormat, 0) == android::Status::NoError,
|
||||||
|
VI::ResultOperationFailed);
|
||||||
|
|
||||||
|
// Assign remaining outputs.
|
||||||
|
*out_target_slot = slot;
|
||||||
|
out_slot_indexes = {0, 1, -1, -1};
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence,
|
||||||
|
Common::Rectangle<s32> crop_region,
|
||||||
|
u32 transform, s32 swap_interval,
|
||||||
|
u64 layer_id, s64 slot) {
|
||||||
|
std::scoped_lock lk{m_guard};
|
||||||
|
|
||||||
|
// Get the layer.
|
||||||
|
VI::Layer* layer;
|
||||||
|
R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id));
|
||||||
|
|
||||||
|
// Get the producer.
|
||||||
|
auto& producer = layer->GetBufferQueue();
|
||||||
|
|
||||||
|
// Request to queue the buffer.
|
||||||
|
std::shared_ptr<android::GraphicBuffer> buffer;
|
||||||
|
R_UNLESS(producer.RequestBuffer(static_cast<s32>(slot), std::addressof(buffer)) ==
|
||||||
|
android::Status::NoError,
|
||||||
|
VI::ResultOperationFailed);
|
||||||
|
|
||||||
|
// Queue the buffer to the producer.
|
||||||
|
android::QueueBufferInput input{};
|
||||||
|
android::QueueBufferOutput output{};
|
||||||
|
input.crop = crop_region;
|
||||||
|
input.fence = fence;
|
||||||
|
input.transform = static_cast<android::NativeWindowTransform>(transform);
|
||||||
|
input.swap_interval = swap_interval;
|
||||||
|
R_UNLESS(producer.QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) ==
|
||||||
|
android::Status::NoError,
|
||||||
|
VI::ResultOperationFailed);
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FbShareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event,
|
||||||
|
u64 layer_id) {
|
||||||
|
std::scoped_lock lk{m_guard};
|
||||||
|
|
||||||
|
// Get the layer.
|
||||||
|
VI::Layer* layer;
|
||||||
|
R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id));
|
||||||
|
|
||||||
|
// Get the producer.
|
||||||
|
auto& producer = layer->GetBufferQueue();
|
||||||
|
|
||||||
|
// Set the event.
|
||||||
|
*out_event = std::addressof(producer.GetNativeHandle());
|
||||||
|
|
||||||
|
// We succeeded.
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::Nvnflinger
|
65
src/core/hle/service/nvnflinger/fb_share_buffer_manager.h
Normal file
65
src/core/hle/service/nvnflinger/fb_share_buffer_manager.h
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/math_util.h"
|
||||||
|
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||||
|
#include "core/hle/service/nvnflinger/ui/fence.h"
|
||||||
|
|
||||||
|
namespace Kernel {
|
||||||
|
class KPageGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Service::Nvnflinger {
|
||||||
|
|
||||||
|
struct SharedMemorySlot {
|
||||||
|
u64 buffer_offset;
|
||||||
|
u64 size;
|
||||||
|
s32 width;
|
||||||
|
s32 height;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(SharedMemorySlot) == 0x18, "SharedMemorySlot has wrong size");
|
||||||
|
|
||||||
|
struct SharedMemoryPoolLayout {
|
||||||
|
s32 num_slots;
|
||||||
|
std::array<SharedMemorySlot, 0x10> slots;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size");
|
||||||
|
|
||||||
|
class FbShareBufferManager final {
|
||||||
|
public:
|
||||||
|
explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger,
|
||||||
|
std::shared_ptr<Nvidia::Module> nvdrv);
|
||||||
|
~FbShareBufferManager();
|
||||||
|
|
||||||
|
Result Initialize(u64* out_buffer_id, u64* out_layer_handle, u64 display_id);
|
||||||
|
Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle,
|
||||||
|
SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id,
|
||||||
|
u64 applet_resource_user_id);
|
||||||
|
Result AcquireSharedFrameBuffer(android::Fence* out_fence, std::array<s32, 4>& out_slots,
|
||||||
|
s64* out_target_slot, u64 layer_id);
|
||||||
|
Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region,
|
||||||
|
u32 transform, s32 swap_interval, u64 layer_id, s64 slot);
|
||||||
|
Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
u64 m_next_buffer_id = 1;
|
||||||
|
u64 m_display_id = 0;
|
||||||
|
u64 m_buffer_id = 0;
|
||||||
|
u64 m_layer_id = 0;
|
||||||
|
u32 m_buffer_nvmap_handle = 0;
|
||||||
|
SharedMemoryPoolLayout m_pool_layout = {};
|
||||||
|
|
||||||
|
std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group;
|
||||||
|
|
||||||
|
std::mutex m_guard;
|
||||||
|
Core::System& m_system;
|
||||||
|
Nvnflinger& m_flinger;
|
||||||
|
std::shared_ptr<Nvidia::Module> m_nvdrv;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::Nvnflinger
|
|
@ -19,6 +19,7 @@ class InputParcel;
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
struct QueueBufferInput final {
|
struct QueueBufferInput final {
|
||||||
explicit QueueBufferInput(InputParcel& parcel);
|
explicit QueueBufferInput(InputParcel& parcel);
|
||||||
|
explicit QueueBufferInput() = default;
|
||||||
|
|
||||||
void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_,
|
void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_,
|
||||||
NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_,
|
NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_,
|
||||||
|
@ -34,7 +35,6 @@ struct QueueBufferInput final {
|
||||||
*fence_ = fence;
|
*fence_ = fence;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
s64 timestamp{};
|
s64 timestamp{};
|
||||||
s32 is_auto_timestamp{};
|
s32 is_auto_timestamp{};
|
||||||
Common::Rectangle<s32> crop{};
|
Common::Rectangle<s32> crop{};
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||||
#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
|
#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
|
||||||
#include "core/hle/service/nvnflinger/buffer_queue_core.h"
|
#include "core/hle/service/nvnflinger/buffer_queue_core.h"
|
||||||
|
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
|
||||||
#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
|
#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
|
||||||
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||||
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
|
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
|
||||||
|
@ -331,4 +332,14 @@ s64 Nvnflinger::GetNextTicks() const {
|
||||||
return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
|
return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FbShareBufferManager& Nvnflinger::GetSystemBufferManager() {
|
||||||
|
const auto lock_guard = Lock();
|
||||||
|
|
||||||
|
if (!system_buffer_manager) {
|
||||||
|
system_buffer_manager = std::make_unique<FbShareBufferManager>(system, *this, nvdrv);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *system_buffer_manager;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Service::Nvnflinger
|
} // namespace Service::Nvnflinger
|
||||||
|
|
|
@ -45,6 +45,9 @@ class BufferQueueProducer;
|
||||||
|
|
||||||
namespace Service::Nvnflinger {
|
namespace Service::Nvnflinger {
|
||||||
|
|
||||||
|
class FbShareBufferManager;
|
||||||
|
class HosBinderDriverServer;
|
||||||
|
|
||||||
class Nvnflinger final {
|
class Nvnflinger final {
|
||||||
public:
|
public:
|
||||||
explicit Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
|
explicit Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
|
||||||
|
@ -90,12 +93,16 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] s64 GetNextTicks() const;
|
[[nodiscard]] s64 GetNextTicks() const;
|
||||||
|
|
||||||
|
FbShareBufferManager& GetSystemBufferManager();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Layer {
|
struct Layer {
|
||||||
std::unique_ptr<android::BufferQueueCore> core;
|
std::unique_ptr<android::BufferQueueCore> core;
|
||||||
std::unique_ptr<android::BufferQueueProducer> producer;
|
std::unique_ptr<android::BufferQueueProducer> producer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
friend class FbShareBufferManager;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] std::unique_lock<std::mutex> Lock() const {
|
[[nodiscard]] std::unique_lock<std::mutex> Lock() const {
|
||||||
return std::unique_lock{*guard};
|
return std::unique_lock{*guard};
|
||||||
|
@ -140,6 +147,8 @@ private:
|
||||||
std::shared_ptr<Core::Timing::EventType> multi_composition_event;
|
std::shared_ptr<Core::Timing::EventType> multi_composition_event;
|
||||||
std::shared_ptr<Core::Timing::EventType> single_composition_event;
|
std::shared_ptr<Core::Timing::EventType> single_composition_event;
|
||||||
|
|
||||||
|
std::unique_ptr<FbShareBufferManager> system_buffer_manager;
|
||||||
|
|
||||||
std::shared_ptr<std::mutex> guard;
|
std::shared_ptr<std::mutex> guard;
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
|
|
@ -20,6 +20,9 @@ public:
|
||||||
static constexpr Fence NoFence() {
|
static constexpr Fence NoFence() {
|
||||||
Fence fence;
|
Fence fence;
|
||||||
fence.fences[0].id = -1;
|
fence.fences[0].id = -1;
|
||||||
|
fence.fences[1].id = -1;
|
||||||
|
fence.fences[2].id = -1;
|
||||||
|
fence.fences[3].id = -1;
|
||||||
return fence;
|
return fence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,7 @@
|
||||||
|
|
||||||
namespace Service::android {
|
namespace Service::android {
|
||||||
|
|
||||||
class GraphicBuffer final {
|
struct GraphicBuffer final {
|
||||||
public:
|
|
||||||
constexpr GraphicBuffer() = default;
|
constexpr GraphicBuffer() = default;
|
||||||
|
|
||||||
constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
|
constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
|
||||||
|
@ -77,7 +76,6 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
u32 magic{};
|
u32 magic{};
|
||||||
s32 width{};
|
s32 width{};
|
||||||
s32 height{};
|
s32 height{};
|
||||||
|
|
|
@ -20,9 +20,12 @@
|
||||||
#include "core/hle/kernel/k_readable_event.h"
|
#include "core/hle/kernel/k_readable_event.h"
|
||||||
#include "core/hle/kernel/k_thread.h"
|
#include "core/hle/kernel/k_thread.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
|
#include "core/hle/service/nvdrv/devices/nvmap.h"
|
||||||
#include "core/hle/service/nvdrv/nvdata.h"
|
#include "core/hle/service/nvdrv/nvdata.h"
|
||||||
|
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||||
#include "core/hle/service/nvnflinger/binder.h"
|
#include "core/hle/service/nvnflinger/binder.h"
|
||||||
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
|
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
|
||||||
|
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
|
||||||
#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
|
#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
|
||||||
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||||
#include "core/hle/service/nvnflinger/parcel.h"
|
#include "core/hle/service/nvnflinger/parcel.h"
|
||||||
|
@ -131,8 +134,9 @@ private:
|
||||||
|
|
||||||
class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
|
class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
|
||||||
public:
|
public:
|
||||||
explicit ISystemDisplayService(Core::System& system_)
|
explicit ISystemDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_)
|
||||||
: ServiceFramework{system_, "ISystemDisplayService"} {
|
: ServiceFramework{system_, "ISystemDisplayService"}, nvnflinger{nvnflinger_} {
|
||||||
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{1200, nullptr, "GetZOrderCountMin"},
|
{1200, nullptr, "GetZOrderCountMin"},
|
||||||
{1202, nullptr, "GetZOrderCountMax"},
|
{1202, nullptr, "GetZOrderCountMax"},
|
||||||
|
@ -170,22 +174,126 @@ public:
|
||||||
{3217, nullptr, "SetDisplayCmuLuma"},
|
{3217, nullptr, "SetDisplayCmuLuma"},
|
||||||
{3218, nullptr, "SetDisplayCrcMode"},
|
{3218, nullptr, "SetDisplayCrcMode"},
|
||||||
{6013, nullptr, "GetLayerPresentationSubmissionTimestamps"},
|
{6013, nullptr, "GetLayerPresentationSubmissionTimestamps"},
|
||||||
{8225, nullptr, "GetSharedBufferMemoryHandleId"},
|
{8225, &ISystemDisplayService::GetSharedBufferMemoryHandleId, "GetSharedBufferMemoryHandleId"},
|
||||||
{8250, nullptr, "OpenSharedLayer"},
|
{8250, &ISystemDisplayService::OpenSharedLayer, "OpenSharedLayer"},
|
||||||
{8251, nullptr, "CloseSharedLayer"},
|
{8251, nullptr, "CloseSharedLayer"},
|
||||||
{8252, nullptr, "ConnectSharedLayer"},
|
{8252, &ISystemDisplayService::ConnectSharedLayer, "ConnectSharedLayer"},
|
||||||
{8253, nullptr, "DisconnectSharedLayer"},
|
{8253, nullptr, "DisconnectSharedLayer"},
|
||||||
{8254, nullptr, "AcquireSharedFrameBuffer"},
|
{8254, &ISystemDisplayService::AcquireSharedFrameBuffer, "AcquireSharedFrameBuffer"},
|
||||||
{8255, nullptr, "PresentSharedFrameBuffer"},
|
{8255, &ISystemDisplayService::PresentSharedFrameBuffer, "PresentSharedFrameBuffer"},
|
||||||
{8256, nullptr, "GetSharedFrameBufferAcquirableEvent"},
|
{8256, &ISystemDisplayService::GetSharedFrameBufferAcquirableEvent, "GetSharedFrameBufferAcquirableEvent"},
|
||||||
{8257, nullptr, "FillSharedFrameBufferColor"},
|
{8257, nullptr, "FillSharedFrameBufferColor"},
|
||||||
{8258, nullptr, "CancelSharedFrameBuffer"},
|
{8258, nullptr, "CancelSharedFrameBuffer"},
|
||||||
{9000, nullptr, "GetDp2hdmiController"},
|
{9000, nullptr, "GetDp2hdmiController"},
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void GetSharedBufferMemoryHandleId(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const u64 buffer_id = rp.PopRaw<u64>();
|
||||||
|
|
||||||
|
LOG_INFO(Service_VI, "called. buffer_id={:#x}", buffer_id);
|
||||||
|
|
||||||
|
struct OutputParameters {
|
||||||
|
s32 nvmap_handle;
|
||||||
|
u64 size;
|
||||||
|
};
|
||||||
|
|
||||||
|
OutputParameters out{};
|
||||||
|
Nvnflinger::SharedMemoryPoolLayout layout{};
|
||||||
|
const auto result = nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId(
|
||||||
|
&out.size, &out.nvmap_handle, &layout, buffer_id, 0);
|
||||||
|
|
||||||
|
ctx.WriteBuffer(&layout, sizeof(layout));
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 6};
|
||||||
|
rb.Push(result);
|
||||||
|
rb.PushRaw(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenSharedLayer(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const u64 layer_id = rp.PopRaw<u64>();
|
||||||
|
|
||||||
|
LOG_INFO(Service_VI, "(STUBBED) called. layer_id={:#x}", layer_id);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectSharedLayer(HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const u64 layer_id = rp.PopRaw<u64>();
|
||||||
|
|
||||||
|
LOG_INFO(Service_VI, "(STUBBED) called. layer_id={:#x}", layer_id);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetSharedFrameBufferAcquirableEvent(HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_VI, "called");
|
||||||
|
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const u64 layer_id = rp.PopRaw<u64>();
|
||||||
|
|
||||||
|
Kernel::KReadableEvent* event{};
|
||||||
|
const auto result = nvnflinger.GetSystemBufferManager().GetSharedFrameBufferAcquirableEvent(
|
||||||
|
&event, layer_id);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||||
|
rb.Push(result);
|
||||||
|
rb.PushCopyObjects(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AcquireSharedFrameBuffer(HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_VI, "called");
|
||||||
|
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const u64 layer_id = rp.PopRaw<u64>();
|
||||||
|
|
||||||
|
struct OutputParameters {
|
||||||
|
android::Fence fence;
|
||||||
|
std::array<s32, 4> slots;
|
||||||
|
s64 target_slot;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(OutputParameters) == 0x40, "OutputParameters has wrong size");
|
||||||
|
|
||||||
|
OutputParameters out{};
|
||||||
|
const auto result = nvnflinger.GetSystemBufferManager().AcquireSharedFrameBuffer(
|
||||||
|
&out.fence, out.slots, &out.target_slot, layer_id);
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 18};
|
||||||
|
rb.Push(result);
|
||||||
|
rb.PushRaw(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PresentSharedFrameBuffer(HLERequestContext& ctx) {
|
||||||
|
LOG_DEBUG(Service_VI, "called");
|
||||||
|
|
||||||
|
struct InputParameters {
|
||||||
|
android::Fence fence;
|
||||||
|
Common::Rectangle<s32> crop_region;
|
||||||
|
u32 window_transform;
|
||||||
|
s32 swap_interval;
|
||||||
|
u64 layer_id;
|
||||||
|
s64 surface_id;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(InputParameters) == 0x50, "InputParameters has wrong size");
|
||||||
|
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
auto input = rp.PopRaw<InputParameters>();
|
||||||
|
|
||||||
|
const auto result = nvnflinger.GetSystemBufferManager().PresentSharedFrameBuffer(
|
||||||
|
input.fence, input.crop_region, input.window_transform, input.swap_interval,
|
||||||
|
input.layer_id, input.surface_id);
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
}
|
||||||
|
|
||||||
void SetLayerZ(HLERequestContext& ctx) {
|
void SetLayerZ(HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const u64 layer_id = rp.Pop<u64>();
|
const u64 layer_id = rp.Pop<u64>();
|
||||||
|
@ -228,6 +336,9 @@ private:
|
||||||
rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games.
|
rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games.
|
||||||
rb.Push<u32>(0);
|
rb.Push<u32>(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Nvnflinger::Nvnflinger& nvnflinger;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
|
class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
|
||||||
|
@ -453,7 +564,7 @@ private:
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.PushIpcInterface<ISystemDisplayService>(system);
|
rb.PushIpcInterface<ISystemDisplayService>(system, nv_flinger);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetManagerDisplayService(HLERequestContext& ctx) {
|
void GetManagerDisplayService(HLERequestContext& ctx) {
|
||||||
|
|
Loading…
Reference in a new issue