summaryrefslogtreecommitdiffstats
path: root/src/core/hle/service/nvnflinger/consumer_base.cpp
blob: e360ebfd8e4d83594ed8196a213c3e287d81f8be (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project
// SPDX-License-Identifier: GPL-3.0-or-later
// Parts of this implementation were based 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/nvnflinger/buffer_item.h"
#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
#include "core/hle/service/nvnflinger/buffer_queue_core.h"
#include "core/hle/service/nvnflinger/consumer_base.h"
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"

namespace Service::android {

ConsumerBase::ConsumerBase(std::shared_ptr<BufferQueueConsumer> consumer_)
    : consumer{std::move(consumer_)} {}

ConsumerBase::~ConsumerBase() {
    std::scoped_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::Abandon() {
    LOG_DEBUG(Service_Nvnflinger, "called");

    std::scoped_lock lock{mutex};

    if (!is_abandoned) {
        this->AbandonLocked();
        is_abandoned = true;
    }
}

void ConsumerBase::AbandonLocked() {
    for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
        this->FreeBufferLocked(i);
    }
    // disconnect from the BufferQueue
    consumer->Disconnect();
    consumer = nullptr;
}

void ConsumerBase::FreeBufferLocked(s32 slot_index) {
    LOG_DEBUG(Service_Nvnflinger, "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) {
    LOG_DEBUG(Service_Nvnflinger, "called");
}

void ConsumerBase::OnFrameReplaced(const BufferItem& item) {
    LOG_DEBUG(Service_Nvnflinger, "called");
}

void ConsumerBase::OnBuffersReleased() {
    std::scoped_lock lock{mutex};

    LOG_DEBUG(Service_Nvnflinger, "called");

    if (is_abandoned) {
        // Nothing to do if we're already abandoned.
        return;
    }

    u64 mask = 0;
    consumer->GetReleasedBuffers(&mask);
    for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
        if (mask & (1ULL << i)) {
            FreeBufferLocked(i);
        }
    }
}

void ConsumerBase::OnSidebandStreamChanged() {}

Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) {
    Status err = consumer->AcquireBuffer(item, present_when);
    if (err != Status::NoError) {
        return err;
    }

    if (item->graphic_buffer != nullptr) {
        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_Nvnflinger, "slot={}", item->slot);

    return Status::NoError;
}

Status ConsumerBase::AddReleaseFenceLocked(s32 slot,
                                           const std::shared_ptr<GraphicBuffer>& graphic_buffer,
                                           const Fence& fence) {
    LOG_DEBUG(Service_Nvnflinger, "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_Nvnflinger, "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