From 79e8cdf595e1fe86f985b88c655aea277ba32692 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Thu, 11 Nov 2021 18:43:30 -0800
Subject: hle: nvflinger: Add implementation for ConsumerBase class.

---
 src/core/CMakeLists.txt                          |   2 +
 src/core/hle/service/nvflinger/consumer_base.cpp | 129 +++++++++++++++++++++++
 src/core/hle/service/nvflinger/consumer_base.h   |  59 +++++++++++
 3 files changed, 190 insertions(+)
 create mode 100644 src/core/hle/service/nvflinger/consumer_base.cpp
 create mode 100644 src/core/hle/service/nvflinger/consumer_base.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index fd2777b11..44d6b3cf7 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -542,6 +542,8 @@ add_library(core STATIC
     hle/service/nvflinger/buffer_queue_defs.h
     hle/service/nvflinger/buffer_slot.h
     hle/service/nvflinger/buffer_transform_flags.h
+    hle/service/nvflinger/consumer_base.cpp
+    hle/service/nvflinger/consumer_base.h
     hle/service/nvflinger/consumer_listener.h
     hle/service/nvflinger/nvflinger.cpp
     hle/service/nvflinger/nvflinger.h
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..127e5930b
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_base.cpp
@@ -0,0 +1,129 @@
+// 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 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, u64 present_when_ns,
+                                         u64 max_frame_number) {
+    if (is_abandoned) {
+        LOG_ERROR(Service_NVFlinger, "consumer is abandoned!");
+        return Status::NoInit;
+    }
+
+    Status err = consumer->AcquireBuffer(item, present_when_ns, 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) {
+    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 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..574ea9781
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_base.h
@@ -0,0 +1,59 @@
+// 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 <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 android {
+
+class BufferItem;
+class BufferQueueConsumer;
+
+class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
+public:
+    void Connect(bool controlled_by_app);
+
+protected:
+    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, u64 present_when_ns, 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);
+    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 android
-- 
cgit v1.2.3