aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/gpu_debugger.h
blob: d6d6ebde984b432b2f556a1a9f6c66ea7921d34c (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
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#pragma once

#include <algorithm>
#include <functional>
#include <vector>
#include "common/logging/log.h"
#include "core/hle/service/gsp/gsp_command.h"

namespace VideoCore {

class GraphicsDebugger {
public:
    // Base class for all objects which need to be notified about GPU events
    class DebuggerObserver {
        friend class GraphicsDebugger;

    public:
        DebuggerObserver() = default;
        virtual ~DebuggerObserver() {
            if (observed) {
                observed->UnregisterObserver(this);
            }
        }

        /**
         * Called when a GX command has been processed and is ready for being
         * read via GraphicsDebugger::ReadGXCommandHistory.
         * @param total_command_count Total number of commands in the GX history
         * @note All methods in this class are called from the GSP thread
         */
        virtual void GXCommandProcessed(int total_command_count) {
            [[maybe_unused]] const Service::GSP::Command& cmd =
                observed->ReadGXCommandHistory(total_command_count - 1);
            LOG_TRACE(Debug_GPU, "Received command: id={:x}", (int)cmd.id.Value());
        }

    protected:
        const GraphicsDebugger* GetDebugger() const {
            return observed;
        }

    private:
        GraphicsDebugger* observed{};
    };

    void GXCommandProcessed(Service::GSP::Command& command_data) {
        if (observers.empty()) {
            return;
        }

        gx_command_history.emplace_back(command_data);
        ForEachObserver([this](DebuggerObserver* observer) {
            observer->GXCommandProcessed(static_cast<int>(this->gx_command_history.size()));
        });
    }

    const Service::GSP::Command& ReadGXCommandHistory(int index) const {
        // TODO: Is this thread-safe?
        return gx_command_history[index];
    }

    void RegisterObserver(DebuggerObserver* observer) {
        // TODO: Check for duplicates
        observers.push_back(observer);
        observer->observed = this;
    }

    void UnregisterObserver(DebuggerObserver* observer) {
        observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
        observer->observed = nullptr;
    }

private:
    void ForEachObserver(std::function<void(DebuggerObserver*)> func) {
        std::for_each(observers.begin(), observers.end(), func);
    }

    std::vector<DebuggerObserver*> observers;
    std::vector<Service::GSP::Command> gx_command_history;
};

} // namespace VideoCore