Browse Source

Only draw framebuffer is frame is not overdue

tags/v1.0.0
Andrew Belt 5 years ago
parent
commit
419a1e7529
4 changed files with 91 additions and 81 deletions
  1. +2
    -0
      include/window.hpp
  2. +1
    -5
      src/app/RackScrollWidget.cpp
  3. +77
    -72
      src/widget/FramebufferWidget.cpp
  4. +11
    -4
      src/window.cpp

+ 2
- 0
include/window.hpp View File

@@ -84,6 +84,7 @@ struct Window {
/** The last known absolute mouse position in the window */
math::Vec mousePos;
std::shared_ptr<Font> uiFont;
double frameTimeStart = 0.f;

struct Internal;
Internal *internal;
@@ -100,6 +101,7 @@ struct Window {
int getMods();
void setFullScreen(bool fullScreen);
bool isFullScreen();
bool isFrameOverdue();

std::shared_ptr<Font> loadFont(const std::string &filename);
std::shared_ptr<Image> loadImage(const std::string &filename);


+ 1
- 5
src/app/RackScrollWidget.cpp View File

@@ -18,11 +18,7 @@ RackScrollWidget::RackScrollWidget() {
}

void RackScrollWidget::step() {
// Set ZoomWidget's zoom every few frames
int frame = APP->window->frame;
if (frame % 10 == 0) {
zoom->setZoom(std::round(settings.zoom * 100) / 100);
}
zoom->setZoom(std::round(settings.zoom * 100) / 100);

// Resize RackWidget to be a bit larger than the viewport
rack->box.size = box.size


+ 77
- 72
src/widget/FramebufferWidget.cpp View File

@@ -18,87 +18,92 @@ FramebufferWidget::~FramebufferWidget() {
void FramebufferWidget::step() {
Widget::step();

// if (random::uniform() > 0.01)
// return;

// Render to framebuffer if dirty.
// Also check that scale has been set by `draw()` yet.
if (dirty && !scale.isZero()) {
// In case we fail drawing the framebuffer, don't try again the next frame, so reset `dirty` here.
dirty = false;
NVGcontext *vg = APP->window->vg;

fbScale = scale;
// Get subpixel offset in range [0, 1)
math::Vec offsetI = offset.floor();
fbOffset = offset.minus(offsetI);

math::Rect localBox;
if (children.empty()) {
localBox = box.zeroPos();
}
else {
localBox = getChildrenBoundingBox();
}

// DEBUG("%g %g %g %g, %g %g, %g %g", RECT_ARGS(localBox), VEC_ARGS(fbOffset), VEC_ARGS(fbScale));
// Transform to world coordinates, then expand to nearest integer coordinates
math::Vec min = localBox.getTopLeft().mult(fbScale).plus(fbOffset).floor();
math::Vec max = localBox.getBottomRight().mult(fbScale).plus(fbOffset).ceil();
fbBox = math::Rect::fromMinMax(min, max);
// DEBUG("%g %g %g %g", RECT_ARGS(fbBox));

math::Vec newFbSize = fbBox.size.mult(APP->window->pixelRatio).ceil();

// Create framebuffer if a new size is needed
if (!fb || !newFbSize.isEqual(fbSize)) {
fbSize = newFbSize;
// Delete old framebuffer
if (fb)
nvgluDeleteFramebuffer(fb);
// Create a framebuffer at the oversampled size
if (fbSize.isFinite() && !fbSize.isZero())
fb = nvgluCreateFramebuffer(vg, fbSize.x * oversample, fbSize.y * oversample, 0);
}

if (!fb)
return;
// It's more important to not lag the frame than to draw the framebuffer
if (APP->window->isFrameOverdue())
return;

nvgluBindFramebuffer(fb);
drawFramebuffer();
nvgluBindFramebuffer(NULL);
// Check that scale has been set by `draw()` yet.
if (scale.isZero())
return;

// If oversampling, create another framebuffer and copy it to actual size.
if (oversample != 1.0) {
NVGLUframebuffer *newFb = nvgluCreateFramebuffer(vg, fbSize.x, fbSize.y, 0);
if (!newFb)
return;
// Only redraw if FramebufferWidget is dirty
if (!dirty)
return;

// Use NanoVG for resizing framebuffers
nvgluBindFramebuffer(newFb);
// In case we fail drawing the framebuffer, don't try again the next frame, so reset `dirty` here.
dirty = false;
NVGcontext *vg = APP->window->vg;

nvgBeginFrame(vg, fbBox.size.x, fbBox.size.y, 1.0);
fbScale = scale;
// Get subpixel offset in range [0, 1)
math::Vec offsetI = offset.floor();
fbOffset = offset.minus(offsetI);

// Draw oversampled framebuffer
nvgBeginPath(vg);
nvgRect(vg, 0.0, 0.0, fbSize.x, fbSize.y);
NVGpaint paint = nvgImagePattern(vg, 0.0, 0.0, fbSize.x, fbSize.y,
0.0, fb->image, 1.0);
nvgFillPaint(vg, paint);
nvgFill(vg);
math::Rect localBox;
if (children.empty()) {
localBox = box.zeroPos();
}
else {
localBox = getChildrenBoundingBox();
}

glViewport(0.0, 0.0, fbSize.x, fbSize.y);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
nvgEndFrame(vg);
nvgReset(vg);
// DEBUG("%g %g %g %g, %g %g, %g %g", RECT_ARGS(localBox), VEC_ARGS(fbOffset), VEC_ARGS(fbScale));
// Transform to world coordinates, then expand to nearest integer coordinates
math::Vec min = localBox.getTopLeft().mult(fbScale).plus(fbOffset).floor();
math::Vec max = localBox.getBottomRight().mult(fbScale).plus(fbOffset).ceil();
fbBox = math::Rect::fromMinMax(min, max);
// DEBUG("%g %g %g %g", RECT_ARGS(fbBox));

nvgluBindFramebuffer(NULL);
math::Vec newFbSize = fbBox.size.mult(APP->window->pixelRatio).ceil();

// Swap the framebuffers
// Create framebuffer if a new size is needed
if (!fb || !newFbSize.isEqual(fbSize)) {
fbSize = newFbSize;
// Delete old framebuffer
if (fb)
nvgluDeleteFramebuffer(fb);
fb = newFb;
}
// Create a framebuffer at the oversampled size
if (fbSize.isFinite() && !fbSize.isZero())
fb = nvgluCreateFramebuffer(vg, fbSize.x * oversample, fbSize.y * oversample, 0);
}

if (!fb)
return;

nvgluBindFramebuffer(fb);
drawFramebuffer();
nvgluBindFramebuffer(NULL);

// If oversampling, create another framebuffer and copy it to actual size.
if (oversample != 1.0) {
NVGLUframebuffer *newFb = nvgluCreateFramebuffer(vg, fbSize.x, fbSize.y, 0);
if (!newFb)
return;

// Use NanoVG for resizing framebuffers
nvgluBindFramebuffer(newFb);

nvgBeginFrame(vg, fbBox.size.x, fbBox.size.y, 1.0);

// Draw oversampled framebuffer
nvgBeginPath(vg);
nvgRect(vg, 0.0, 0.0, fbSize.x, fbSize.y);
NVGpaint paint = nvgImagePattern(vg, 0.0, 0.0, fbSize.x, fbSize.y,
0.0, fb->image, 1.0);
nvgFillPaint(vg, paint);
nvgFill(vg);

glViewport(0.0, 0.0, fbSize.x, fbSize.y);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
nvgEndFrame(vg);
nvgReset(vg);

nvgluBindFramebuffer(NULL);

// Swap the framebuffers
nvgluDeleteFramebuffer(fb);
fb = newFb;
}
}



+ 11
- 4
src/window.cpp View File

@@ -308,7 +308,7 @@ void Window::run() {

frame = 0;
while(!glfwWindowShouldClose(win)) {
double startTime = glfwGetTime();
frameTimeStart = glfwGetTime();

// Poll events
glfwPollEvents();
@@ -382,9 +382,9 @@ void Window::run() {
}

// Limit frame rate
double endTime = glfwGetTime();
double frameTimeEnd = glfwGetTime();
if (settings.frameRateLimit > 0.0) {
double frameDuration = endTime - startTime;
double frameDuration = frameTimeEnd - frameTimeStart;
double waitDuration = 1.0 / settings.frameRateLimit - frameDuration;
if (waitDuration > 0.0) {
std::this_thread::sleep_for(std::chrono::duration<double>(waitDuration));
@@ -392,7 +392,7 @@ void Window::run() {
}

// Compute actual frame rate
endTime = glfwGetTime();
frameTimeEnd = glfwGetTime();
// DEBUG("%g fps", 1 / (endTime - startTime));
frame++;
}
@@ -449,6 +449,13 @@ bool Window::isFullScreen() {
return monitor != NULL;
}

bool Window::isFrameOverdue() {
if (settings.frameRateLimit == 0.0)
return false;
double frameDuration = glfwGetTime() - frameTimeStart;
return frameDuration > 1.0 / settings.frameRateLimit;
}

std::shared_ptr<Font> Window::loadFont(const std::string &filename) {
auto sp = internal->fontCache[filename].lock();
if (!sp)


Loading…
Cancel
Save