diff options
Diffstat (limited to 'src')
60 files changed, 2984 insertions, 796 deletions
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 4afc1369a..9120cc178 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -119,6 +119,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Service, NPNS) \ SUB(Service, NS) \ SUB(Service, NVDRV) \ + SUB(Service, NVFlinger) \ SUB(Service, OLSC) \ SUB(Service, PCIE) \ SUB(Service, PCTL) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 2b6e4daa7..f803ab796 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -87,6 +87,7 @@ enum class Class : u8 { Service_NPNS, ///< The NPNS service Service_NS, ///< The NS services Service_NVDRV, ///< The NVDRV (Nvidia driver) service + Service_NVFlinger, ///< The NVFlinger service Service_OLSC, ///< The OLSC service Service_PCIE, ///< The PCIe service Service_PCTL, ///< The PCTL (Parental control) service diff --git a/src/common/math_util.h b/src/common/math_util.h index 510c4e56d..54485bf53 100644 --- a/src/common/math_util.h +++ b/src/common/math_util.h @@ -4,6 +4,7 @@ #pragma once +#include <algorithm> #include <cstdlib> #include <type_traits> @@ -20,10 +21,32 @@ struct Rectangle { constexpr Rectangle() = default; + constexpr Rectangle(T width, T height) : right(width), bottom(height) {} + constexpr Rectangle(T left_, T top_, T right_, T bottom_) : left(left_), top(top_), right(right_), bottom(bottom_) {} - [[nodiscard]] T GetWidth() const { + [[nodiscard]] constexpr T Left() const { + return left; + } + + [[nodiscard]] constexpr T Top() const { + return top; + } + + [[nodiscard]] constexpr T Right() const { + return right; + } + + [[nodiscard]] constexpr T Bottom() const { + return bottom; + } + + [[nodiscard]] constexpr bool IsEmpty() const { + return (GetWidth() <= 0) || (GetHeight() <= 0); + } + + [[nodiscard]] constexpr T GetWidth() const { if constexpr (std::is_floating_point_v<T>) { return std::abs(right - left); } else { @@ -31,7 +54,7 @@ struct Rectangle { } } - [[nodiscard]] T GetHeight() const { + [[nodiscard]] constexpr T GetHeight() const { if constexpr (std::is_floating_point_v<T>) { return std::abs(bottom - top); } else { @@ -39,18 +62,35 @@ struct Rectangle { } } - [[nodiscard]] Rectangle<T> TranslateX(const T x) const { + [[nodiscard]] constexpr Rectangle<T> TranslateX(const T x) const { return Rectangle{left + x, top, right + x, bottom}; } - [[nodiscard]] Rectangle<T> TranslateY(const T y) const { + [[nodiscard]] constexpr Rectangle<T> TranslateY(const T y) const { return Rectangle{left, top + y, right, bottom + y}; } - [[nodiscard]] Rectangle<T> Scale(const float s) const { + [[nodiscard]] constexpr Rectangle<T> Scale(const float s) const { return Rectangle{left, top, static_cast<T>(static_cast<float>(left + GetWidth()) * s), static_cast<T>(static_cast<float>(top + GetHeight()) * s)}; } + + [[nodiscard]] constexpr bool operator==(const Rectangle<T>& rhs) const { + return (left == rhs.left) && (top == rhs.top) && (right == rhs.right) && + (bottom == rhs.bottom); + } + + [[nodiscard]] constexpr bool operator!=(const Rectangle<T>& rhs) const { + return !operator==(rhs); + } + + [[nodiscard]] constexpr bool Intersect(const Rectangle<T>& with, Rectangle<T>* result) const { + result->left = std::max(left, with.left); + result->top = std::max(top, with.top); + result->right = std::min(right, with.right); + result->bottom = std::min(bottom, with.bottom); + return !result->IsEmpty(); + } }; template <typename T> diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a6f442316..6536d0544 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -535,10 +535,35 @@ add_library(core STATIC hle/service/nvdrv/nvmemp.h hle/service/nvdrv/syncpoint_manager.cpp hle/service/nvdrv/syncpoint_manager.h - hle/service/nvflinger/buffer_queue.cpp - hle/service/nvflinger/buffer_queue.h + hle/service/nvflinger/binder.h + hle/service/nvflinger/buffer_item.h + hle/service/nvflinger/buffer_item_consumer.cpp + hle/service/nvflinger/buffer_item_consumer.h + hle/service/nvflinger/buffer_queue_consumer.cpp + hle/service/nvflinger/buffer_queue_consumer.h + hle/service/nvflinger/buffer_queue_core.cpp + hle/service/nvflinger/buffer_queue_core.h + hle/service/nvflinger/buffer_queue_defs.h + hle/service/nvflinger/buffer_queue_producer.cpp + hle/service/nvflinger/buffer_queue_producer.h + hle/service/nvflinger/buffer_slot.h + hle/service/nvflinger/buffer_transform_flags.h + hle/service/nvflinger/consumer_base.cpp + hle/service/nvflinger/consumer_base.h + hle/service/nvflinger/consumer_listener.h + hle/service/nvflinger/graphic_buffer_producer.cpp + hle/service/nvflinger/graphic_buffer_producer.h + hle/service/nvflinger/hos_binder_driver_server.cpp + hle/service/nvflinger/hos_binder_driver_server.h hle/service/nvflinger/nvflinger.cpp hle/service/nvflinger/nvflinger.h + hle/service/nvflinger/parcel.h + hle/service/nvflinger/pixel_format.h + hle/service/nvflinger/producer_listener.h + hle/service/nvflinger/status.h + hle/service/nvflinger/ui/fence.h + hle/service/nvflinger/ui/graphic_buffer.h + hle/service/nvflinger/window.h hle/service/olsc/olsc.cpp hle/service/olsc/olsc.h hle/service/pcie/pcie.cpp diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 68f1e9060..9fad45fe1 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -38,18 +38,16 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& void nvdisp_disp0::OnOpen(DeviceFD fd) {} void nvdisp_disp0::OnClose(DeviceFD fd) {} -void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, - u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform, +void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, + u32 height, u32 stride, android::BufferTransformFlags transform, const Common::Rectangle<int>& crop_rect) { const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); LOG_TRACE(Service, "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", addr, offset, width, height, stride, format); - const auto pixel_format = static_cast<Tegra::FramebufferConfig::PixelFormat>(format); - const auto transform_flags = static_cast<Tegra::FramebufferConfig::TransformFlags>(transform); - const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, - stride, pixel_format, transform_flags, crop_rect}; + const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, + stride, format, transform, crop_rect}; system.GetPerfStats().EndSystemFrame(); system.GPU().SwapBuffers(&framebuffer); diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index de01e1d5f..30b5da429 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -9,7 +9,8 @@ #include "common/common_types.h" #include "common/math_util.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" -#include "core/hle/service/nvflinger/buffer_queue.h" +#include "core/hle/service/nvflinger/buffer_transform_flags.h" +#include "core/hle/service/nvflinger/pixel_format.h" namespace Service::Nvidia::Devices { @@ -31,8 +32,8 @@ public: void OnClose(DeviceFD fd) override; /// Performs a screen flip, drawing the buffer pointed to by the handle. - void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, - NVFlinger::BufferQueue::BufferTransformFlags transform, + void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, + u32 stride, android::BufferTransformFlags transform, const Common::Rectangle<int>& crop_rect); private: diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 0a043e386..dde5b1507 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -187,7 +187,7 @@ NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::ve return NvResult::Success; } -static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) { +static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) { return { Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, Tegra::SubmissionMode::Increasing), @@ -198,7 +198,8 @@ static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) { }; } -static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) { +static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence, + u32 add_increment) { std::vector<Tegra::CommandHeader> result{ Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, Tegra::SubmissionMode::Increasing), @@ -213,7 +214,7 @@ static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, return result; } -static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence, +static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence, u32 add_increment) { std::vector<Tegra::CommandHeader> result{ Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1, diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 3e4f3b6a7..b2e943e45 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -109,7 +109,7 @@ private: static_assert(sizeof(IoctlGetErrorNotification) == 16, "IoctlGetErrorNotification is incorrect size"); - static_assert(sizeof(Fence) == 8, "Fence is incorrect size"); + static_assert(sizeof(NvFence) == 8, "Fence is incorrect size"); struct IoctlAllocGpfifoEx { u32_le num_entries{}; @@ -127,7 +127,7 @@ private: u32_le num_entries{}; // in u32_le flags{}; // in u32_le unk0{}; // in (1 works) - Fence fence_out{}; // out + NvFence fence_out{}; // out u32_le unk1{}; // in u32_le unk2{}; // in u32_le unk3{}; // in @@ -153,13 +153,13 @@ private: BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt BitField<8, 1, u32_le> increment; // increment the returned fence } flags; - Fence fence_out{}; // returned new fence object for others to wait on + NvFence fence_out{}; // returned new fence object for others to wait on u32 AddIncrementValue() const { return flags.add_increment.Value() << 1; } }; - static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence), + static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence), "IoctlSubmitGpfifo is incorrect size"); struct IoctlGetWaitbase { @@ -194,7 +194,7 @@ private: std::shared_ptr<nvmap> nvmap_dev; SyncpointManager& syncpoint_manager; - Fence channel_fence; + NvFence channel_fence; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h index 5ab221fc1..3069c3c80 100644 --- a/src/core/hle/service/nvdrv/nvdata.h +++ b/src/core/hle/service/nvdrv/nvdata.h @@ -16,17 +16,11 @@ using DeviceFD = s32; constexpr DeviceFD INVALID_NVDRV_FD = -1; -struct Fence { +struct NvFence { s32 id; u32 value; }; - -static_assert(sizeof(Fence) == 8, "Fence has wrong size"); - -struct MultiFence { - u32 num_fences; - std::array<Fence, 4> fences; -}; +static_assert(sizeof(NvFence) == 8, "NvFence has wrong size"); enum class NvResult : u32 { Success = 0x0, diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index a5af5b785..11cf6c0d1 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -12,6 +12,7 @@ #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/syncpoint_manager.h" +#include "core/hle/service/nvflinger/ui/fence.h" #include "core/hle/service/service.h" namespace Core { @@ -37,7 +38,7 @@ class nvdevice; /// Represents an Nvidia event struct NvEvent { Kernel::KEvent* event{}; - Fence fence{}; + NvFence fence{}; }; struct EventInterface { diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h new file mode 100644 index 000000000..7d0d4d819 --- /dev/null +++ b/src/core/hle/service/nvflinger/binder.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2014 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/binder/IBinder.h + +#pragma once + +#include "common/common_types.h" + +namespace Kernel { +class HLERequestContext; +class KReadableEvent; +} // namespace Kernel + +namespace Service::android { + +enum class TransactionId { + RequestBuffer = 1, + SetBufferCount = 2, + DequeueBuffer = 3, + DetachBuffer = 4, + DetachNextBuffer = 5, + AttachBuffer = 6, + QueueBuffer = 7, + CancelBuffer = 8, + Query = 9, + Connect = 10, + Disconnect = 11, + AllocateBuffers = 13, + SetPreallocatedBuffer = 14, + GetBufferHistory = 17, +}; + +class IBinder { +public: + virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, + u32 flags) = 0; + virtual Kernel::KReadableEvent& GetNativeHandle() = 0; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_item.h b/src/core/hle/service/nvflinger/buffer_item.h new file mode 100644 index 000000000..64b82b851 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_item.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2014 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItem.h + +#pragma once + +#include <memory> + +#include "common/common_types.h" +#include "common/math_util.h" +#include "core/hle/service/nvflinger/ui/fence.h" +#include "core/hle/service/nvflinger/window.h" + +namespace Service::android { + +class GraphicBuffer; + +class BufferItem final { +public: + constexpr BufferItem() = default; + + std::shared_ptr<GraphicBuffer> graphic_buffer; + Fence fence; + Common::Rectangle<s32> crop; + NativeWindowTransform transform{}; + u32 scaling_mode{}; + s64 timestamp{}; + bool is_auto_timestamp{}; + u64 frame_number{}; + + // The default value for buf, used to indicate this doesn't correspond to a slot. + static constexpr s32 INVALID_BUFFER_SLOT = -1; + union { + s32 slot{INVALID_BUFFER_SLOT}; + s32 buf; + }; + + bool is_droppable{}; + bool acquire_called{}; + bool transform_to_display_inverse{}; + s32 swap_interval{}; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp new file mode 100644 index 000000000..7f32c0775 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2012 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferItemConsumer.cpp + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/service/nvflinger/buffer_item.h" +#include "core/hle/service/nvflinger/buffer_item_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_consumer.h" + +namespace Service::android { + +BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_) + : ConsumerBase{std::move(consumer_)} {} + +Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, + bool wait_for_fence) { + if (!item) { + return Status::BadValue; + } + + std::unique_lock lock(mutex); + + if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) { + if (status != Status::NoBufferAvailable) { + LOG_ERROR(Service_NVFlinger, "Failed to acquire buffer: {}", status); + } + return status; + } + + if (wait_for_fence) { + UNIMPLEMENTED(); + } + + item->graphic_buffer = slots[item->slot].graphic_buffer; + + return Status::NoError; +} + +Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) { + std::unique_lock lock(mutex); + + if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence); + status != Status::NoError) { + LOG_ERROR(Service_NVFlinger, "Failed to add fence: {}", status); + } + + if (const auto status = ReleaseBufferLocked(item.buf, item.graphic_buffer); + status != Status::NoError) { + LOG_WARNING(Service_NVFlinger, "Failed to release buffer: {}", status); + return status; + } + + return Status::NoError; +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.h b/src/core/hle/service/nvflinger/buffer_item_consumer.h new file mode 100644 index 000000000..536db81e2 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_item_consumer.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2012 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItemConsumer.h + +#pragma once + +#include <chrono> +#include <memory> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/consumer_base.h" +#include "core/hle/service/nvflinger/status.h" + +namespace Service::android { + +class BufferItem; + +class BufferItemConsumer final : public ConsumerBase { +public: + explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer); + Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when, + bool wait_for_fence = true); + Status ReleaseBuffer(const BufferItem& item, Fence& release_fence); +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp new file mode 100644 index 000000000..677bec932 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2014 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp + +#include "common/logging/log.h" +#include "core/hle/service/nvflinger/buffer_item.h" +#include "core/hle/service/nvflinger/buffer_queue_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_core.h" +#include "core/hle/service/nvflinger/producer_listener.h" + +namespace Service::android { + +BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_) + : core{std::move(core_)}, slots{core->slots} {} + +BufferQueueConsumer::~BufferQueueConsumer() = default; + +Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, + std::chrono::nanoseconds expected_present, + u64 max_frame_number) { + s32 num_dropped_buffers{}; + + std::shared_ptr<IProducerListener> listener; + { + std::unique_lock lock(core->mutex); + + // Check that the consumer doesn't currently have the maximum number of buffers acquired. + const s32 num_acquired_buffers{ + static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) { + return slot.buffer_state == BufferState::Acquired; + }))}; + + if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) { + LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})", + num_acquired_buffers, core->max_acquired_buffer_count); + return Status::InvalidOperation; + } + + // Check if the queue is empty. + if (core->queue.empty()) { + return Status::NoBufferAvailable; + } + + auto front(core->queue.begin()); + + // If expected_present is specified, we may not want to return a buffer yet. + if (expected_present.count() != 0) { + constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second + + // The expected_present argument indicates when the buffer is expected to be + // presented on-screen. + while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) { + const auto& buffer_item{core->queue[1]}; + + // If dropping entry[0] would leave us with a buffer that the consumer is not yet + // ready for, don't drop it. + if (max_frame_number && buffer_item.frame_number > max_frame_number) { + break; + } + + // If entry[1] is timely, drop entry[0] (and repeat). + const auto desired_present = buffer_item.timestamp; + if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC || + desired_present > expected_present.count()) { + // This buffer is set to display in the near future, or desired_present is + // garbage. + LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present, + expected_present.count()); + break; + } + + LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present, + expected_present.count(), core->queue.size()); + + if (core->StillTracking(*front)) { + // Front buffer is still in mSlots, so mark the slot as free + slots[front->slot].buffer_state = BufferState::Free; + core->free_buffers.push_back(front->slot); + listener = core->connected_producer_listener; + ++num_dropped_buffers; + } + + core->queue.erase(front); + front = core->queue.begin(); + } + + // See if the front buffer is ready to be acquired. + const auto desired_present = front->timestamp; + const auto buffer_is_due = + desired_present <= expected_present.count() || + desired_present > expected_present.count() + MAX_REASONABLE_NSEC; + const auto consumer_is_ready = + max_frame_number > 0 ? front->frame_number <= max_frame_number : true; + + if (!buffer_is_due || !consumer_is_ready) { + LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present, + expected_present.count()); + return Status::PresentLater; + } + + LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present, + expected_present.count()); + } + + const auto slot = front->slot; + *out_buffer = *front; + + LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot); + + // If the front buffer is still being tracked, update its slot state + if (core->StillTracking(*front)) { + slots[slot].acquire_called = true; + slots[slot].needs_cleanup_on_release = false; + slots[slot].buffer_state = BufferState::Acquired; + slots[slot].fence = Fence::NoFence(); + } + + // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr + // to avoid unnecessarily remapping this buffer on the consumer side. + if (out_buffer->acquire_called) { + out_buffer->graphic_buffer = nullptr; + } + + core->queue.erase(front); + + // We might have freed a slot while dropping old buffers, or the producer may be blocked + // waiting for the number of buffers in the queue to decrease. + core->SignalDequeueCondition(); + } + + if (listener != nullptr) { + for (s32 i = 0; i < num_dropped_buffers; ++i) { + listener->OnBufferReleased(); + } + } + + return Status::NoError; +} + +Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence) { + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_NVFlinger, "slot {} out of range", slot); + return Status::BadValue; + } + + std::shared_ptr<IProducerListener> listener; + { + std::unique_lock lock(core->mutex); + + // If the frame number has changed because the buffer has been reallocated, we can ignore + // this ReleaseBuffer for the old buffer. + if (frame_number != slots[slot].frame_number) { + return Status::StaleBufferSlot; + } + + // Make sure this buffer hasn't been queued while acquired by the consumer. + auto current(core->queue.begin()); + while (current != core->queue.end()) { + if (current->slot == slot) { + LOG_ERROR(Service_NVFlinger, "buffer slot {} pending release is currently queued", + slot); + return Status::BadValue; + } + ++current; + } + + if (slots[slot].buffer_state == BufferState::Acquired) { + slots[slot].fence = release_fence; + slots[slot].buffer_state = BufferState::Free; + + core->free_buffers.push_back(slot); + + listener = core->connected_producer_listener; + + LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); + } else if (slots[slot].needs_cleanup_on_release) { + LOG_DEBUG(Service_NVFlinger, "releasing a stale buffer slot {} (state = {})", slot, + slots[slot].buffer_state); + + slots[slot].needs_cleanup_on_release = false; + + return Status::StaleBufferSlot; + } else { + LOG_ERROR(Service_NVFlinger, "attempted to release buffer slot {} but its state was {}", + slot, slots[slot].buffer_state); + + return Status::BadValue; + } + + core->dequeue_condition.notify_all(); + } + + // Call back without lock held + if (listener != nullptr) { + listener->OnBufferReleased(); + } + + return Status::NoError; +} + +Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_listener, + bool controlled_by_app) { + if (consumer_listener == nullptr) { + LOG_ERROR(Service_NVFlinger, "consumer_listener may not be nullptr"); + return Status::BadValue; + } + + LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app); + + BufferQueueCore::AutoLock lock(core); + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + core->consumer_listener = consumer_listener; + core->consumer_controlled_by_app = controlled_by_app; + + return Status::NoError; +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h new file mode 100644 index 000000000..f22854394 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2014 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueConsumer.h + +#pragma once + +#include <chrono> +#include <memory> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/buffer_queue_defs.h" +#include "core/hle/service/nvflinger/status.h" + +namespace Service::android { + +class BufferItem; +class BufferQueueCore; +class IConsumerListener; + +class BufferQueueConsumer final { +public: + explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_); + ~BufferQueueConsumer(); + + Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present, + u64 max_frame_number = 0); + Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); + Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app); + +private: + std::shared_ptr<BufferQueueCore> core; + BufferQueueDefs::SlotsType& slots; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp new file mode 100644 index 000000000..eb93b43ee --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2014 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueCore.cpp + +#include "common/assert.h" + +#include "core/hle/service/nvflinger/buffer_queue_core.h" + +namespace Service::android { + +BufferQueueCore::BufferQueueCore() : lock{mutex, std::defer_lock} { + for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { + free_slots.insert(slot); + } +} + +BufferQueueCore::~BufferQueueCore() = default; + +void BufferQueueCore::NotifyShutdown() { + std::unique_lock lk(mutex); + + is_shutting_down = true; + + SignalDequeueCondition(); +} + +void BufferQueueCore::SignalDequeueCondition() { + dequeue_condition.notify_all(); +} + +bool BufferQueueCore::WaitForDequeueCondition() { + if (is_shutting_down) { + return false; + } + + dequeue_condition.wait(lock); + + return true; +} + +s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const { + // If DequeueBuffer is allowed to error out, we don't have to add an extra buffer. + if (!use_async_buffer) { + return max_acquired_buffer_count; + } + + if (dequeue_buffer_cannot_block || async) { + return max_acquired_buffer_count + 1; + } + + return max_acquired_buffer_count; +} + +s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const { + return GetMinUndequeuedBufferCountLocked(async) + 1; +} + +s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const { + const auto min_buffer_count = GetMinMaxBufferCountLocked(async); + auto max_buffer_count = std::max(default_max_buffer_count, min_buffer_count); + + if (override_max_buffer_count != 0) { + ASSERT(override_max_buffer_count >= min_buffer_count); + max_buffer_count = override_max_buffer_count; + } + + // Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed + // need to have their slots preserved. + for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { + const auto state = slots[slot].buffer_state; + if (state == BufferState::Queued || state == BufferState::Dequeued) { + max_buffer_count = slot + 1; + } + } + + return max_buffer_count; +} + +s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const { + return static_cast<s32>(std::count_if(slots.begin(), slots.end(), + [](const auto& slot) { return slot.is_preallocated; })); +} + +void BufferQueueCore::FreeBufferLocked(s32 slot) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + const auto had_buffer = slots[slot].graphic_buffer != nullptr; + + slots[slot].graphic_buffer.reset(); + + if (slots[slot].buffer_state == BufferState::Acquired) { + slots[slot].needs_cleanup_on_release = true; + } + + if (slots[slot].buffer_state != BufferState::Free) { + free_slots.insert(slot); + } else if (had_buffer) { + // If the slot was FREE, but we had a buffer, we need to move this slot from the free + // buffers list to the the free slots list. + free_buffers.remove(slot); + free_slots.insert(slot); + } + + slots[slot].buffer_state = BufferState::Free; + slots[slot].acquire_called = false; + slots[slot].frame_number = 0; + slots[slot].fence = Fence::NoFence(); +} + +void BufferQueueCore::FreeAllBuffersLocked() { + queue.clear(); + buffer_has_been_queued = false; + + for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) { + FreeBufferLocked(slot); + } +} + +bool BufferQueueCore::StillTracking(const BufferItem& item) const { + const BufferSlot& slot = slots[item.slot]; + + return (slot.graphic_buffer != nullptr) && (item.graphic_buffer == slot.graphic_buffer); +} + +void BufferQueueCore::WaitWhileAllocatingLocked() const { + while (is_allocating) { + std::unique_lock lk(mutex); + is_allocating_condition.wait(lk); + } +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h new file mode 100644 index 000000000..a3cd89f1c --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_core.h @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2014 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueCore.h + +#pragma once + +#include <condition_variable> +#include <list> +#include <memory> +#include <mutex> +#include <set> +#include <vector> + +#include "core/hle/service/nvflinger/buffer_item.h" +#include "core/hle/service/nvflinger/buffer_queue_defs.h" +#include "core/hle/service/nvflinger/pixel_format.h" +#include "core/hle/service/nvflinger/status.h" +#include "core/hle/service/nvflinger/window.h" + +namespace Service::android { + +class IConsumerListener; +class IProducerListener; + +class BufferQueueCore final { + friend class BufferQueueProducer; + friend class BufferQueueConsumer; + +public: + static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT; + + BufferQueueCore(); + ~BufferQueueCore(); + + void NotifyShutdown(); + +private: + void SignalDequeueCondition(); + bool WaitForDequeueCondition(); + + s32 GetMinUndequeuedBufferCountLocked(bool async) const; + s32 GetMinMaxBufferCountLocked(bool async) const; + s32 GetMaxBufferCountLocked(bool async) const; + s32 GetPreallocatedBufferCountLocked() const; + void FreeBufferLocked(s32 slot); + void FreeAllBuffersLocked(); + bool StillTracking(const BufferItem& item) const; + void WaitWhileAllocatingLocked() const; + +private: + class AutoLock final { + public: + AutoLock(std::shared_ptr<BufferQueueCore>& core_) : core{core_} { + core->lock.lock(); + } + + ~AutoLock() { + core->lock.unlock(); + } + + private: + std::shared_ptr<BufferQueueCore>& core; + }; + +private: + mutable std::mutex mutex; + mutable std::unique_lock<std::mutex> lock; + bool is_abandoned{}; + bool consumer_controlled_by_app{}; + std::shared_ptr<IConsumerListener> consumer_listener; + u32 consumer_usage_bit{}; + NativeWindowApi connected_api{NativeWindowApi::NoConnectedApi}; + std::shared_ptr<IProducerListener> connected_producer_listener; + BufferQueueDefs::SlotsType slots{}; + std::vector<BufferItem> queue; + std::set<s32> free_slots; + std::list<s32> free_buffers; + s32 override_max_buffer_count{}; + mutable std::condition_variable dequeue_condition; + const bool use_async_buffer{}; // This is always disabled on HOS + bool dequeue_buffer_cannot_block{}; + PixelFormat default_buffer_format{PixelFormat::Rgba8888}; + u32 default_width{1}; + u32 default_height{1}; + s32 default_max_buffer_count{2}; + const s32 max_acquired_buffer_count{}; // This is always zero on HOS + bool buffer_has_been_queued{}; + u64 frame_counter{}; + u32 transform_hint{}; + bool is_allocating{}; + mutable std::condition_variable is_allocating_condition; + bool allow_allocation{true}; + u64 buffer_age{}; + bool is_shutting_down{}; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_defs.h b/src/core/hle/service/nvflinger/buffer_queue_defs.h new file mode 100644 index 000000000..387d3d36a --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_defs.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2014 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueDefs.h + +#pragma once + +#include <array> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/buffer_slot.h" + +namespace Service::android::BufferQueueDefs { + +// BufferQueue will keep track of at most this value of buffers. +constexpr s32 NUM_BUFFER_SLOTS = 64; + +using SlotsType = std::array<BufferSlot, NUM_BUFFER_SLOTS>; + +} // namespace Service::android::BufferQueueDefs diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp new file mode 100644 index 000000000..078091904 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp @@ -0,0 +1,937 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2014 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueProducer.cpp + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/core.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/kernel/k_writable_event.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvflinger/buffer_queue_core.h" +#include "core/hle/service/nvflinger/buffer_queue_producer.h" +#include "core/hle/service/nvflinger/consumer_listener.h" +#include "core/hle/service/nvflinger/parcel.h" +#include "core/hle/service/nvflinger/ui/graphic_buffer.h" +#include "core/hle/service/nvflinger/window.h" +#include "core/hle/service/vi/vi.h" + +namespace Service::android { + +BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, + std::shared_ptr<BufferQueueCore> buffer_queue_core_) + : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots) { + buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); +} + +BufferQueueProducer::~BufferQueueProducer() { + service_context.CloseEvent(buffer_wait_event); +} + +Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + BufferQueueCore::AutoLock lock(core); + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return Status::BadValue; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot, + slots[slot].buffer_state); + return Status::BadValue; + } + + slots[slot].request_buffer_called = true; + *buf = slots[slot].graphic_buffer; + + return Status::NoError; +} + +Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { + LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count); + std::shared_ptr<IConsumerListener> listener; + + { + BufferQueueCore::AutoLock lock(core); + core->WaitWhileAllocatingLocked(); + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + if (buffer_count > BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_NVFlinger, "buffer_count {} too large (max {})", buffer_count, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return Status::BadValue; + } + + // There must be no dequeued buffers when changing the buffer count. + for (s32 s{}; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (slots[s].buffer_state == BufferState::Dequeued) { + LOG_ERROR(Service_NVFlinger, "buffer owned by producer"); + return Status::BadValue; + } + } + + if (buffer_count == 0) { + core->override_max_buffer_count = 0; + core->SignalDequeueCondition(); + return Status::NoError; + } + + const s32 min_buffer_slots = core->GetMinMaxBufferCountLocked(false); + if (buffer_count < min_buffer_slots) { + LOG_ERROR(Service_NVFlinger, "requested buffer count {} is less than minimum {}", + buffer_count, min_buffer_slots); + return Status::BadValue; + } + + // Here we are guaranteed that the producer doesn't have any dequeued buffers and will + // release all of its buffer references. + if (core->GetPreallocatedBufferCountLocked() <= 0) { + core->FreeAllBuffersLocked(); + } + + core->override_max_buffer_count = buffer_count; + core->SignalDequeueCondition(); + buffer_wait_event->GetWritableEvent().Signal(); + listener = core->consumer_listener; + } + + // Call back without lock held + if (listener != nullptr) { + listener->OnBuffersReleased(); + } + + return Status::NoError; +} + +Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, + Status* returnFlags) const { + bool try_again = true; + + while (try_again) { + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + const s32 max_buffer_count = core->GetMaxBufferCountLocked(async); + if (async && core->override_max_buffer_count) { + if (core->override_max_buffer_count < max_buffer_count) { + LOG_ERROR(Service_NVFlinger, "async mode is invalid with buffer count override"); + return Status::BadValue; + } + } + + // Free up any buffers that are in slots beyond the max buffer count + for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + ASSERT(slots[s].buffer_state == BufferState::Free); + if (slots[s].graphic_buffer != nullptr) { + core->FreeBufferLocked(s); + *returnFlags |= Status::ReleaseAllBuffers; + } + } + + s32 dequeued_count{}; + s32 acquired_count{}; + for (s32 s{}; s < max_buffer_count; ++s) { + switch (slots[s].buffer_state) { + case BufferState::Dequeued: + ++dequeued_count; + break; + case BufferState::Acquired: + ++acquired_count; + break; + default: + break; + } + } + + // Producers are not allowed to dequeue more than one buffer if they did not set a buffer + // count + if (!core->override_max_buffer_count && dequeued_count) { + LOG_ERROR(Service_NVFlinger, + "can't dequeue multiple buffers without setting the buffer count"); + return Status::InvalidOperation; + } + + // See whether a buffer has been queued since the last SetBufferCount so we know whether to + // perform the min undequeued buffers check below + if (core->buffer_has_been_queued) { + // Make sure the producer is not trying to dequeue more buffers than allowed + const s32 new_undequeued_count = max_buffer_count - (dequeued_count + 1); + const s32 min_undequeued_count = core->GetMinUndequeuedBufferCountLocked(async); + if (new_undequeued_count < min_undequeued_count) { + LOG_ERROR(Service_NVFlinger, + "min undequeued buffer count({}) exceeded (dequeued={} undequeued={})", + min_undequeued_count, dequeued_count, new_undequeued_count); + return Status::InvalidOperation; + } + } + + *found = BufferQueueCore::INVALID_BUFFER_SLOT; + + // If we disconnect and reconnect quickly, we can be in a state where our slots are empty + // but we have many buffers in the queue. This can cause us to run out of memory if we + // outrun the consumer. Wait here if it looks like we have too many buffers queued up. + const bool too_many_buffers = core->queue.size() > static_cast<size_t>(max_buffer_count); + if (too_many_buffers) { + LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size()); + } else { + if (!core->free_buffers.empty()) { + auto slot = core->free_buffers.begin(); + *found = *slot; + core->free_buffers.erase(slot); + } else if (core->allow_allocation && !core->free_slots.empty()) { + auto slot = core->free_slots.begin(); + // Only return free slots up to the max buffer count + if (*slot < max_buffer_count) { + *found = *slot; + core->free_slots.erase(slot); + } + } + } + + // If no buffer is found, or if the queue has too many buffers outstanding, wait for a + // buffer to be acquired or released, or for the max buffer count to change. + try_again = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || too_many_buffers; + if (try_again) { + // Return an error if we're in non-blocking mode (producer and consumer are controlled + // by the application). + if (core->dequeue_buffer_cannot_block && + (acquired_count <= core->max_acquired_buffer_count)) { + return Status::WouldBlock; + } + + if (!core->WaitForDequeueCondition()) { + // We are no longer running + return Status::NoError; + } + } + } + + return Status::NoError; +} + +Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool async, u32 width, + u32 height, PixelFormat format, u32 usage) { + LOG_DEBUG(Service_NVFlinger, "async={} w={} h={} format={}, usage={}", async ? "true" : "false", + width, height, format, usage); + + if ((width != 0 && height == 0) || (width == 0 && height != 0)) { + LOG_ERROR(Service_NVFlinger, "invalid size: w={} h={}", width, height); + return Status::BadValue; + } + + Status return_flags = Status::NoError; + bool attached_by_consumer = false; + { + BufferQueueCore::AutoLock lock(core); + core->WaitWhileAllocatingLocked(); + if (format == PixelFormat::NoFormat) { + format = core->default_buffer_format; + } + + // Enable the usage bits the consumer requested + usage |= core->consumer_usage_bit; + const bool use_default_size = !width && !height; + if (use_default_size) { + width = core->default_width; + height = core->default_height; + } + + s32 found = BufferItem::INVALID_BUFFER_SLOT; + while (found == BufferItem::INVALID_BUFFER_SLOT) { + Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags); + if (status != Status::NoError) { + return status; + } + + // This should not happen + if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { + LOG_DEBUG(Service_NVFlinger, "no available buffer slots"); + return Status::Busy; + } + + const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer); + + // If we are not allowed to allocate new buffers, WaitForFreeSlotThenRelock must have + // returned a slot containing a buffer. If this buffer would require reallocation to + // meet the requested attributes, we free it and attempt to get another one. + if (!core->allow_allocation) { + if (buffer->NeedsReallocation(width, height, format, usage)) { + core->FreeBufferLocked(found); + found = BufferItem::INVALID_BUFFER_SLOT; + continue; + } + } + } + + *out_slot = found; + attached_by_consumer = slots[found].attached_by_consumer; + slots[found].buffer_state = BufferState::Dequeued; + + const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer); + + if ((buffer == nullptr) || buffer->NeedsReallocation(width, height, format, usage)) { + slots[found].acquire_called = false; + slots[found].graphic_buffer = nullptr; + slots[found].request_buffer_called = false; + slots[found].fence = Fence::NoFence(); + core->buffer_age = 0; + return_flags |= Status::BufferNeedsReallocation; + } else { + // We add 1 because that will be the frame number when this buffer + // is queued + core->buffer_age = core->frame_counter + 1 - slots[found].frame_number; + } + + LOG_DEBUG(Service_NVFlinger, "setting buffer age to {}", core->buffer_age); + + *out_fence = slots[found].fence; + + slots[found].fence = Fence::NoFence(); + } + + if ((return_flags & Status::BufferNeedsReallocation) != Status::None) { + LOG_DEBUG(Service_NVFlinger, "allocating a new buffer for slot {}", *out_slot); + + auto graphic_buffer = std::make_shared<GraphicBuffer>(width, height, format, usage); + if (graphic_buffer == nullptr) { + LOG_ERROR(Service_NVFlinger, "creating GraphicBuffer failed"); + return Status::NoMemory; + } + + { + BufferQueueCore::AutoLock lock(core); + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + slots[*out_slot].graphic_buffer = graphic_buffer; + } + } + + if (attached_by_consumer) { + return_flags |= Status::BufferNeedsReallocation; + } + + LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot, + slots[*out_slot].frame_number, return_flags); + return return_flags; +} + +Status BufferQueueProducer::DetachBuffer(s32 slot) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + BufferQueueCore::AutoLock lock(core); + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_NVFlinger, "slot {} out of range [0, {})", slot, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return Status::BadValue; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot, + slots[slot].buffer_state); + return Status::BadValue; + } else if (!slots[slot].request_buffer_called) { + LOG_ERROR(Service_NVFlinger, "buffer in slot {} has not been requested", slot); + return Status::BadValue; + } + + core->FreeBufferLocked(slot); + core->SignalDequeueCondition(); + + return Status::NoError; +} + +Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, + Fence* out_fence) { + if (out_buffer == nullptr) { + LOG_ERROR(Service_NVFlinger, "out_buffer must not be nullptr"); + return Status::BadValue; + } else if (out_fence == nullptr) { + LOG_ERROR(Service_NVFlinger, "out_fence must not be nullptr"); + return Status::BadValue; + } + + BufferQueueCore::AutoLock lock(core); + + core->WaitWhileAllocatingLocked(); + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + if (core->free_buffers.empty()) { + return Status::NoMemory; + } + + const s32 found = core->free_buffers.front(); + core->free_buffers.remove(found); + + LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found); + + *out_buffer = slots[found].graphic_buffer; + *out_fence = slots[found].fence; + + core->FreeBufferLocked(found); + + return Status::NoError; +} + +Status BufferQueueProducer::AttachBuffer(s32* out_slot, + const std::shared_ptr<GraphicBuffer>& buffer) { + if (out_slot == nullptr) { + LOG_ERROR(Service_NVFlinger, "out_slot must not be nullptr"); + return Status::BadValue; + } else if (buffer == nullptr) { + LOG_ERROR(Service_NVFlinger, "Cannot attach nullptr buffer"); + return Status::BadValue; + } + + BufferQueueCore::AutoLock lock(core); + core->WaitWhileAllocatingLocked(); + + Status return_flags = Status::NoError; + s32 found{}; + + const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags); + if (status != Status::NoError) { + return status; + } + + if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { + LOG_ERROR(Service_NVFlinger, "No available buffer slots"); + return Status::Busy; + } + + *out_slot = found; + + LOG_DEBUG(Service_NVFlinger, "Returning slot {} flags={}", *out_slot, return_flags); + + slots[*out_slot].graphic_buffer = buffer; + slots[*out_slot].buffer_state = BufferState::Dequeued; + slots[*out_slot].fence = Fence::NoFence(); + slots[*out_slot].request_buffer_called = true; + + return return_flags; +} + +Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, + QueueBufferOutput* output) { + s64 timestamp{}; + bool is_auto_timestamp{}; + Common::Rectangle<s32> crop; + NativeWindowScalingMode scaling_mode{}; + NativeWindowTransform transform; + u32 sticky_transform_{}; + bool async{}; + s32 swap_interval{}; + Fence fence{}; + + input.Deflate(×tamp, &is_auto_timestamp, &crop, &scaling_mode, &transform, + &sticky_transform_, &async, &swap_interval, &fence); + + switch (scaling_mode) { + case NativeWindowScalingMode::Freeze: + case NativeWindowScalingMode::ScaleToWindow: + case NativeWindowScalingMode::ScaleCrop: + case NativeWindowScalingMode::NoScaleCrop: + break; + default: + LOG_ERROR(Service_NVFlinger, "unknown scaling mode {}", scaling_mode); + return Status::BadValue; + } + + std::shared_ptr<IConsumerListener> frameAvailableListener; + std::shared_ptr<IConsumerListener> frameReplacedListener; + s32 callback_ticket{}; + BufferItem item; + + { + BufferQueueCore::AutoLock lock(core); + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + const s32 max_buffer_count = core->GetMaxBufferCountLocked(async); + if (async && core->override_max_buffer_count) { + if (core->override_max_buffer_count < max_buffer_count) { + LOG_ERROR(Service_NVFlinger, "async mode is invalid with " + "buffer count override"); + return Status::BadValue; + } + } + + if (slot < 0 || slot >= max_buffer_count) { + LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot, + max_buffer_count); + return Status::BadValue; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_NVFlinger, + "slot {} is not owned by the producer " + "(state = {})", + slot, slots[slot].buffer_state); + return Status::BadValue; + } else if (!slots[slot].request_buffer_called) { + LOG_ERROR(Service_NVFlinger, + "slot {} was queued without requesting " + "a buffer", + slot); + return Status::BadValue; + } + + LOG_DEBUG(Service_NVFlinger, + "slot={} frame={} time={} crop=[{},{},{},{}] transform={} scale={}", slot, + core->frame_counter + 1, timestamp, crop.Left(), crop.Top(), crop.Right(), + crop.Bottom(), transform, scaling_mode); + + const std::shared_ptr<GraphicBuffer>& graphic_buffer(slots[slot].graphic_buffer); + Common::Rectangle<s32> buffer_rect(graphic_buffer->Width(), graphic_buffer->Height()); + Common::Rectangle<s32> cropped_rect; + [[maybe_unused]] const bool unused = crop.Intersect(buffer_rect, &cropped_rect); + + if (cropped_rect != crop) { + LOG_ERROR(Service_NVFlinger, "crop rect is not contained within the buffer in slot {}", + slot); + return Status::BadValue; + } + + slots[slot].fence = fence; + slots[slot].buffer_state = BufferState::Queued; + ++core->frame_counter; + slots[slot].frame_number = core->frame_counter; + + item.acquire_called = slots[slot].acquire_called; + item.graphic_buffer = slots[slot].graphic_buffer; + item.crop = crop; + item.transform = transform & ~NativeWindowTransform::InverseDisplay; + item.transform_to_display_inverse = + (transform & NativeWindowTransform::InverseDisplay) != NativeWindowTransform::None; + item.scaling_mode = static_cast<u32>(scaling_mode); + item.timestamp = timestamp; + item.is_auto_timestamp = is_auto_timestamp; + item.frame_number = core->frame_counter; + item.slot = slot; + item.fence = fence; + item.is_droppable = core->dequeue_buffer_cannot_block || async; + item.swap_interval = swap_interval; + sticky_transform = sticky_transform_; + + if (core->queue.empty()) { + // When the queue is empty, we can simply queue this buffer + core->queue.push_back(item); + frameAvailableListener = core->consumer_listener; + } else { + // When the queue is not empty, we need to look at the front buffer + // state to see if we need to replace it + auto front(core->queue.begin()); + + if (front->is_droppable) { + // If the front queued buffer is still being tracked, we first + // mark it as freed + if (core->StillTracking(*front)) { + slots[front->slot].buffer_state = BufferState::Free; + core->free_buffers.push_front(front->slot); + } + // Overwrite the droppable buffer with the incoming one + *front = item; + frameReplacedListener = core->consumer_listener; + } else { + core->queue.push_back(item); + frameAvailableListener = core->consumer_listener; + } + } + + core->buffer_has_been_queued = true; + core->SignalDequeueCondition(); + output->Inflate(core->default_width, core->default_height, core->transform_hint, + static_cast<u32>(core->queue.size())); + + // Take a ticket for the callback functions + callback_ticket = next_callback_ticket++; + } + + // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the + // consumer shouldn't need it + item.graphic_buffer.reset(); + item.slot = BufferItem::INVALID_BUFFER_SLOT; + + // Call back without the main BufferQueue lock held, but with the callback lock held so we can + // ensure that callbacks occur in order + { + std::unique_lock lock(callback_mutex); + while (callback_ticket != current_callback_ticket) { + std::unique_lock<std::mutex> lk(callback_mutex); + callback_condition.wait(lk); + } + + if (frameAvailableListener != nullptr) { + frameAvailableListener->OnFrameAvailable(item); + } else if (frameReplacedListener != nullptr) { + frameReplacedListener->OnFrameReplaced(item); + } + + ++current_callback_ticket; + callback_condition.notify_all(); + } + + return Status::NoError; +} + +void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + BufferQueueCore::AutoLock lock(core); + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return; + } + + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot, + BufferQueueDefs::NUM_BUFFER_SLOTS); + return; + } else if (slots[slot].buffer_state != BufferState::Dequeued) { + LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot, + slots[slot].buffer_state); + return; + } + + core->free_buffers.push_front(slot); + slots[slot].buffer_state = BufferState::Free; + slots[slot].fence = fence; + + core->SignalDequeueCondition(); + buffer_wait_event->GetWritableEvent().Signal(); +} + +Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) { + BufferQueueCore::AutoLock lock(core); + + if (out_value == nullptr) { + LOG_ERROR(Service_NVFlinger, "outValue was nullptr"); + return Status::BadValue; + } + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + u32 value{}; + switch (what) { + case NativeWindow::Width: + value = core->default_width; + break; + case NativeWindow::Height: + value = core->default_height; + break; + case NativeWindow::Format: + value = static_cast<u32>(core->default_buffer_format); + break; + case NativeWindow::MinUndequeedBuffers: + value = core->GetMinUndequeuedBufferCountLocked(false); + break; + case NativeWindow::StickyTransform: + value = sticky_transform; + break; + case NativeWindow::ConsumerRunningBehind: + value = (core->queue.size() > 1); + break; + case NativeWindow::ConsumerUsageBits: + value = core->consumer_usage_bit; + break; + case NativeWindow::BufferAge: + if (core->buffer_age > INT32_MAX) { + value = 0; + } else { + value = static_cast<u32>(core->buffer_age); + } + break; + default: + UNREACHABLE(); + return Status::BadValue; + } + + LOG_DEBUG(Service_NVFlinger, "what = {}, value = {}", what, value); + + *out_value = static_cast<s32>(value); + + return Status::NoError; +} + +Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener, + NativeWindowApi api, bool producer_controlled_by_app, + QueueBufferOutput* output) { + BufferQueueCore::AutoLock lock(core); + + LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api, + producer_controlled_by_app); + + if (core->is_abandoned) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned"); + return Status::NoInit; + } + + if (core->consumer_listener == nullptr) { + LOG_ERROR(Service_NVFlinger, "BufferQueue has no consumer"); + return Status::NoInit; + } + + if (output == nullptr) { + LOG_ERROR(Service_NVFlinger, "output was nullptr"); + return Status::BadValue; + } + + if (core->connected_api != NativeWindowApi::NoConnectedApi) { + LOG_ERROR(Service_NVFlinger, "already connected (cur = {} req = {})", core->connected_api, + api); + return Status::BadValue; + } + + Status status = Status::NoError; + switch (api) { + case NativeWindowApi::Egl: + case NativeWindowApi::Cpu: + case NativeWindowApi::Media: + case NativeWindowApi::Camera: + core->connected_api = api; + output->Inflate(core->default_width, core->default_height, core->transform_hint, + static_cast<u32>(core->queue.size())); + core->connected_producer_listener = listener; + break; + default: + LOG_ERROR(Service_NVFlinger, "unknown api = {}", api); + status = Status::BadValue; + break; + } + + core->buffer_has_been_queued = false; + core->dequeue_buffer_cannot_block = + core->consumer_controlled_by_app && producer_controlled_by_app; + core->allow_allocation = true; + + return status; +} + +Status BufferQueueProducer::Disconnect(NativeWindowApi api) { + LOG_DEBUG(Service_NVFlinger, "api = {}", api); + + Status status = Status::NoError; + std::shared_ptr<IConsumerListener> listener; + + { + BufferQueueCore::AutoLock lock(core); + + core->WaitWhileAllocatingLocked(); + + if (core->is_abandoned) { + // Disconnecting after the surface has been abandoned is a no-op. + return Status::NoError; + } + + switch (api) { + case NativeWindowApi::Egl: + case NativeWindowApi::Cpu: + case NativeWindowApi::Media: + case NativeWindowApi::Camera: + if (core->connected_api == api) { + core->FreeAllBuffersLocked(); + core->connected_producer_listener = nullptr; + core->connected_api = NativeWindowApi::NoConnectedApi; + core->SignalDequeueCondition(); + buffer_wait_event->GetWritableEvent().Signal(); + listener = core->consumer_listener; + } else if (core->connected_api != NativeWindowApi::NoConnectedApi) { + LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})", + core->connected_api, api); + status = Status::BadValue; + } + break; + default: + LOG_ERROR(Service_NVFlinger, "unknown api = {}", api); + status = Status::BadValue; + break; + } + } + + // Call back without lock held + if (listener != nullptr) { + listener->OnBuffersReleased(); + } + + return status; +} + +Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, + const std::shared_ptr<GraphicBuffer>& buffer) { + LOG_DEBUG(Service_NVFlinger, "slot {}", slot); + + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + return Status::BadValue; + } + + BufferQueueCore::AutoLock lock(core); + + slots[slot] = {}; + slots[slot].graphic_buffer = buffer; + + // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for + // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this. + if (buffer) { + slots[slot].is_preallocated = true; + + core->override_max_buffer_count = core->GetPreallocatedBufferCountLocked(); + core->default_width = buffer->Width(); + core->default_height = buffer->Height(); + core->default_buffer_format = buffer->Format(); + } + + core->SignalDequeueCondition(); + buffer_wait_event->GetWritableEvent().Signal(); + + return Status::NoError; +} + +void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) { + Status status{Status::NoError}; + Parcel parcel_in{ctx.ReadBuffer()}; + Parcel parcel_out{}; + + switch (code) { + case TransactionId::Connect: { + const auto enable_listener = parcel_in.Read<bool>(); + const auto api = parcel_in.Read<NativeWindowApi>(); + const auto producer_controlled_by_app = parcel_in.Read<bool>(); + + UNIMPLEMENTED_IF_MSG(enable_listener, "Listener is unimplemented!"); + + std::shared_ptr<IProducerListener> listener; + QueueBufferOutput output{}; + + status = Connect(listener, api, producer_controlled_by_app, &output); + + parcel_out.Write(output); + break; + } + case TransactionId::SetPreallocatedBuffer: { + const auto slot = parcel_in.Read<s32>(); + const auto buffer = parcel_in.ReadObject<GraphicBuffer>(); + + status = SetPreallocatedBuffer(slot, buffer); + break; + } + case TransactionId::DequeueBuffer: { + const auto is_async = parcel_in.Read<bool>(); + const auto width = parcel_in.Read<u32>(); + const auto height = parcel_in.Read<u32>(); + const auto pixel_format = parcel_in.Read<PixelFormat>(); + const auto usage = parcel_in.Read<u32>(); + + s32 slot{}; + Fence fence{}; + + status = DequeueBuffer(&slot, &fence, is_async, width, height, pixel_format, usage); + + parcel_out.Write(slot); + parcel_out.WriteObject(&fence); + break; + } + case TransactionId::RequestBuffer: { + const auto slot = parcel_in.Read<s32>(); + + std::shared_ptr<GraphicBuffer> buf; + + status = RequestBuffer(slot, &buf); + + parcel_out.WriteObject(buf); + break; + } + case TransactionId::QueueBuffer: { + const auto slot = parcel_in.Read<s32>(); + + QueueBufferInput input{parcel_in}; + QueueBufferOutput output; + + status = QueueBuffer(slot, input, &output); + + parcel_out.Write(output); + break; + } + case TransactionId::Query: { + const auto what = parcel_in.Read<NativeWindow>(); + + s32 value{}; + + status = Query(what, &value); + + parcel_out.Write(value); + break; + } + case TransactionId::CancelBuffer: { + const auto slot = parcel_in.Read<s32>(); + const auto fence = parcel_in.ReadFlattened<Fence>(); + + CancelBuffer(slot, fence); + break; + } + case TransactionId::Disconnect: { + const auto api = parcel_in.Read<NativeWindowApi>(); + + status = Disconnect(api); + break; + } + case TransactionId::DetachBuffer: { + const auto slot = parcel_in.Read<s32>(); + + status = DetachBuffer(slot); + break; + } + case TransactionId::SetBufferCount: { + const auto buffer_count = parcel_in.Read<s32>(); + + status = SetBufferCount(buffer_count); + break; + } + case TransactionId::GetBufferHistory: + LOG_WARNING(Service_NVFlinger, "(STUBBED) called, transaction=GetBufferHistory"); + break; + default: + ASSERT_MSG(false, "Unimplemented TransactionId {}", code); + break; + } + + parcel_out.Write(status); + + ctx.WriteBuffer(parcel_out.Serialize()); +} + +Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() { + return buffer_wait_event->GetReadableEvent(); +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h new file mode 100644 index 000000000..5ddeebe0c --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2014 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueProducer.h + +#pragma once + +#include <condition_variable> +#include <memory> +#include <mutex> + +#include "common/common_funcs.h" +#include "core/hle/service/nvdrv/nvdata.h" +#include "core/hle/service/nvflinger/binder.h" +#include "core/hle/service/nvflinger/buffer_queue_defs.h" +#include "core/hle/service/nvflinger/buffer_slot.h" +#include "core/hle/service/nvflinger/graphic_buffer_producer.h" +#include "core/hle/service/nvflinger/pixel_format.h" +#include "core/hle/service/nvflinger/status.h" +#include "core/hle/service/nvflinger/window.h" + +namespace Kernel { +class KernelCore; +class KEvent; +class KReadableEvent; +class KWritableEvent; +} // namespace Kernel + +namespace Service::KernelHelpers { +class ServiceContext; +} // namespace Service::KernelHelpers + +namespace Service::android { + +class BufferQueueCore; +class IProducerListener; + +class BufferQueueProducer final : public IBinder { +public: + explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_, + std::shared_ptr<BufferQueueCore> buffer_queue_core_); + ~BufferQueueProducer(); + + void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override; + + Kernel::KReadableEvent& GetNativeHandle() override; + +public: + Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf); + Status SetBufferCount(s32 buffer_count); + Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width, + u32 height, PixelFormat format, u32 usage); + Status DetachBuffer(s32 slot); + Status DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, Fence* out_fence); + Status AttachBuffer(s32* outSlot, const std::shared_ptr<GraphicBuffer>& buffer); + Status QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output); + void CancelBuffer(s32 slot, const Fence& fence); + Status Query(NativeWindow what, s32* out_value); + Status Connect(const std::shared_ptr<IProducerListener>& listener, NativeWindowApi api, + bool producer_controlled_by_app, QueueBufferOutput* output); + + Status Disconnect(NativeWindowApi api); + Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer); + +private: + BufferQueueProducer(const BufferQueueProducer&) = delete; + + Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* returnFlags) const; + + Kernel::KEvent* buffer_wait_event{}; + Service::KernelHelpers::ServiceContext& service_context; + + std::shared_ptr<BufferQueueCore> core; + BufferQueueDefs::SlotsType& slots; + u32 sticky_transform{}; + std::mutex callback_mutex; + s32 next_callback_ticket{}; + s32 current_callback_ticket{}; + std::condition_variable callback_condition; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_slot.h b/src/core/hle/service/nvflinger/buffer_slot.h new file mode 100644 index 000000000..e3ea58910 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_slot.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2014 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h + +#pragma once + +#include <memory> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/ui/fence.h" + +namespace Service::android { + +class GraphicBuffer; + +enum class BufferState : u32 { + Free = 0, + Dequeued = 1, + Queued = 2, + Acquired = 3, +}; + +struct BufferSlot final { + constexpr BufferSlot() = default; + + std::shared_ptr<GraphicBuffer> graphic_buffer; + BufferState buffer_state{BufferState::Free}; + bool request_buffer_called{}; + u64 frame_number{}; + Fence fence; + bool acquire_called{}; + bool needs_cleanup_on_release{}; + bool attached_by_consumer{}; + bool is_preallocated{}; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/buffer_transform_flags.h b/src/core/hle/service/nvflinger/buffer_transform_flags.h new file mode 100644 index 000000000..e8e6300e3 --- /dev/null +++ b/src/core/hle/service/nvflinger/buffer_transform_flags.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project + +#pragma once + +#include "common/common_types.h" + +namespace Service::android { + +enum class BufferTransformFlags : u32 { + /// No transform flags are set + Unset = 0x00, + /// Flip source image horizontally (around the vertical axis) + FlipH = 0x01, + /// Flip source image vertically (around the horizontal axis) + FlipV = 0x02, + /// Rotate source image 90 degrees clockwise + Rotate90 = 0x04, + /// Rotate source image 180 degrees + Rotate180 = 0x03, + /// Rotate source image 270 degrees clockwise + Rotate270 = 0x07, +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/consumer_base.cpp b/src/core/hle/service/nvflinger/consumer_base.cpp new file mode 100644 index 000000000..3ccbb7fb8 --- /dev/null +++ b/src/core/hle/service/nvflinger/consumer_base.cpp @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2010 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/ConsumerBase.cpp + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/service/nvflinger/buffer_item.h" +#include "core/hle/service/nvflinger/buffer_queue_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_core.h" +#include "core/hle/service/nvflinger/consumer_base.h" +#include "core/hle/service/nvflinger/ui/graphic_buffer.h" + +namespace Service::android { + +ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_) + : consumer{std::move(consumer_)} {} + +ConsumerBase::~ConsumerBase() { + std::unique_lock lock(mutex); + + ASSERT_MSG(is_abandoned, "consumer is not abandoned!"); +} + +void ConsumerBase::Connect(bool controlled_by_app) { + consumer->Connect(shared_from_this(), controlled_by_app); +} + +void ConsumerBase::FreeBufferLocked(s32 slot_index) { + LOG_DEBUG(Service_NVFlinger, "slot_index={}", slot_index); + + slots[slot_index].graphic_buffer = nullptr; + slots[slot_index].fence = Fence::NoFence(); + slots[slot_index].frame_number = 0; +} + +void ConsumerBase::OnFrameAvailable(const BufferItem& item) { + std::unique_lock lock(mutex); + LOG_DEBUG(Service_NVFlinger, "called"); +} + +void ConsumerBase::OnFrameReplaced(const BufferItem& item) { + std::unique_lock lock(mutex); + LOG_DEBUG(Service_NVFlinger, "called"); +} + +void ConsumerBase::OnBuffersReleased() { + std::unique_lock lock(mutex); + LOG_DEBUG(Service_NVFlinger, "called"); +} + +void ConsumerBase::OnSidebandStreamChanged() {} + +Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when, + u64 max_frame_number) { + if (is_abandoned) { + LOG_ERROR(Service_NVFlinger, "consumer is abandoned!"); + return Status::NoInit; + } + + Status err = consumer->AcquireBuffer(item, present_when, max_frame_number); + if (err != Status::NoError) { + return err; + } + + if (item->graphic_buffer != nullptr) { + if (slots[item->slot].graphic_buffer != nullptr) { + FreeBufferLocked(item->slot); + } + slots[item->slot].graphic_buffer = item->graphic_buffer; + } + + slots[item->slot].frame_number = item->frame_number; + slots[item->slot].fence = item->fence; + + LOG_DEBUG(Service_NVFlinger, "slot={}", item->slot); + + return Status::NoError; +} + +Status ConsumerBase::AddReleaseFenceLocked(s32 slot, + const std::shared_ptr<GraphicBuffer> graphic_buffer, + const Fence& fence) { + LOG_DEBUG(Service_NVFlinger, "slot={}", slot); + + // If consumer no longer tracks this graphic_buffer, we can safely + // drop this fence, as it will never be received by the producer. + + if (!StillTracking(slot, graphic_buffer)) { + return Status::NoError; + } + + slots[slot].fence = fence; + + return Status::NoError; +} + +Status ConsumerBase::ReleaseBufferLocked(s32 slot, + const std::shared_ptr<GraphicBuffer> graphic_buffer) { + // If consumer no longer tracks this graphic_buffer (we received a new + // buffer on the same slot), the buffer producer is definitely no longer + // tracking it. + + if (!StillTracking(slot, graphic_buffer)) { + return Status::NoError; + } + + LOG_DEBUG(Service_NVFlinger, "slot={}", slot); + Status err = consumer->ReleaseBuffer(slot, slots[slot].frame_number, slots[slot].fence); + if (err == Status::StaleBufferSlot) { + FreeBufferLocked(slot); + } + + slots[slot].fence = Fence::NoFence(); + + return err; +} + +bool ConsumerBase::StillTracking(s32 slot, + const std::shared_ptr<GraphicBuffer> graphic_buffer) const { + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { + return false; + } + + return (slots[slot].graphic_buffer != nullptr && + slots[slot].graphic_buffer->Handle() == graphic_buffer->Handle()); +} + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/consumer_base.h b/src/core/hle/service/nvflinger/consumer_base.h new file mode 100644 index 000000000..9ab949420 --- /dev/null +++ b/src/core/hle/service/nvflinger/consumer_base.h @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2010 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/ConsumerBase.h + +#pragma once + +#include <array> +#include <chrono> +#include <memory> +#include <mutex> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/buffer_queue_defs.h" +#include "core/hle/service/nvflinger/consumer_listener.h" +#include "core/hle/service/nvflinger/status.h" + +namespace Service::android { + +class BufferItem; +class BufferQueueConsumer; + +class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> { +public: + void Connect(bool controlled_by_app); + +protected: + explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_); + virtual ~ConsumerBase(); + + virtual void OnFrameAvailable(const BufferItem& item) override; + virtual void OnFrameReplaced(const BufferItem& item) override; + virtual void OnBuffersReleased() override; + virtual void OnSidebandStreamChanged() override; + + void FreeBufferLocked(s32 slot_index); + Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when, + u64 max_frame_number = 0); + Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer); + bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const; + Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer, + const Fence& fence); + + struct Slot final { + std::shared_ptr<GraphicBuffer> graphic_buffer; + Fence fence; + u64 frame_number{}; + }; + +protected: + std::array<Slot, BufferQueueDefs::NUM_BUFFER_SLOTS> slots; + + bool is_abandoned{}; + + std::unique_ptr<BufferQueueConsumer> consumer; + + mutable std::mutex mutex; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/consumer_listener.h b/src/core/hle/service/nvflinger/consumer_listener.h new file mode 100644 index 000000000..b6d1c3e9a --- /dev/null +++ b/src/core/hle/service/nvflinger/consumer_listener.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2014 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IConsumerListener.h + +#pragma once + +namespace Service::android { + +class BufferItem; + +/// ConsumerListener is the interface through which the BufferQueue notifies the consumer of events +/// that the consumer may wish to react to. +class IConsumerListener { +public: + IConsumerListener() = default; + virtual ~IConsumerListener() = default; + + virtual void OnFrameAvailable(const BufferItem& item) = 0; + virtual void OnFrameReplaced(const BufferItem& item) = 0; + virtual void OnBuffersReleased() = 0; + virtual void OnSidebandStreamChanged() = 0; +}; + +}; // namespace Service::android diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp new file mode 100644 index 000000000..d4da98ddb --- /dev/null +++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2010 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp + +#pragma once + +#include "core/hle/service/nvflinger/graphic_buffer_producer.h" +#include "core/hle/service/nvflinger/parcel.h" + +namespace Service::android { + +QueueBufferInput::QueueBufferInput(Parcel& parcel) { + parcel.ReadFlattened(*this); +} + +QueueBufferOutput::QueueBufferOutput() = default; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.h b/src/core/hle/service/nvflinger/graphic_buffer_producer.h new file mode 100644 index 000000000..98d27871c --- /dev/null +++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.h @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2010 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/math_util.h" +#include "core/hle/service/nvflinger/ui/fence.h" +#include "core/hle/service/nvflinger/window.h" + +namespace Service::android { + +class Parcel; + +#pragma pack(push, 1) +struct QueueBufferInput final { + explicit QueueBufferInput(Parcel& parcel); + + void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_, + NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_, + u32* sticky_transform_, bool* async_, s32* swap_interval_, Fence* fence_) const { + *timestamp_ = timestamp; + *is_auto_timestamp_ = static_cast<bool>(is_auto_timestamp); + *crop_ = crop; + *scaling_mode_ = scaling_mode; + *transform_ = transform; + *sticky_transform_ = sticky_transform; + *async_ = static_cast<bool>(async); + *swap_interval_ = swap_interval; + *fence_ = fence; + } + +private: + s64 timestamp{}; + s32 is_auto_timestamp{}; + Common::Rectangle<s32> crop{}; + NativeWindowScalingMode scaling_mode{}; + NativeWindowTransform transform{}; + u32 sticky_transform{}; + s32 async{}; + s32 swap_interval{}; + Fence fence{}; +}; +#pragma pack(pop) +static_assert(sizeof(QueueBufferInput) == 84, "QueueBufferInput has wrong size"); + +struct QueueBufferOutput final { + QueueBufferOutput(); + + void Deflate(u32* width_, u32* height_, u32* transform_hint_, u32* num_pending_buffers_) const { + *width_ = width; + *height_ = height; + *transform_hint_ = transform_hint; + *num_pending_buffers_ = num_pending_buffers; + } + + void Inflate(u32 width_, u32 height_, u32 transform_hint_, u32 num_pending_buffers_) { + width = width_; + height = height_; + transform_hint = transform_hint_; + num_pending_buffers = num_pending_buffers_; + } + +private: + u32 width{}; + u32 height{}; + u32 transform_hint{}; + u32 num_pending_buffers{}; +}; +static_assert(sizeof(QueueBufferOutput) == 16, "QueueBufferOutput has wrong size"); + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp new file mode 100644 index 000000000..0c937d682 --- /dev/null +++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project + +#include <mutex> + +#include "common/common_types.h" +#include "core/hle/service/nvflinger/hos_binder_driver_server.h" + +namespace Service::NVFlinger { + +HosBinderDriverServer::HosBinderDriverServer(Core::System& system_) + : service_context(system_, "HosBinderDriverServer") {} + +HosBinderDriverServer::~HosBinderDriverServer() {} + +u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) { + std::lock_guard lk{lock}; + + last_id++; + + producers[last_id] = std::move(binder); + + return last_id; +} + +android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) { + std::lock_guard lk{lock}; + + if (auto search = producers.find(id); search != producers.end()) { + return search->second.get(); + } + + return {}; +} + +} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.h b/src/core/hle/service/nvflinger/hos_binder_driver_server.h new file mode 100644 index 000000000..cbca87fa0 --- /dev/null +++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project + +#pragma once + +#include <memory> +#include <mutex> +#include <unordered_map> + +#include "common/common_types.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvflinger/binder.h" + +namespace Core { +class System; +} + +namespace Service::NVFlinger { + +class HosBinderDriverServer final { +public: + explicit HosBinderDriverServer(Core::System& system_); + ~HosBinderDriverServer(); + + u64 RegisterProducer(std::unique_ptr<android::IBinder>&& binder); + + android::IBinder* TryGetProducer(u64 id); + +private: + KernelHelpers::ServiceContext service_context; + + std::unordered_map<u64, std::unique_ptr<android::IBinder>> producers; + std::mutex lock; + u64 last_id{}; +}; + +} // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 01e69de30..76ce1fbfd 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project #include <algorithm> #include <optional> @@ -16,8 +15,11 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/nvdrv.h" -#include "core/hle/service/nvflinger/buffer_queue.h" +#include "core/hle/service/nvflinger/buffer_item_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_core.h" +#include "core/hle/service/nvflinger/hos_binder_driver_server.h" #include "core/hle/service/nvflinger/nvflinger.h" +#include "core/hle/service/nvflinger/ui/graphic_buffer.h" #include "core/hle/service/vi/display/vi_display.h" #include "core/hle/service/vi/layer/vi_layer.h" #include "video_core/gpu.h" @@ -53,13 +55,14 @@ void NVFlinger::SplitVSync(std::stop_token stop_token) { } } -NVFlinger::NVFlinger(Core::System& system_) - : system(system_), service_context(system_, "nvflinger") { - displays.emplace_back(0, "Default", service_context, system); - displays.emplace_back(1, "External", service_context, system); - displays.emplace_back(2, "Edid", service_context, system); - displays.emplace_back(3, "Internal", service_context, system); - displays.emplace_back(4, "Null", service_context, system); +NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_) + : system(system_), service_context(system_, "nvflinger"), + hos_binder_driver_server(hos_binder_driver_server_) { + displays.emplace_back(0, "Default", hos_binder_driver_server, service_context, system); + displays.emplace_back(1, "External", hos_binder_driver_server, service_context, system); + displays.emplace_back(2, "Edid", hos_binder_driver_server, service_context, system); + displays.emplace_back(3, "Internal", hos_binder_driver_server, service_context, system); + displays.emplace_back(4, "Null", hos_binder_driver_server, service_context, system); guard = std::make_shared<std::mutex>(); // Schedule the screen composition events @@ -83,12 +86,15 @@ NVFlinger::NVFlinger(Core::System& system_) } NVFlinger::~NVFlinger() { - for (auto& buffer_queue : buffer_queues) { - buffer_queue->Disconnect(); - } if (!system.IsMulticore()) { system.CoreTiming().UnscheduleEvent(composition_event, 0); } + + for (auto& display : displays) { + for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { + display.GetLayer(layer).Core().NotifyShutdown(); + } + } } void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { @@ -125,10 +131,8 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { } void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { - const u32 buffer_queue_id = next_buffer_queue_id++; - buffer_queues.emplace_back( - std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context)); - display.CreateLayer(layer_id, *buffer_queues.back()); + const auto buffer_id = next_buffer_queue_id++; + display.CreateLayer(layer_id, buffer_id); } void NVFlinger::CloseLayer(u64 layer_id) { @@ -147,7 +151,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) { return std::nullopt; } - return layer->GetBufferQueue().GetId(); + return layer->GetBinderId(); } Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) { @@ -161,18 +165,6 @@ Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) { return &display->GetVSyncEvent(); } -BufferQueue* NVFlinger::FindBufferQueue(u32 id) { - const auto lock_guard = Lock(); - const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), - [id](const auto& queue) { return queue->GetId() == id; }); - - if (itr == buffer_queues.end()) { - return nullptr; - } - - return itr->get(); -} - VI::Display* NVFlinger::FindDisplay(u64 display_id) { const auto itr = std::find_if(displays.begin(), displays.end(), @@ -246,23 +238,22 @@ void NVFlinger::Compose() { // TODO(Subv): Support more than 1 layer. VI::Layer& layer = display.GetLayer(0); - auto& buffer_queue = layer.GetBufferQueue(); - // Search for a queued buffer and acquire it - auto buffer = buffer_queue.AcquireBuffer(); + android::BufferItem buffer{}; + const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false); - if (!buffer) { + if (status != android::Status::NoError) { continue; } - const auto& igbp_buffer = buffer->get().igbp_buffer; + const auto& igbp_buffer = *buffer.graphic_buffer; if (!system.IsPoweredOn()) { return; // We are likely shutting down } auto& gpu = system.GPU(); - const auto& multi_fence = buffer->get().multi_fence; + const auto& multi_fence = buffer.fence; guard->unlock(); for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { const auto& fence = multi_fence.fences[fence_id]; @@ -278,12 +269,18 @@ void NVFlinger::Compose() { auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); ASSERT(nvdisp); - nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.external_format, - igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, - buffer->get().transform, buffer->get().crop_rect); + Common::Rectangle<int> crop_rect{ + static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()), + static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())}; + + nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(), + igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), + static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect); + + swap_interval = buffer.swap_interval; - swap_interval = buffer->get().swap_interval; - buffer_queue.ReleaseBuffer(buffer->get().slot); + auto fence = android::Fence::NoFence(); + layer.GetConsumer().ReleaseBuffer(buffer, fence); } } diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 7935cf773..ed160f6f9 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project #pragma once @@ -37,13 +36,16 @@ class Display; class Layer; } // namespace Service::VI -namespace Service::NVFlinger { +namespace Service::android { +class BufferQueueCore; +class BufferQueueProducer; +} // namespace Service::android -class BufferQueue; +namespace Service::NVFlinger { class NVFlinger final { public: - explicit NVFlinger(Core::System& system_); + explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_); ~NVFlinger(); /// Sets the NVDrv module instance to use to send buffers to the GPU. @@ -72,9 +74,6 @@ public: /// If an invalid display ID is provided, then nullptr is returned. [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id); - /// Obtains a buffer queue identified by the ID. - [[nodiscard]] BufferQueue* FindBufferQueue(u32 id); - /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when /// finished. void Compose(); @@ -82,6 +81,12 @@ public: [[nodiscard]] s64 GetNextTicks() const; private: + struct Layer { + std::unique_ptr<android::BufferQueueCore> core; + std::unique_ptr<android::BufferQueueProducer> producer; + }; + +private: [[nodiscard]] std::unique_lock<std::mutex> Lock() const { return std::unique_lock{*guard}; } @@ -111,7 +116,6 @@ private: std::shared_ptr<Nvidia::Module> nvdrv; std::list<VI::Display> displays; - std::vector<std::unique_ptr<BufferQueue>> buffer_queues; /// Id to use for the next layer that is created, this counter is shared among all displays. u64 next_layer_id = 1; @@ -131,6 +135,8 @@ private: std::jthread vsync_thread; KernelHelpers::ServiceContext service_context; + + HosBinderDriverServer& hos_binder_driver_server; }; } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/parcel.h b/src/core/hle/service/nvflinger/parcel.h new file mode 100644 index 000000000..aa36e6479 --- /dev/null +++ b/src/core/hle/service/nvflinger/parcel.h @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project + +#pragma once + +#include <memory> +#include <vector> + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/common_types.h" + +namespace Service::android { + +class Parcel final { +public: + static constexpr std::size_t DefaultBufferSize = 0x40; + + Parcel() : buffer(DefaultBufferSize) {} + + template <typename T> + explicit Parcel(const T& out_data) : buffer(DefaultBufferSize) { + Write(out_data); + } + + explicit Parcel(std::vector<u8> in_data) : buffer(std::move(in_data)) { + DeserializeHeader(); + [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); + } + + template <typename T> + void Read(T& val) { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); + ASSERT(read_index + sizeof(T) <= buffer.size()); + + std::memcpy(&val, buffer.data() + read_index, sizeof(T)); + read_index += sizeof(T); + read_index = Common::AlignUp(read_index, 4); + } + + template <typename T> + T Read() { + T val; + Read(val); + return val; + } + + template <typename T> + void ReadFlattened(T& val) { + const auto flattened_size = Read<s64>(); + ASSERT(sizeof(T) == flattened_size); + Read(val); + } + + template <typename T> + T ReadFlattened() { + T val; + ReadFlattened(val); + return val; + } + + template <typename T> + T ReadUnaligned() { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); + ASSERT(read_index + sizeof(T) <= buffer.size()); + + T val; + std::memcpy(&val, buffer.data() + read_index, sizeof(T)); + read_index += sizeof(T); + return val; + } + + template <typename T> + const std::shared_ptr<T> ReadObject() { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); + + const auto is_valid{Read<bool>()}; + + if (is_valid) { + auto result = std::make_shared<T>(); + ReadFlattened(*result); + return result; + } + + return {}; + } + + std::u16string ReadInterfaceToken() { + [[maybe_unused]] const u32 unknown = Read<u32>(); + const u32 length = Read<u32>(); + + std::u16string token; + token.reserve(length + 1); + + for (u32 ch = 0; ch < length + 1; ++ch) { + token.push_back(ReadUnaligned<u16>()); + } + + read_index = Common::AlignUp(read_index, 4); + + return token; + } + + template <typename T> + void Write(const T& val) { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); + + if (buffer.size() < write_index + sizeof(T)) { + buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize); + } + + std::memcpy(buffer.data() + write_index, &val, sizeof(T)); + write_index += sizeof(T); + write_index = Common::AlignUp(write_index, 4); + } + + template <typename T> + void WriteObject(const T* ptr) { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); + + if (!ptr) { + Write<u32>(0); + return; + } + + Write<u32>(1); + Write<s64>(sizeof(T)); + Write(*ptr); + } + + template <typename T> + void WriteObject(const std::shared_ptr<T> ptr) { + WriteObject(ptr.get()); + } + + void DeserializeHeader() { + ASSERT(buffer.size() > sizeof(Header)); + + Header header{}; + std::memcpy(&header, buffer.data(), sizeof(Header)); + + read_index = header.data_offset; + } + + std::vector<u8> Serialize() const { + ASSERT(read_index == 0); + + Header header{}; + header.data_size = static_cast<u32>(write_index - sizeof(Header)); + header.data_offset = sizeof(Header); + header.objects_size = 4; + header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size); + std::memcpy(buffer.data(), &header, sizeof(Header)); + + return buffer; + } + +private: + struct Header { + u32 data_size; + u32 data_offset; + u32 objects_size; + u32 objects_offset; + }; + static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size"); + + mutable std::vector<u8> buffer; + std::size_t read_index = 0; + std::size_t write_index = sizeof(Header); +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/pixel_format.h b/src/core/hle/service/nvflinger/pixel_format.h new file mode 100644 index 000000000..8a77d8bea --- /dev/null +++ b/src/core/hle/service/nvflinger/pixel_format.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project + +#pragma once + +#include "common/common_types.h" + +namespace Service::android { + +enum class PixelFormat : u32 { + NoFormat = 0, + Rgba8888 = 1, + Rgbx8888 = 2, + Rgb888 = 3, + Rgb565 = 4, + Bgra8888 = 5, + Rgba5551 = 6, + Rgba4444 = 7, +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/producer_listener.h b/src/core/hle/service/nvflinger/producer_listener.h new file mode 100644 index 000000000..468e06431 --- /dev/null +++ b/src/core/hle/service/nvflinger/producer_listener.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2014 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IProducerListener.h + +#pragma once + +namespace Service::android { + +class IProducerListener { +public: + virtual void OnBufferReleased() = 0; +}; + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/status.h b/src/core/hle/service/nvflinger/status.h new file mode 100644 index 000000000..a003eda89 --- /dev/null +++ b/src/core/hle/service/nvflinger/status.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Service::android { + +enum class Status : s32 { + None = 0, + NoError = 0, + StaleBufferSlot = 1, + NoBufferAvailable = 2, + PresentLater = 3, + WouldBlock = -11, + NoMemory = -12, + Busy = -16, + NoInit = -19, + BadValue = -22, + InvalidOperation = -37, + BufferNeedsReallocation = 1, + ReleaseAllBuffers = 2, +}; +DECLARE_ENUM_FLAG_OPERATORS(Status); + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/ui/fence.h b/src/core/hle/service/nvflinger/ui/fence.h new file mode 100644 index 000000000..4a74e26a3 --- /dev/null +++ b/src/core/hle/service/nvflinger/ui/fence.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2012 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/Fence.h + +#pragma once + +#include <array> + +#include "common/common_types.h" +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Service::android { + +class Fence { +public: + constexpr Fence() = default; + + static constexpr Fence NoFence() { + Fence fence; + fence.fences[0].id = -1; + return fence; + } + +public: + u32 num_fences{}; + std::array<Service::Nvidia::NvFence, 4> fences{}; +}; +static_assert(sizeof(Fence) == 36, "Fence has wrong size"); + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/ui/graphic_buffer.h b/src/core/hle/service/nvflinger/ui/graphic_buffer.h new file mode 100644 index 000000000..7abbf78ba --- /dev/null +++ b/src/core/hle/service/nvflinger/ui/graphic_buffer.h @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project +// Copyright 2007 The Android Open Source Project +// Parts of this implementation were base on: +// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/GraphicBuffer.h + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/service/nvflinger/pixel_format.h" + +namespace Service::android { + +class GraphicBuffer final { +public: + constexpr GraphicBuffer() = default; + + constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) + : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_}, + usage{static_cast<s32>(usage_)} {} + + constexpr u32 Width() const { + return static_cast<u32>(width); + } + + constexpr u32 Height() const { + return static_cast<u32>(height); + } + + constexpr u32 Stride() const { + return static_cast<u32>(stride); + } + + constexpr u32 Usage() const { + return static_cast<u32>(usage); + } + + constexpr PixelFormat Format() const { + return format; + } + + constexpr u32 BufferId() const { + return buffer_id; + } + + constexpr PixelFormat ExternalFormat() const { + return external_format; + } + + constexpr u32 Handle() const { + return handle; + } + + constexpr u32 Offset() const { + return offset; + } + + constexpr bool NeedsReallocation(u32 width_, u32 height_, PixelFormat format_, + u32 usage_) const { + if (static_cast<s32>(width_) != width) { + return true; + } + + if (static_cast<s32>(height_) != height) { + return true; + } + + if (format_ != format) { + return true; + } + + if ((static_cast<u32>(usage) & usage_) != usage_) { + return true; + } + + return false; + } + +private: + u32 magic{}; + s32 width{}; + s32 height{}; + s32 stride{}; + PixelFormat format{}; + s32 usage{}; + INSERT_PADDING_WORDS(1); + u32 index{}; + INSERT_PADDING_WORDS(3); + u32 buffer_id{}; + INSERT_PADDING_WORDS(6); + PixelFormat external_format{}; + INSERT_PADDING_WORDS(10); + u32 handle{}; + u32 offset{}; + INSERT_PADDING_WORDS(60); +}; +static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size"); + +} // namespace Service::android diff --git a/src/core/hle/service/nvflinger/window.h b/src/core/hle/service/nvflinger/window.h new file mode 100644 index 000000000..e26f8160e --- /dev/null +++ b/src/core/hle/service/nvflinger/window.h @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright 2021 yuzu Emulator Project + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Service::android { + +/// Attributes queryable with Query +enum class NativeWindow : s32 { + Width = 0, + Height = 1, + Format = 2, + MinUndequeedBuffers = 3, + QueuesToWindowComposer = 4, + ConcreteType = 5, + DefaultWidth = 6, + DefaultHeight = 7, + TransformHint = 8, + ConsumerRunningBehind = 9, + ConsumerUsageBits = 10, + StickyTransform = 11, + DefaultDataSpace = 12, + BufferAge = 13, +}; + +/// Parameter for Connect/Disconnect +enum class NativeWindowApi : s32 { + NoConnectedApi = 0, + Egl = 1, + Cpu = 2, + Media = 3, + Camera = 4, +}; + +/// Scaling mode parameter for QueueBuffer +enum class NativeWindowScalingMode : s32 { + Freeze = 0, + ScaleToWindow = 1, + ScaleCrop = 2, + NoScaleCrop = 3, +}; + +/// Transform parameter for QueueBuffer +enum class NativeWindowTransform : u32 { + None = 0x0, + InverseDisplay = 0x08, +}; +DECLARE_ENUM_FLAG_OPERATORS(NativeWindowTransform); + +} // namespace Service::android diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index eb1138313..ab3286db9 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -49,6 +49,7 @@ #include "core/hle/service/npns/npns.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvflinger/hos_binder_driver_server.h" #include "core/hle/service/nvflinger/nvflinger.h" #include "core/hle/service/olsc/olsc.h" #include "core/hle/service/pcie/pcie.h" @@ -230,7 +231,8 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& sessi /// Initialize Services Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) - : nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system)} { + : hos_binder_driver_server{std::make_unique<NVFlinger::HosBinderDriverServer>(system)}, + nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system, *hos_binder_driver_server)} { // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it // here and pass it into the respective InstallInterfaces functions. @@ -290,7 +292,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system SSL::InstallInterfaces(*sm, system); Time::InstallInterfaces(system); USB::InstallInterfaces(*sm, system); - VI::InstallInterfaces(*sm, system, *nv_flinger); + VI::InstallInterfaces(*sm, system, *nv_flinger, *hos_binder_driver_server); WLAN::InstallInterfaces(*sm, system); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index c9d6b879d..b9ab2c465 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -33,8 +33,9 @@ class FileSystemController; } namespace NVFlinger { +class HosBinderDriverServer; class NVFlinger; -} +} // namespace NVFlinger namespace SM { class ServiceManager; @@ -236,6 +237,7 @@ public: ~Services(); private: + std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server; std::unique_ptr<NVFlinger::NVFlinger> nv_flinger; }; diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index b7705c02a..558022511 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -13,14 +13,34 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nvflinger/buffer_item_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_consumer.h" +#include "core/hle/service/nvflinger/buffer_queue_core.h" +#include "core/hle/service/nvflinger/buffer_queue_producer.h" +#include "core/hle/service/nvflinger/hos_binder_driver_server.h" #include "core/hle/service/vi/display/vi_display.h" #include "core/hle/service/vi/layer/vi_layer.h" namespace Service::VI { -Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, - Core::System& system_) - : display_id{id}, name{std::move(name_)}, service_context{service_context_} { +struct BufferQueue { + std::shared_ptr<android::BufferQueueCore> core; + std::unique_ptr<android::BufferQueueProducer> producer; + std::unique_ptr<android::BufferQueueConsumer> consumer; +}; + +static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context) { + auto buffer_queue_core = std::make_shared<android::BufferQueueCore>(); + return {buffer_queue_core, + std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core), + std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)}; +} + +Display::Display(u64 id, std::string name_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_, + KernelHelpers::ServiceContext& service_context_, Core::System& system_) + : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_}, + service_context{service_context_} { vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); } @@ -44,21 +64,29 @@ void Display::SignalVSyncEvent() { vsync_event->GetWritableEvent().Signal(); } -void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) { - // TODO(Subv): Support more than 1 layer. +void Display::CreateLayer(u64 layer_id, u32 binder_id) { ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); - layers.emplace_back(std::make_shared<Layer>(layer_id, buffer_queue)); + auto [core, producer, consumer] = CreateBufferQueue(service_context); + + auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer)); + buffer_item_consumer->Connect(false); + + layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer, + std::move(buffer_item_consumer))); + + hos_binder_driver_server.RegisterProducer(std::move(producer)); } void Display::CloseLayer(u64 layer_id) { - std::erase_if(layers, [layer_id](const auto& layer) { return layer->GetID() == layer_id; }); + std::erase_if(layers, + [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; }); } Layer* Display::FindLayer(u64 layer_id) { const auto itr = - std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) { - return layer->GetID() == layer_id; + std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) { + return layer->GetLayerId() == layer_id; }); if (itr == layers.end()) { @@ -70,8 +98,8 @@ Layer* Display::FindLayer(u64 layer_id) { const Layer* Display::FindLayer(u64 layer_id) const { const auto itr = - std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) { - return layer->GetID() == layer_id; + std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) { + return layer->GetLayerId() == layer_id; }); if (itr == layers.end()) { diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 329f4ba86..e93d084ee 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h @@ -15,12 +15,17 @@ namespace Kernel { class KEvent; } -namespace Service::NVFlinger { -class BufferQueue; +namespace Service::android { +class BufferQueueProducer; } + namespace Service::KernelHelpers { class ServiceContext; -} // namespace Service::KernelHelpers +} + +namespace Service::NVFlinger { +class HosBinderDriverServer; +} namespace Service::VI { @@ -35,12 +40,13 @@ public: /// Constructs a display with a given unique ID and name. /// /// @param id The unique ID for this display. + /// @param hos_binder_driver_server_ NVFlinger HOSBinderDriver server instance. /// @param service_context_ The ServiceContext for the owning service. /// @param name_ The name for this display. /// @param system_ The global system instance. /// - Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, - Core::System& system_); + Display(u64 id, std::string name_, NVFlinger::HosBinderDriverServer& hos_binder_driver_server_, + KernelHelpers::ServiceContext& service_context_, Core::System& system_); ~Display(); /// Gets the unique ID assigned to this display. @@ -64,6 +70,10 @@ public: /// Gets a layer for this display based off an index. const Layer& GetLayer(std::size_t index) const; + std::size_t GetNumLayers() const { + return layers.size(); + } + /// Gets the readable vsync event. Kernel::KReadableEvent& GetVSyncEvent(); @@ -72,10 +82,10 @@ public: /// Creates and adds a layer to this display with the given ID. /// - /// @param layer_id The ID to assign to the created layer. - /// @param buffer_queue The buffer queue for the layer instance to use. + /// @param layer_id The ID to assign to the created layer. + /// @param binder_id The ID assigned to the buffer queue. /// - void CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue); + void CreateLayer(u64 layer_id, u32 binder_id); /// Closes and removes a layer from this display with the given ID. /// @@ -104,9 +114,10 @@ public: private: u64 display_id; std::string name; + NVFlinger::HosBinderDriverServer& hos_binder_driver_server; KernelHelpers::ServiceContext& service_context; - std::vector<std::shared_ptr<Layer>> layers; + std::vector<std::unique_ptr<Layer>> layers; Kernel::KEvent* vsync_event{}; }; diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp index 9bc382587..93858e91f 100644 --- a/src/core/hle/service/vi/layer/vi_layer.cpp +++ b/src/core/hle/service/vi/layer/vi_layer.cpp @@ -6,7 +6,11 @@ namespace Service::VI { -Layer::Layer(u64 id, NVFlinger::BufferQueue& queue) : layer_id{id}, buffer_queue{queue} {} +Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, + android::BufferQueueProducer& binder_, + std::shared_ptr<android::BufferItemConsumer>&& consumer_) + : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move( + consumer_)} {} Layer::~Layer() = default; diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h index ebdd85505..c28b14450 100644 --- a/src/core/hle/service/vi/layer/vi_layer.h +++ b/src/core/hle/service/vi/layer/vi_layer.h @@ -4,11 +4,15 @@ #pragma once +#include <memory> + #include "common/common_types.h" -namespace Service::NVFlinger { -class BufferQueue; -} +namespace Service::android { +class BufferItemConsumer; +class BufferQueueCore; +class BufferQueueProducer; +} // namespace Service::android namespace Service::VI { @@ -17,10 +21,13 @@ class Layer { public: /// Constructs a layer with a given ID and buffer queue. /// - /// @param id The ID to assign to this layer. - /// @param queue The buffer queue for this layer to use. + /// @param layer_id_ The ID to assign to this layer. + /// @param binder_id_ The binder ID to assign to this layer. + /// @param binder_ The buffer producer queue for this layer to use. /// - Layer(u64 id, NVFlinger::BufferQueue& queue); + Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, + android::BufferQueueProducer& binder_, + std::shared_ptr<android::BufferItemConsumer>&& consumer_); ~Layer(); Layer(const Layer&) = delete; @@ -30,23 +37,47 @@ public: Layer& operator=(Layer&&) = delete; /// Gets the ID for this layer. - u64 GetID() const { + u64 GetLayerId() const { return layer_id; } + /// Gets the binder ID for this layer. + u32 GetBinderId() const { + return binder_id; + } + /// Gets a reference to the buffer queue this layer is using. - NVFlinger::BufferQueue& GetBufferQueue() { - return buffer_queue; + android::BufferQueueProducer& GetBufferQueue() { + return binder; } /// Gets a const reference to the buffer queue this layer is using. - const NVFlinger::BufferQueue& GetBufferQueue() const { - return buffer_queue; + const android::BufferQueueProducer& GetBufferQueue() const { + return binder; + } + + android::BufferItemConsumer& GetConsumer() { + return *consumer; + } + + const android::BufferItemConsumer& GetConsumer() const { + return *consumer; + } + + android::BufferQueueCore& Core() { + return core; + } + + const android::BufferQueueCore& Core() const { + return core; } private: - u64 layer_id; - NVFlinger::BufferQueue& buffer_queue; + const u64 layer_id; + const u32 binder_id; + android::BufferQueueCore& core; + android::BufferQueueProducer& binder; + std::shared_ptr<android::BufferItemConsumer> consumer; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 75ee3e5e4..430cbc546 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -22,8 +22,11 @@ #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvflinger/buffer_queue.h" +#include "core/hle/service/nvflinger/binder.h" +#include "core/hle/service/nvflinger/buffer_queue_producer.h" +#include "core/hle/service/nvflinger/hos_binder_driver_server.h" #include "core/hle/service/nvflinger/nvflinger.h" +#include "core/hle/service/nvflinger/parcel.h" #include "core/hle/service/service.h" #include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_m.h" @@ -57,447 +60,24 @@ struct DisplayInfo { }; static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); -class Parcel { +class NativeWindow final { public: - // This default size was chosen arbitrarily. - static constexpr std::size_t DefaultBufferSize = 0x40; - Parcel() : buffer(DefaultBufferSize) {} - explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {} - virtual ~Parcel() = default; - - template <typename T> - T Read() { - static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); - ASSERT(read_index + sizeof(T) <= buffer.size()); - - T val; - std::memcpy(&val, buffer.data() + read_index, sizeof(T)); - read_index += sizeof(T); - read_index = Common::AlignUp(read_index, 4); - return val; - } - - template <typename T> - T ReadUnaligned() { - static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); - ASSERT(read_index + sizeof(T) <= buffer.size()); - - T val; - std::memcpy(&val, buffer.data() + read_index, sizeof(T)); - read_index += sizeof(T); - return val; - } - - std::vector<u8> ReadBlock(std::size_t length) { - ASSERT(read_index + length <= buffer.size()); - const u8* const begin = buffer.data() + read_index; - const u8* const end = begin + length; - std::vector<u8> data(begin, end); - read_index += length; - read_index = Common::AlignUp(read_index, 4); - return data; - } - - std::u16string ReadInterfaceToken() { - [[maybe_unused]] const u32 unknown = Read<u32_le>(); - const u32 length = Read<u32_le>(); - - std::u16string token{}; - - for (u32 ch = 0; ch < length + 1; ++ch) { - token.push_back(ReadUnaligned<u16_le>()); - } - - read_index = Common::AlignUp(read_index, 4); - - return token; - } - - template <typename T> - void Write(const T& val) { - static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); - - if (buffer.size() < write_index + sizeof(T)) { - buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize); - } - - std::memcpy(buffer.data() + write_index, &val, sizeof(T)); - write_index += sizeof(T); - write_index = Common::AlignUp(write_index, 4); - } - - template <typename T> - void WriteObject(const T& val) { - static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable."); - - const u32_le size = static_cast<u32>(sizeof(val)); - Write(size); - // TODO(Subv): Support file descriptors. - Write<u32_le>(0); // Fd count. - Write(val); - } - - void Deserialize() { - ASSERT(buffer.size() > sizeof(Header)); - - Header header{}; - std::memcpy(&header, buffer.data(), sizeof(Header)); - - read_index = header.data_offset; - DeserializeData(); - } - - std::vector<u8> Serialize() { - ASSERT(read_index == 0); - write_index = sizeof(Header); - - SerializeData(); - - Header header{}; - header.data_size = static_cast<u32_le>(write_index - sizeof(Header)); - header.data_offset = sizeof(Header); - header.objects_size = 4; - header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size); - std::memcpy(buffer.data(), &header, sizeof(Header)); - - return buffer; - } - -protected: - virtual void SerializeData() {} - - virtual void DeserializeData() {} - -private: - struct Header { - u32_le data_size; - u32_le data_offset; - u32_le objects_size; - u32_le objects_offset; - }; - static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size"); - - std::vector<u8> buffer; - std::size_t read_index = 0; - std::size_t write_index = 0; -}; - -class NativeWindow : public Parcel { -public: - explicit NativeWindow(u32 id) { - data.id = id; - } - ~NativeWindow() override = default; - -protected: - void SerializeData() override { - Write(data); - } - -private: - struct Data { - u32_le magic = 2; - u32_le process_id = 1; - u32_le id; - INSERT_PADDING_WORDS(3); - std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'}; - INSERT_PADDING_WORDS(2); - }; - static_assert(sizeof(Data) == 0x28, "ParcelData has wrong size"); - - Data data{}; -}; - -class IGBPConnectRequestParcel : public Parcel { -public: - explicit IGBPConnectRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - data = Read<Data>(); - } - - struct Data { - u32_le unk; - u32_le api; - u32_le producer_controlled_by_app; - }; - - Data data; -}; - -class IGBPConnectResponseParcel : public Parcel { -public: - explicit IGBPConnectResponseParcel(u32 width, u32 height) { - data.width = width; - data.height = height; - } - ~IGBPConnectResponseParcel() override = default; - -protected: - void SerializeData() override { - Write(data); - } - -private: - struct Data { - u32_le width; - u32_le height; - u32_le transform_hint; - u32_le num_pending_buffers; - u32_le status; - }; - static_assert(sizeof(Data) == 20, "ParcelData has wrong size"); - - Data data{}; -}; - -/// Represents a parcel containing one int '0' as its data -/// Used by DetachBuffer and Disconnect -class IGBPEmptyResponseParcel : public Parcel { -protected: - void SerializeData() override { - Write(data); - } - -private: - struct Data { - u32_le unk_0{}; - }; - - Data data{}; -}; - -class IGBPSetPreallocatedBufferRequestParcel : public Parcel { -public: - explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer_) - : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - data = Read<Data>(); - if (data.contains_object != 0) { - buffer_container = Read<BufferContainer>(); - } - } - - struct Data { - u32_le slot; - u32_le contains_object; - }; - - struct BufferContainer { - u32_le graphic_buffer_length; - INSERT_PADDING_WORDS(1); - NVFlinger::IGBPBuffer buffer{}; - }; - - Data data{}; - BufferContainer buffer_container{}; -}; - -class IGBPSetPreallocatedBufferResponseParcel : public Parcel { -protected: - void SerializeData() override { - // TODO(Subv): Find out what this means - Write<u32>(0); - } -}; - -class IGBPCancelBufferRequestParcel : public Parcel { -public: - explicit IGBPCancelBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - data = Read<Data>(); - } - - struct Data { - u32_le slot; - Service::Nvidia::MultiFence multi_fence; - }; - - Data data; -}; - -class IGBPCancelBufferResponseParcel : public Parcel { -protected: - void SerializeData() override { - Write<u32>(0); // Success - } -}; - -class IGBPDequeueBufferRequestParcel : public Parcel { -public: - explicit IGBPDequeueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - data = Read<Data>(); - } - - struct Data { - u32_le pixel_format; - u32_le width; - u32_le height; - u32_le get_frame_timestamps; - u32_le usage; - }; - - Data data; -}; - -class IGBPDequeueBufferResponseParcel : public Parcel { -public: - explicit IGBPDequeueBufferResponseParcel(u32 slot_, Nvidia::MultiFence& multi_fence_) - : slot(slot_), multi_fence(multi_fence_) {} - -protected: - void SerializeData() override { - Write(slot); - Write<u32_le>(1); - WriteObject(multi_fence); - Write<u32_le>(0); - } - - u32_le slot; - Service::Nvidia::MultiFence multi_fence; -}; - -class IGBPRequestBufferRequestParcel : public Parcel { -public: - explicit IGBPRequestBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - slot = Read<u32_le>(); - } - - u32_le slot; -}; - -class IGBPRequestBufferResponseParcel : public Parcel { -public: - explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer_) : buffer(buffer_) {} - ~IGBPRequestBufferResponseParcel() override = default; - -protected: - void SerializeData() override { - // TODO(Subv): Figure out what this value means, writing non-zero here will make libnx - // try to read an IGBPBuffer object from the parcel. - Write<u32_le>(1); - WriteObject(buffer); - Write<u32_le>(0); - } - - NVFlinger::IGBPBuffer buffer; -}; - -class IGBPQueueBufferRequestParcel : public Parcel { -public: - explicit IGBPQueueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - data = Read<Data>(); - } - - struct Data { - u32_le slot; - INSERT_PADDING_WORDS(3); - u32_le timestamp; - s32_le is_auto_timestamp; - s32_le crop_top; - s32_le crop_left; - s32_le crop_right; - s32_le crop_bottom; - s32_le scaling_mode; - NVFlinger::BufferQueue::BufferTransformFlags transform; - u32_le sticky_transform; - INSERT_PADDING_WORDS(1); - u32_le swap_interval; - Service::Nvidia::MultiFence multi_fence; - - Common::Rectangle<int> GetCropRect() const { - return {crop_left, crop_top, crop_right, crop_bottom}; - } - }; - static_assert(sizeof(Data) == 96, "ParcelData has wrong size"); - - Data data; -}; - -class IGBPQueueBufferResponseParcel : public Parcel { -public: - explicit IGBPQueueBufferResponseParcel(u32 width, u32 height) { - data.width = width; - data.height = height; - } - ~IGBPQueueBufferResponseParcel() override = default; - -protected: - void SerializeData() override { - Write(data); - } - -private: - struct Data { - u32_le width; - u32_le height; - u32_le transform_hint; - u32_le num_pending_buffers; - u32_le status; - }; - static_assert(sizeof(Data) == 20, "ParcelData has wrong size"); - - Data data{}; -}; - -class IGBPQueryRequestParcel : public Parcel { -public: - explicit IGBPQueryRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) { - Deserialize(); - } - - void DeserializeData() override { - [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); - type = Read<u32_le>(); - } - - u32 type; -}; - -class IGBPQueryResponseParcel : public Parcel { -public: - explicit IGBPQueryResponseParcel(u32 value_) : value{value_} {} - ~IGBPQueryResponseParcel() override = default; - -protected: - void SerializeData() override { - Write(value); - } + constexpr explicit NativeWindow(u32 id_) : id{id_} {} private: - u32_le value; + const u32 magic = 2; + const u32 process_id = 1; + const u32 id; + INSERT_PADDING_WORDS(3); + std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'}; + INSERT_PADDING_WORDS(2); }; +static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size"); class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { public: - explicit IHOSBinderDriver(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) - : ServiceFramework{system_, "IHOSBinderDriver"}, nv_flinger(nv_flinger_) { + explicit IHOSBinderDriver(Core::System& system_, NVFlinger::HosBinderDriverServer& server_) + : ServiceFramework{system_, "IHOSBinderDriver"}, server(server_) { static const FunctionInfo functions[] = { {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, @@ -508,147 +88,16 @@ public: } private: - enum class TransactionId { - RequestBuffer = 1, - SetBufferCount = 2, - DequeueBuffer = 3, - DetachBuffer = 4, - DetachNextBuffer = 5, - AttachBuffer = 6, - QueueBuffer = 7, - CancelBuffer = 8, - Query = 9, - Connect = 10, - Disconnect = 11, - - AllocateBuffers = 13, - SetPreallocatedBuffer = 14, - - GetBufferHistory = 17 - }; - void TransactParcel(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u32 id = rp.Pop<u32>(); - const auto transaction = static_cast<TransactionId>(rp.Pop<u32>()); + const auto transaction = static_cast<android::TransactionId>(rp.Pop<u32>()); const u32 flags = rp.Pop<u32>(); LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, transaction, flags); - auto& buffer_queue = *nv_flinger.FindBufferQueue(id); - - switch (transaction) { - case TransactionId::Connect: { - IGBPConnectRequestParcel request{ctx.ReadBuffer()}; - IGBPConnectResponseParcel response{static_cast<u32>(DisplayResolution::UndockedWidth), - static_cast<u32>(DisplayResolution::UndockedHeight)}; - - buffer_queue.Connect(); - - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::SetPreallocatedBuffer: { - IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()}; - - buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer_container.buffer); - - IGBPSetPreallocatedBufferResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::DequeueBuffer: { - IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; - const u32 width{request.data.width}; - const u32 height{request.data.height}; - - do { - if (auto result = buffer_queue.DequeueBuffer(width, height); result) { - // Buffer is available - IGBPDequeueBufferResponseParcel response{result->first, *result->second}; - ctx.WriteBuffer(response.Serialize()); - break; - } - } while (buffer_queue.IsConnected()); - - break; - } - case TransactionId::RequestBuffer: { - IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()}; - - auto& buffer = buffer_queue.RequestBuffer(request.slot); - IGBPRequestBufferResponseParcel response{buffer}; - ctx.WriteBuffer(response.Serialize()); - - break; - } - case TransactionId::QueueBuffer: { - IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()}; - - buffer_queue.QueueBuffer(request.data.slot, request.data.transform, - request.data.GetCropRect(), request.data.swap_interval, - request.data.multi_fence); - - IGBPQueueBufferResponseParcel response{1280, 720}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::Query: { - IGBPQueryRequestParcel request{ctx.ReadBuffer()}; - - const u32 value = - buffer_queue.Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type)); - - IGBPQueryResponseParcel response{value}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::CancelBuffer: { - IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()}; - - buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence); - - IGBPCancelBufferResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::Disconnect: { - LOG_WARNING(Service_VI, "(STUBBED) called, transaction=Disconnect"); - const auto buffer = ctx.ReadBuffer(); - - buffer_queue.Disconnect(); - - IGBPEmptyResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::DetachBuffer: { - const auto buffer = ctx.ReadBuffer(); - - IGBPEmptyResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::SetBufferCount: { - LOG_WARNING(Service_VI, "(STUBBED) called, transaction=SetBufferCount"); - [[maybe_unused]] const auto buffer = ctx.ReadBuffer(); - - IGBPEmptyResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - case TransactionId::GetBufferHistory: { - LOG_WARNING(Service_VI, "(STUBBED) called, transaction=GetBufferHistory"); - [[maybe_unused]] const auto buffer = ctx.ReadBuffer(); - - IGBPEmptyResponseParcel response{}; - ctx.WriteBuffer(response.Serialize()); - break; - } - default: - ASSERT_MSG(false, "Unimplemented"); - } + server.TryGetProducer(id)->Transact(ctx, transaction, flags); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); @@ -674,13 +123,13 @@ private: LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); - // TODO(Subv): Find out what this actually is. IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); - rb.PushCopyObjects(nv_flinger.FindBufferQueue(id)->GetBufferWaitEvent()); + rb.PushCopyObjects(server.TryGetProducer(id)->GetNativeHandle()); } - NVFlinger::NVFlinger& nv_flinger; +private: + NVFlinger::HosBinderDriverServer& server; }; class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { @@ -937,7 +386,40 @@ private: class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { public: - explicit IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); + IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) + : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_}, + hos_binder_driver_server{hos_binder_driver_server_} { + + static const FunctionInfo functions[] = { + {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"}, + {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"}, + {102, &IApplicationDisplayService::GetManagerDisplayService, + "GetManagerDisplayService"}, + {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService, + "GetIndirectDisplayTransactionService"}, + {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"}, + {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"}, + {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"}, + {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"}, + {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"}, + {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"}, + {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"}, + {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"}, + {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"}, + {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"}, + {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"}, + {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"}, + {2450, &IApplicationDisplayService::GetIndirectLayerImageMap, + "GetIndirectLayerImageMap"}, + {2451, nullptr, "GetIndirectLayerImageCropMap"}, + {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo, + "GetIndirectLayerImageRequiredMemoryInfo"}, + {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"}, + {5203, nullptr, "GetDisplayVsyncEventForDebug"}, + }; + RegisterHandlers(functions); + } private: enum class ConvertedScaleMode : u64 { @@ -961,7 +443,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger); + rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server); } void GetSystemDisplayService(Kernel::HLERequestContext& ctx) { @@ -985,7 +467,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger); + rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server); } void OpenDisplay(Kernel::HLERequestContext& ctx) { @@ -1089,7 +571,7 @@ private: void ListDisplays(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_VI, "(STUBBED) called"); - DisplayInfo display_info; + const DisplayInfo display_info; ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); @@ -1124,8 +606,8 @@ private: return; } - NativeWindow native_window{*buffer_queue_id}; - const auto buffer_size = ctx.WriteBuffer(native_window.Serialize()); + const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}}; + const auto buffer_size = ctx.WriteBuffer(parcel.Serialize()); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); @@ -1170,8 +652,8 @@ private: return; } - NativeWindow native_window{*buffer_queue_id}; - const auto buffer_size = ctx.WriteBuffer(native_window.Serialize()); + const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}}; + const auto buffer_size = ctx.WriteBuffer(parcel.Serialize()); IPC::ResponseBuilder rb{ctx, 6}; rb.Push(ResultSuccess); @@ -1287,39 +769,9 @@ private: } NVFlinger::NVFlinger& nv_flinger; + NVFlinger::HosBinderDriverServer& hos_binder_driver_server; }; -IApplicationDisplayService::IApplicationDisplayService(Core::System& system_, - NVFlinger::NVFlinger& nv_flinger_) - : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_} { - static const FunctionInfo functions[] = { - {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"}, - {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"}, - {102, &IApplicationDisplayService::GetManagerDisplayService, "GetManagerDisplayService"}, - {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService, - "GetIndirectDisplayTransactionService"}, - {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"}, - {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"}, - {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"}, - {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"}, - {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"}, - {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"}, - {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"}, - {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"}, - {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"}, - {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"}, - {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"}, - {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"}, - {2450, &IApplicationDisplayService::GetIndirectLayerImageMap, "GetIndirectLayerImageMap"}, - {2451, nullptr, "GetIndirectLayerImageCropMap"}, - {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo, - "GetIndirectLayerImageRequiredMemoryInfo"}, - {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"}, - {5203, nullptr, "GetDisplayVsyncEventForDebug"}, - }; - RegisterHandlers(functions); -} - static bool IsValidServiceAccess(Permission permission, Policy policy) { if (permission == Permission::User) { return policy == Policy::User; @@ -1333,7 +785,9 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) { } void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system, - NVFlinger::NVFlinger& nv_flinger, Permission permission) { + NVFlinger::NVFlinger& nv_flinger, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server, + Permission permission) { IPC::RequestParser rp{ctx}; const auto policy = rp.PopEnum<Policy>(); @@ -1346,14 +800,18 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); - rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger); + rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger, hos_binder_driver_server); } void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system, - NVFlinger::NVFlinger& nv_flinger) { - std::make_shared<VI_M>(system, nv_flinger)->InstallAsService(service_manager); - std::make_shared<VI_S>(system, nv_flinger)->InstallAsService(service_manager); - std::make_shared<VI_U>(system, nv_flinger)->InstallAsService(service_manager); + NVFlinger::NVFlinger& nv_flinger, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server) { + std::make_shared<VI_M>(system, nv_flinger, hos_binder_driver_server) + ->InstallAsService(service_manager); + std::make_shared<VI_S>(system, nv_flinger, hos_binder_driver_server) + ->InstallAsService(service_manager); + std::make_shared<VI_U>(system, nv_flinger, hos_binder_driver_server) + ->InstallAsService(service_manager); } } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h index 2fd7f8e61..d68f2646b 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h @@ -15,8 +15,9 @@ class HLERequestContext; } namespace Service::NVFlinger { +class HosBinderDriverServer; class NVFlinger; -} +} // namespace Service::NVFlinger namespace Service::SM { class ServiceManager; @@ -47,11 +48,14 @@ enum class Policy { namespace detail { void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system, - NVFlinger::NVFlinger& nv_flinger, Permission permission); + NVFlinger::NVFlinger& nv_flinger, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server, + Permission permission); } // namespace detail /// Registers all VI services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system, - NVFlinger::NVFlinger& nv_flinger); + NVFlinger::NVFlinger& nv_flinger, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server); } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp index 87db1c416..be0255f3d 100644 --- a/src/core/hle/service/vi/vi_m.cpp +++ b/src/core/hle/service/vi/vi_m.cpp @@ -8,8 +8,10 @@ namespace Service::VI { -VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) - : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_} { +VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) + : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{ + hos_binder_driver_server_} { static const FunctionInfo functions[] = { {2, &VI_M::GetDisplayService, "GetDisplayService"}, {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, @@ -22,7 +24,8 @@ VI_M::~VI_M() = default; void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_VI, "called"); - detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::Manager); + detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server, + Permission::Manager); } } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h index d79c41beb..efbd34e09 100644 --- a/src/core/hle/service/vi/vi_m.h +++ b/src/core/hle/service/vi/vi_m.h @@ -15,20 +15,23 @@ class HLERequestContext; } namespace Service::NVFlinger { +class HosBinderDriverServer; class NVFlinger; -} +} // namespace Service::NVFlinger namespace Service::VI { class VI_M final : public ServiceFramework<VI_M> { public: - explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); + explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_); ~VI_M() override; private: void GetDisplayService(Kernel::HLERequestContext& ctx); NVFlinger::NVFlinger& nv_flinger; + NVFlinger::HosBinderDriverServer& hos_binder_driver_server; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp index 5cd22f7df..7996a6811 100644 --- a/src/core/hle/service/vi/vi_s.cpp +++ b/src/core/hle/service/vi/vi_s.cpp @@ -8,8 +8,10 @@ namespace Service::VI { -VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) - : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_} { +VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) + : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{ + hos_binder_driver_server_} { static const FunctionInfo functions[] = { {1, &VI_S::GetDisplayService, "GetDisplayService"}, {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, @@ -22,7 +24,8 @@ VI_S::~VI_S() = default; void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_VI, "called"); - detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::System); + detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server, + Permission::System); } } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h index 5f1f8f290..3812c5061 100644 --- a/src/core/hle/service/vi/vi_s.h +++ b/src/core/hle/service/vi/vi_s.h @@ -15,20 +15,23 @@ class HLERequestContext; } namespace Service::NVFlinger { +class HosBinderDriverServer; class NVFlinger; -} +} // namespace Service::NVFlinger namespace Service::VI { class VI_S final : public ServiceFramework<VI_S> { public: - explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); + explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_); ~VI_S() override; private: void GetDisplayService(Kernel::HLERequestContext& ctx); NVFlinger::NVFlinger& nv_flinger; + NVFlinger::HosBinderDriverServer& hos_binder_driver_server; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp index 0079d51f0..57c888313 100644 --- a/src/core/hle/service/vi/vi_u.cpp +++ b/src/core/hle/service/vi/vi_u.cpp @@ -8,8 +8,10 @@ namespace Service::VI { -VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) - : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_} { +VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_) + : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{ + hos_binder_driver_server_} { static const FunctionInfo functions[] = { {0, &VI_U::GetDisplayService, "GetDisplayService"}, {1, nullptr, "GetDisplayServiceWithProxyNameExchange"}, @@ -22,7 +24,8 @@ VI_U::~VI_U() = default; void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_VI, "called"); - detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::User); + detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server, + Permission::User); } } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h index 8e3885c73..b08e56576 100644 --- a/src/core/hle/service/vi/vi_u.h +++ b/src/core/hle/service/vi/vi_u.h @@ -15,20 +15,23 @@ class HLERequestContext; } namespace Service::NVFlinger { +class HosBinderDriverServer; class NVFlinger; -} +} // namespace Service::NVFlinger namespace Service::VI { class VI_U final : public ServiceFramework<VI_U> { public: - explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); + explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_, + NVFlinger::HosBinderDriverServer& hos_binder_driver_server_); ~VI_U() override; private: void GetDisplayService(Kernel::HLERequestContext& ctx); NVFlinger::NVFlinger& nv_flinger; + NVFlinger::HosBinderDriverServer& hos_binder_driver_server; }; } // namespace Service::VI diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h index b1d455e30..93349bb78 100644 --- a/src/video_core/framebuffer_config.h +++ b/src/video_core/framebuffer_config.h @@ -6,41 +6,22 @@ #include "common/common_types.h" #include "common/math_util.h" +#include "core/hle/service/nvflinger/buffer_transform_flags.h" +#include "core/hle/service/nvflinger/pixel_format.h" namespace Tegra { + /** * Struct describing framebuffer configuration */ struct FramebufferConfig { - enum class PixelFormat : u32 { - A8B8G8R8_UNORM = 1, - RGB565_UNORM = 4, - B8G8R8A8_UNORM = 5, - }; - - enum class TransformFlags : u32 { - /// No transform flags are set - Unset = 0x00, - /// Flip source image horizontally (around the vertical axis) - FlipH = 0x01, - /// Flip source image vertically (around the horizontal axis) - FlipV = 0x02, - /// Rotate source image 90 degrees clockwise - Rotate90 = 0x04, - /// Rotate source image 180 degrees - Rotate180 = 0x03, - /// Rotate source image 270 degrees clockwise - Rotate270 = 0x07, - }; - VAddr address{}; u32 offset{}; u32 width{}; u32 height{}; u32 stride{}; - PixelFormat pixel_format{}; - - TransformFlags transform_flags{}; + Service::android::PixelFormat pixel_format{}; + Service::android::BufferTransformFlags transform_flags{}; Common::Rectangle<int> crop_rect; }; diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 26b8ea233..97c029140 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -8,6 +8,7 @@ #include "common/bit_field.h" #include "common/common_types.h" +#include "core/hle/service/nvdrv/nvdata.h" #include "video_core/cdma_pusher.h" #include "video_core/framebuffer_config.h" diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 795c97831..f8f29013a 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -323,12 +323,12 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, GLint internal_format; switch (framebuffer.pixel_format) { - case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM: + case Service::android::PixelFormat::Rgba8888: internal_format = GL_RGBA8; texture.gl_format = GL_RGBA; texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; break; - case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: + case Service::android::PixelFormat::Rgb565: internal_format = GL_RGB565; texture.gl_format = GL_RGB; texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; @@ -464,8 +464,8 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { const auto& texcoords = screen_info.display_texcoords; auto left = texcoords.left; auto right = texcoords.right; - if (framebuffer_transform_flags != Tegra::FramebufferConfig::TransformFlags::Unset) { - if (framebuffer_transform_flags == Tegra::FramebufferConfig::TransformFlags::FlipV) { + if (framebuffer_transform_flags != Service::android::BufferTransformFlags::Unset) { + if (framebuffer_transform_flags == Service::android::BufferTransformFlags::FlipV) { // Flip the framebuffer vertically left = texcoords.right; right = texcoords.left; diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 35706cf05..aa206878b 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -46,7 +46,7 @@ struct TextureInfo { GLsizei height; GLenum gl_format; GLenum gl_type; - Tegra::FramebufferConfig::PixelFormat pixel_format; + Service::android::PixelFormat pixel_format; }; /// Structure used for storing information about the display target for the Switch screen @@ -135,7 +135,7 @@ private: std::vector<u8> gl_framebuffer_data; /// Used for transforming the framebuffer orientation - Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{}; + Service::android::BufferTransformFlags framebuffer_transform_flags{}; Common::Rectangle<int> framebuffer_crop_rect; }; diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 0ec85682b..d893c1952 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -94,11 +94,11 @@ std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) { VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { switch (framebuffer.pixel_format) { - case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM: + case Service::android::PixelFormat::Rgba8888: return VK_FORMAT_A8B8G8R8_UNORM_PACK32; - case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: + case Service::android::PixelFormat::Rgb565: return VK_FORMAT_R5G6B5_UNORM_PACK16; - case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM: + case Service::android::PixelFormat::Bgra8888: return VK_FORMAT_B8G8R8A8_UNORM; default: UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", @@ -1390,9 +1390,9 @@ void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfi auto right = texcoords.right; switch (framebuffer_transform_flags) { - case Tegra::FramebufferConfig::TransformFlags::Unset: + case Service::android::BufferTransformFlags::Unset: break; - case Tegra::FramebufferConfig::TransformFlags::FlipV: + case Service::android::BufferTransformFlags::FlipV: // Flip the framebuffer vertically left = texcoords.right; right = texcoords.left; diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp index a36015c8c..5f428d35d 100644 --- a/src/video_core/surface.cpp +++ b/src/video_core/surface.cpp @@ -190,13 +190,13 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format) } } -PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) { +PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format) { switch (format) { - case Tegra::FramebufferConfig::PixelFormat::A8B8G8R8_UNORM: + case Service::android::PixelFormat::Rgba8888: return PixelFormat::A8B8G8R8_UNORM; - case Tegra::FramebufferConfig::PixelFormat::RGB565_UNORM: + case Service::android::PixelFormat::Rgb565: return PixelFormat::R5G6B5_UNORM; - case Tegra::FramebufferConfig::PixelFormat::B8G8R8A8_UNORM: + case Service::android::PixelFormat::Bgra8888: return PixelFormat::B8G8R8A8_UNORM; default: UNIMPLEMENTED_MSG("Unimplemented format={}", format); diff --git a/src/video_core/surface.h b/src/video_core/surface.h index 33e8d24ab..5704cf16a 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h @@ -460,7 +460,7 @@ PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format); PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format); -PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format); +PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format); SurfaceType GetFormatType(PixelFormat pixel_format); |