summarylogtreecommitdiffstats
path: root/dxvk-mangohud.patch
blob: 13fbdee8db9aafb67b4357650b8aa7b8245263fc (plain)
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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
diff --git a/README.md b/README.md
index 25d03230..e8a53432 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,29 @@
+# MangoHud Specific
+
+- `DXVK_HUD_OFFSET_X` Set X offset of the DVXK Hud.
+- `DXVK_HUD_OFFSET_Y` Set Y offset of the DVXK Hud.
+- `DXVK_LOG_TO_FILE` Turn on logging and select path/filename (Fps,Cpu load,Gpu load)
+- Logging Gpu load requires either mangogpuload or gpuload hud options
+
+# Hud options
+- `mangogpuload` : Shows current gpu load.
+- `mangocpuload` : Shows current cpu load.
+
+# Keybinds
+- `F2`  : Toggle Logging on/off
+- `F12` : Toggle Hud on/off
+
+# MangoLog file
+
+ When you press F2, a file is created with your chosen name + date/time stamp.
+ this file can be uploaded to https://flightlessmango.com/logs/new to create graphs automatically.
+ you can share the created page with others, just link it.
+ 
+ #### Multiple log files
+ 
+ It's possible to upload multiple files, you can rename them to your preferred names and upload them in a batch.
+ The graphs will then use those names in the data.
+
 # DXVK
 
 A Vulkan-based translation layer for Direct3D 10/11 which allows running 3D applications on Linux using Wine.
diff --git a/src/dxvk/dxvk_cpu.h b/src/dxvk/dxvk_cpu.h
new file mode 100644
index 00000000..b2c8736a
--- /dev/null
+++ b/src/dxvk/dxvk_cpu.h
@@ -0,0 +1,172 @@
+#include <cmath>
+#include <iomanip>
+#include <array>
+#include <vector>
+#include <algorithm>
+#include <iterator>
+#include <thread>
+#include <sstream>
+#include <fstream>
+using namespace std;
+
+const int NUM_CPU_STATES = 10;
+
+struct Cpus{
+  size_t num;
+  string name;
+  float value;
+  string output;
+};
+
+size_t numCpuCores = dxvk::thread::hardware_concurrency();
+size_t arraySize = numCpuCores + 1;
+std::vector<Cpus> cpuArray;
+
+void coreCounting(){
+  cpuArray.push_back({0, "CPU:"});
+  for (size_t i = 0; i < arraySize; i++) {
+    size_t offset = i;
+    stringstream ss;
+    ss << "CPU" << offset << ":";
+    string cpuNameString = ss.str();
+    cpuArray.push_back({i+1 , cpuNameString});
+  }
+}
+
+std::string m_cpuUtilizationString;
+
+enum CPUStates
+{
+	S_USER = 0,
+	S_NICE,
+	S_SYSTEM,
+	S_IDLE,
+	S_IOWAIT,
+	S_IRQ,
+	S_SOFTIRQ,
+	S_STEAL,
+	S_GUEST,
+	S_GUEST_NICE
+};
+
+typedef struct CPUData
+{
+	std::string cpu;
+	size_t times[NUM_CPU_STATES];
+} CPUData;
+
+void ReadStatsCPU(std::vector<CPUData> & entries)
+{
+	std::ifstream fileStat("/proc/stat");
+
+	std::string line;
+
+	const std::string STR_CPU("cpu");
+	const std::size_t LEN_STR_CPU = STR_CPU.size();
+	const std::string STR_TOT("tot");
+
+	while(std::getline(fileStat, line))
+	{
+		// cpu stats line found
+		if(!line.compare(0, LEN_STR_CPU, STR_CPU))
+		{
+			std::istringstream ss(line);
+
+			// store entry
+			entries.emplace_back(CPUData());
+			CPUData & entry = entries.back();
+
+			// read cpu label
+			ss >> entry.cpu;
+
+			if(entry.cpu.size() > LEN_STR_CPU)
+				entry.cpu.erase(0, LEN_STR_CPU);
+			else
+				entry.cpu = STR_TOT;
+
+			// read times
+			for(int i = 0; i < NUM_CPU_STATES; ++i)
+				ss >> entry.times[i];
+		}
+	}
+}
+
+size_t GetIdleTime(const CPUData & e)
+{
+	return	e.times[S_IDLE] +
+			e.times[S_IOWAIT];
+}
+
+size_t GetActiveTime(const CPUData & e)
+{
+	return	e.times[S_USER] +
+			e.times[S_NICE] +
+			e.times[S_SYSTEM] +
+			e.times[S_IRQ] +
+			e.times[S_SOFTIRQ] +
+			e.times[S_STEAL] +
+			e.times[S_GUEST] +
+			e.times[S_GUEST_NICE];
+}
+
+void PrintStats(const std::vector<CPUData> & entries1, const std::vector<CPUData> & entries2)
+{
+	const size_t NUM_ENTRIES = entries1.size();
+
+	for(size_t i = 0; i < NUM_ENTRIES; ++i)
+	{
+		const CPUData & e1 = entries1[i];
+		const CPUData & e2 = entries2[i];
+
+		const float ACTIVE_TIME	= static_cast<float>(GetActiveTime(e2) - GetActiveTime(e1));
+		const float IDLE_TIME	= static_cast<float>(GetIdleTime(e2) - GetIdleTime(e1));
+		const float TOTAL_TIME	= ACTIVE_TIME + IDLE_TIME;
+
+    cpuArray[i].value = (truncf(100.f * ACTIVE_TIME / TOTAL_TIME) * 10 / 10);
+	}
+}
+
+int getCpuUsage()
+{
+	std::vector<CPUData> entries1;
+	std::vector<CPUData> entries2;
+
+	// snapshot 1
+	ReadStatsCPU(entries1);
+
+	// 100ms pause
+	std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+	// snapshot 2
+	ReadStatsCPU(entries2);
+
+	// print output
+	PrintStats(entries1, entries2);
+
+	return 0;
+}
+
+
+void updateCpuStrings(){
+  for (size_t i = 0; i < arraySize; i++) {
+    size_t spacing = 10;
+    string value = to_string(cpuArray[i].value);
+    value.erase( value.find_last_not_of('0') + 1, std::string::npos );
+    size_t correctionValue = (spacing - cpuArray[i].name.length()) - value.length();
+    string correction = "";
+    for (size_t i = 0; i < correctionValue; i++) {
+          correction.append(" ");
+        }
+        stringstream ss;
+        if (i < 11) {
+          if (i == 0) {
+            ss << cpuArray[i].name << correction << cpuArray[i].value << "%";
+          } else {
+            ss << cpuArray[i].name << correction << cpuArray[i].value << "%";
+          }
+        } else {
+          ss << cpuArray[i].name << correction << cpuArray[i].value << "%";
+        }
+        cpuArray[i].output = ss.str();
+      }
+    }
\ No newline at end of file
diff --git a/src/dxvk/hud/dxvk_hud.cpp b/src/dxvk/hud/dxvk_hud.cpp
index 4fcd3bd2..b6e23c4c 100644
--- a/src/dxvk/hud/dxvk_hud.cpp
+++ b/src/dxvk/hud/dxvk_hud.cpp
@@ -1,9 +1,13 @@
-#include <cstring>
 #include <version.h>
-
 #include "dxvk_hud.h"
+#include <windows.h>
+#include <time.h>
 
 namespace dxvk::hud {
+  float Hud::offset_x_float = 0;
+  float Hud::offset_y_float = 0;
+  bool show_hud = true;
+  int lastPress;
   
   Hud::Hud(
     const Rc<DxvkDevice>& device,
@@ -60,6 +64,14 @@ namespace dxvk::hud {
   
   Rc<Hud> Hud::createHud(const Rc<DxvkDevice>& device) {
     std::string hudElements = env::getEnvVar("DXVK_HUD");
+    std::string offset_x = env::getEnvVar("DXVK_HUD_OFFSET_X");
+    std::string offset_y = env::getEnvVar("DXVK_HUD_OFFSET_Y");
+    
+    if (!offset_x.empty())
+      offset_x_float = stof(offset_x);
+    
+    if (!offset_y.empty())
+      offset_y_float = stof(offset_y);
 
     if (hudElements.empty())
       hudElements = device->config().hud;
@@ -82,33 +94,45 @@ namespace dxvk::hud {
     m_renderer.beginFrame(ctx, m_uniformData.surfaceSize);
   }
 
-
   void Hud::renderHudElements(const Rc<DxvkContext>& ctx) {
-    HudPos position = { 8.0f, 24.0f };
+    if(GetAsyncKeyState(VK_F12) & 0x8000)
+    {
+      if (GetTickCount() - lastPress > 500){
+        lastPress = GetTickCount();
+        if (show_hud){
+          show_hud = false;
+        } else {
+          show_hud = true;
+        }
+      }
+    } 
     
-    if (m_config.elements.test(HudElement::DxvkVersion)) {
-      m_renderer.drawText(ctx, 16.0f,
-        { position.x, position.y },
-        { 1.0f, 1.0f, 1.0f, 1.0f },
-        "DXVK " DXVK_VERSION);
-      position.y += 24.0f;
-    }
-
-    if (m_config.elements.test(HudElement::DxvkClientApi)) {
-      m_renderer.drawText(ctx, 16.0f,
-        { position.x, position.y },
-        { 1.0f, 1.0f, 1.0f, 1.0f },
-        m_device->clientApi());
-      position.y += 24.0f;
-    }
-
-    if (m_config.elements.test(HudElement::DeviceInfo)) {
-      position = m_hudDeviceInfo.render(
-        ctx, m_renderer, position);
-    }
+    HudPos position = { offset_x_float + 8.0f, offset_y_float + 24.0f };
     
-    position = m_hudFramerate.render(ctx, m_renderer, position);
-    position = m_hudStats    .render(ctx, m_renderer, position);
+    if (show_hud){
+      if (m_config.elements.test(HudElement::DxvkVersion)) {
+        m_renderer.drawText(ctx, 16.0f,
+          { position.x, position.y },
+          { 1.0f, 1.0f, 1.0f, 1.0f },
+          "DXVK " DXVK_VERSION);
+        position.y += 24.0f;
+      }
+
+      if (m_config.elements.test(HudElement::DxvkClientApi)) {
+        m_renderer.drawText(ctx, 16.0f,
+          { position.x, position.y },
+          { 1.0f, 1.0f, 1.0f, 1.0f },
+          m_device->clientApi());
+        position.y += 24.0f;
+      }
+
+      if (m_config.elements.test(HudElement::DeviceInfo)) {
+        position = m_hudDeviceInfo.render(
+          ctx, m_renderer, position);
+      }
+      position = m_hudFramerate.render(ctx, m_renderer, position);
+      position = m_hudStats    .render(ctx, m_renderer, position);
+    }
   }
   
   
diff --git a/src/dxvk/hud/dxvk_hud.h b/src/dxvk/hud/dxvk_hud.h
index 3b702c3b..71779977 100644
--- a/src/dxvk/hud/dxvk_hud.h
+++ b/src/dxvk/hud/dxvk_hud.h
@@ -29,6 +29,8 @@ namespace dxvk::hud {
   class Hud : public RcObject {
     
   public:
+    static float offset_x_float;
+    static float offset_y_float;
     
     Hud(
       const Rc<DxvkDevice>& device,
diff --git a/src/dxvk/hud/dxvk_hud_config.cpp b/src/dxvk/hud/dxvk_hud_config.cpp
index fe1745bd..020a394b 100644
--- a/src/dxvk/hud/dxvk_hud_config.cpp
+++ b/src/dxvk/hud/dxvk_hud_config.cpp
@@ -16,6 +16,9 @@ namespace dxvk::hud {
     { "version",      HudElement::DxvkVersion       },
     { "api",          HudElement::DxvkClientApi     },
     { "compiler",     HudElement::CompilerActivity  },
+    { "mangogpuload", HudElement::GpuLoad           },
+    { "mangocpuload", HudElement::CpuLoad           },
+    { "mangocpuload", HudElement::Logging           },
   }};
   
   
diff --git a/src/dxvk/hud/dxvk_hud_config.h b/src/dxvk/hud/dxvk_hud_config.h
index 05a1e4b6..5d571663 100644
--- a/src/dxvk/hud/dxvk_hud_config.h
+++ b/src/dxvk/hud/dxvk_hud_config.h
@@ -22,6 +22,9 @@ namespace dxvk::hud {
     DxvkVersion       = 8,
     DxvkClientApi     = 9,
     CompilerActivity  = 10,
+    GpuLoad           = 11,
+    CpuLoad           = 12,
+    Logging           = 13,
   };
   
   using HudElements = Flags<HudElement>;
diff --git a/src/dxvk/hud/dxvk_hud_fps.cpp b/src/dxvk/hud/dxvk_hud_fps.cpp
index f8cb6e7d..84eb2db6 100644
--- a/src/dxvk/hud/dxvk_hud_fps.cpp
+++ b/src/dxvk/hud/dxvk_hud_fps.cpp
@@ -1,7 +1,22 @@
 #include "dxvk_hud_fps.h"
+#include "dxvk_hud_stats.h"
+#include "../dxvk_cpu.h"
+#include <time.h>
 
 #include <cmath>
 #include <iomanip>
+using namespace std;
+time_t now_log = time(0);
+tm *log_time = localtime(&now_log);
+fstream f;
+
+struct logData{
+  float fps;
+  float cpu;
+  uint64_t gpu;
+};
+
+std::vector<logData> logArray;
 
 namespace dxvk::hud {
   
@@ -9,8 +24,8 @@ namespace dxvk::hud {
   : m_elements  (elements),
     m_fpsString ("FPS: "),
     m_prevFpsUpdate(Clock::now()),
-    m_prevFtgUpdate(Clock::now()) {
-    
+    m_prevFtgUpdate(Clock::now()),
+    m_prevLogUpdate(Clock::now()) {
   }
   
   
@@ -25,11 +40,48 @@ namespace dxvk::hud {
     TimePoint now = Clock::now();
     TimeDiff elapsedFps = std::chrono::duration_cast<TimeDiff>(now - m_prevFpsUpdate);
     TimeDiff elapsedFtg = std::chrono::duration_cast<TimeDiff>(now - m_prevFtgUpdate);
+    TimeDiff elapsedLog = std::chrono::duration_cast<TimeDiff>(now - m_prevLogUpdate);
     m_prevFtgUpdate = now;
+
+    if(GetAsyncKeyState(VK_F2) & 0x8000)
+    {
+      elapsedF2 = std::chrono::duration_cast<TimeDiff>(now - m_prevF2Press);
+      if (elapsedF2.count() > UpdateInterval || elapsedF2.count() == 0) {
+          if (mango_logging){
+            m_prevF2Press = now;
+            mango_logging = false;
+            for (size_t i = 0; i < logArray.size(); i++) {
+              f << logArray[i].fps << "," << logArray[i].cpu << "," << logArray[i].gpu << endl;
+            }
+            f.close();
+            logArray.clear();
+          } else {
+            m_prevF2Press = now;
+            now_log = time(0);
+            log_time = localtime(&now_log);
+            mango_logging = true;
+            string date = to_string(log_time->tm_year + 1900) + "-" + to_string(1 + log_time->tm_mon) + "-" + to_string(log_time->tm_mday) + "_" + to_string(1 + log_time->tm_hour) + "-" + to_string(1 + log_time->tm_min) + "-" + to_string(1 + log_time->tm_sec);
+            f.open(logging + "_" + date, f.out | f.app);
+          }
+        } 
+      }
+    
+    if (elapsedLog.count() >= LogUpdateInterval) {
+      fps = (10'000'000ll * m_frameCount) / elapsedFps.count();
+      if (!logging.empty()){
+        if (mango_logging){
+          logArray.push_back({float(fps / 10 + (float(fps % 10) / 10)), cpuArray[0].value, gpuLoad});
+        }
+      }
+      m_prevLogUpdate = now;
+    }
     
-    // Update FPS string
     if (elapsedFps.count() >= UpdateInterval) {
-      const int64_t fps = (10'000'000ll * m_frameCount) / elapsedFps.count();
+    // Update FPS string
+      coreCounting();
+      dxvk::thread([this] () { getCpuUsage();});
+      updateCpuStrings();
+      m_cpuUtilizationString = str::format(cpuArray[0].output);
       m_fpsString = str::format("FPS: ", fps / 10, ".", fps % 10);
       
       m_prevFpsUpdate = now;
@@ -46,32 +98,81 @@ namespace dxvk::hud {
     const Rc<DxvkContext>&  context,
           HudRenderer&      renderer,
           HudPos            position) {
+    if (m_elements.test(HudElement::GpuLoad)) {
+      position = this->renderGpuText(
+        context, renderer, position);
+      }
+    if (m_elements.test(HudElement::CpuLoad)) {
+      position = this->renderCpuText(
+        context, renderer, position);
+    }
     if (m_elements.test(HudElement::Framerate)) {
       position = this->renderFpsText(
         context, renderer, position);
-    }
+      }
     
     if (m_elements.test(HudElement::Frametimes)) {
       position = this->renderFrametimeGraph(
         context, renderer, position);
     }
     
+    if (mango_logging && !logging.empty()) {
+      this->renderLogging(context, renderer,
+        { float(renderer.surfaceSize().width) - 250.0f, float(renderer.surfaceSize().height) - 20.0f });
+    }
+    
     return position;
   }
   
   
-  HudPos HudFps::renderFpsText(
-    const Rc<DxvkContext>&  context,
-          HudRenderer&      renderer,
-          HudPos            position) {
+  
+  HudPos HudFps::renderGpuText(
+  const Rc<DxvkContext>&  context,
+        HudRenderer&      renderer,
+        HudPos            position) {
+  renderer.drawText(context, 16.0f,
+    { position.x, position.y },
+    { 1.0f, 1.0f, 1.0f, 1.0f },
+    m_gpuLoadString);
+
+  return HudPos { position.x, position.y + 24 };
+}
+
+HudPos HudFps::renderCpuText(
+const Rc<DxvkContext>&  context,
+      HudRenderer&      renderer,
+      HudPos            position) {
+renderer.drawText(context, 16.0f,
+  { position.x, position.y },
+  { 1.0f, 1.0f, 1.0f, 1.0f },
+  m_cpuUtilizationString);
+
+return HudPos { position.x, position.y + 24 };
+}  
+
+HudPos HudFps::renderFpsText(
+  const Rc<DxvkContext>&  context,
+  HudRenderer&      renderer,
+  HudPos            position) {
     renderer.drawText(context, 16.0f,
       { position.x, position.y },
       { 1.0f, 1.0f, 1.0f, 1.0f },
       m_fpsString);
+      
+      return HudPos { position.x, position.y + 24 };
+    }
     
-    return HudPos { position.x, position.y + 24 };
-  }
-  
+  HudPos HudFps::renderLogging(
+    const Rc<DxvkContext>&  context,
+    HudRenderer&      renderer,
+    HudPos            position) {
+      renderer.drawText(context, 16.0f,
+        { position.x, position.y },
+        { 1.0f, 1.0f, 1.0f, 1.0f },
+        "Currently Logging...");
+        
+        return HudPos { position.x, position.y};
+      }
   
   HudPos HudFps::renderFrametimeGraph(
     const Rc<DxvkContext>&  context,
diff --git a/src/dxvk/hud/dxvk_hud_fps.h b/src/dxvk/hud/dxvk_hud_fps.h
index c8c4b984..b1476889 100644
--- a/src/dxvk/hud/dxvk_hud_fps.h
+++ b/src/dxvk/hud/dxvk_hud_fps.h
@@ -17,8 +17,9 @@ namespace dxvk::hud {
     using TimeDiff  = std::chrono::microseconds;
     using TimePoint = typename Clock::time_point;
     
-    constexpr static uint32_t NumDataPoints  = 300;
-    constexpr static int64_t  UpdateInterval = 500'000;
+    constexpr static uint32_t NumDataPoints     = 300;
+    constexpr static int64_t  UpdateInterval    = 500'000;
+    constexpr static int64_t  LogUpdateInterval = 100'000;
   public:
     
     HudFps(HudElements elements);
@@ -36,23 +37,46 @@ namespace dxvk::hud {
     const HudElements m_elements;
     
     std::string m_fpsString;
+    bool mango_logging = false;
+    time_t lastPress = time(0);
+    std::string logging = env::getEnvVar("DXVK_LOG_TO_FILE");
+    int64_t fps;
     
     TimePoint m_prevFpsUpdate;
     TimePoint m_prevFtgUpdate;
+    TimePoint m_prevLogUpdate;
+    TimePoint m_prevF2Press;
+    TimeDiff  elapsedF2;
     int64_t   m_frameCount = 0;
     
     std::array<float, NumDataPoints>  m_dataPoints  = {};
     uint32_t                          m_dataPointId = 0;
+
+    HudPos renderGpuText(
+      const Rc<DxvkContext>&  context,
+      HudRenderer&      renderer,
+      HudPos            position);
+      
+    HudPos renderCpuText(
+      const Rc<DxvkContext>&  context,
+            HudRenderer&      renderer,
+            HudPos            position);
     
     HudPos renderFpsText(
       const Rc<DxvkContext>&  context,
             HudRenderer&      renderer,
             HudPos            position);
     
+    
     HudPos renderFrametimeGraph(
       const Rc<DxvkContext>&  context,
             HudRenderer&      renderer,
             HudPos            position);
+            
+    HudPos renderLogging(
+      const Rc<DxvkContext>&  context,
+            HudRenderer&      renderer,
+            HudPos            position);
     
   };
   
diff --git a/src/dxvk/hud/dxvk_hud_stats.cpp b/src/dxvk/hud/dxvk_hud_stats.cpp
index 995f186b..be8a5eb7 100644
--- a/src/dxvk/hud/dxvk_hud_stats.cpp
+++ b/src/dxvk/hud/dxvk_hud_stats.cpp
@@ -1,5 +1,7 @@
 #include "dxvk_hud_stats.h"
 
+std::string m_gpuLoadString = "GPU: ";
+uint64_t gpuLoad;
 namespace dxvk::hud {
   
   HudStats::HudStats(HudElements elements)
@@ -24,7 +26,7 @@ namespace dxvk::hud {
 
     // GPU load is a bit more complex than that since
     // we don't want to update this every frame
-    if (m_elements.test(HudElement::StatGpuLoad))
+    if (m_elements.test(HudElement::GpuLoad) || m_elements.test(HudElement::StatGpuLoad))
       this->updateGpuLoad();
   }
   
@@ -70,8 +72,8 @@ namespace dxvk::hud {
       uint64_t busyTicks = ticks > m_diffGpuIdleTicks
         ? uint64_t(ticks - m_diffGpuIdleTicks)
         : uint64_t(0);
-
-      m_gpuLoadString = str::format("GPU: ", (100 * busyTicks) / ticks, "%");
+      gpuLoad = 100 * busyTicks / ticks;
+      m_gpuLoadString = str::format("GPU:   ", (100 * busyTicks) / ticks, "%");
     }
   }
 
@@ -224,7 +226,8 @@ namespace dxvk::hud {
       HudElement::StatPipelines,
       HudElement::StatMemory,
       HudElement::StatGpuLoad,
-      HudElement::CompilerActivity);
+      HudElement::CompilerActivity,
+      HudElement::GpuLoad);
   }
   
 }
diff --git a/src/dxvk/hud/dxvk_hud_stats.h b/src/dxvk/hud/dxvk_hud_stats.h
index 227f600c..b7d740f1 100644
--- a/src/dxvk/hud/dxvk_hud_stats.h
+++ b/src/dxvk/hud/dxvk_hud_stats.h
@@ -7,6 +7,8 @@
 #include "dxvk_hud_config.h"
 #include "dxvk_hud_renderer.h"
 
+extern std::string m_gpuLoadString;
+extern uint64_t gpuLoad;
 namespace dxvk::hud {
   
   /**
@@ -44,8 +46,6 @@ namespace dxvk::hud {
     uint64_t m_prevGpuIdleTicks = 0;
     uint64_t m_diffGpuIdleTicks = 0;
     
-    std::string m_gpuLoadString = "GPU: ";
-
     void updateGpuLoad();
     
     HudPos printDrawCallStats(