Skip to content

Commit bd430ea

Browse files
committed
GPU Display: Better implementation of FPS limiting
1 parent f642553 commit bd430ea

17 files changed

Lines changed: 63 additions & 68 deletions

‎GPU/GPUTracking/Definitions/GPUSettingsList.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,8 @@ AddOption(openGLCore, bool, false, "", 0, "Use renderer path for OpenGL core pro
504504
AddOption(drawQualityMSAA, int32_t, 0, "", 0, "MultiSample Anti Aliasing")
505505
AddOption(drawQualityDownsampleFSAA, int32_t, 0, "", 0, "Downsampling Anti Aliasing")
506506
AddOption(drawQualityVSync, bool, true, "", 0, "Enable Vertical Sync")
507-
AddOption(maxFPSRate, int32_t, 0, "", 0, "Do not limit FPS but run at maximum possible rate")
507+
AddOption(maxFPSRate, bool, false, "", 0, "Do not limit FPS but run at maximum possible rate")
508+
AddOption(fpsRate, int32_t, 100, "", 0, "Target frame rate (frames per second) if not running at maximum rate (0 = no limit)")
508509
AddOption(useGLIndirectDraw, bool, true, "", 0, "Use OpenGL indirect draws to reduce number of draw calls")
509510
AddOption(screenshotScaleFactor, int32_t, 1, "", 0, "Resolution scale factor when taking screenshots")
510511
AddOption(fov, int32_t, 45, "", 0, "Display FOV")

‎GPU/GPUTracking/Interface/GPUO2InterfaceDisplay.cxx‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ int32_t GPUO2InterfaceDisplay::show(const GPUTrackingInOutPointers* ptrs)
7474
int32_t GPUO2InterfaceDisplay::endDisplay()
7575
{
7676
mFrontend->DisplayExit();
77+
while (mFrontend->IsRunning()) {
78+
usleep(10000);
79+
}
7780
return 0;
7881
}
7982

‎GPU/GPUTracking/display/GPUDisplayInterface.h‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,9 @@ class GPUDisplayFrontendInterface
6060
virtual bool EnableSendKey() = 0;
6161
virtual int32_t getDisplayControl() const = 0;
6262
virtual int32_t getSendKey() const = 0;
63-
virtual int32_t getNeedUpdate() const = 0;
6463
virtual void setDisplayControl(int32_t v) = 0;
64+
virtual bool IsRunning() const = 0;
6565
virtual void setSendKey(int32_t v) = 0;
66-
virtual void setNeedUpdate(int32_t v) = 0;
6766
virtual const char* frontendName() const = 0;
6867

6968
protected:

‎GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1404,7 +1404,7 @@ void GPUDisplayBackendVulkan::clearImage(VulkanImage& image)
14041404

14051405
int32_t GPUDisplayBackendVulkan::InitBackendA()
14061406
{
1407-
mEnableValidationLayers = mDisplay->param() && mDisplay->GetProcessingSettings().debugLevel >= 2;
1407+
mEnableValidationLayers = (mDisplay->param() && mDisplay->GetProcessingSettings().debugLevel >= 2) || (getenv("GPUCA_VULKAN_VALIDATION") && atoi(getenv("GPUCA_VULKAN_VALIDATION")));
14081408
mFramesInFlight = 2;
14091409

14101410
createDevice();

‎GPU/GPUTracking/display/frontend/GPUDisplayFrontend.cxx‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
#include "GPULogging.h"
3939
#include <cstring>
40+
#include <thread>
4041

4142
#ifdef GPUCA_BUILD_EVENT_DISPLAY_QT
4243
#include "GPUDisplayGUIWrapper.h"
@@ -63,6 +64,23 @@ void* GPUDisplayFrontend::FrontendThreadWrapper(void* ptr)
6364
return ((void*)(size_t)retVal);
6465
}
6566

67+
void GPUDisplayFrontend::waitForFPSLimit()
68+
{
69+
mNeedUpdate = 0;
70+
const auto now = std::chrono::steady_clock::now();
71+
const auto& cfg = mDisplay->cfgR();
72+
if (cfg.maxFPSRate || cfg.fpsRate <= 0) {
73+
mNextFrameTime = now;
74+
return;
75+
}
76+
const auto period = std::chrono::duration_cast<std::chrono::steady_clock::duration>(std::chrono::duration<double>(1. / cfg.fpsRate));
77+
mNextFrameTime += period;
78+
if (mNextFrameTime < now - period) { // More than one frame behind (slow rendering, first frame), do not try to catch up
79+
mNextFrameTime = now;
80+
}
81+
std::this_thread::sleep_until(mNextFrameTime);
82+
}
83+
6684
void GPUDisplayFrontend::HandleSendKey()
6785
{
6886
if (mSendKey) {
@@ -73,6 +91,7 @@ void GPUDisplayFrontend::HandleSendKey()
7391

7492
void GPUDisplayFrontend::HandleKey(uint8_t key) { mDisplay->HandleKey(key); }
7593
int32_t GPUDisplayFrontend::DrawGLScene() { return mDisplay->DrawGLScene(); }
94+
7695
void GPUDisplayFrontend::ResizeScene(int32_t width, int32_t height)
7796
{
7897
mDisplayHeight = height;

‎GPU/GPUTracking/display/frontend/GPUDisplayFrontend.h‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "GPUCommonDef.h"
1919
#include "GPUDisplayInterface.h"
2020
#include <memory>
21+
#include <chrono>
2122

2223
namespace o2::gpu
2324
{
@@ -49,7 +50,8 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface
4950
static constexpr int32_t GL_MIN_VERSION_MINOR = 5;
5051

5152
int32_t StartDisplay(); // Start the display. This function returns, and should spawn a thread that runs the display, and calls InitDisplay
52-
void DisplayExit() override = 0; // Stop the display. Display thread should call ExitDisplay and the function returns after the thread has terminated
53+
void DisplayExit() override = 0; // Stop the display. Display thread should call ExitDisplay and then wait that IsRunning() returns false
54+
bool IsRunning() const override { return mDisplayRunning; }
5355
virtual void SwitchFullscreen(bool set) = 0; // Toggle full-screen mode
5456
virtual void ToggleMaximized(bool set) = 0; // Maximize window
5557
virtual void SetVSync(bool enable) = 0; // Enable / disable vsync
@@ -63,10 +65,8 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface
6365

6466
int32_t getDisplayControl() const override { return mDisplayControl; }
6567
int32_t getSendKey() const override { return mSendKey; }
66-
int32_t getNeedUpdate() const override { return mNeedUpdate; }
6768
void setDisplayControl(int32_t v) override { mDisplayControl = v; }
6869
void setSendKey(int32_t v) override { mSendKey = v; }
69-
void setNeedUpdate(int32_t v) override { mNeedUpdate = v; }
7070

7171
frontendTypes frontendType() const { return mFrontendType; }
7272
const char* frontendName() const override { return mFrontendName; }
@@ -83,6 +83,7 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface
8383
protected:
8484
virtual int32_t FrontendMain() = 0;
8585
static void* FrontendThreadWrapper(void*);
86+
void waitForFPSLimit(); // Sleep until the next frame is due according to fpsRate (no sleep with maxFPSRate or fpsRate <= 0)
8687

8788
static constexpr int32_t INIT_WIDTH = 1024, INIT_HEIGHT = 768; // Initial window size, before maximizing
8889
static constexpr const char* DISPLAY_WINDOW_NAME = "GPU CA Standalone Event Display"; // Title of event display set by backend
@@ -129,13 +130,14 @@ class GPUDisplayFrontend : public GPUDisplayFrontendInterface
129130
int32_t mDisplayWidth = INIT_WIDTH;
130131
int32_t mCanDrawText = 0; // 1 = in compat mode, 2 = with shader
131132

132-
int32_t mMaxFPSRate = 0; // run at highest possible frame rate, do not sleep in between frames
133+
std::chrono::steady_clock::time_point mNextFrameTime;
133134

134135
GPUDisplay* mDisplay = nullptr; // Ptr to display, not owning, set by display when it connects to backend
135136
GPUDisplayBackend* mBackend = nullptr; // Ptr to backend, not owning
136137

137138
frontendTypes mFrontendType = TYPE_INVALID;
138139
const char* mFrontendName = nullptr;
140+
volatile bool mDisplayRunning = false;
139141

140142
std::unique_ptr<GPUDisplayGUIWrapper> mGUI;
141143

‎GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.cxx‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ int32_t GPUDisplayFrontendGlfw::FrontendMain()
282282
}
283283

284284
pthread_mutex_lock(&mSemLockExit);
285-
mGlfwRunning = true;
285+
mDisplayRunning = true;
286286
pthread_mutex_unlock(&mSemLockExit);
287287

288288
if (mBackend->ExtInit()) {
@@ -304,6 +304,7 @@ int32_t GPUDisplayFrontendGlfw::FrontendMain()
304304
if (backend()->backendType() == GPUDisplayBackend::TYPE_OPENGL) {
305305
glfwSwapBuffers(mWindow);
306306
}
307+
waitForFPSLimit();
307308
glfwPollEvents();
308309
}
309310

@@ -312,7 +313,7 @@ int32_t GPUDisplayFrontendGlfw::FrontendMain()
312313
pthread_mutex_lock(&mSemLockExit);
313314
glfwDestroyWindow(mWindow);
314315
glfwTerminate();
315-
mGlfwRunning = false;
316+
mDisplayRunning = false;
316317
pthread_mutex_unlock(&mSemLockExit);
317318

318319
return 0;
@@ -321,13 +322,10 @@ int32_t GPUDisplayFrontendGlfw::FrontendMain()
321322
void GPUDisplayFrontendGlfw::DisplayExit()
322323
{
323324
pthread_mutex_lock(&mSemLockExit);
324-
if (mGlfwRunning) {
325+
if (mDisplayRunning) {
325326
glfwSetWindowShouldClose(mWindow, true);
326327
}
327328
pthread_mutex_unlock(&mSemLockExit);
328-
while (mGlfwRunning) {
329-
usleep(10000);
330-
}
331329
}
332330

333331
void GPUDisplayFrontendGlfw::OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton)

‎GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlfw.h‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ class GPUDisplayFrontendGlfw : public GPUDisplayFrontend
5454

5555
GLFWwindow* mWindow;
5656

57-
volatile bool mGlfwRunning = false;
5857
pthread_mutex_t mSemLockExit = PTHREAD_MUTEX_INITIALIZER;
5958
int32_t mWindowX = 0;
6059
int32_t mWindowY = 0;

‎GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.cxx‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ GPUDisplayFrontendGlut::GPUDisplayFrontendGlut()
3232
mFrontendName = "GLUT";
3333
}
3434

35-
void GPUDisplayFrontendGlut::displayFunc()
35+
void GPUDisplayFrontendGlut::displayFunc() // TODO: Functions should be lambdas, to get rid of the static me variable!
3636
{
3737
me->DrawGLScene();
3838
glutSwapBuffers();
39+
me->waitForFPSLimit();
3940
}
4041

4142
void GPUDisplayFrontendGlut::glutLoopFunc()
@@ -261,12 +262,12 @@ int32_t GPUDisplayFrontendGlut::FrontendMain()
261262
ToggleMaximized(true);
262263

263264
pthread_mutex_lock(&mSemLockExit);
264-
mGlutRunning = true;
265+
mDisplayRunning = true;
265266
pthread_mutex_unlock(&mSemLockExit);
266267
glutMainLoop();
267268
mDisplayControl = 2;
268269
pthread_mutex_lock(&mSemLockExit);
269-
mGlutRunning = false;
270+
mDisplayRunning = false;
270271
pthread_mutex_unlock(&mSemLockExit);
271272

272273
ExitDisplay();
@@ -276,13 +277,10 @@ int32_t GPUDisplayFrontendGlut::FrontendMain()
276277
void GPUDisplayFrontendGlut::DisplayExit()
277278
{
278279
pthread_mutex_lock(&mSemLockExit);
279-
if (mGlutRunning) {
280+
if (mDisplayRunning) {
280281
glutLeaveMainLoop();
281282
}
282283
pthread_mutex_unlock(&mSemLockExit);
283-
while (mGlutRunning) {
284-
usleep(10000);
285-
}
286284
}
287285

288286
void GPUDisplayFrontendGlut::OpenGLPrint(const char* s, float x, float y, float r, float g, float b, float a, bool fromBotton)

‎GPU/GPUTracking/display/frontend/GPUDisplayFrontendGlut.h‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ class GPUDisplayFrontendGlut : public GPUDisplayFrontend
4848
static int32_t GetKey(int32_t key);
4949
static void GetKey(int32_t keyin, int32_t& keyOut, int32_t& keyPressOut, bool special);
5050

51-
volatile bool mGlutRunning = false;
5251
pthread_mutex_t mSemLockExit = PTHREAD_MUTEX_INITIALIZER;
5352

5453
int32_t mWidth = INIT_WIDTH;

0 commit comments

Comments
 (0)