summaryrefslogtreecommitdiffstats
path: root/src/audio_core/renderer/performance/performance_manager.h
blob: b82176bef120410c4a83c11fc6e968c0d10f36e7 (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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <chrono>
#include <memory>
#include <span>

#include "audio_core/common/audio_renderer_parameter.h"
#include "audio_core/renderer/performance/performance_detail.h"
#include "audio_core/renderer/performance/performance_entry.h"
#include "audio_core/renderer/performance/performance_entry_addresses.h"
#include "audio_core/renderer/performance/performance_frame_header.h"
#include "common/common_types.h"

namespace AudioCore::AudioRenderer {
class BehaviorInfo;
class MemoryPoolInfo;

enum class PerformanceVersion {
    Version1,
    Version2,
};

enum class PerformanceSysDetailType {
    PcmInt16 = 15,
    PcmFloat = 16,
    Adpcm = 17,
    LightLimiter = 37,
};

enum class PerformanceState {
    Invalid,
    Start,
    Stop,
};

/**
 * Manages performance information.
 *
 * The performance buffer is split into frames, each comprised of:
 *     Frame header - Information about the number of entries/details and some others
 *     Entries      - Created when starting to generate types of commands, such as voice
 * commands, mix commands, sink commands etc. Details      - Created for specific commands
 * within each group. Up to MaxDetailEntries per frame.
 *
 * A current frame is written to by the AudioRenderer, and before it processes the next command
 * list, the current frame is copied to a ringbuffer of history frames. These frames are then
 * output back to the game if it supplies a performance buffer to RequestUpdate.
 *
 * Two versions currently exist, version 2 adds a few extra fields to the header, and a new
 * SysDetail type which is seemingly unused.
 */
class PerformanceManager {
public:
    static constexpr size_t MaxDetailEntries = 100;

    struct InParameter {
        /* 0x00 */ s32 target_node_id;
        /* 0x04 */ char unk04[0xC];
    };
    static_assert(sizeof(InParameter) == 0x10,
                  "PerformanceManager::InParameter has the wrong size!");

    struct OutStatus {
        /* 0x00 */ s32 history_size;
        /* 0x04 */ char unk04[0xC];
    };
    static_assert(sizeof(OutStatus) == 0x10, "PerformanceManager::OutStatus has the wrong size!");

    /**
     * Calculate the required size for the performance workbuffer.
     *
     * @param behavior - Check which version is supported.
     * @param params    - Input parameters.
     * @return Required workbuffer size.
     */
    static u64 GetRequiredBufferSizeForPerformanceMetricsPerFrame(
        const BehaviorInfo& behavior, const AudioRendererParameterInternal& params) {
        u64 entry_count{params.voices + params.effects + params.sub_mixes + params.sinks + 1};
        switch (behavior.GetPerformanceMetricsDataFormat()) {
        case 1:
            return sizeof(PerformanceFrameHeaderVersion1) +
                   PerformanceManager::MaxDetailEntries * sizeof(PerformanceDetailVersion1) +
                   entry_count * sizeof(PerformanceEntryVersion1);
        case 2:
            return sizeof(PerformanceFrameHeaderVersion2) +
                   PerformanceManager::MaxDetailEntries * sizeof(PerformanceDetailVersion2) +
                   entry_count * sizeof(PerformanceEntryVersion2);
        }

        LOG_WARNING(Service_Audio, "Invalid PerformanceMetrics version, assuming version 1");
        return sizeof(PerformanceFrameHeaderVersion1) +
               PerformanceManager::MaxDetailEntries * sizeof(PerformanceDetailVersion1) +
               entry_count * sizeof(PerformanceEntryVersion1);
    }

    virtual ~PerformanceManager() = default;

    /**
     * Initialize the performance manager.
     *
     * @param workbuffer      - Workbuffer to use for performance frames.
     * @param workbuffer_size - Size of the workbuffer.
     * @param params          - Input parameters.
     * @param behavior       - Behaviour to check version and data format.
     * @param memory_pool     - Used to translate the workbuffer address for the DSP.
     */
    virtual void Initialize(std::span<u8> workbuffer, u64 workbuffer_size,
                            const AudioRendererParameterInternal& params,
                            const BehaviorInfo& behavior, const MemoryPoolInfo& memory_pool);

    /**
     * Check if the manager is initialized.
     *
     * @return True if initialized, otherwise false.
     */
    virtual bool IsInitialized() const;

    /**
     * Copy the waiting performance frames to the output buffer.
     *
     * @param out_buffer - Output buffer to store performance frames.
     * @param out_size   - Size of the output buffer.
     * @return Size in bytes that were written to the buffer.
     */
    virtual u32 CopyHistories(u8* out_buffer, u64 out_size);

    /**
     * Setup a new sys detail in the current frame, filling in addresses with offsets to the
     * current workbuffer, to be written by the AudioRenderer. Note: This version is
     * unused/incomplete.
     *
     * @param addresses       - Filled with pointers to the new entry, which should be passed to
     * the AudioRenderer with Performance commands to be written.
     * @param unk             - Unknown.
     * @param sys_detail_type - Sys detail type.
     * @param node_id         - Node id for this entry.
     * @return True if a new entry was created and the offsets are valid, otherwise false.
     */
    virtual bool GetNextEntry(PerformanceEntryAddresses& addresses, u32** unk,
                              PerformanceSysDetailType sys_detail_type, s32 node_id);

    /**
     * Setup a new entry in the current frame, filling in addresses with offsets to the current
     * workbuffer, to be written by the AudioRenderer.
     *
     * @param addresses       - Filled with pointers to the new entry, which should be passed to
     * the AudioRenderer with Performance commands to be written.
     * @param entry_type      - The type of this entry. See PerformanceEntryType
     * @param node_id         - Node id for this entry.
     * @return True if a new entry was created and the offsets are valid, otherwise false.
     */
    virtual bool GetNextEntry(PerformanceEntryAddresses& addresses, PerformanceEntryType entry_type,
                              s32 node_id);

    /**
     * Setup a new detail in the current frame, filling in addresses with offsets to the current
     * workbuffer, to be written by the AudioRenderer.
     *
     * @param addresses       - Filled with pointers to the new detail, which should be passed
     * to the AudioRenderer with Performance commands to be written.
     * @param entry_type      - The type of this detail. See PerformanceEntryType
     * @param node_id         - Node id for this detail.
     * @return True if a new detail was created and the offsets are valid, otherwise false.
     */
    virtual bool GetNextEntry(PerformanceEntryAddresses& addresses,
                              PerformanceDetailType detail_type, PerformanceEntryType entry_type,
                              s32 node_id);

    /**
     * Save the current frame to the ring buffer.
     *
     * @param dsp_behind           - Did the AudioRenderer fall behind and not
     *                               finish processing the command list?
     * @param voices_dropped       - The number of voices that were dropped.
     * @param rendering_start_tick - The tick rendering started.
     */
    virtual void TapFrame(bool dsp_behind, u32 voices_dropped, u64 rendering_start_tick);

    /**
     * Check if the node id is a detail type.
     *
     * @return True if the node is a detail type, otherwise false.
     */
    virtual bool IsDetailTarget(u32 target_node_id) const;

    /**
     * Set the given node to be a detail type.
     *
     * @param target_node_id - Node to set.
     */
    virtual void SetDetailTarget(u32 target_node_id);

private:
    /**
     * Create the performance manager.
     *
     * @param version - Performance version to create.
     */
    void CreateImpl(size_t version);

    std::unique_ptr<PerformanceManager>
        /// Impl for the performance manager, may be version 1 or 2.
        impl;
};

template <PerformanceVersion Version, typename FrameHeaderVersion, typename EntryVersion,
          typename DetailVersion>
class PerformanceManagerImpl : public PerformanceManager {
public:
    void Initialize(std::span<u8> workbuffer, u64 workbuffer_size,
                    const AudioRendererParameterInternal& params, const BehaviorInfo& behavior,
                    const MemoryPoolInfo& memory_pool) override;
    bool IsInitialized() const override;
    u32 CopyHistories(u8* out_buffer, u64 out_size) override;
    bool GetNextEntry(PerformanceEntryAddresses& addresses, u32** unk,
                      PerformanceSysDetailType sys_detail_type, s32 node_id) override;
    bool GetNextEntry(PerformanceEntryAddresses& addresses, PerformanceEntryType entry_type,
                      s32 node_id) override;
    bool GetNextEntry(PerformanceEntryAddresses& addresses, PerformanceDetailType detail_type,
                      PerformanceEntryType entry_type, s32 node_id) override;
    void TapFrame(bool dsp_behind, u32 voices_dropped, u64 rendering_start_tick) override;
    bool IsDetailTarget(u32 target_node_id) const override;
    void SetDetailTarget(u32 target_node_id) override;

private:
    /// Workbuffer used to store the current performance frame
    std::span<u8> workbuffer{};
    /// DSP address of the workbuffer, used by the AudioRenderer
    CpuAddr translated_buffer{};
    /// Current frame index
    u32 history_frame_index{};
    /// Current frame header
    FrameHeaderVersion* frame_header{};
    /// Current frame entry buffer
    std::span<EntryVersion> entry_buffer{};
    /// Current frame detail buffer
    std::span<DetailVersion> detail_buffer{};
    /// Current frame entry count
    u32 entry_count{};
    /// Current frame detail count
    u32 detail_count{};
    /// Ringbuffer of previous frames
    std::span<u8> frame_history{};
    /// Current history frame header
    FrameHeaderVersion* frame_history_header{};
    /// Current history entry buffer
    std::span<EntryVersion> frame_history_entries{};
    /// Current history detail buffer
    std::span<DetailVersion> frame_history_details{};
    /// Current history ringbuffer write index
    u32 output_frame_index{};
    /// Last history frame index that was written back to the game
    u32 last_output_frame_index{};
    /// Maximum number of history frames in the ringbuffer
    u32 max_frames{};
    /// Number of entries per frame
    u32 entries_per_frame{};
    /// Maximum number of details per frame
    u32 max_detail_count{};
    /// Frame size in bytes
    u64 frame_size{};
    /// Is the performance manager initialized?
    bool is_initialized{};
    /// Target node id
    u32 target_node_id{};
    /// Performance version in use
    PerformanceVersion version{};
};

} // namespace AudioCore::AudioRenderer