Browse Source

Platform: add GlfwApplication::tickEvent() and setMinimalLoopPeriod().

Co-authored-by: Vladimír Vondruš <mosra@centrum.cz>
pull/419/merge
Andreas Leroux 2 years ago committed by Vladimír Vondruš
parent
commit
a9daddfb63
  1. 63
      src/Magnum/Platform/GlfwApplication.cpp
  2. 39
      src/Magnum/Platform/GlfwApplication.h
  3. 12
      src/Magnum/Platform/Test/GlfwApplicationTest.cpp

63
src/Magnum/Platform/GlfwApplication.cpp

@ -33,10 +33,12 @@
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Utility/Arguments.h>
#include <Corrade/Utility/Unicode.h>
#include <Corrade/Utility/System.h>
#include "Magnum/ImageView.h"
#include "Magnum/PixelFormat.h"
#include "Magnum/Math/ConfigurationValue.h"
#include "Magnum/Math/Time.h"
#include "Magnum/Platform/ScreenedApplication.hpp"
#include "Magnum/Platform/Implementation/DpiScaling.h"
@ -47,6 +49,7 @@
namespace Magnum { namespace Platform {
using namespace Containers::Literals;
using namespace Math::Literals;
#ifdef GLFW_TRUE
/* The docs say that it's the same, verify that just in case */
@ -56,9 +59,11 @@ static_assert(GLFW_TRUE == true && GLFW_FALSE == false, "GLFW does not have sane
enum class GlfwApplication::Flag: UnsignedByte {
Redraw = 1 << 0,
TextInputActive = 1 << 1,
Exit = 1 << 2,
VSyncEnabled = 1 << 2,
NoTickEvent = 1 << 3,
Exit = 1 << 4,
#ifdef CORRADE_TARGET_APPLE
HiDpiWarningPrinted = 1 << 3
HiDpiWarningPrinted = 1 << 5
#endif
};
@ -737,6 +742,19 @@ Vector2 GlfwApplication::dpiScaling() const {
void GlfwApplication::setSwapInterval(const Int interval) {
glfwSwapInterval(interval);
/* Remember whether VSync is enabled for mainLoopIteration() to use
minimal loop period or not. Unlike SDL2 where it's possible to check
whether the VSync was actually set, here it's purely hope-based.
Sorry. */
if(interval) _flags |= Flag::VSyncEnabled;
else _flags &= ~Flag::VSyncEnabled;
}
void GlfwApplication::setMinimalLoopPeriod(const Nanoseconds time) {
CORRADE_ASSERT(time >= 0_nsec,
"Platform::Sdl2Application::setMinimalLoopPeriod(): expected non-negative time, got" << time, );
_minimalLoopPeriodNanoseconds = Long(time);
}
void GlfwApplication::redraw() { _flags |= Flag::Redraw; }
@ -776,16 +794,39 @@ bool GlfwApplication::mainLoopIteration() {
*/
if(glfwGetWindowUserPointer(_window) != this) setupCallbacks();
/* If redrawing, poll for events immediately after drawEvent() (which could
be setting the Redraw flag again, thus doing constant redraw). If not,
avoid spinning the CPU by waiting for the next input event. */
const Nanoseconds timeBefore = _minimalLoopPeriodNanoseconds ? glfwGetTime()*1.0_sec : Nanoseconds{};
glfwPollEvents();
/* Tick event */
if(!(_flags & Flag::NoTickEvent)) tickEvent();
/* Draw event */
if(_flags & Flag::Redraw) {
_flags &= ~Flag::Redraw;
drawEvent();
glfwPollEvents();
} else glfwWaitEvents();
return !glfwWindowShouldClose(_window);
/* If VSync is not enabled, delay to prevent CPU hogging (if set) */
if(!(_flags & Flag::VSyncEnabled) && _minimalLoopPeriodNanoseconds) {
const Nanoseconds loopTime = glfwGetTime()*1.0_sec - timeBefore;
if(loopTime < _minimalLoopPeriodNanoseconds*1_nsec)
Utility::System::sleep((_minimalLoopPeriodNanoseconds*1_nsec - loopTime)/1.0_msec);
}
return !(_flags & Flag::Exit || glfwWindowShouldClose(_window));
}
/* If not drawing anything, delay to prevent CPU hogging (if set) */
if(_minimalLoopPeriodNanoseconds) {
const Nanoseconds loopTime = glfwGetTime()*1.0_sec - timeBefore;
if(loopTime < _minimalLoopPeriodNanoseconds*1_nsec)
Utility::System::sleep((_minimalLoopPeriodNanoseconds*1_nsec - loopTime)/1.0_msec);
}
/* Then, if the tick event doesn't need to be called periodically, wait
indefinitely for next input event */
if(_flags & Flag::NoTickEvent) glfwWaitEvents();
return !(_flags & Flag::Exit || glfwWindowShouldClose(_window));
}
void GlfwApplication::exit(int exitCode) {
@ -886,6 +927,12 @@ void GlfwApplication::exitEvent(ExitEvent& event) {
event.setAccepted();
}
void GlfwApplication::tickEvent() {
/* If this got called, the tick event is not implemented by user and thus
we don't need to call it ever again */
_flags |= Flag::NoTickEvent;
}
void GlfwApplication::viewportEvent(ViewportEvent&) {}
void GlfwApplication::keyPressEvent(KeyEvent&) {}
void GlfwApplication::keyReleaseEvent(KeyEvent&) {}

39
src/Magnum/Platform/GlfwApplication.h

@ -529,6 +529,25 @@ class GlfwApplication {
*/
void setSwapInterval(Int interval);
/**
* @brief Set minimal loop period
* @m_since_latest
*
* This setting reduces the main loop frequency in case
* @ref setSwapInterval() wasn't called at all, was called with
* @cpp 0 @ce, or no drawing is done and just @ref tickEvent() is being
* executed. The @p time is expected to be non-negative, default is
* @cpp 0_nsec @ce (i.e., looping at maximum frequency). If the
* application is drawing on the screen and VSync was enabled by
* calling @ref setSwapInterval(), this setting is ignored.
*
* Note that as the VSync default is driver-dependent,
* @ref setSwapInterval() has to be explicitly called to make the
* interaction between the two work correctly.
* @see @ref setSwapInterval()
*/
void setMinimalLoopPeriod(Nanoseconds time);
/** @copydoc Sdl2Application::redraw() */
void redraw();
@ -739,6 +758,24 @@ class GlfwApplication {
* @}
*/
protected:
/**
* @brief Tick event
* @m_since_latest
*
* If implemented, this function is called periodically after
* processing all input events and before draw event even though there
* might be no input events and redraw is not requested. Useful e.g.
* for asynchronous task polling. Use @ref setMinimalLoopPeriod() /
* @ref setSwapInterval() to control main loop frequency.
*
* If this implementation gets called from its @cpp override @ce, it
* will effectively stop the tick event from being fired and the app
* returns back to waiting for input events. This can be used to
* disable the tick event when not needed.
*/
virtual void tickEvent();
private:
enum class Flag: UnsignedByte;
typedef Containers::EnumSet<Flag> Flags;
@ -766,6 +803,8 @@ class GlfwApplication {
Vector2 _commandLineDpiScaling, _configurationDpiScaling;
GLFWwindow* _window{nullptr};
/* Not using Nanoseconds as that would require including Time.h */
UnsignedInt _minimalLoopPeriodNanoseconds;
Flags _flags;
#ifdef MAGNUM_TARGET_GL
/* Has to be in an Optional because we delay-create it in a constructor

12
src/Magnum/Platform/Test/GlfwApplicationTest.cpp

@ -30,6 +30,7 @@
#include <Corrade/Utility/Resource.h>
#include "Magnum/ImageView.h"
#include "Magnum/Math/Time.h"
#include "Magnum/Math/ConfigurationValue.h"
#include "Magnum/Platform/GlfwApplication.h"
#include "Magnum/Trade/AbstractImporter.h"
@ -159,6 +160,7 @@ Debug& operator<<(Debug& debug, const Application::KeyEvent::Key value) {
}
using namespace Containers::Literals;
using namespace Math::Literals;
struct GlfwApplicationTest: Platform::Application {
explicit GlfwApplicationTest(const Arguments& arguments);
@ -247,6 +249,16 @@ struct GlfwApplicationTest: Platform::Application {
Debug{} << "text input event:" << event.text();
}
/* Uncomment to test the tick event. It should run at given minimal loop
period even if not redrawing, it should not run at a different period
when redrawing constantly. */
#if 0
void tickEvent() override {
setMinimalLoopPeriod(250.0_msec);
Debug{} << "tick event:" << Seconds{glfwGetTime()*1.0_sec};
}
#endif
private:
bool _redraw = false;
bool _vsync = false;

Loading…
Cancel
Save