diff options
Diffstat (limited to 'src/core/hle')
49 files changed, 2888 insertions, 749 deletions
| 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 | 
