]> git.sesse.net Git - nageru/blob - nageru/basic_stats.cpp
Make the Futatabi PAUSED subtitle a bit more regular and like the PLAYING one.
[nageru] / nageru / basic_stats.cpp
1 #include "basic_stats.h"
2 #include "shared/metrics.h"
3
4 #include <assert.h>
5 #include <sys/resource.h>
6 #include <epoxy/gl.h>
7
8 // Epoxy seems to be missing these. Taken from the NVX_gpu_memory_info spec.
9 #ifndef GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX
10 #define GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX          0x9047
11 #endif
12 #ifndef GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX
13 #define GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX    0x9048
14 #endif
15 #ifndef GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX
16 #define GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX  0x9049
17 #endif
18 #ifndef GPU_MEMORY_INFO_EVICTION_COUNT_NVX
19 #define GPU_MEMORY_INFO_EVICTION_COUNT_NVX            0x904A
20 #endif
21 #ifndef GPU_MEMORY_INFO_EVICTED_MEMORY_NVX
22 #define GPU_MEMORY_INFO_EVICTED_MEMORY_NVX            0x904B
23 #endif
24
25 using namespace std;
26 using namespace std::chrono;
27
28 bool uses_mlock = false;
29
30 BasicStats::BasicStats(bool verbose, bool use_opengl)
31         : verbose(verbose)
32 {
33         start = steady_clock::now();
34
35         metric_start_time_seconds = get_timestamp_for_metrics();
36         global_metrics.add("frames_output_total", &metric_frames_output_total);
37         global_metrics.add("frames_output_dropped", &metric_frames_output_dropped);
38         global_metrics.add("start_time_seconds", &metric_start_time_seconds, Metrics::TYPE_GAUGE);
39         global_metrics.add("memory_used_bytes", &metrics_memory_used_bytes);
40         global_metrics.add("memory_locked_limit_bytes", &metrics_memory_locked_limit_bytes);
41
42         // TODO: It would be nice to compile this out entirely for Kaeru,
43         // to avoid pulling in the symbols from libGL/Epoxy.
44         if (use_opengl) {
45                 gpu_memory_stats.reset(new GPUMemoryStats(verbose));
46         }
47 }
48
49 void BasicStats::update(int frame_num, int stats_dropped_frames)
50 {
51         steady_clock::time_point now = steady_clock::now();
52         double elapsed = duration<double>(now - start).count();
53
54         metric_frames_output_total = frame_num;
55         metric_frames_output_dropped = stats_dropped_frames;
56
57         if (frame_num % 100 != 0) {
58                 return;
59         }
60
61         if (verbose) {
62                 printf("%d frames (%d dropped) in %.3f seconds = %.1f fps (%.1f ms/frame)",
63                         frame_num, stats_dropped_frames, elapsed, frame_num / elapsed,
64                         1e3 * elapsed / frame_num);
65         }
66
67         // Check our memory usage, to see if we are close to our mlockall()
68         // limit (if at all set).
69         rusage used;
70         if (getrusage(RUSAGE_SELF, &used) == -1) {
71                 perror("getrusage(RUSAGE_SELF)");
72                 assert(false);
73         }
74         metrics_memory_used_bytes = used.ru_maxrss * 1024;
75
76         if (uses_mlock) {
77                 rlimit limit;
78                 if (getrlimit(RLIMIT_MEMLOCK, &limit) == -1) {
79                         perror("getrlimit(RLIMIT_MEMLOCK)");
80                         assert(false);
81                 }
82                 metrics_memory_locked_limit_bytes = limit.rlim_cur;
83
84                 if (verbose) {
85                         if (limit.rlim_cur == 0) {
86                                 printf(", using %ld MB memory (locked)",
87                                                 long(used.ru_maxrss / 1024));
88                         } else {
89                                 printf(", using %ld / %ld MB lockable memory (%.1f%%)",
90                                                 long(used.ru_maxrss / 1024),
91                                                 long(limit.rlim_cur / 1048576),
92                                                 float(100.0 * (used.ru_maxrss * 1024.0) / limit.rlim_cur));
93                         }
94                 }
95         } else {
96                 metrics_memory_locked_limit_bytes = 0.0 / 0.0;
97                 if (verbose) {
98                         printf(", using %ld MB memory (not locked)",
99                                         long(used.ru_maxrss / 1024));
100                 }
101         }
102
103         if (gpu_memory_stats != nullptr) {
104                 gpu_memory_stats->update();
105         }
106
107         if (verbose) {
108                 printf("\n");
109         }
110 }
111
112 GPUMemoryStats::GPUMemoryStats(bool verbose)
113         : verbose(verbose)
114 {
115         // GL_NV_query_memory is exposed but supposedly only works on
116         // Quadro/Titan cards, so we use GL_NVX_gpu_memory_info even though it's
117         // formally marked as experimental.
118         // Intel/Mesa doesn't seem to have anything comparable (at least nothing
119         // that gets the amount of _available_ memory).
120         supported = epoxy_has_gl_extension("GL_NVX_gpu_memory_info");
121         if (supported) {
122                 global_metrics.add("memory_gpu_total_bytes", &metric_memory_gpu_total_bytes, Metrics::TYPE_GAUGE);
123                 global_metrics.add("memory_gpu_dedicated_bytes", &metric_memory_gpu_dedicated_bytes, Metrics::TYPE_GAUGE);
124                 global_metrics.add("memory_gpu_used_bytes", &metric_memory_gpu_used_bytes, Metrics::TYPE_GAUGE);
125                 global_metrics.add("memory_gpu_evicted_bytes", &metric_memory_gpu_evicted_bytes, Metrics::TYPE_GAUGE);
126                 global_metrics.add("memory_gpu_evictions", &metric_memory_gpu_evictions);
127         }
128 }
129
130 void GPUMemoryStats::update()
131 {
132         if (!supported) {
133                 return;
134         }
135
136         GLint total, dedicated, available, evicted, evictions;
137         glGetIntegerv(GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &total);
138         glGetIntegerv(GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &dedicated);
139         glGetIntegerv(GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &available);
140         glGetIntegerv(GPU_MEMORY_INFO_EVICTED_MEMORY_NVX, &evicted);
141         glGetIntegerv(GPU_MEMORY_INFO_EVICTION_COUNT_NVX, &evictions);
142
143         if (glGetError() == 0) {
144                 metric_memory_gpu_total_bytes = int64_t(total) * 1024;
145                 metric_memory_gpu_dedicated_bytes = int64_t(dedicated) * 1024;
146                 metric_memory_gpu_used_bytes = int64_t(total - available) * 1024;
147                 metric_memory_gpu_evicted_bytes = int64_t(evicted) * 1024;
148                 metric_memory_gpu_evictions = evictions;
149
150                 if (verbose) {
151                         printf(", using %d / %d MB GPU memory (%.1f%%)",
152                                 (total - available) / 1024, total / 1024,
153                                 float(100.0 * (total - available) / total));
154                 }
155         }
156 }