@@ -32,6 +32,8 @@ struct FramebufferWidget : Widget { | |||||
NVGLUframebuffer* getFramebuffer(); | NVGLUframebuffer* getFramebuffer(); | ||||
math::Vec getFramebufferSize(); | math::Vec getFramebufferSize(); | ||||
void setScale(math::Vec scale); | void setScale(math::Vec scale); | ||||
void onContextCreate(const ContextCreateEvent& e) override; | |||||
void onContextDestroy(const ContextDestroyEvent& e) override; | |||||
}; | }; | ||||
@@ -452,13 +452,33 @@ struct Widget : WeakBase { | |||||
virtual void onHide(const HideEvent& e) { | virtual void onHide(const HideEvent& e) { | ||||
recurseEvent(&Widget::onHide, e); | recurseEvent(&Widget::onHide, e); | ||||
} | } | ||||
/** Occurs after the Window, OpenGL context, and Nanovg are created. | |||||
Recurses. | |||||
*/ | |||||
struct ContextCreateEvent : BaseEvent { | |||||
NVGcontext* vg; | |||||
}; | |||||
virtual void onContextCreate(const ContextCreateEvent& e) { | |||||
recurseEvent(&Widget::onContextCreate, e); | |||||
} | |||||
/** Occurs before the Window, OpenGL context, and Nanovg are destroyed. | |||||
Recurses. | |||||
*/ | |||||
struct ContextDestroyEvent : BaseEvent { | |||||
NVGcontext* vg; | |||||
}; | |||||
virtual void onContextDestroy(const ContextDestroyEvent& e) { | |||||
recurseEvent(&Widget::onContextDestroy, e); | |||||
} | |||||
}; | }; | ||||
} // namespace widget | } // namespace widget | ||||
/** Deprecated Rack v1 event namespace. | /** Deprecated Rack v1 event namespace. | ||||
Use `FooEvent` instead of `event::Foo` in new code. | |||||
Use `FooEvent` (defined in widget::Widget) instead of `event::Foo` in new code. | |||||
*/ | */ | ||||
namespace event { | namespace event { | ||||
using Base = widget::BaseEvent; | using Base = widget::BaseEvent; | ||||
@@ -50,7 +50,7 @@ void FramebufferWidget::setDirty(bool dirty) { | |||||
void FramebufferWidget::onDirty(const DirtyEvent& e) { | void FramebufferWidget::onDirty(const DirtyEvent& e) { | ||||
dirty = true; | |||||
setDirty(); | |||||
Widget::onDirty(e); | Widget::onDirty(e); | ||||
} | } | ||||
@@ -71,7 +71,7 @@ void FramebufferWidget::step() { | |||||
return; | return; | ||||
// In case we fail drawing the framebuffer, don't try again the next frame, so reset `dirty` here. | // In case we fail drawing the framebuffer, don't try again the next frame, so reset `dirty` here. | ||||
dirty = false; | |||||
setDirty(false); | |||||
NVGcontext* vg = APP->window->vg; | NVGcontext* vg = APP->window->vg; | ||||
internal->fbScale = internal->scale; | internal->fbScale = internal->scale; | ||||
@@ -186,12 +186,12 @@ void FramebufferWidget::draw(const DrawArgs& args) { | |||||
if (dirtyOnSubpixelChange && !(math::isNear(internal->offsetF.x, internal->fbOffsetF.x, 0.01f) && math::isNear(internal->offsetF.y, internal->fbOffsetF.y, 0.01f))) { | if (dirtyOnSubpixelChange && !(math::isNear(internal->offsetF.x, internal->fbOffsetF.x, 0.01f) && math::isNear(internal->offsetF.y, internal->fbOffsetF.y, 0.01f))) { | ||||
// If drawing to a new subpixel location, rerender in the next frame. | // If drawing to a new subpixel location, rerender in the next frame. | ||||
// DEBUG("%p dirty subpixel", this); | // DEBUG("%p dirty subpixel", this); | ||||
dirty = true; | |||||
setDirty(); | |||||
} | } | ||||
if (!internal->scale.equals(internal->fbScale)) { | if (!internal->scale.equals(internal->fbScale)) { | ||||
// If rescaled, rerender in the next frame. | // If rescaled, rerender in the next frame. | ||||
// DEBUG("%p dirty scale", this); | // DEBUG("%p dirty scale", this); | ||||
dirty = true; | |||||
setDirty(); | |||||
} | } | ||||
math::Vec scaleRatio = math::Vec(1, 1); | math::Vec scaleRatio = math::Vec(1, 1); | ||||
@@ -285,5 +285,18 @@ void FramebufferWidget::setScale(math::Vec scale) { | |||||
} | } | ||||
void FramebufferWidget::onContextCreate(const ContextCreateEvent& e) { | |||||
setDirty(); | |||||
Widget::onContextCreate(e); | |||||
} | |||||
void FramebufferWidget::onContextDestroy(const ContextDestroyEvent& e) { | |||||
if (internal->fb) | |||||
nvgluDeleteFramebuffer(internal->fb); | |||||
Widget::onContextDestroy(e); | |||||
} | |||||
} // namespace widget | } // namespace widget | ||||
} // namespace rack | } // namespace rack |
@@ -327,6 +327,11 @@ Window::Window() { | |||||
// Load default Blendish font | // Load default Blendish font | ||||
uiFont = loadFont(asset::system("res/fonts/DejaVuSans.ttf")); | uiFont = loadFont(asset::system("res/fonts/DejaVuSans.ttf")); | ||||
bndSetFont(uiFont->handle); | bndSetFont(uiFont->handle); | ||||
if (APP->scene) { | |||||
widget::Widget::ContextCreateEvent e; | |||||
APP->scene->onContextCreate(e); | |||||
} | |||||
} | } | ||||
@@ -344,6 +349,11 @@ Window::~Window() { | |||||
settings::windowPos = math::Vec(winX, winY); | settings::windowPos = math::Vec(winX, winY); | ||||
} | } | ||||
if (APP->scene) { | |||||
widget::Widget::ContextDestroyEvent e; | |||||
APP->scene->onContextDestroy(e); | |||||
} | |||||
#if defined NANOVG_GL2 | #if defined NANOVG_GL2 | ||||
nvgDeleteGL2(vg); | nvgDeleteGL2(vg); | ||||
#elif defined NANOVG_GL3 | #elif defined NANOVG_GL3 | ||||
@@ -422,30 +432,32 @@ void Window::step() { | |||||
glfwGetWindowSize(win, &winWidth, &winHeight); | glfwGetWindowSize(win, &winWidth, &winHeight); | ||||
windowRatio = (float)fbWidth / winWidth; | windowRatio = (float)fbWidth / winWidth; | ||||
// DEBUG("%f %f %d %d", pixelRatio, windowRatio, fbWidth, winWidth); | |||||
// Resize scene | |||||
APP->scene->box.size = math::Vec(fbWidth, fbHeight).div(pixelRatio); | |||||
// Step scene | |||||
APP->scene->step(); | |||||
// Render scene | |||||
bool visible = glfwGetWindowAttrib(win, GLFW_VISIBLE) && !glfwGetWindowAttrib(win, GLFW_ICONIFIED); | |||||
if (visible) { | |||||
// Update and render | |||||
nvgBeginFrame(vg, fbWidth, fbHeight, pixelRatio); | |||||
nvgScale(vg, pixelRatio, pixelRatio); | |||||
// Draw scene | |||||
widget::Widget::DrawArgs args; | |||||
args.vg = vg; | |||||
args.clipBox = APP->scene->box.zeroPos(); | |||||
APP->scene->draw(args); | |||||
glViewport(0, 0, fbWidth, fbHeight); | |||||
glClearColor(0.0, 0.0, 0.0, 1.0); | |||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); | |||||
nvgEndFrame(vg); | |||||
if (APP->scene) { | |||||
// DEBUG("%f %f %d %d", pixelRatio, windowRatio, fbWidth, winWidth); | |||||
// Resize scene | |||||
APP->scene->box.size = math::Vec(fbWidth, fbHeight).div(pixelRatio); | |||||
// Step scene | |||||
APP->scene->step(); | |||||
// Render scene | |||||
bool visible = glfwGetWindowAttrib(win, GLFW_VISIBLE) && !glfwGetWindowAttrib(win, GLFW_ICONIFIED); | |||||
if (visible) { | |||||
// Update and render | |||||
nvgBeginFrame(vg, fbWidth, fbHeight, pixelRatio); | |||||
nvgScale(vg, pixelRatio, pixelRatio); | |||||
// Draw scene | |||||
widget::Widget::DrawArgs args; | |||||
args.vg = vg; | |||||
args.clipBox = APP->scene->box.zeroPos(); | |||||
APP->scene->draw(args); | |||||
glViewport(0, 0, fbWidth, fbHeight); | |||||
glClearColor(0.0, 0.0, 0.0, 1.0); | |||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); | |||||
nvgEndFrame(vg); | |||||
} | |||||
} | } | ||||
glfwSwapBuffers(win); | glfwSwapBuffers(win); | ||||
@@ -223,6 +223,12 @@ int main(int argc, char* argv[]) { | |||||
INFO("Running window"); | INFO("Running window"); | ||||
APP->window->run(); | APP->window->run(); | ||||
INFO("Stopped window"); | INFO("Stopped window"); | ||||
delete APP->window; | |||||
APP->window = NULL; | |||||
INFO("Re-creating window"); | |||||
APP->window = new Window; | |||||
APP->window->run(); | |||||
} | } | ||||
// Destroy context | // Destroy context | ||||