mirror of https://github.com/adamdruppe/arsd.git
Overhaul pixelpresenter’s timing
This commit is contained in:
parent
8ffc0b327d
commit
0d81bbff41
140
pixelpresenter.d
140
pixelpresenter.d
|
@ -33,7 +33,7 @@
|
||||||
then jumps back to black and the process repeats.
|
then jumps back to black and the process repeats.
|
||||||
|
|
||||||
---
|
---
|
||||||
void main() {
|
int main() {
|
||||||
// Internal resolution of the images (“frames”) we will render.
|
// Internal resolution of the images (“frames”) we will render.
|
||||||
// From the PixelPresenter’s perspective,
|
// From the PixelPresenter’s perspective,
|
||||||
// these are the “fully-rendered frames” that it will blit to screen.
|
// these are the “fully-rendered frames” that it will blit to screen.
|
||||||
|
@ -53,8 +53,9 @@
|
||||||
// This variable will be “shared” across events (and frames).
|
// This variable will be “shared” across events (and frames).
|
||||||
int blueChannel = 0;
|
int blueChannel = 0;
|
||||||
|
|
||||||
// Run the eventloop
|
// Run the eventloop.
|
||||||
presenter.eventLoop(delegate() {
|
// The callback delegate will get executed every ~16ms (≙ ~60FPS) and schedule a redraw.
|
||||||
|
return presenter.eventLoop(16, delegate() {
|
||||||
// Update the frame(buffer) here…
|
// Update the frame(buffer) here…
|
||||||
|
|
||||||
// Construct an RGB color value.
|
// Construct an RGB color value.
|
||||||
|
@ -107,7 +108,8 @@
|
||||||
ubyte color = 0;
|
ubyte color = 0;
|
||||||
byte colorDelta = 2;
|
byte colorDelta = 2;
|
||||||
|
|
||||||
// Run the eventloop
|
// Run the eventloop.
|
||||||
|
// Note how the callback delegate returns a [LoopCtrl] instance.
|
||||||
presenter.eventLoop(delegate() {
|
presenter.eventLoop(delegate() {
|
||||||
// Determine the start and end index of the current line in the
|
// Determine the start and end index of the current line in the
|
||||||
// framebuffer.
|
// framebuffer.
|
||||||
|
@ -130,6 +132,9 @@
|
||||||
++line;
|
++line;
|
||||||
if (line == resolution.height)
|
if (line == resolution.height)
|
||||||
line = 0;
|
line = 0;
|
||||||
|
|
||||||
|
// Schedule a redraw in ~16ms.
|
||||||
|
return LoopCtrl.redrawIn(16);
|
||||||
}, delegate(MouseEvent ev) {
|
}, delegate(MouseEvent ev) {
|
||||||
// toggle fullscreen mode on double-click
|
// toggle fullscreen mode on double-click
|
||||||
if (ev.doubleClick) {
|
if (ev.doubleClick) {
|
||||||
|
@ -148,7 +153,6 @@ import arsd.simpledisplay;
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- Complete documentation
|
- Complete documentation
|
||||||
- Usage example(s)
|
|
||||||
- Additional renderer implementations:
|
- Additional renderer implementations:
|
||||||
- a `ScreenPainter`-based renderer
|
- a `ScreenPainter`-based renderer
|
||||||
- a legacy OpenGL renderer (maybe)
|
- a legacy OpenGL renderer (maybe)
|
||||||
|
@ -156,9 +160,10 @@ import arsd.simpledisplay;
|
||||||
- Minimum window size
|
- Minimum window size
|
||||||
- or something similar
|
- or something similar
|
||||||
- to ensure `Scaling.integer` doesn’t break “unexpectedly”
|
- to ensure `Scaling.integer` doesn’t break “unexpectedly”
|
||||||
- Fix timing
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private enum hasTimer = is(Timer == class);
|
||||||
|
|
||||||
///
|
///
|
||||||
alias Pixel = Color;
|
alias Pixel = Color;
|
||||||
|
|
||||||
|
@ -441,18 +446,33 @@ interface PixelRenderer {
|
||||||
Called once during setup.
|
Called once during setup.
|
||||||
Perform initialization tasks in here.
|
Perform initialization tasks in here.
|
||||||
|
|
||||||
|
$(NOTE
|
||||||
|
The final thing a setup function does
|
||||||
|
is usually to call `reconfigure()` on the renderer.
|
||||||
|
)
|
||||||
|
|
||||||
Params:
|
Params:
|
||||||
pro = Pointer to the [PresenterObjects] of the presenter. To be stored for later use.
|
pro = Pointer to the [PresenterObjects] of the presenter. To be stored for later use.
|
||||||
+/
|
+/
|
||||||
public void setup(PresenterObjects* pro);
|
public void setup(PresenterObjects* pro);
|
||||||
|
|
||||||
/++
|
/++
|
||||||
Reconfigure renderer
|
Reconfigures the renderer
|
||||||
|
|
||||||
Called upon configuration changes.
|
Called upon configuration changes.
|
||||||
The new config can be found in the [PresenterObjects] received during `setup()`.
|
The new config can be found in the [PresenterObjects] received during `setup()`.
|
||||||
+/
|
+/
|
||||||
public void reconfigure();
|
public void reconfigure();
|
||||||
|
|
||||||
|
/++
|
||||||
|
Schedules a redraw
|
||||||
|
+/
|
||||||
|
public void redrawSchedule();
|
||||||
|
|
||||||
|
/++
|
||||||
|
Triggers a redraw
|
||||||
|
+/
|
||||||
|
public void redrawNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -657,6 +677,14 @@ final class OpenGL3PixelRenderer : PixelRenderer {
|
||||||
_clear = true;
|
_clear = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void redrawSchedule() {
|
||||||
|
_pro.window.redrawOpenGlSceneSoon();
|
||||||
|
}
|
||||||
|
|
||||||
|
void redrawNow() {
|
||||||
|
_pro.window.redrawOpenGlSceneNow();
|
||||||
|
}
|
||||||
|
|
||||||
private {
|
private {
|
||||||
static immutable GLfloat[] vertices = [
|
static immutable GLfloat[] vertices = [
|
||||||
//dfmt off
|
//dfmt off
|
||||||
|
@ -677,6 +705,32 @@ final class OpenGL3PixelRenderer : PixelRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
struct LoopCtrl {
|
||||||
|
int interval; /// in milliseconds
|
||||||
|
bool redraw; ///
|
||||||
|
|
||||||
|
///
|
||||||
|
@disable this();
|
||||||
|
|
||||||
|
@safe pure nothrow @nogc:
|
||||||
|
|
||||||
|
private this(int interval, bool redraw) {
|
||||||
|
this.interval = interval;
|
||||||
|
this.redraw = redraw;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
static LoopCtrl waitFor(int intervalMS) {
|
||||||
|
return LoopCtrl(intervalMS, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
static LoopCtrl redrawIn(int intervalMS) {
|
||||||
|
return LoopCtrl(intervalMS, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/++
|
/++
|
||||||
+/
|
+/
|
||||||
final class PixelPresenter {
|
final class PixelPresenter {
|
||||||
|
@ -684,6 +738,10 @@ final class PixelPresenter {
|
||||||
private {
|
private {
|
||||||
PresenterObjects* _pro;
|
PresenterObjects* _pro;
|
||||||
PixelRenderer _renderer;
|
PixelRenderer _renderer;
|
||||||
|
|
||||||
|
static if (hasTimer) {
|
||||||
|
Timer _timer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ctors
|
// ctors
|
||||||
|
@ -772,15 +830,68 @@ final class PixelPresenter {
|
||||||
// public functions
|
// public functions
|
||||||
public {
|
public {
|
||||||
|
|
||||||
///
|
/++
|
||||||
int eventLoop(T...)(T eventHandlers) if (T.length == 0 || is(T[0] == delegate)) {
|
Runs the event loop (with a pulse timer)
|
||||||
|
|
||||||
|
A redraw will be scheduled automatically each pulse.
|
||||||
|
+/
|
||||||
|
int eventLoop(T...)(long pulseTimeout, void delegate() onPulse, T eventHandlers) {
|
||||||
|
// run event-loop with pulse timer
|
||||||
return _pro.window.eventLoop(
|
return _pro.window.eventLoop(
|
||||||
16, // ~60 FPS
|
pulseTimeout,
|
||||||
delegate() { eventHandlers[0](); _pro.window.redrawOpenGlSceneSoon(); },
|
delegate() { onPulse(); this.scheduleRedraw(); },
|
||||||
eventHandlers[1 .. $],
|
eventHandlers,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//dfmt off
|
||||||
|
/++
|
||||||
|
Runs the event loop
|
||||||
|
|
||||||
|
Redraws have to manually scheduled through [scheduleRedraw] when using this overload.
|
||||||
|
+/
|
||||||
|
int eventLoop(T...)(T eventHandlers) if (
|
||||||
|
(T.length == 0) || (is(T[0] == delegate) && !is(typeof(() { return T[0](); }()) == LoopCtrl))
|
||||||
|
) {
|
||||||
|
return _pro.window.eventLoop(eventHandlers);
|
||||||
|
}
|
||||||
|
//dfmt on
|
||||||
|
|
||||||
|
static if (hasTimer) {
|
||||||
|
/++
|
||||||
|
Runs the event loop
|
||||||
|
with [LoopCtrl] timing mechanism
|
||||||
|
+/
|
||||||
|
int eventLoop(T...)(LoopCtrl delegate() callback, T eventHandlers) {
|
||||||
|
if (callback !is null) {
|
||||||
|
LoopCtrl prev = LoopCtrl(1, true);
|
||||||
|
|
||||||
|
_timer = new Timer(prev.interval, delegate() {
|
||||||
|
// redraw if requested by previous ctrl message
|
||||||
|
if (prev.redraw) {
|
||||||
|
_renderer.redrawNow();
|
||||||
|
prev.redraw = false; // done
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute callback
|
||||||
|
const LoopCtrl ctrl = callback();
|
||||||
|
|
||||||
|
// different than previous ctrl message?
|
||||||
|
if (ctrl.interval != prev.interval) {
|
||||||
|
// update timer
|
||||||
|
_timer.changeTime(ctrl.interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// save ctrl message
|
||||||
|
prev = ctrl;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// run event-loop
|
||||||
|
return _pro.window.eventLoop(0, eventHandlers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
PixelBuffer framebuffer() @safe pure nothrow @nogc {
|
PixelBuffer framebuffer() @safe pure nothrow @nogc {
|
||||||
return _pro.framebuffer;
|
return _pro.framebuffer;
|
||||||
|
@ -793,6 +904,11 @@ final class PixelPresenter {
|
||||||
//_renderer.reconfigure(config);
|
//_renderer.reconfigure(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
void scheduleRedraw() {
|
||||||
|
_renderer.redrawSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
bool isFullscreen() {
|
bool isFullscreen() {
|
||||||
return _pro.window.fullscreen;
|
return _pro.window.fullscreen;
|
||||||
|
|
Loading…
Reference in New Issue