aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/gpu_logging/gpu_logging.h
blob: b16988ed0b351e7f33aefca58283473b0a7bd186 (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
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <array>
#include <chrono>
#include <memory>
#include <mutex>
#include <set>
#include <span>
#include <string>
#include <ankerl/unordered_dense.h>
#include <vector>

#include "common/common_types.h"

// Forward declarations
namespace Common::FS {
class IOFile;
}

namespace Vulkan {
class Device;
}

namespace GPU::Logging {

enum class LogLevel : u8 {
    Off = 0,
    Errors = 1,
    Standard = 2,
    Verbose = 3,
    All = 4,
};

enum class DriverType : u8 {
    Unknown,
    Turnip,   // Mesa Turnip driver
    Qualcomm, // Qualcomm proprietary driver
};

// Ring buffer entry for tracking Vulkan API calls
struct VulkanCallEntry {
    std::chrono::microseconds timestamp;
    std::string call_name;    // e.g., "vkCmdDraw", "vkBeginRenderPass"
    std::string parameters;   // Serialized parameters
    int result;               // VkResult return code
    u32 thread_id;
};

// GPU memory allocation entry
struct MemoryAllocationEntry {
    uintptr_t memory_handle;
    u64 size;
    u32 memory_flags;
    std::chrono::microseconds timestamp;
    bool is_device_local;
    bool is_host_visible;
};

// GPU state snapshot for crash dumps
struct GPUStateSnapshot {
    std::vector<VulkanCallEntry> recent_calls;    // Last N API calls
    std::vector<std::string> active_shaders;      // Currently bound shaders
    std::string pipeline_state;                   // Current pipeline state
    std::string memory_status;                    // Current memory allocations
    std::string driver_debug_info;                // Driver-specific debug data
    std::chrono::microseconds timestamp;
    DriverType driver_type;
};

/// Main GPU logging system singleton
class GPULogger {
public:
    static GPULogger& GetInstance();

    // Prevent copying
    GPULogger(const GPULogger&) = delete;
    GPULogger& operator=(const GPULogger&) = delete;

    // Initialization and control
    void Initialize(LogLevel level, DriverType detected_driver = DriverType::Unknown);
    void Shutdown();

    // Logging API
    void LogVulkanCall(const std::string& call_name, const std::string& params, int result);
    void LogMemoryAllocation(uintptr_t memory, u64 size, u32 memory_flags);
    void LogMemoryDeallocation(uintptr_t memory);
    void LogShaderCompilation(const std::string& shader_name, const std::string& shader_info);
    void LogPipelineStateChange(const std::string& state_info);
    void LogDriverDebugInfo(const std::string& debug_info);

    // Extension usage tracking
    void LogExtensionUsage(const std::string& extension_name, const std::string& function_name);

    // Render pass logging
    void LogRenderPassBegin(const std::string& render_pass_info);
    void LogRenderPassEnd();

    // Pipeline binding logging
    void LogPipelineBind(bool is_compute, const std::string& pipeline_info);

    // Descriptor set binding logging
    void LogDescriptorSetBind(const std::string& descriptor_info);

    // Pipeline barrier logging
    void LogPipelineBarrier(const std::string& barrier_info);

    // Image operation logging
    void LogImageOperation(const std::string& operation, const std::string& image_info);

    // Clear operation logging
    void LogClearOperation(const std::string& clear_info);

    // Crash handling
    GPUStateSnapshot GetCurrentSnapshot();
    void DumpStateToFile(const std::string& crash_reason);

    // Settings
    void SetLogLevel(LogLevel level);
    void EnableVulkanCallTracking(bool enabled);
    void EnableMemoryTracking(bool enabled);
    void EnableDriverDebugInfo(bool enabled);
    void SetRingBufferSize(size_t entries);

    // Query
    LogLevel GetLogLevel() const;
    DriverType GetDriverType() const;
    std::string GetStatistics() const;
    bool IsInitialized() const;

private:
    GPULogger();
    ~GPULogger();

    // Helper functions
    void WriteToLog(const std::string& message);
    void RotateLogFile();
    std::string FormatTimestamp(std::chrono::microseconds timestamp) const;
    std::string FormatMemorySize(u64 bytes) const;

    // State
    bool initialized = false;
    LogLevel current_level = LogLevel::Off;
    DriverType detected_driver = DriverType::Unknown;

    // Ring buffer for API calls
    std::vector<VulkanCallEntry> call_ring_buffer;
    size_t ring_buffer_index = 0;
    size_t ring_buffer_size = 512;
    mutable std::mutex ring_buffer_mutex;

    // Memory tracking
    ankerl::unordered_dense::map<uintptr_t, MemoryAllocationEntry> memory_allocations;
    mutable std::mutex memory_mutex;

    // Statistics
    u64 total_vulkan_calls = 0;
    u64 total_allocations = 0;
    u64 total_deallocations = 0;
    u64 current_allocated_bytes = 0;
    u64 peak_allocated_bytes = 0;

    // File backend for GPU logs
    std::unique_ptr<Common::FS::IOFile> gpu_log_file;
    mutable std::mutex file_mutex;
    u64 bytes_written = 0;

    // Feature flags
    bool track_vulkan_calls = true;
    bool track_memory = false;
    bool capture_driver_debug = false;

    // Extension usage tracking
    std::set<std::string> used_extensions;
    mutable std::mutex extension_mutex;

    // Stored state for crash dumps
    std::string stored_driver_debug_info;
    std::string stored_pipeline_state;
    mutable std::mutex state_mutex;
};

[[nodiscard]] bool IsActive() noexcept;

void DumpSpirvShader(u64 shader_hash, std::span<const u32> spirv_code);

// Helper to get stage name from index
inline const char* GetShaderStageName(size_t stage_index) {
    static constexpr std::array<const char*, 5> stage_names{
        "vertex", "tess_control", "tess_eval", "geometry", "fragment"
    };
    return stage_index < stage_names.size() ? stage_names[stage_index] : "unknown";
}

} // namespace GPU::Logging