aboutsummaryrefslogtreecommitdiff
path: root/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
blob: 30c145e545cf67606ce9eeb1532bcfbfb3a2ebf4 (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
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2014 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/BufferQueueCore.cpp

#include "common/assert.h"

#include "core/hle/service/nvnflinger/buffer_queue_core.h"

namespace Service::android {

BufferQueueCore::BufferQueueCore() = default;

BufferQueueCore::~BufferQueueCore() = default;

void BufferQueueCore::PushHistory(u64 frame_number, s64 queue_time, s64 presentation_time, BufferState state) {
    std::lock_guard lk(buffer_history_mutex);

    auto it = buffer_history_map.find(frame_number);
    if (it != buffer_history_map.end()) {
        it->second.state = state;
        return;
    }

    buffer_history_map.emplace(frame_number, BufferHistoryInfo{
        frame_number,
        queue_time,
        presentation_time,
        state
    });
    buffer_history_order.push_back(frame_number);

    if (buffer_history_order.size() > BUFFER_HISTORY_SIZE) {
        u64 oldest_frame = buffer_history_order.front();
        buffer_history_order.pop_front();
        buffer_history_map.erase(oldest_frame);
    }
}

void BufferQueueCore::UpdateHistory(u64 frame_number, BufferState state) {
    std::lock_guard lk(buffer_history_mutex);

    auto it = buffer_history_map.find(frame_number);
    if (it != buffer_history_map.end()) {
        it->second.state = state;
    }
}

void BufferQueueCore::SignalDequeueCondition() {
    dequeue_possible.store(true);
    dequeue_condition.notify_all();
}

bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) {
    dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); });
    dequeue_possible.store(false);

    return true;
}

s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const {
    if (!use_async_buffer) {
        return 0;
    }

    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);
}

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);
        return override_max_buffer_count;
    }

    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_Nvnflinger, "slot {}", slot);

    slots[slot].graphic_buffer.reset();

    if (slots[slot].buffer_state == BufferState::Acquired) {
        slots[slot].needs_cleanup_on_release = true;
    }

    slots[slot].buffer_state = BufferState::Free;
    slots[slot].frame_number = UINT32_MAX;
    slots[slot].acquire_called = false;
    slots[slot].fence = Fence::NoFence();
}

void BufferQueueCore::FreeAllBuffersLocked() {
    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) {
        is_allocating_condition.wait(mutex);
    }
}

} // namespace Service::android