service: nvnflinger: Implement shared buffer
Co-authored-by: Liam <byteslice@airmail.cc>
This commit is contained in:
		| @@ -698,6 +698,8 @@ add_library(core STATIC | ||||
|     hle/service/nvnflinger/consumer_base.cpp | ||||
|     hle/service/nvnflinger/consumer_base.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.h | ||||
|     hle/service/nvnflinger/hos_binder_driver_server.cpp | ||||
|   | ||||
| @@ -45,13 +45,6 @@ public: | ||||
|         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 { | ||||
|         // Input | ||||
|         u32_le size{}; | ||||
| @@ -113,6 +106,13 @@ private: | ||||
|     NvResult IocParam(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::NvMap& file; | ||||
| }; | ||||
|   | ||||
| @@ -15,7 +15,7 @@ | ||||
|  | ||||
| namespace Service::android { | ||||
|  | ||||
| class GraphicBuffer; | ||||
| struct GraphicBuffer; | ||||
|  | ||||
| class BufferItem final { | ||||
| public: | ||||
|   | ||||
| @@ -13,7 +13,7 @@ | ||||
|  | ||||
| namespace Service::android { | ||||
|  | ||||
| class GraphicBuffer; | ||||
| struct GraphicBuffer; | ||||
|  | ||||
| enum class BufferState : u32 { | ||||
|     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) | ||||
| struct QueueBufferInput final { | ||||
|     explicit QueueBufferInput(InputParcel& parcel); | ||||
|     explicit QueueBufferInput() = default; | ||||
|  | ||||
|     void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_, | ||||
|                  NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_, | ||||
| @@ -34,7 +35,6 @@ struct QueueBufferInput final { | ||||
|         *fence_ = fence; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     s64 timestamp{}; | ||||
|     s32 is_auto_timestamp{}; | ||||
|     Common::Rectangle<s32> crop{}; | ||||
|   | ||||
| @@ -17,6 +17,7 @@ | ||||
| #include "core/hle/service/nvdrv/nvdrv.h" | ||||
| #include "core/hle/service/nvnflinger/buffer_item_consumer.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/nvnflinger.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)); | ||||
| } | ||||
|  | ||||
| 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 | ||||
|   | ||||
| @@ -45,6 +45,9 @@ class BufferQueueProducer; | ||||
|  | ||||
| namespace Service::Nvnflinger { | ||||
|  | ||||
| class FbShareBufferManager; | ||||
| class HosBinderDriverServer; | ||||
|  | ||||
| class Nvnflinger final { | ||||
| public: | ||||
|     explicit Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_); | ||||
| @@ -90,12 +93,16 @@ public: | ||||
|  | ||||
|     [[nodiscard]] s64 GetNextTicks() const; | ||||
|  | ||||
|     FbShareBufferManager& GetSystemBufferManager(); | ||||
|  | ||||
| private: | ||||
|     struct Layer { | ||||
|         std::unique_ptr<android::BufferQueueCore> core; | ||||
|         std::unique_ptr<android::BufferQueueProducer> producer; | ||||
|     }; | ||||
|  | ||||
|     friend class FbShareBufferManager; | ||||
|  | ||||
| private: | ||||
|     [[nodiscard]] std::unique_lock<std::mutex> Lock() const { | ||||
|         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> single_composition_event; | ||||
|  | ||||
|     std::unique_ptr<FbShareBufferManager> system_buffer_manager; | ||||
|  | ||||
|     std::shared_ptr<std::mutex> guard; | ||||
|  | ||||
|     Core::System& system; | ||||
|   | ||||
| @@ -20,6 +20,9 @@ public: | ||||
|     static constexpr Fence NoFence() { | ||||
|         Fence fence; | ||||
|         fence.fences[0].id = -1; | ||||
|         fence.fences[1].id = -1; | ||||
|         fence.fences[2].id = -1; | ||||
|         fence.fences[3].id = -1; | ||||
|         return fence; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -12,8 +12,7 @@ | ||||
|  | ||||
| namespace Service::android { | ||||
|  | ||||
| class GraphicBuffer final { | ||||
| public: | ||||
| struct GraphicBuffer final { | ||||
|     constexpr GraphicBuffer() = default; | ||||
|  | ||||
|     constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) | ||||
| @@ -77,7 +76,6 @@ public: | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     u32 magic{}; | ||||
|     s32 width{}; | ||||
|     s32 height{}; | ||||
|   | ||||
| @@ -20,9 +20,12 @@ | ||||
| #include "core/hle/kernel/k_readable_event.h" | ||||
| #include "core/hle/kernel/k_thread.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/nvdrv.h" | ||||
| #include "core/hle/service/nvnflinger/binder.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/nvnflinger.h" | ||||
| #include "core/hle/service/nvnflinger/parcel.h" | ||||
| @@ -131,8 +134,9 @@ private: | ||||
|  | ||||
| class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { | ||||
| public: | ||||
|     explicit ISystemDisplayService(Core::System& system_) | ||||
|         : ServiceFramework{system_, "ISystemDisplayService"} { | ||||
|     explicit ISystemDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_) | ||||
|         : ServiceFramework{system_, "ISystemDisplayService"}, nvnflinger{nvnflinger_} { | ||||
|         // clang-format off | ||||
|         static const FunctionInfo functions[] = { | ||||
|             {1200, nullptr, "GetZOrderCountMin"}, | ||||
|             {1202, nullptr, "GetZOrderCountMax"}, | ||||
| @@ -170,22 +174,126 @@ public: | ||||
|             {3217, nullptr, "SetDisplayCmuLuma"}, | ||||
|             {3218, nullptr, "SetDisplayCrcMode"}, | ||||
|             {6013, nullptr, "GetLayerPresentationSubmissionTimestamps"}, | ||||
|             {8225, nullptr, "GetSharedBufferMemoryHandleId"}, | ||||
|             {8250, nullptr, "OpenSharedLayer"}, | ||||
|             {8225, &ISystemDisplayService::GetSharedBufferMemoryHandleId, "GetSharedBufferMemoryHandleId"}, | ||||
|             {8250, &ISystemDisplayService::OpenSharedLayer, "OpenSharedLayer"}, | ||||
|             {8251, nullptr, "CloseSharedLayer"}, | ||||
|             {8252, nullptr, "ConnectSharedLayer"}, | ||||
|             {8252, &ISystemDisplayService::ConnectSharedLayer, "ConnectSharedLayer"}, | ||||
|             {8253, nullptr, "DisconnectSharedLayer"}, | ||||
|             {8254, nullptr, "AcquireSharedFrameBuffer"}, | ||||
|             {8255, nullptr, "PresentSharedFrameBuffer"}, | ||||
|             {8256, nullptr, "GetSharedFrameBufferAcquirableEvent"}, | ||||
|             {8254, &ISystemDisplayService::AcquireSharedFrameBuffer, "AcquireSharedFrameBuffer"}, | ||||
|             {8255, &ISystemDisplayService::PresentSharedFrameBuffer, "PresentSharedFrameBuffer"}, | ||||
|             {8256, &ISystemDisplayService::GetSharedFrameBufferAcquirableEvent, "GetSharedFrameBufferAcquirableEvent"}, | ||||
|             {8257, nullptr, "FillSharedFrameBufferColor"}, | ||||
|             {8258, nullptr, "CancelSharedFrameBuffer"}, | ||||
|             {9000, nullptr, "GetDp2hdmiController"}, | ||||
|         }; | ||||
|         // clang-format on | ||||
|         RegisterHandlers(functions); | ||||
|     } | ||||
|  | ||||
| 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) { | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|         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.Push<u32>(0); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     Nvnflinger::Nvnflinger& nvnflinger; | ||||
| }; | ||||
|  | ||||
| class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { | ||||
| @@ -453,7 +564,7 @@ private: | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.PushIpcInterface<ISystemDisplayService>(system); | ||||
|         rb.PushIpcInterface<ISystemDisplayService>(system, nv_flinger); | ||||
|     } | ||||
|  | ||||
|     void GetManagerDisplayService(HLERequestContext& ctx) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Narr the Reg
					Narr the Reg