From 7ef708729e781f98fa51346f637a57808311ac86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaakko=20Kera=CC=88nen?= Date: Sun, 19 Feb 2017 17:47:00 +0200 Subject: [PATCH] Widgets|UI|libappfw: Draw GUI widgets with fewer draw calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most GUI widgets now get batched when drawing. Added a special-purpose “batch.guiwidget” shader for drawing GUI widgets. --- .../client/include/ui/home/columnwidget.h | 2 + .../client/include/ui/widgets/taskbarwidget.h | 2 +- .../renderer.pack/shaders.dei | 1 + .../renderer.pack/shaders/batch.dei | 57 ++++++ .../apps/client/src/ui/home/columnwidget.cpp | 36 +++- .../apps/client/src/ui/widgets/busywidget.cpp | 2 + .../apps/client/src/ui/widgets/gamewidget.cpp | 1 + .../client/src/ui/widgets/taskbarwidget.cpp | 42 ++-- doomsday/sdk/libappfw/include/de/Painter | 1 + .../de/framework/atlasproceduralimage.h | 2 +- .../de/framework/childwidgetorganizer.h | 7 +- .../include/de/framework/gltextcomposer.h | 9 +- .../include/de/framework/guirootwidget.h | 5 +- .../libappfw/include/de/framework/guiwidget.h | 4 +- .../libappfw/include/de/framework/painter.h | 88 +++++++++ .../include/de/framework/proceduralimage.h | 5 +- .../de/framework/styleproceduralimage.h | 2 +- .../include/de/widgets/buttonwidget.h | 2 +- .../libappfw/include/de/widgets/labelwidget.h | 29 ++- .../include/de/widgets/lineeditwidget.h | 2 +- .../libappfw/include/de/widgets/panelwidget.h | 2 +- .../include/de/widgets/popupmenuwidget.h | 2 +- .../libappfw/include/de/widgets/popupwidget.h | 2 +- .../include/de/widgets/progresswidget.h | 2 +- .../include/de/widgets/scrollareawidget.h | 4 +- .../include/de/widgets/sliderwidget.h | 2 +- doomsday/sdk/libappfw/src/gltextcomposer.cpp | 4 +- doomsday/sdk/libappfw/src/guirootwidget.cpp | 28 ++- doomsday/sdk/libappfw/src/guiwidget.cpp | 94 +++++---- doomsday/sdk/libappfw/src/painter.cpp | 184 ++++++++++++++++++ .../sdk/libappfw/src/widgets/buttonwidget.cpp | 12 +- .../libappfw/src/widgets/documentwidget.cpp | 88 ++++++--- .../libappfw/src/widgets/foldpanelwidget.cpp | 4 +- .../sdk/libappfw/src/widgets/labelwidget.cpp | 104 ++++++---- .../libappfw/src/widgets/lineeditwidget.cpp | 8 +- .../sdk/libappfw/src/widgets/logwidget.cpp | 5 +- .../sdk/libappfw/src/widgets/panelwidget.cpp | 32 +-- .../libappfw/src/widgets/popupmenuwidget.cpp | 8 +- .../sdk/libappfw/src/widgets/popupwidget.cpp | 6 +- .../libappfw/src/widgets/progresswidget.cpp | 12 +- .../libappfw/src/widgets/scrollareawidget.cpp | 38 ++-- .../sdk/libappfw/src/widgets/sliderwidget.cpp | 42 ++-- .../sdk/libappfw/src/widgets/togglewidget.cpp | 4 +- .../net.dengine.test.appfw.pack/shaders.dei | 1 + .../shaders/batch.dei | 57 ++++++ .../shaders/hsv.glsl | 123 ++++++++++++ 46 files changed, 928 insertions(+), 239 deletions(-) create mode 100644 doomsday/apps/client/net.dengine.client.pack/renderer.pack/shaders/batch.dei create mode 100644 doomsday/sdk/libappfw/include/de/Painter create mode 100644 doomsday/sdk/libappfw/include/de/framework/painter.h create mode 100644 doomsday/sdk/libappfw/src/painter.cpp create mode 100644 doomsday/tests/test_appfw/net.dengine.test.appfw.pack/shaders/batch.dei create mode 100644 doomsday/tests/test_appfw/net.dengine.test.appfw.pack/shaders/hsv.glsl diff --git a/doomsday/apps/client/include/ui/home/columnwidget.h b/doomsday/apps/client/include/ui/home/columnwidget.h index ef32371a01..5acac33010 100644 --- a/doomsday/apps/client/include/ui/home/columnwidget.h +++ b/doomsday/apps/client/include/ui/home/columnwidget.h @@ -57,6 +57,8 @@ class ColumnWidget : public de::GuiWidget void mouseActivity(QObject const *columnWidget); protected: + //void glInit() override; + //void glDeinit() override; void updateStyle() override; private: diff --git a/doomsday/apps/client/include/ui/widgets/taskbarwidget.h b/doomsday/apps/client/include/ui/widgets/taskbarwidget.h index aab2d87afc..586758f37b 100644 --- a/doomsday/apps/client/include/ui/widgets/taskbarwidget.h +++ b/doomsday/apps/client/include/ui/widgets/taskbarwidget.h @@ -46,7 +46,7 @@ class TaskBarWidget : public de::GuiWidget de::Rule const &shift(); // Events. - void viewResized(); + //void viewResized(); void update(); void drawContent(); bool handleEvent(de::Event const &event); diff --git a/doomsday/apps/client/net.dengine.client.pack/renderer.pack/shaders.dei b/doomsday/apps/client/net.dengine.client.pack/renderer.pack/shaders.dei index a8bff7ff42..609d173248 100644 --- a/doomsday/apps/client/net.dengine.client.pack/renderer.pack/shaders.dei +++ b/doomsday/apps/client/net.dengine.client.pack/renderer.pack/shaders.dei @@ -10,6 +10,7 @@ @include @include +@include @include @include @include diff --git a/doomsday/apps/client/net.dengine.client.pack/renderer.pack/shaders/batch.dei b/doomsday/apps/client/net.dengine.client.pack/renderer.pack/shaders/batch.dei new file mode 100644 index 0000000000..0ce8070a50 --- /dev/null +++ b/doomsday/apps/client/net.dengine.client.pack/renderer.pack/shaders/batch.dei @@ -0,0 +1,57 @@ +batch { + # Shader for UI widgets. + shader guiwidget { + vertex = " + uniform highp mat4 uMvpMatrix; + uniform highp vec4 uColor[DENG_MAX_BATCH_UNIFORMS]; + uniform highp float uSaturation[DENG_MAX_BATCH_UNIFORMS]; + uniform highp vec4 uScissorRect[DENG_MAX_BATCH_UNIFORMS]; + + attribute highp vec4 aVertex; + attribute highp vec2 aUV; + attribute highp vec4 aColor; + attribute highp float aIndex; // uColor + + varying highp vec2 vUV; + varying highp vec4 vColor; + varying highp vec4 vScissor; + varying highp float vSaturation; + + void main(void) { + gl_Position = uMvpMatrix * aVertex; + vUV = aUV; + + int index = int(aIndex); + vColor = aColor * uColor[index]; + vScissor = uScissorRect[index]; + vSaturation = uSaturation[index]; + }" + include.fragment + fragment = " + uniform sampler2D uTex; + + varying highp vec2 vUV; + varying highp vec4 vColor; + varying highp vec4 vScissor; + varying highp float vSaturation; + + void main(void) { + // Check the scissor first. + if (gl_FragCoord.x < vScissor.x || gl_FragCoord.x > vScissor.z || + gl_FragCoord.y < vScissor.y || gl_FragCoord.y > vScissor.w) { + discard; + } + gl_FragColor = texture2D(uTex, vUV); + + // Optionally adjust color saturation. + if (vSaturation < 1.0) { + highp vec4 hsv = rgbToHsv(gl_FragColor); + hsv.y *= vSaturation; + gl_FragColor = hsvToRgb(hsv); + } + + // Final vertex color. + gl_FragColor *= vColor; + }" + } +} diff --git a/doomsday/apps/client/src/ui/home/columnwidget.cpp b/doomsday/apps/client/src/ui/home/columnwidget.cpp index 57bf165d6f..0c6f56eac7 100644 --- a/doomsday/apps/client/src/ui/home/columnwidget.cpp +++ b/doomsday/apps/client/src/ui/home/columnwidget.cpp @@ -64,7 +64,7 @@ DENG_GUI_PIMPL(ColumnWidget) return update; } - void glMakeGeometry(DefaultVertexBuf::Builder &verts, Rectanglef const &rect) override + void glMakeGeometry(GuiVertexBuilder &verts, Rectanglef const &rect) override { if (!allocId().isNone()) { @@ -97,14 +97,15 @@ DENG_GUI_PIMPL(ColumnWidget) Rule const *maxContentWidth = nullptr; Vector4f backTintColor; - GLUniform uSaturation { "uSaturation", GLUniform::Float }; // background saturation + //GLProgram bgProgram; + //GLUniform uSaturation { "uSaturation", GLUniform::Float }; // background saturation + //GLUniform uBgColor { "uColor", GLUniform::Vec4 }; Animation backSaturation { 0.f, Animation::Linear }; Impl(Public *i) : Base(i) { back = new LabelWidget; - back->setShaderId("generic.textured.hsv.color_ucolor"); - back->shaderProgram() << uSaturation; + //back->setCustomShader(&bgProgram); back->margins().setZero(); scrollArea = new ScrollAreaWidget; @@ -124,6 +125,19 @@ DENG_GUI_PIMPL(ColumnWidget) { releaseRef(maxContentWidth); } + + /*void glInit() + { + root().shaders().build(bgProgram, "generic.textured.hsv.color_ucolor") + << uSaturation + << uBgColor + << root().uAtlas(); + } + + void glDeinit() + { + bgProgram.clear(); + }*/ }; ColumnWidget::ColumnWidget(String const &name) @@ -210,9 +224,21 @@ void ColumnWidget::update() { GuiWidget::update(); - d->uSaturation = d->backSaturation; + d->back->setSaturation(d->backSaturation); + /*d->uSaturation = d->backSaturation; + d->uBgColor = Vector4f(1, 1, 1, visibleOpacity());*/ } +/*void ColumnWidget::glInit() +{ + d->glInit(); +} + +void ColumnWidget::glDeinit() +{ + d->bgProgram.clear(); +}*/ + void ColumnWidget::updateStyle() { GuiWidget::updateStyle(); diff --git a/doomsday/apps/client/src/ui/widgets/busywidget.cpp b/doomsday/apps/client/src/ui/widgets/busywidget.cpp index 63a46da7da..14488a1106 100644 --- a/doomsday/apps/client/src/ui/widgets/busywidget.cpp +++ b/doomsday/apps/client/src/ui/widgets/busywidget.cpp @@ -122,6 +122,7 @@ void BusyWidget::drawContent() if (Con_TransitionInProgress()) { + root().painter().flush(); GLState::push() .setViewport(Rectangleui::fromSize(GLState::current().target().size())) .apply(); @@ -140,6 +141,7 @@ void BusyWidget::drawContent() if (d->haveTransitionFrame()) { + root().painter().flush(); GLState::push() .setAlphaTest(false) .setBlend(false) diff --git a/doomsday/apps/client/src/ui/widgets/gamewidget.cpp b/doomsday/apps/client/src/ui/widgets/gamewidget.cpp index 54add217d0..7e2e195985 100644 --- a/doomsday/apps/client/src/ui/widgets/gamewidget.cpp +++ b/doomsday/apps/client/src/ui/widgets/gamewidget.cpp @@ -228,6 +228,7 @@ void GameWidget::drawContent() if (isDisabled() || !GL_IsFullyInited() || !App_GameLoaded()) return; + root().painter().flush(); GLState::push(); Rectanglei pos; diff --git a/doomsday/apps/client/src/ui/widgets/taskbarwidget.cpp b/doomsday/apps/client/src/ui/widgets/taskbarwidget.cpp index 29c3140bcc..06df5819c0 100644 --- a/doomsday/apps/client/src/ui/widgets/taskbarwidget.cpp +++ b/doomsday/apps/client/src/ui/widgets/taskbarwidget.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -120,10 +121,11 @@ DENG_GUI_PIMPL(TaskBarWidget) int maxSpace; // GL objects: - Drawable drawable; - GLUniform uMvpMatrix; - GLUniform uColor; - Matrix4f projMatrix; + //GuiVertexBuilder verts; + //Drawable drawable; + //GLUniform uMvpMatrix; + //GLUniform uColor; + //Matrix4f projMatrix; Impl(Public *i) : Base(i) @@ -136,10 +138,10 @@ DENG_GUI_PIMPL(TaskBarWidget) , configMenu(0) , multiMenu(0) , mouseWasTrappedWhenOpening(false) - , uMvpMatrix("uMvpMatrix", GLUniform::Mat4) - , uColor ("uColor", GLUniform::Vec4) + //, uMvpMatrix("uMvpMatrix", GLUniform::Mat4) + //, uColor ("uColor", GLUniform::Vec4) { - uColor = Vector4f(1, 1, 1, 1); + //uColor = Vector4f(1, 1, 1, 1); self().set(Background(style().colors().colorf("background"))); vertShift = new AnimationRule(0); @@ -206,18 +208,19 @@ DENG_GUI_PIMPL(TaskBarWidget) void glInit() { - drawable.addBuffer(new VertexBuf); + //drawable.addBuffer(new VertexBuf); - shaders().build(drawable.program(), "generic.color_ucolor") + /*shaders().build(drawable.program(), "generic.color_ucolor") << uMvpMatrix << uColor; - updateProjection(); + updateProjection();*/ } void glDeinit() { - drawable.clear(); + //drawable.clear(); + //verts.clear(); } void updateLogoButtonText() @@ -245,22 +248,23 @@ DENG_GUI_PIMPL(TaskBarWidget) logo->setText(text); } - void updateProjection() + /*void updateProjection() { uMvpMatrix = root().projMatrix2D(); - } + }*/ void updateGeometry() { + /* Rectanglei pos; if (self().hasChangedPlace(pos) || self().geometryRequested()) { self().requestGeometry(false); - VertexBuf::Builder verts; + verts.clear(); self().glMakeGeometry(verts); - drawable.buffer().setVertices(gl::TriangleStrip, verts, gl::Static); - } + //drawable.buffer().setVertices(gl::TriangleStrip, verts, gl::Static); + }*/ } GuiWidget &itemWidget(PopupMenuWidget *menu, uint pos) const @@ -560,11 +564,11 @@ void TaskBarWidget::glDeinit() d->glDeinit(); } -void TaskBarWidget::viewResized() +/*void TaskBarWidget::viewResized() { GuiWidget::viewResized(); - d->updateProjection(); -} + //d->updateProjection(); +}*/ void TaskBarWidget::update() { diff --git a/doomsday/sdk/libappfw/include/de/Painter b/doomsday/sdk/libappfw/include/de/Painter new file mode 100644 index 0000000000..746134420c --- /dev/null +++ b/doomsday/sdk/libappfw/include/de/Painter @@ -0,0 +1 @@ +#include "framework/painter.h" diff --git a/doomsday/sdk/libappfw/include/de/framework/atlasproceduralimage.h b/doomsday/sdk/libappfw/include/de/framework/atlasproceduralimage.h index 64fbf8f3eb..06ec5ac6ff 100644 --- a/doomsday/sdk/libappfw/include/de/framework/atlasproceduralimage.h +++ b/doomsday/sdk/libappfw/include/de/framework/atlasproceduralimage.h @@ -106,7 +106,7 @@ class LIBAPPFW_PUBLIC AtlasProceduralImage : public ProceduralImage release(); } - void glMakeGeometry(DefaultVertexBuf::Builder &verts, Rectanglef const &rect) + void glMakeGeometry(GuiVertexBuilder &verts, Rectanglef const &rect) { if (_atlas) { diff --git a/doomsday/sdk/libappfw/include/de/framework/childwidgetorganizer.h b/doomsday/sdk/libappfw/include/de/framework/childwidgetorganizer.h index 98eef0c29d..2fe6db5bfa 100644 --- a/doomsday/sdk/libappfw/include/de/framework/childwidgetorganizer.h +++ b/doomsday/sdk/libappfw/include/de/framework/childwidgetorganizer.h @@ -35,11 +35,6 @@ namespace de { * the ChildWidgetOrganizer::IWidgetFactory interface. Also, third parties * may observe widget creation and updates and alter the widget as they choose. * - * @todo Virtualization: it is not required that all the items of the context - * are represented by widgets on screen at the same time. In contexts with - * large numbers of items, virtualization should be applied to keep only a - * subset/range of items present as widgets. - * * @ingroup appfw */ class LIBAPPFW_PUBLIC ChildWidgetOrganizer @@ -125,7 +120,7 @@ class LIBAPPFW_PUBLIC ChildWidgetOrganizer void setVirtualizationEnabled(bool enabled); /** - * Enables or disables child recycling. Deleted children will be put up for + * Enables or disables child recycling. Deleted children will be put up for * recycling instead of being deleted, and new children will first be taken * from the set of old recycled widgets. * diff --git a/doomsday/sdk/libappfw/include/de/framework/gltextcomposer.h b/doomsday/sdk/libappfw/include/de/framework/gltextcomposer.h index 3e2535b991..c6c53ead10 100644 --- a/doomsday/sdk/libappfw/include/de/framework/gltextcomposer.h +++ b/doomsday/sdk/libappfw/include/de/framework/gltextcomposer.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "../ui/defs.h" #include "../FontLineWrapping" @@ -40,10 +41,10 @@ namespace de { */ class LIBAPPFW_PUBLIC GLTextComposer : public Asset { -public: +/*public: typedef Vertex2TexRgba Vertex; typedef GLBufferT VertexBuf; - typedef VertexBuf::Builder Vertices; + typedef VertexBuf::Builder Vertices;*/ public: GLTextComposer(); @@ -89,7 +90,7 @@ class LIBAPPFW_PUBLIC GLTextComposer : public Asset */ void releaseLinesOutsideRange(); - void makeVertices(Vertices &triStrip, + void makeVertices(GuiVertexBuilder &triStrip, Vector2i const &topLeft, ui::Alignment const &lineAlign, Vector4f const &color = Vector4f(1, 1, 1, 1)); @@ -104,7 +105,7 @@ class LIBAPPFW_PUBLIC GLTextComposer : public Asset * @param lineAlign Horizontal alignment for each line within the paragraph. * @param color Vertex color for the generated vertices. */ - void makeVertices(Vertices &triStrip, + void makeVertices(GuiVertexBuilder &triStrip, Rectanglei const &rect, ui::Alignment const &alignInRect, ui::Alignment const &lineAlign, diff --git a/doomsday/sdk/libappfw/include/de/framework/guirootwidget.h b/doomsday/sdk/libappfw/include/de/framework/guirootwidget.h index 63c9b9ec5e..e0b377f9de 100644 --- a/doomsday/sdk/libappfw/include/de/framework/guirootwidget.h +++ b/doomsday/sdk/libappfw/include/de/framework/guirootwidget.h @@ -30,8 +30,9 @@ namespace de { -class GuiWidget; class FocusWidget; +class GuiWidget; +class Painter; /** * Graphical root widget. @@ -75,6 +76,8 @@ class LIBAPPFW_PUBLIC GuiRootWidget : public RootWidget static GLShaderBank &shaders(); + Painter &painter(); + /** * Returns the default projection for 2D graphics. */ diff --git a/doomsday/sdk/libappfw/include/de/framework/guiwidget.h b/doomsday/sdk/libappfw/include/de/framework/guiwidget.h index f464015f31..076a6c9c63 100644 --- a/doomsday/sdk/libappfw/include/de/framework/guiwidget.h +++ b/doomsday/sdk/libappfw/include/de/framework/guiwidget.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "../Style" @@ -305,6 +306,7 @@ class LIBAPPFW_PUBLIC GuiWidget : public QObject, public Widget void setFont(DotPath const &id); void setTextColor(DotPath const &id); void set(Background const &bg); + void setSaturation(float saturation); Font const &font() const; DotPath const &fontId() const; @@ -542,7 +544,7 @@ public slots: * * @param verts Vertex builder. */ - virtual void glMakeGeometry(DefaultVertexBuf::Builder &verts); + virtual void glMakeGeometry(GuiVertexBuilder &verts); /** * Checks if the widget's rectangle has changed. diff --git a/doomsday/sdk/libappfw/include/de/framework/painter.h b/doomsday/sdk/libappfw/include/de/framework/painter.h new file mode 100644 index 0000000000..f1b799134e --- /dev/null +++ b/doomsday/sdk/libappfw/include/de/framework/painter.h @@ -0,0 +1,88 @@ +/** @file painter.h GUI painter. + * + * @authors Copyright (c) 2017 Jaakko Keränen + * + * @par License + * LGPL: http://www.gnu.org/licenses/lgpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#ifndef LIBAPPFW_PAINTER_H +#define LIBAPPFW_PAINTER_H + +#include "../libappfw.h" +#include +#include +#include +#include + +namespace de { + +struct LIBAPPFW_PUBLIC GuiVertex +{ + Vector2f pos; + Vector2f texCoord; + Vector4f rgba; + float batchIndex; + + LIBGUI_DECLARE_VERTEX_FORMAT(4) +}; + +typedef VertexBuilder::Vertices GuiVertexBuilder; + +/** + * GUI painter. + */ +class LIBAPPFW_PUBLIC Painter +{ +public: + Painter(); + + void init(); + + void deinit(); + + void setProgram(GLProgram &program); + + void useDefaultProgram(); + + void setTexture(GLUniform &uTex); + + void setModelViewProjection(Matrix4f const &mvp); + + void setNormalizedScissor(Rectanglef const &normScissorRect = Rectanglef(0, 0, 1, 1)); + + Rectanglef normalizedScissor() const; + + void setColor(Vector4f const &color); + + void setSaturation(float saturation); + + /** + * Draw a triangle strip. + * + * @param vertices Vertices to draw. Batch indices in the array are updated by + * the draw queue. + */ + void drawTriangleStrip(QVector &vertices); + + void flush(); + + void finish(); + +private: + DENG2_PRIVATE(d) +}; + +} // namespace de + +#endif // LIBAPPFW_PAINTER_H diff --git a/doomsday/sdk/libappfw/include/de/framework/proceduralimage.h b/doomsday/sdk/libappfw/include/de/framework/proceduralimage.h index a2595fdb8b..c34a8fc149 100644 --- a/doomsday/sdk/libappfw/include/de/framework/proceduralimage.h +++ b/doomsday/sdk/libappfw/include/de/framework/proceduralimage.h @@ -21,6 +21,7 @@ #include #include +#include #include "../libappfw.h" @@ -39,7 +40,7 @@ class LIBAPPFW_PUBLIC ProceduralImage public: typedef Vector2f Size; typedef Vector4f Color; - typedef GLBufferT DefaultVertexBuf; + //typedef GLBufferT DefaultVertexBuf; public: ProceduralImage(Size const &size = Size()); @@ -61,7 +62,7 @@ class LIBAPPFW_PUBLIC ProceduralImage virtual void glInit(); // called repeatedly virtual void glDeinit(); - virtual void glMakeGeometry(DefaultVertexBuf::Builder &verts, Rectanglef const &rect) = 0; + virtual void glMakeGeometry(GuiVertexBuilder &verts, Rectanglef const &rect) = 0; DENG2_AS_IS_METHODS() diff --git a/doomsday/sdk/libappfw/include/de/framework/styleproceduralimage.h b/doomsday/sdk/libappfw/include/de/framework/styleproceduralimage.h index 77401426d6..19b7f044a9 100644 --- a/doomsday/sdk/libappfw/include/de/framework/styleproceduralimage.h +++ b/doomsday/sdk/libappfw/include/de/framework/styleproceduralimage.h @@ -82,7 +82,7 @@ class StyleProceduralImage : public ProceduralImage _id = Id::None; } - void glMakeGeometry(DefaultVertexBuf::Builder &verts, Rectanglef const &rect) + void glMakeGeometry(GuiVertexBuilder &verts, Rectanglef const &rect) { if (!_id.isNone()) { diff --git a/doomsday/sdk/libappfw/include/de/widgets/buttonwidget.h b/doomsday/sdk/libappfw/include/de/widgets/buttonwidget.h index 1db32669ec..576fff5594 100644 --- a/doomsday/sdk/libappfw/include/de/widgets/buttonwidget.h +++ b/doomsday/sdk/libappfw/include/de/widgets/buttonwidget.h @@ -132,7 +132,7 @@ public slots: void pressed(); protected: - void updateModelViewProjection(GLUniform &uMvp) override; + bool updateModelViewProjection(Matrix4f &mvp) override; void updateStyle() override; private: diff --git a/doomsday/sdk/libappfw/include/de/widgets/labelwidget.h b/doomsday/sdk/libappfw/include/de/widgets/labelwidget.h index b4777fac03..bfae25d151 100644 --- a/doomsday/sdk/libappfw/include/de/widgets/labelwidget.h +++ b/doomsday/sdk/libappfw/include/de/widgets/labelwidget.h @@ -253,14 +253,22 @@ class LIBAPPFW_PUBLIC LabelWidget : public GuiWidget, public IAssetGroup */ void setAppearanceAnimation(AppearanceAnimation method, TimeDelta const &span = 0.0); - void setShaderId(DotPath const &shaderId); + //void setShaderId(DotPath const &shaderId); - GLProgram &shaderProgram(); + //GLProgram &shaderProgram(); + + /** + * Sets a custom shader used when rendering the widget. + * + * @param program Shader program. Ownership not taken. @c nullptr to use the + * default UI shader. + */ + void setCustomShader(GLProgram *program); // Events. - void viewResized(); - void update(); - void drawContent(); + //void viewResized() override; + void update() override; + void drawContent() override; struct ContentLayout { Rectanglef image; @@ -273,16 +281,17 @@ class LIBAPPFW_PUBLIC LabelWidget : public GuiWidget, public IAssetGroup static LabelWidget *newWithText(String const &label, GuiWidget *parent = 0); protected: - void glInit(); - void glDeinit(); - void glMakeGeometry(DefaultVertexBuf::Builder &verts); - void updateStyle(); + void glInit() override; + void glDeinit() override; + void glMakeGeometry(GuiVertexBuilder &verts) override; + void updateStyle() override; /** * Called before drawing to update the model-view-projection matrix. * Derived classes may override this to set a custom matrix for the label. + * @return @c true, if a customized matrix was set. */ - virtual void updateModelViewProjection(GLUniform &uMvp); + virtual bool updateModelViewProjection(Matrix4f &mvp); private: DENG2_PRIVATE(d) diff --git a/doomsday/sdk/libappfw/include/de/widgets/lineeditwidget.h b/doomsday/sdk/libappfw/include/de/widgets/lineeditwidget.h index 7be00c59f1..9c0190fa63 100644 --- a/doomsday/sdk/libappfw/include/de/widgets/lineeditwidget.h +++ b/doomsday/sdk/libappfw/include/de/widgets/lineeditwidget.h @@ -85,7 +85,7 @@ class LIBAPPFW_PUBLIC LineEditWidget : public GuiWidget, public shell::AbstractL protected: void glInit() override; void glDeinit() override; - void glMakeGeometry(DefaultVertexBuf::Builder &verts) override; + void glMakeGeometry(GuiVertexBuilder &verts) override; void updateStyle() override; int maximumWidth() const override; diff --git a/doomsday/sdk/libappfw/include/de/widgets/panelwidget.h b/doomsday/sdk/libappfw/include/de/widgets/panelwidget.h index abaafd65de..893dff039e 100644 --- a/doomsday/sdk/libappfw/include/de/widgets/panelwidget.h +++ b/doomsday/sdk/libappfw/include/de/widgets/panelwidget.h @@ -137,7 +137,7 @@ public slots: void glInit(); void glDeinit(); void drawContent(); - void glMakeGeometry(DefaultVertexBuf::Builder &verts); + void glMakeGeometry(GuiVertexBuilder &verts); virtual void preparePanelForOpening(); virtual void panelClosing(); diff --git a/doomsday/sdk/libappfw/include/de/widgets/popupmenuwidget.h b/doomsday/sdk/libappfw/include/de/widgets/popupmenuwidget.h index 326d912702..a308a492e7 100644 --- a/doomsday/sdk/libappfw/include/de/widgets/popupmenuwidget.h +++ b/doomsday/sdk/libappfw/include/de/widgets/popupmenuwidget.h @@ -48,7 +48,7 @@ class LIBAPPFW_PUBLIC PopupMenuWidget : public PopupWidget void update() override; protected: - void glMakeGeometry(DefaultVertexBuf::Builder &verts) override; + void glMakeGeometry(GuiVertexBuilder &verts) override; void preparePanelForOpening() override; void panelClosing() override; diff --git a/doomsday/sdk/libappfw/include/de/widgets/popupwidget.h b/doomsday/sdk/libappfw/include/de/widgets/popupwidget.h index 333f518aa7..f961b7a0ff 100644 --- a/doomsday/sdk/libappfw/include/de/widgets/popupwidget.h +++ b/doomsday/sdk/libappfw/include/de/widgets/popupwidget.h @@ -118,7 +118,7 @@ class LIBAPPFW_PUBLIC PopupWidget : public PanelWidget bool handleEvent(Event const &event) override; protected: - void glMakeGeometry(DefaultVertexBuf::Builder &verts) override; + void glMakeGeometry(GuiVertexBuilder &verts) override; void updateStyle() override; void preparePanelForOpening() override; diff --git a/doomsday/sdk/libappfw/include/de/widgets/progresswidget.h b/doomsday/sdk/libappfw/include/de/widgets/progresswidget.h index de85713d30..17f9688530 100644 --- a/doomsday/sdk/libappfw/include/de/widgets/progresswidget.h +++ b/doomsday/sdk/libappfw/include/de/widgets/progresswidget.h @@ -90,7 +90,7 @@ class LIBAPPFW_PUBLIC ProgressWidget : public LabelWidget protected: void glInit(); void glDeinit(); - void glMakeGeometry(DefaultVertexBuf::Builder &verts); + void glMakeGeometry(GuiVertexBuilder &verts); void updateStyle(); private: diff --git a/doomsday/sdk/libappfw/include/de/widgets/scrollareawidget.h b/doomsday/sdk/libappfw/include/de/widgets/scrollareawidget.h index 07a0fe5691..a83130d35f 100644 --- a/doomsday/sdk/libappfw/include/de/widgets/scrollareawidget.h +++ b/doomsday/sdk/libappfw/include/de/widgets/scrollareawidget.h @@ -143,11 +143,11 @@ class LIBAPPFW_PUBLIC ScrollAreaWidget : public GuiWidget */ void enableIndicatorDraw(bool enabled); - void glMakeScrollIndicatorGeometry(DefaultVertexBuf::Builder &verts, + void glMakeScrollIndicatorGeometry(GuiVertexBuilder &verts, Vector2f const &origin = Vector2f(0, 0)); // Events. - void viewResized() override; + //void viewResized() override; void update() override; void drawContent() override; bool handleEvent(Event const &event) override; diff --git a/doomsday/sdk/libappfw/include/de/widgets/sliderwidget.h b/doomsday/sdk/libappfw/include/de/widgets/sliderwidget.h index 60533f62ad..ab98329b27 100644 --- a/doomsday/sdk/libappfw/include/de/widgets/sliderwidget.h +++ b/doomsday/sdk/libappfw/include/de/widgets/sliderwidget.h @@ -63,7 +63,7 @@ class LIBAPPFW_PUBLIC SliderWidget : public GuiWidget ddouble displayFactor() const; // Events. - void viewResized(); +// void viewResized(); void update(); void drawContent(); bool handleEvent(Event const &event); diff --git a/doomsday/sdk/libappfw/src/gltextcomposer.cpp b/doomsday/sdk/libappfw/src/gltextcomposer.cpp index 9a9a459d8e..3d2757ee47 100644 --- a/doomsday/sdk/libappfw/src/gltextcomposer.cpp +++ b/doomsday/sdk/libappfw/src/gltextcomposer.cpp @@ -436,7 +436,7 @@ void GLTextComposer::forceUpdate() d->needRedo = true; } -void GLTextComposer::makeVertices(Vertices &triStrip, +void GLTextComposer::makeVertices(GuiVertexBuilder &triStrip, Vector2i const &topLeft, Alignment const &lineAlign, Vector4f const &color) @@ -444,7 +444,7 @@ void GLTextComposer::makeVertices(Vertices &triStrip, makeVertices(triStrip, Rectanglei(topLeft, topLeft), AlignTopLeft, lineAlign, color); } -void GLTextComposer::makeVertices(Vertices &triStrip, +void GLTextComposer::makeVertices(GuiVertexBuilder &triStrip, Rectanglei const &rect, Alignment const &alignInRect, Alignment const &lineAlign, diff --git a/doomsday/sdk/libappfw/src/guirootwidget.cpp b/doomsday/sdk/libappfw/src/guirootwidget.cpp index a5a681b2f2..27a5d2a243 100644 --- a/doomsday/sdk/libappfw/src/guirootwidget.cpp +++ b/doomsday/sdk/libappfw/src/guirootwidget.cpp @@ -17,17 +17,18 @@ */ #include "de/GuiRootWidget" -#include "de/GuiWidget" #include "de/BaseGuiApp" -#include "de/Style" #include "de/BaseWindow" #include "de/FocusWidget" +#include "de/GuiWidget" +#include "de/Painter" +#include "de/Style" -#include -#include -#include #include #include +#include +#include +#include #include #include @@ -120,6 +121,7 @@ DENG2_PIMPL(GuiRootWidget) QScopedPointer atlas; ///< Shared atlas for most UI graphics/text. GLUniform uTexAtlas; TextureBank texBank; ///< Bank for the atlas contents. + Painter painter; FocusWidget *focusIndicator; bool noFramesDrawnYet; QList *> focusStack; @@ -308,6 +310,11 @@ GLShaderBank &GuiRootWidget::shaders() return BaseGuiApp::shaders(); } +Painter &GuiRootWidget::painter() +{ + return d->painter; +} + Matrix4f GuiRootWidget::projMatrix2D() const { RootWidget::Size const size = viewSize(); @@ -431,16 +438,27 @@ void GuiRootWidget::draw() dsize const depthBeforeDrawing = GLState::stackDepth(); #endif + d->painter.init(); + d->painter.setModelViewProjection(projMatrix2D()); + d->painter.setTexture(uAtlas()); + d->painter.setNormalizedScissor(); + RootWidget::draw(); + d->painter.flush(); + DENG2_ASSERT(GLState::stackDepth() == depthBeforeDrawing); } void GuiRootWidget::drawUntil(Widget &until) { + d->painter.setNormalizedScissor(); + NotifyArgs args = notifyArgsForDraw(); args.until = &until; notifyTree(args); + + d->painter.flush(); } } // namespace de diff --git a/doomsday/sdk/libappfw/src/guiwidget.cpp b/doomsday/sdk/libappfw/src/guiwidget.cpp index df6a1a33f7..90361d7622 100644 --- a/doomsday/sdk/libappfw/src/guiwidget.cpp +++ b/doomsday/sdk/libappfw/src/guiwidget.cpp @@ -43,18 +43,27 @@ DENG2_PIMPL(GuiWidget) , DENG2_OBSERVES(Widget, ParentChange) #endif { + enum + { + Inited = 0x1, + NeedGeometry = 0x2, + StyleChanged = 0x4, + FirstUpdateAfterCreation + = 0x8, + DefaultFlags = NeedGeometry | FirstUpdateAfterCreation, + }; + RuleRectangle rule; ///< Visual rule, used when drawing. std::unique_ptr hitRule; ///< Used only for hit testing. By default matches the visual rule. ui::Margins margins; Rectanglei savedPos; - bool inited; - bool needGeometry; - bool styleChanged; + duint32 flags; Attributes attribs; Background background; Animation opacity; Animation opacityWhenDisabled; - bool firstUpdateAfterCreation; + Rectanglef oldClip; // when drawing children + float saturation = 1.f; QList eventHandlers; // Style. @@ -78,13 +87,10 @@ DENG2_PIMPL(GuiWidget) Impl(Public *i) : Base(i) , margins("gap") - , inited(false) - , needGeometry(true) - , styleChanged(false) + , flags(DefaultFlags) , attribs(DefaultAttributes) , opacity(1.f, Animation::Linear) , opacityWhenDisabled(1.f, Animation::Linear) - , firstUpdateAfterCreation(true) , fontId("default") , textColorId("text") { @@ -113,14 +119,14 @@ DENG2_PIMPL(GuiWidget) * first before beginning destruction. */ #ifdef DENG2_DEBUG - if (inited) qDebug() << "GuiWidget" << thisPublic << self().name() << "is still inited!"; - DENG2_ASSERT(!inited); + if (flags & Inited) qDebug() << "GuiWidget" << thisPublic << self().name() << "is still inited!"; + DENG2_ASSERT(!(flags & Inited)); #endif } void marginsChanged() { - styleChanged = true; + flags |= StyleChanged; } #ifdef DENG2_DEBUG @@ -249,6 +255,7 @@ DENG2_PIMPL(GuiWidget) DENG2_ASSERT(background.blur != 0); if (background.blur) { + self().root().painter().flush(); background.blur->drawBlurredRect(self().rule().recti(), background.solidFill); } return; @@ -262,6 +269,12 @@ DENG2_PIMPL(GuiWidget) return; } + // Ensure normal drawing is complete. + auto &painter = self().root().painter(); + painter.flush(); + + auto const oldClip = painter.normalizedScissor(); + // Make sure blurring is initialized. initBlur(); @@ -304,6 +317,8 @@ DENG2_PIMPL(GuiWidget) { self().drawBlurredRect(self().rule().recti(), blurColor, blurOpacity); } + + painter.setNormalizedScissor(oldClip); } inline float currentOpacity() const @@ -318,7 +333,7 @@ DENG2_PIMPL(GuiWidget) { opacityWhenDisabled.setValue(opac, .3f); } - if (firstUpdateAfterCreation || + if ((flags & FirstUpdateAfterCreation) || !attribs.testFlag(AnimateOpacityWhenEnabledOrDisabled)) { opacityWhenDisabled.finish(); @@ -595,7 +610,7 @@ DotPath const &GuiWidget::textColorId() const void GuiWidget::setFont(DotPath const &id) { d->fontId = id; - d->styleChanged = true; + d->flags |= Impl::StyleChanged; } ColorBank::Color GuiWidget::textColor() const @@ -611,7 +626,7 @@ ColorBank::Colorf GuiWidget::textColorf() const void GuiWidget::setTextColor(DotPath const &id) { d->textColorId = id; - d->styleChanged = true; + d->flags |= Impl::StyleChanged; } RuleRectangle &GuiWidget::rule() @@ -712,6 +727,11 @@ void GuiWidget::set(Background const &bg) requestGeometry(); } +void GuiWidget::setSaturation(float saturation) +{ + d->saturation = saturation; +} + bool GuiWidget::isClipped() const { return behavior().testFlag(ContentClipping); @@ -809,11 +829,11 @@ void GuiWidget::restoreState() void GuiWidget::initialize() { - if (d->inited) return; + if (d->flags & Impl::Inited) return; try { - d->inited = true; + d->flags |= Impl::Inited; glInit(); if (d->attribs.testFlag(RetainStatePersistently)) @@ -830,7 +850,7 @@ void GuiWidget::initialize() void GuiWidget::deinitialize() { - if (!d->inited) return; + if (!(d->flags & Impl::Inited)) return; try { @@ -841,7 +861,7 @@ void GuiWidget::deinitialize() d->saveState(); } - d->inited = false; + applyFlagOperation(d->flags, Impl::Inited, false); d->deinitBlur(); glDeinit(); } @@ -859,13 +879,13 @@ void GuiWidget::viewResized() void GuiWidget::update() { - if (!d->inited) + if (!(d->flags & Impl::Inited)) { initialize(); } - if (d->styleChanged) + if (d->flags & Impl::StyleChanged) { - d->styleChanged = false; + applyFlagOperation(d->flags, Impl::StyleChanged, false); updateStyle(); } auto const familyAttribs = familyAttributes(); @@ -874,33 +894,37 @@ void GuiWidget::update() { d->updateOpacityForDisabledWidgets(); } - d->firstUpdateAfterCreation = false; + applyFlagOperation(d->flags, Impl::FirstUpdateAfterCreation, false); } void GuiWidget::draw() { - if (d->inited && !isHidden() && visibleOpacity() > 0 && !d->isClipCulled()) + if ((d->flags & Impl::Inited) && !isHidden() && visibleOpacity() > 0 && !d->isClipCulled()) { #ifdef DENG2_DEBUG // Detect mistakes in GLState stack usage. dsize const depthBeforeDrawingWidget = GLState::stackDepth(); #endif - d->drawBlurredBackground(); if (!d->attribs.testFlag(DontDrawContent)) { + auto &painter = root().painter(); + painter.setSaturation(d->saturation); + + Rectanglef const oldClip = painter.normalizedScissor(); if (isClipped()) { - GLState::push().setNormalizedScissor(normalizedRect()); + painter.setNormalizedScissor(oldClip & normalizedRect()); } drawContent(); if (isClipped()) { - GLState::pop(); + painter.setNormalizedScissor(oldClip); } + painter.setSaturation(1.f); } DENG2_ASSERT(GLState::stackDepth() == depthBeforeDrawingWidget); @@ -935,7 +959,7 @@ bool GuiWidget::handleEvent(Event const &event) (key.ddKey() == DDKEY_LEFTARROW || key.ddKey() == DDKEY_RIGHTARROW || key.ddKey() == DDKEY_UPARROW || - key.ddKey() == DDKEY_DOWNARROW)) + key.ddKey() == DDKEY_DOWNARROW )) { root().focusIndicator().fadeIn(); root().setFocus(d->findAdjacentWidgetToFocus( @@ -1100,17 +1124,17 @@ void GuiWidget::drawBlurredRect(Rectanglei const &rect, Vector4f const &color, f void GuiWidget::requestGeometry(bool yes) { - d->needGeometry = yes; + applyFlagOperation(d->flags, Impl::NeedGeometry, yes); } bool GuiWidget::geometryRequested() const { - return d->needGeometry; + return (d->flags & Impl::NeedGeometry) != 0; } bool GuiWidget::isInitialized() const { - return d->inited; + return (d->flags & Impl::Inited) != 0; } bool GuiWidget::canBeFocused() const @@ -1146,7 +1170,7 @@ PopupWidget *GuiWidget::findParentPopup() const return nullptr; } -void GuiWidget::glMakeGeometry(DefaultVertexBuf::Builder &verts) +void GuiWidget::glMakeGeometry(GuiVertexBuilder &verts) { auto &rootWgt = root(); float const thick = d->toDevicePixels(d->background.thickness); @@ -1227,7 +1251,7 @@ bool GuiWidget::hasChangedPlace(Rectanglei ¤tPlace) bool GuiWidget::hasBeenUpdated() const { - return !d->firstUpdateAfterCreation; + return !(d->flags & Impl::FirstUpdateAfterCreation); } void GuiWidget::updateStyle() @@ -1242,7 +1266,9 @@ void GuiWidget::preDrawChildren() { if (behavior().testFlag(ChildVisibilityClipping)) { - GLState::push().setNormalizedScissor(normalizedRect()); + auto &painter = root().painter(); + d->oldClip = painter.normalizedScissor(); + painter.setNormalizedScissor(d->oldClip & normalizedRect()); } } @@ -1250,7 +1276,7 @@ void GuiWidget::postDrawChildren() { if (behavior().testFlag(ChildVisibilityClipping)) { - GLState::pop(); + root().painter().setNormalizedScissor(d->oldClip); } // Focus indicator is an overlay. diff --git a/doomsday/sdk/libappfw/src/painter.cpp b/doomsday/sdk/libappfw/src/painter.cpp new file mode 100644 index 0000000000..c4df38352c --- /dev/null +++ b/doomsday/sdk/libappfw/src/painter.cpp @@ -0,0 +1,184 @@ +/** @file painter.cpp GUI painter. + * + * @authors Copyright (c) 2017 Jaakko Keränen + * + * @par License + * LGPL: http://www.gnu.org/licenses/lgpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +#include "de/Painter" +#include "de/BaseGuiApp" + +#include +#include +#include +#include + +namespace de { + +using namespace de::internal; + +DENG2_PIMPL(Painter), public Asset +{ + GLAtlasBuffer vertexBuf; ///< Per-frame allocations. + GLDrawQueue queue; + GLProgram batchProgram; + GLUniform uMvpMatrix { "uMvpMatrix", GLUniform::Mat4 }; + Rectanglef normScissorRect; + + Impl(Public *i) + : Base(i) + , vertexBuf(GuiVertex::formatSpec()) + { + vertexBuf.setUsage(gl::Dynamic); + vertexBuf.setMaxElementCount(2048); + } + + ~Impl() + { + deinit(); + } + + void init() + { + BaseGuiApp::shaders().build(batchProgram, "batch.guiwidget") + << uMvpMatrix; + self().useDefaultProgram(); + setState(true); + } + + void deinit() + { + batchProgram.clear(); + setState(false); + } +}; + +Painter::Painter() + : d(new Impl(this)) +{} + +void Painter::init() +{ + if (!d->isReady()) + { + d->init(); + } +} + +void Painter::deinit() +{ + d->deinit(); +} + +void Painter::setProgram(GLProgram &program) +{ + program << d->uMvpMatrix; + d->queue.setProgram(program); +} + +void Painter::useDefaultProgram() +{ + d->queue.setProgram(d->batchProgram, "uColor", GLUniform::Vec4Array); +} + +void Painter::setTexture(GLUniform &uTex) +{ + flush(); + d->batchProgram << uTex; +} + +void Painter::setModelViewProjection(Matrix4f const &mvp) +{ + flush(); + d->uMvpMatrix = mvp; +} + +void Painter::setNormalizedScissor(Rectanglef const &normScissorRect) +{ + DENG2_ASSERT(normScissorRect.left() >= 0); + DENG2_ASSERT(normScissorRect.right() <= 1); + DENG2_ASSERT(normScissorRect.top() >= 0); + DENG2_ASSERT(normScissorRect.bottom() <= 1); + + d->normScissorRect = normScissorRect; + + Rectangleui const vp = GLState::current().viewport(); + + Rectangleui scis = Rectangleui(Vector2ui(normScissorRect.left() * vp.width(), + normScissorRect.top() * vp.height()), + Vector2ui(std::ceil(normScissorRect.right() * vp.width()), + std::ceil(normScissorRect.bottom() * vp.height()))) + .moved(vp.topLeft); + + scis = GLState::current().target().scaleToActiveRect(scis); + + d->queue.setScissorRect(Vector4f(scis.left(), int(vp.height()) - scis.bottom(), + scis.right(), int(vp.height()) - scis.top())); +} + +Rectanglef Painter::normalizedScissor() const +{ + return d->normScissorRect; +} + +void Painter::setColor(Vector4f const &color) +{ + d->queue.setBufferVector(color); +} + +void Painter::setSaturation(float saturation) +{ + d->queue.setBufferSaturation(saturation); +} + +/*GLDrawQueue &Painter::drawQueue() +{ + return d->queue; +} + +GLAtlasBuffer &Painter::vertexBuffer() +{ + return d->vertexBuf; +}*/ + +void Painter::drawTriangleStrip(QVector &vertices) +{ + DENG2_ASSERT(d->isReady()); + std::unique_ptr sub(d->vertexBuf.alloc(dsize(vertices.size()))); + sub->setBatchVertices(d->queue.batchIndex(), dsize(vertices.size()), &vertices[0]); + d->queue.drawBuffer(*sub); // enqueues indices to be drawn +} + +void Painter::flush() +{ + DENG2_ASSERT(d->isReady()); + d->queue.flush(); +} + +void Painter::finish() +{ + DENG2_ASSERT(d->isReady()); + d->queue.finish(); + d->vertexBuf.clear(); +} + +AttribSpec const GuiVertex::_spec[] = { + { AttribSpec::Position, 2, GL_FLOAT, false, sizeof(GuiVertex), 0 }, + { AttribSpec::TexCoord0, 2, GL_FLOAT, false, sizeof(GuiVertex), 2 * sizeof(float) }, + { AttribSpec::Color, 4, GL_FLOAT, false, sizeof(GuiVertex), 4 * sizeof(float) }, + { AttribSpec::Index, 1, GL_FLOAT, false, sizeof(GuiVertex), 8 * sizeof(float) }, +}; +LIBGUI_VERTEX_FORMAT_SPEC(GuiVertex, 9 * sizeof(float)) + +} // namespace de diff --git a/doomsday/sdk/libappfw/src/widgets/buttonwidget.cpp b/doomsday/sdk/libappfw/src/widgets/buttonwidget.cpp index 3b99304788..1ddb57f15c 100644 --- a/doomsday/sdk/libappfw/src/widgets/buttonwidget.cpp +++ b/doomsday/sdk/libappfw/src/widgets/buttonwidget.cpp @@ -394,19 +394,19 @@ bool ButtonWidget::handleEvent(Event const &event) return LabelWidget::handleEvent(event); } -void ButtonWidget::updateModelViewProjection(GLUniform &uMvp) +bool ButtonWidget::updateModelViewProjection(Matrix4f &mvp) { - uMvp = root().projMatrix2D(); - if (!fequal(d->scale, 1.f)) { Rectanglef const &pos = rule().rect(); // Apply a scale animation to indicate button response. - uMvp = uMvp.toMatrix4f() * - Matrix4f::scaleThenTranslate(d->scale, pos.middle()) * - Matrix4f::translate(-pos.middle()); + mvp = root().projMatrix2D() * + Matrix4f::scaleThenTranslate(d->scale, pos.middle()) * + Matrix4f::translate(-pos.middle()); + return true; } + return false; } void ButtonWidget::updateStyle() diff --git a/doomsday/sdk/libappfw/src/widgets/documentwidget.cpp b/doomsday/sdk/libappfw/src/widgets/documentwidget.cpp index ce55e6a99f..890706fa38 100644 --- a/doomsday/sdk/libappfw/src/widgets/documentwidget.cpp +++ b/doomsday/sdk/libappfw/src/widgets/documentwidget.cpp @@ -24,13 +24,13 @@ namespace de { -static int const ID_BACKGROUND = 1; // does not scroll -static int const ID_TEXT = 2; // scrolls +//static int const ID_BACKGROUND = 1; // does not scroll +//static int const ID_TEXT = 2; // scrolls DENG_GUI_PIMPL(DocumentWidget), public Font::RichFormat::IStyle { - typedef DefaultVertexBuf VertexBuf; + //typedef DefaultVertexBuf VertexBuf; ProgressWidget *progress = nullptr; @@ -51,12 +51,15 @@ public Font::RichFormat::IStyle // GL objects. TextDrawable glText; - Drawable drawable; - Matrix4f modelMatrix; - GLState clippedTextState; - GLUniform uMvpMatrix { "uMvpMatrix", GLUniform::Mat4 }; - GLUniform uScrollMvpMatrix { "uMvpMatrix", GLUniform::Mat4 }; - GLUniform uColor { "uColor", GLUniform::Vec4 }; + //Drawable drawable; + //Matrix4f modelMatrix; + //GLState clippedTextState; + //GLUniform uMvpMatrix { "uMvpMatrix", GLUniform::Mat4 }; + //GLUniform uScrollMvpMatrix { "uMvpMatrix", GLUniform::Mat4 }; + //GLUniform uColor { "uColor", GLUniform::Vec4 }; + GuiVertexBuilder bgVerts; + GuiVertexBuilder textVerts; + Matrix4f scrollMvpMatrix; Impl(Public *i) : Base(i) { @@ -140,7 +143,7 @@ public Font::RichFormat::IStyle self().setIndicatorUv(atlas().imageRectf(root().solidWhitePixel()).middle()); - drawable.addBuffer(ID_BACKGROUND, new VertexBuf); + /*drawable.addBuffer(ID_BACKGROUND, new VertexBuf); drawable.addBuffer(ID_TEXT, new VertexBuf); shaders().build(drawable.program(), "generic.textured.color_ucolor") @@ -149,14 +152,16 @@ public Font::RichFormat::IStyle shaders().build(drawable.addProgram(ID_TEXT), "generic.textured.color_ucolor") << uScrollMvpMatrix << uColor << uAtlas(); drawable.setProgram(ID_TEXT, drawable.program(ID_TEXT)); - drawable.setState(ID_TEXT, clippedTextState); + drawable.setState(ID_TEXT, clippedTextState);*/ } void glDeinit() { atlas().audienceForReposition() -= this; glText.deinit(); - drawable.clear(); + //drawable.clear(); + bgVerts.clear(); + textVerts.clear(); } void atlasContentRepositioned(Atlas &atlas) @@ -208,12 +213,13 @@ public Font::RichFormat::IStyle if (!self().geometryRequested()) return; // Background and scroll indicator. - VertexBuf::Builder verts; - self().glMakeGeometry(verts); - drawable.buffer(ID_BACKGROUND) - .setVertices(gl::TriangleStrip, verts, self().isScrolling()? gl::Dynamic : gl::Static); + bgVerts.clear(); + //VertexBuf::Builder verts; + self().glMakeGeometry(bgVerts); + /*drawable.buffer(ID_BACKGROUND) + .setVertices(gl::TriangleStrip, verts, self().isScrolling()? gl::Dynamic : gl::Static);*/ - uMvpMatrix = root().projMatrix2D(); + //uMvpMatrix = root().projMatrix2D(); if (!progress->isVisible()) { @@ -233,15 +239,16 @@ public Font::RichFormat::IStyle glText.setRange(visRange); glText.update(); // alloc visible lines - VertexBuf::Builder verts; - glText.makeVertices(verts, Vector2i(0, 0), ui::AlignLeft); - drawable.buffer(ID_TEXT).setVertices(gl::TriangleStrip, verts, gl::Static); + //VertexBuf::Builder verts; + textVerts.clear(); + glText.makeVertices(textVerts, Vector2i(0, 0), ui::AlignLeft); + //drawable.buffer(ID_TEXT).setVertices(gl::TriangleStrip, verts, gl::Static); // Update content size to match the generated vertices exactly. self().setContentWidth(glText.verticesMaxWidth()); } - uScrollMvpMatrix = root().projMatrix2D() * + scrollMvpMatrix = root().projMatrix2D() * Matrix4f::translate(Vector2f(self().contentRule().left().valuei(), self().contentRule().top().valuei())); } @@ -254,13 +261,35 @@ public Font::RichFormat::IStyle { updateGeometry(); - uColor = Vector4f(1, 1, 1, self().visibleOpacity()); + //painter.flush(); - // Update the scissor for the text. - clippedTextState = GLState::current(); - clippedTextState.setNormalizedScissor(self().normalizedContentRect()); + Vector4f const color = Vector4f(1, 1, 1, self().visibleOpacity()); - drawable.draw(); + auto &painter = root().painter(); + if (bgVerts) + { + painter.setColor(color); + painter.drawTriangleStrip(bgVerts); + } + if (textVerts) + { + painter.setModelViewProjection(scrollMvpMatrix); + + // Update the scissor for the text. + auto const oldClip = painter.normalizedScissor(); + painter.setNormalizedScissor(oldClip & self().normalizedContentRect()); + //GLState::push() + //clippedTextState = GLState::current(); + /*clippedTextState.setNormalizedScissor(self().normalizedContentRect());*/ + + //drawable.draw(); + painter.setColor(color); + painter.drawTriangleStrip(textVerts); + painter.setNormalizedScissor(oldClip); + painter.setModelViewProjection(root().projMatrix2D()); + } + + //GLState::pop(); } }; @@ -279,10 +308,11 @@ void DocumentWidget::setText(String const &styledText) if (styledText != d->glText.text()) { // Show the progress indicator until the text is ready for drawing. - if (d->drawable.hasBuffer(ID_TEXT)) + d->textVerts.clear(); + /*if (d->drawable.hasBuffer(ID_TEXT)) { d->drawable.buffer(ID_TEXT).clear(); - } + }*/ d->progress->show(); @@ -332,7 +362,7 @@ void DocumentWidget::viewResized() { ScrollAreaWidget::viewResized(); - d->uMvpMatrix = root().projMatrix2D(); + //d->uMvpMatrix = root().projMatrix2D(); requestGeometry(); } diff --git a/doomsday/sdk/libappfw/src/widgets/foldpanelwidget.cpp b/doomsday/sdk/libappfw/src/widgets/foldpanelwidget.cpp index 0300356c44..7605c30ea2 100644 --- a/doomsday/sdk/libappfw/src/widgets/foldpanelwidget.cpp +++ b/doomsday/sdk/libappfw/src/widgets/foldpanelwidget.cpp @@ -48,7 +48,7 @@ DENG2_PIMPL_NOREF(FoldPanelWidget) /// We'll report the status as changed if the image was animating or its /// size was updated. - bool update() + bool update() override { bool changed = animating; @@ -78,7 +78,7 @@ DENG2_PIMPL_NOREF(FoldPanelWidget) return changed; } - void glMakeGeometry(DefaultVertexBuf::Builder &verts, Rectanglef const &rect) + void glMakeGeometry(GuiVertexBuilder &verts, Rectanglef const &rect) override { GuiRootWidget &root = fold.root(); Atlas &atlas = root.atlas(); diff --git a/doomsday/sdk/libappfw/src/widgets/labelwidget.cpp b/doomsday/sdk/libappfw/src/widgets/labelwidget.cpp index 954afae94a..213857a6ba 100644 --- a/doomsday/sdk/libappfw/src/widgets/labelwidget.cpp +++ b/doomsday/sdk/libappfw/src/widgets/labelwidget.cpp @@ -32,8 +32,6 @@ using namespace ui; DENG_GUI_PIMPL(LabelWidget), public Font::RichFormat::IStyle { - typedef DefaultVertexBuf VertexBuf; - AssetGroup assets; SizePolicy horizPolicy; SizePolicy vertPolicy; @@ -77,11 +75,13 @@ public Font::RichFormat::IStyle mutable Vector2ui latestTextSize; bool wasVisible; - QScopedPointer image; - QScopedPointer overlayImage; - Drawable drawable; - GLUniform uMvpMatrix; - GLUniform uColor; + std::unique_ptr image; + std::unique_ptr overlayImage; + GuiVertexBuilder verts; + GLProgram *extProgram; + //Drawable drawable; + //GLUniform uMvpMatrix; + //GLUniform uColor; Impl(Public *i) : Base(i) @@ -104,15 +104,16 @@ public Font::RichFormat::IStyle , shaderId ("generic.textured.color_ucolor") , richStyle (0) , wasVisible (true) - , uMvpMatrix ("uMvpMatrix", GLUniform::Mat4) - , uColor ("uColor", GLUniform::Vec4) + , extProgram (nullptr) + //, uMvpMatrix ("uMvpMatrix", GLUniform::Mat4) + //, uColor ("uColor", GLUniform::Vec4) { width = new ConstantRule(0); height = new ConstantRule(0); minHeight = new IndirectRule; outHeight = new OperatorRule(OperatorRule::Maximum, *height, *minHeight); - uColor = Vector4f(1, 1, 1, 1); + //uColor = Vector4f(1, 1, 1, 1); updateStyle(); // The readiness of the LabelWidget depends on glText being ready. @@ -203,18 +204,18 @@ public Font::RichFormat::IStyle void glInit() { - drawable.addBuffer(new VertexBuf); + /*drawable.addBuffer(new VertexBuf); shaders().build(drawable.program(), shaderId) - << uMvpMatrix << uColor << uAtlas(); + << uMvpMatrix << uColor << uAtlas();*/ glText.init(atlas(), self().font(), this); - if (!image.isNull()) + if (image) { image->glInit(); } - if (!overlayImage.isNull()) + if (overlayImage) { overlayImage->glInit(); } @@ -222,13 +223,14 @@ public Font::RichFormat::IStyle void glDeinit() { - drawable.clear(); + verts.clear(); + //drawable.clear(); glText.deinit(); - if (!image.isNull()) + if (image) { image->glDeinit(); } - if (!overlayImage.isNull()) + if (overlayImage) { overlayImage->glDeinit(); } @@ -236,7 +238,7 @@ public Font::RichFormat::IStyle bool hasImage() const { - return !image.isNull() && image->size() != ProceduralImage::Size(0, 0); + return image && image->size() != ProceduralImage::Size(0, 0); } bool hasText() const @@ -246,7 +248,7 @@ public Font::RichFormat::IStyle Vector2f imageSize() const { - Vector2f size = image.isNull()? Vector2f() : image->size(); + Vector2f size = image? image->size() : Vector2f(); // Override components separately. if (overrideImageSize.x > 0) { @@ -513,11 +515,11 @@ public Font::RichFormat::IStyle void updateGeometry() { // Update the image on the atlas. - if (!image.isNull() && image->update()) + if (image && image->update()) { self().requestGeometry(); } - if (!overlayImage.isNull() && overlayImage->update()) + if (overlayImage && overlayImage->update()) { self().requestGeometry(); } @@ -537,9 +539,10 @@ public Font::RichFormat::IStyle return; } - VertexBuf::Builder verts; + verts.clear(); + //VertexBuf::Builder verts; self().glMakeGeometry(verts); - drawable.buffer().setVertices(gl::TriangleStrip, verts, gl::Static); + //drawable.buffer().setVertices(gl::TriangleStrip, verts, gl::Static); self().requestGeometry(false); } @@ -548,8 +551,34 @@ public Font::RichFormat::IStyle { updateGeometry(); - self().updateModelViewProjection(uMvpMatrix); - drawable.draw(); + //self().updateModelViewProjection(uMvpMatrix); + //drawable.draw(); + + if (verts) + { + auto &painter = root().painter(); + + Matrix4f mvp; + bool const isCustomMvp = self().updateModelViewProjection(mvp); + if (isCustomMvp) + { + painter.setModelViewProjection(mvp); + } + if (extProgram) + { + painter.setProgram(*extProgram); + } + painter.setColor(Vector4f(1, 1, 1, self().visibleOpacity())); + painter.drawTriangleStrip(verts); + if (extProgram) + { + painter.useDefaultProgram(); + } + if (isCustomMvp) + { + painter.setModelViewProjection(self().root().projMatrix2D()); + } + } } Rule const *widthRule() const @@ -634,7 +663,7 @@ void LabelWidget::setStyleImage(DotPath const &id, String const &heightFromFont) ProceduralImage *LabelWidget::image() const { - return d->image.data(); + return d->image.get(); } void LabelWidget::setOverlayImage(ProceduralImage *overlayProcImage, ui::Alignment const &alignment) @@ -806,7 +835,7 @@ void LabelWidget::update() void LabelWidget::drawContent() { - d->uColor = Vector4f(1, 1, 1, visibleOpacity()); + //d->uColor = Vector4f(1, 1, 1, visibleOpacity()); d->draw(); } @@ -825,7 +854,7 @@ void LabelWidget::glDeinit() d->glDeinit(); } -void LabelWidget::glMakeGeometry(DefaultVertexBuf::Builder &verts) +void LabelWidget::glMakeGeometry(GuiVertexBuilder &verts) { // Background/frame. GuiWidget::glMakeGeometry(verts); @@ -859,7 +888,7 @@ void LabelWidget::glMakeGeometry(DefaultVertexBuf::Builder &verts) d->glText.makeVertices(verts, layout.text, d->lineAlign, d->lineAlign, d->textGLColor); } - if (!d->overlayImage.isNull()) + if (d->overlayImage) { Rectanglef rect = Rectanglef::fromSize(d->overlayImage->size()); applyAlignment(d->overlayAlign, rect, contentRect()); @@ -872,16 +901,16 @@ void LabelWidget::updateStyle() d->updateStyle(); } -void LabelWidget::updateModelViewProjection(GLUniform &uMvp) +bool LabelWidget::updateModelViewProjection(Matrix4f &) { - uMvp = root().projMatrix2D(); + return false; } -void LabelWidget::viewResized() +/*void LabelWidget::viewResized() { GuiWidget::viewResized(); updateModelViewProjection(d->uMvpMatrix); -} +}*/ void LabelWidget::setWidthPolicy(SizePolicy policy) { @@ -924,7 +953,12 @@ void LabelWidget::setAppearanceAnimation(AppearanceAnimation method, TimeDelta c } } -void LabelWidget::setShaderId(DotPath const &shaderId) +void LabelWidget::setCustomShader(GLProgram *program) +{ + d->extProgram = program; +} + +/*void LabelWidget::setShaderId(DotPath const &shaderId) { d->shaderId = shaderId; } @@ -932,7 +966,7 @@ void LabelWidget::setShaderId(DotPath const &shaderId) GLProgram &LabelWidget::shaderProgram() { return d->drawable.program(); -} +}*/ LabelWidget *LabelWidget::newWithText(String const &label, GuiWidget *parent) { diff --git a/doomsday/sdk/libappfw/src/widgets/lineeditwidget.cpp b/doomsday/sdk/libappfw/src/widgets/lineeditwidget.cpp index f5ae5e9b6f..b12fa37aa8 100644 --- a/doomsday/sdk/libappfw/src/widgets/lineeditwidget.cpp +++ b/doomsday/sdk/libappfw/src/widgets/lineeditwidget.cpp @@ -38,7 +38,7 @@ static duint const ID_BUF_CURSOR = 2; DENG_GUI_PIMPL(LineEditWidget) { - typedef GLBufferT VertexBuf; + typedef GLBufferT VertexBuf; AnimationRule *height; FontLineWrapping &wraps; @@ -324,7 +324,7 @@ void LineEditWidget::glDeinit() d->glDeinit(); } -void LineEditWidget::glMakeGeometry(DefaultVertexBuf::Builder &verts) +void LineEditWidget::glMakeGeometry(GuiVertexBuilder &verts) { GuiWidget::glMakeGeometry(verts); @@ -385,7 +385,7 @@ void LineEditWidget::focusLost() if (d->hint && d->showingHint()) { - d->hint->setOpacity(1, 1, .5f); + d->hint->setOpacity(1, 1, 0.5); } } @@ -408,6 +408,8 @@ void LineEditWidget::update() void LineEditWidget::drawContent() { + root().painter().flush(); + float const opac = visibleOpacity(); // Blink the cursor. diff --git a/doomsday/sdk/libappfw/src/widgets/logwidget.cpp b/doomsday/sdk/libappfw/src/widgets/logwidget.cpp index 6e2f1b91ed..7989fa5353 100644 --- a/doomsday/sdk/libappfw/src/widgets/logwidget.cpp +++ b/doomsday/sdk/libappfw/src/widgets/logwidget.cpp @@ -42,7 +42,7 @@ DENG_GUI_PIMPL(LogWidget), DENG2_OBSERVES(Atlas, OutOfSpace), public Font::RichFormat::IStyle { - typedef GLBufferT VertexBuf; + typedef GLBufferT VertexBuf; /** * Cached log entry ready for drawing. TextDrawable takes the styled text of the @@ -212,7 +212,7 @@ public Font::RichFormat::IStyle return update() + heightDelta; } - void make(GLTextComposer::Vertices &verts, int y) + void make(GuiVertexBuilder &verts, int y) { DENG2_ASSERT(isReady()); drawable.makeVertices(verts, Vector2i(0, y), AlignLeft); @@ -865,6 +865,7 @@ void LogWidget::update() void LogWidget::drawContent() { + root().painter().flush(); d->draw(); ScrollAreaWidget::drawContent(); } diff --git a/doomsday/sdk/libappfw/src/widgets/panelwidget.cpp b/doomsday/sdk/libappfw/src/widgets/panelwidget.cpp index c187040fa3..e26af66aed 100644 --- a/doomsday/sdk/libappfw/src/widgets/panelwidget.cpp +++ b/doomsday/sdk/libappfw/src/widgets/panelwidget.cpp @@ -36,7 +36,7 @@ static TimeDelta const CLOSING_ANIM_SPAN = 0.3; DENG_GUI_PIMPL(PanelWidget) , DENG2_OBSERVES(Asset, StateChange) { - typedef DefaultVertexBuf VertexBuf; + //typedef DefaultVertexBuf VertexBuf; bool waitForContentReady = true; bool eatMouseEvents = true; @@ -49,8 +49,9 @@ DENG_GUI_PIMPL(PanelWidget) std::unique_ptr pendingShow; // GL objects. - Drawable drawable; - GLUniform uMvpMatrix { "uMvpMatrix", GLUniform::Mat4 }; + GuiVertexBuilder verts; + //Drawable drawable; + //GLUniform uMvpMatrix { "uMvpMatrix", GLUniform::Mat4 }; Impl(Public *i) : Base(i) { @@ -68,14 +69,15 @@ DENG_GUI_PIMPL(PanelWidget) void glInit() { - drawable.addBuffer(new VertexBuf); + /*drawable.addBuffer(new VertexBuf); shaders().build(drawable.program(), "generic.textured.color") - << uMvpMatrix << uAtlas(); + << uMvpMatrix << uAtlas();*/ } void glDeinit() { - drawable.clear(); + //drawable.clear(); + verts.clear(); } bool isVerticalAnimation() const @@ -113,9 +115,10 @@ DENG_GUI_PIMPL(PanelWidget) { self().requestGeometry(false); - VertexBuf::Builder verts; + //VertexBuf::Builder verts; + verts.clear(); self().glMakeGeometry(verts); - drawable.buffer().setVertices(gl::TriangleStrip, verts, gl::Static); + //drawable.buffer().setVertices(gl::TriangleStrip, verts, gl::Static); } } @@ -195,6 +198,7 @@ DENG_GUI_PIMPL(PanelWidget) // We can't delete the AssetGroup right now because we are in the middle // of an audience notification from it. + pendingShow->audienceForStateChange() -= this; trash(pendingShow.release()); } } @@ -299,7 +303,7 @@ void PanelWidget::viewResized() { GuiWidget::viewResized(); - d->uMvpMatrix = root().projMatrix2D(); + //d->uMvpMatrix = root().projMatrix2D(); requestGeometry(); } @@ -387,10 +391,16 @@ void PanelWidget::dismiss() void PanelWidget::drawContent() { d->updateGeometry(); - d->drawable.draw(); + + if (d->verts) + { + auto &painter = root().painter(); + painter.setColor(Vector4f(1, 1, 1, 1)); + painter.drawTriangleStrip(d->verts); + } } -void PanelWidget::glMakeGeometry(DefaultVertexBuf::Builder &verts) +void PanelWidget::glMakeGeometry(GuiVertexBuilder &verts) { GuiWidget::glMakeGeometry(verts); } diff --git a/doomsday/sdk/libappfw/src/widgets/popupmenuwidget.cpp b/doomsday/sdk/libappfw/src/widgets/popupmenuwidget.cpp index 82488094f3..c3ecf3fdc1 100644 --- a/doomsday/sdk/libappfw/src/widgets/popupmenuwidget.cpp +++ b/doomsday/sdk/libappfw/src/widgets/popupmenuwidget.cpp @@ -65,7 +65,7 @@ DENG_GUI_PIMPL(PopupMenuWidget) setSize(Vector2f(1, 1)); } - void glInit() + void glInit() override { if (_id.isNone()) { @@ -73,12 +73,12 @@ DENG_GUI_PIMPL(PopupMenuWidget) } } - void glDeinit() + void glDeinit() override { _id = Id::None; } - void glMakeGeometry(DefaultVertexBuf::Builder &verts, Rectanglef const &rect) + void glMakeGeometry(GuiVertexBuilder &verts, Rectanglef const &rect) override { if (!_id.isNone()) { @@ -455,7 +455,7 @@ void PopupMenuWidget::update() d->updateIfScrolled(); } -void PopupMenuWidget::glMakeGeometry(DefaultVertexBuf::Builder &verts) +void PopupMenuWidget::glMakeGeometry(GuiVertexBuilder &verts) { PopupWidget::glMakeGeometry(verts); diff --git a/doomsday/sdk/libappfw/src/widgets/popupwidget.cpp b/doomsday/sdk/libappfw/src/widgets/popupwidget.cpp index f297041112..2d7fac1d5c 100644 --- a/doomsday/sdk/libappfw/src/widgets/popupwidget.cpp +++ b/doomsday/sdk/libappfw/src/widgets/popupwidget.cpp @@ -412,7 +412,7 @@ bool PopupWidget::handleEvent(Event const &event) return PanelWidget::handleEvent(event); } -void PopupWidget::glMakeGeometry(DefaultVertexBuf::Builder &verts) +void PopupWidget::glMakeGeometry(GuiVertexBuilder &verts) { if (rule().recti().isNull()) return; // Still closed. @@ -422,8 +422,8 @@ void PopupWidget::glMakeGeometry(DefaultVertexBuf::Builder &verts) if (dir == ui::NoDirection) return; // Anchor triangle. - DefaultVertexBuf::Builder tri; - DefaultVertexBuf::Type v; + GuiVertexBuilder tri; + GuiVertex v; v.rgba = background().solidFill; v.texCoord = root().atlas().imageRectf(root().solidWhitePixel()).middle(); diff --git a/doomsday/sdk/libappfw/src/widgets/progresswidget.cpp b/doomsday/sdk/libappfw/src/widgets/progresswidget.cpp index d553fd553d..729b907791 100644 --- a/doomsday/sdk/libappfw/src/widgets/progresswidget.cpp +++ b/doomsday/sdk/libappfw/src/widgets/progresswidget.cpp @@ -30,9 +30,9 @@ DENG_GUI_PIMPL(ProgressWidget), public Lockable Rangei range; Rangef visualRange { 0, 1 }; Animation pos { 0, Animation::Linear }; - bool posChanging = false; float angle = 0; float rotationSpeed = 20; + bool posChanging = false; bool mini = false; Id gearTex; DotPath colorId { "progress.light.wheel" }; @@ -76,7 +76,7 @@ DENG_GUI_PIMPL(ProgressWidget), public Lockable gearTex = Id::None; } - void makeRingGeometry(DefaultVertexBuf::Builder &verts) + void makeRingGeometry(GuiVertexBuilder &verts) { ContentLayout layout; self().contentLayout(layout); @@ -119,8 +119,8 @@ DENG_GUI_PIMPL(ProgressWidget), public Lockable Matrix4f const rotation = Matrix4f::rotateAround(tc.middle(), -angle); - DefaultVertexBuf::Builder gear; - DefaultVertexBuf::Type v; + GuiVertexBuilder gear; + GuiVertex v; v.rgba = style().colors().colorf(colorId) * Vector4f(1, 1, 1, mini? 1.f : 1.9f); for (int i = 0; i <= edgeCount; ++i) @@ -141,7 +141,7 @@ DENG_GUI_PIMPL(ProgressWidget), public Lockable verts += gear; } - void makeDotsGeometry(DefaultVertexBuf::Builder &verts) + void makeDotsGeometry(GuiVertexBuilder &verts) { Image::Size const dotSize = atlas().imageRect(root().tinyDot()).size(); @@ -316,7 +316,7 @@ void ProgressWidget::glDeinit() LabelWidget::glDeinit(); } -void ProgressWidget::glMakeGeometry(DefaultVertexBuf::Builder &verts) +void ProgressWidget::glMakeGeometry(GuiVertexBuilder &verts) { DENG2_GUARD(d); diff --git a/doomsday/sdk/libappfw/src/widgets/scrollareawidget.cpp b/doomsday/sdk/libappfw/src/widgets/scrollareawidget.cpp index 61f7f7e8f5..4427f28362 100644 --- a/doomsday/sdk/libappfw/src/widgets/scrollareawidget.cpp +++ b/doomsday/sdk/libappfw/src/widgets/scrollareawidget.cpp @@ -21,7 +21,6 @@ #include "de/ScrollAreaWidget" -#include #include #include #include @@ -60,9 +59,10 @@ DENG_GUI_PIMPL(ScrollAreaWidget), public Lockable bool scrollBarHover = false; Rectanglef scrollBarVisRect; Rectanglef scrollBarLaneRect; - Drawable drawable; - GLUniform uMvpMatrix { "uMvpMatrix", GLUniform::Mat4 }; - GLUniform uColor { "uColor", GLUniform::Vec4 }; + //Drawable drawable; + //GLUniform uMvpMatrix { "uMvpMatrix", GLUniform::Mat4 }; + //GLUniform uColor { "uColor", GLUniform::Vec4 }; + GuiVertexBuilder verts; Impl(Public *i) : Base(i) { @@ -92,17 +92,18 @@ DENG_GUI_PIMPL(ScrollAreaWidget), public Lockable { if (indicatorDrawEnabled) { - DefaultVertexBuf *buf = new DefaultVertexBuf; + /*DefaultVertexBuf *buf = new DefaultVertexBuf; drawable.addBuffer(buf); shaders().build(drawable.program(), "generic.textured.color_ucolor") - << uMvpMatrix << uAtlas() << uColor; + << uMvpMatrix << uAtlas() << uColor;*/ } } void glDeinit() { - drawable.clear(); + verts.clear(); + //drawable.clear(); } void updateStyle() @@ -618,7 +619,7 @@ void ScrollAreaWidget::glDeinit() d->glDeinit(); } -void ScrollAreaWidget::glMakeScrollIndicatorGeometry(DefaultVertexBuf::Builder &verts, +void ScrollAreaWidget::glMakeScrollIndicatorGeometry(GuiVertexBuilder &verts, Vector2f const &origin) { // Draw the scroll indicator. @@ -641,11 +642,11 @@ void ScrollAreaWidget::glMakeScrollIndicatorGeometry(DefaultVertexBuf::Builder & } } -void ScrollAreaWidget::viewResized() +/*void ScrollAreaWidget::viewResized() { GuiWidget::viewResized(); - d->uMvpMatrix = root().projMatrix2D(); -} + //d->uMvpMatrix = root().projMatrix2D(); +}*/ void ScrollAreaWidget::update() { @@ -675,7 +676,8 @@ void ScrollAreaWidget::drawContent() { if (d->indicatorDrawEnabled) { - d->uColor = Vector4f(1, 1, 1, visibleOpacity()); + auto &painter = root().painter(); + painter.setColor(Vector4f(1, 1, 1, visibleOpacity())); // The indicator is quite simple, so just keep it dynamic. This will // also avoid the need to detect when the indicator is moving and @@ -683,11 +685,15 @@ void ScrollAreaWidget::drawContent() setIndicatorUv(root().atlas().imageRectf(root().solidWhitePixel()).middle()); - DefaultVertexBuf::Builder verts; - glMakeScrollIndicatorGeometry(verts, rule().recti().topLeft + margins().toVector().xy()); - d->drawable.buffer().setVertices(gl::TriangleStrip, verts, gl::Dynamic); + d->verts.clear(); //DefaultVertexBuf::Builder verts; + glMakeScrollIndicatorGeometry(d->verts, rule().recti().topLeft + margins().toVector().xy()); + //d->drawable.buffer().setVertices(gl::TriangleStrip, verts, gl::Dynamic); - d->drawable.draw(); + if (d->verts) + { + //d->drawable.draw(); + painter.drawTriangleStrip(d->verts); + } } } diff --git a/doomsday/sdk/libappfw/src/widgets/sliderwidget.cpp b/doomsday/sdk/libappfw/src/widgets/sliderwidget.cpp index 475ddc0aa5..e425ea3df3 100644 --- a/doomsday/sdk/libappfw/src/widgets/sliderwidget.cpp +++ b/doomsday/sdk/libappfw/src/widgets/sliderwidget.cpp @@ -105,9 +105,10 @@ DENG_GUI_PIMPL(SliderWidget) NUM_LABELS }; TextDrawable labels[NUM_LABELS]; - Drawable drawable; - GLUniform uMvpMatrix; - GLUniform uColor; + GuiVertexBuilder verts; + //Drawable drawable; + //GLUniform uMvpMatrix; + //GLUniform uColor; Impl(Public *i) : Base(i), @@ -117,9 +118,9 @@ DENG_GUI_PIMPL(SliderWidget) precision(0), displayFactor(1), state(Inert), - animating(false), - uMvpMatrix("uMvpMatrix", GLUniform::Mat4), - uColor ("uColor", GLUniform::Vec4) + animating(false) + //uMvpMatrix("uMvpMatrix", GLUniform::Mat4), + //uColor ("uColor", GLUniform::Vec4) { self().setFont("slider.label"); @@ -144,11 +145,11 @@ DENG_GUI_PIMPL(SliderWidget) void glInit() { - DefaultVertexBuf *buf = new DefaultVertexBuf; - drawable.addBuffer(buf); + //DefaultVertexBuf *buf = new DefaultVertexBuf; + //drawable.addBuffer(buf); - shaders().build(drawable.program(), "generic.textured.color_ucolor") - << uMvpMatrix << uColor << uAtlas(); + /*shaders().build(drawable.program(), "generic.textured.color_ucolor") + << uMvpMatrix << uColor << uAtlas();*/ for (int i = 0; i < int(NUM_LABELS); ++i) { @@ -161,7 +162,8 @@ DENG_GUI_PIMPL(SliderWidget) void glDeinit() { - drawable.clear(); + //drawable.clear(); + verts.clear(); for (int i = 0; i < int(NUM_LABELS); ++i) { labels[i].deinit(); @@ -210,7 +212,8 @@ DENG_GUI_PIMPL(SliderWidget) Vector4i const margin = self().margins().toVector(); rect = rect.adjusted(margin.xy(), -margin.zw()); - DefaultVertexBuf::Builder verts; + //DefaultVertexBuf::Builder verts; + verts.clear(); self().glMakeGeometry(verts); // Determine the area where the slider is moving. @@ -279,8 +282,8 @@ DENG_GUI_PIMPL(SliderWidget) state == Grabbed? invTextColor : textColor); } - drawable.buffer() - .setVertices(gl::TriangleStrip, verts, animating? gl::Dynamic : gl::Static); + /*drawable.buffer() + .setVertices(gl::TriangleStrip, verts, animating? gl::Dynamic : gl::Static);*/ self().requestGeometry(false); } @@ -289,8 +292,9 @@ DENG_GUI_PIMPL(SliderWidget) { updateGeometry(); - uColor = Vector4f(1, 1, 1, self().visibleOpacity()); - drawable.draw(); + auto &painter = root().painter(); + painter.setColor(Vector4f(1, 1, 1, self().visibleOpacity())); + painter.drawTriangleStrip(verts); //drawable.draw(); } void setState(State st) @@ -381,7 +385,7 @@ DENG_GUI_PIMPL(SliderWidget) void updateRangeLabels() { labels[Start].setText(minLabel.isEmpty()? QString::number(range.start * displayFactor, 'f', precision) : minLabel); - labels[End].setText(maxLabel.isEmpty()? QString::number(range.end * displayFactor, 'f', precision) : maxLabel); + labels[End] .setText(maxLabel.isEmpty()? QString::number(range.end * displayFactor, 'f', precision) : maxLabel); } void startGrab(MouseEvent const &ev) @@ -539,12 +543,12 @@ ddouble SliderWidget::displayFactor() const return d->displayFactor; } -void SliderWidget::viewResized() +/*void SliderWidget::viewResized() { GuiWidget::viewResized(); d->uMvpMatrix = root().projMatrix2D(); -} +}*/ void SliderWidget::update() { diff --git a/doomsday/sdk/libappfw/src/widgets/togglewidget.cpp b/doomsday/sdk/libappfw/src/widgets/togglewidget.cpp index edbad85185..884bbca77a 100644 --- a/doomsday/sdk/libappfw/src/widgets/togglewidget.cpp +++ b/doomsday/sdk/libappfw/src/widgets/togglewidget.cpp @@ -57,7 +57,7 @@ DENG2_OBSERVES(ButtonWidget, Press) _pos.finish(); } - bool update() + bool update() override { if (_animating) { @@ -67,7 +67,7 @@ DENG2_OBSERVES(ButtonWidget, Press) return false; } - void glMakeGeometry(DefaultVertexBuf::Builder &verts, Rectanglef const &rect) + void glMakeGeometry(GuiVertexBuilder &verts, Rectanglef const &rect) override { float p = _pos; diff --git a/doomsday/tests/test_appfw/net.dengine.test.appfw.pack/shaders.dei b/doomsday/tests/test_appfw/net.dengine.test.appfw.pack/shaders.dei index 5e48d400c3..5345c75665 100644 --- a/doomsday/tests/test_appfw/net.dengine.test.appfw.pack/shaders.dei +++ b/doomsday/tests/test_appfw/net.dengine.test.appfw.pack/shaders.dei @@ -10,6 +10,7 @@ @include @include +@include group vr { group oculusrift { diff --git a/doomsday/tests/test_appfw/net.dengine.test.appfw.pack/shaders/batch.dei b/doomsday/tests/test_appfw/net.dengine.test.appfw.pack/shaders/batch.dei new file mode 100644 index 0000000000..20fcc142a0 --- /dev/null +++ b/doomsday/tests/test_appfw/net.dengine.test.appfw.pack/shaders/batch.dei @@ -0,0 +1,57 @@ +batch { + # Shader for UI widgets. + shader guiwidget { + vertex = " + uniform highp mat4 uMvpMatrix; + uniform highp vec4 uColor[DENG_MAX_BATCH_UNIFORMS]; + uniform highp float uSaturation[DENG_MAX_BATCH_UNIFORMS]; + uniform highp vec4 uScissorRect[DENG_MAX_BATCH_UNIFORMS]; + + attribute highp vec4 aVertex; + attribute highp vec2 aUV; + attribute highp vec4 aColor; + attribute highp float aIndex; // uColor + + varying highp vec2 vUV; + varying highp vec4 vColor; + varying highp vec4 vScissor; + varying highp float vSaturation; + + void main(void) { + gl_Position = uMvpMatrix * aVertex; + vUV = aUV; + + int index = int(aIndex); + vColor = aColor * uColor[index]; + vScissor = uScissorRect[index]; + vSaturation = uSaturation[index]; + }" + include.fragment + fragment = " + uniform sampler2D uTex; + + varying highp vec2 vUV; + varying highp vec4 vColor; + varying highp vec4 vScissor; + varying highp float vSaturation; + + void main(void) { + // Check the scissor first. + if (gl_FragCoord.x < vScissor.x || gl_FragCoord.x > vScissor.z || + gl_FragCoord.y < vScissor.y || gl_FragCoord.y > vScissor.w) { + discard; + } + gl_FragColor = texture2D(uTex, vUV); + + // Optionally adjust color saturation. + if (vSaturation < 1.0) { + highp vec4 hsv = rgbToHsv(gl_FragColor); + hsv.y *= vSaturation; + gl_FragColor = hsvToRgb(hsv); + } + + // Final vertex color. + gl_FragColor *= vColor; + }" + } +} diff --git a/doomsday/tests/test_appfw/net.dengine.test.appfw.pack/shaders/hsv.glsl b/doomsday/tests/test_appfw/net.dengine.test.appfw.pack/shaders/hsv.glsl new file mode 100644 index 0000000000..6c2aaadd4c --- /dev/null +++ b/doomsday/tests/test_appfw/net.dengine.test.appfw.pack/shaders/hsv.glsl @@ -0,0 +1,123 @@ +/* + * The Doomsday Engine Project + * Common OpenGL Shaders: HSV/RGB Color Conversions + * + * Copyright (c) 2016-2017 Jaakko Keränen + * + * @par License + * LGPL: http://www.gnu.org/licenses/lgpl.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. You should have received a copy of + * the GNU Lesser General Public License along with this program; if not, see: + * http://www.gnu.org/licenses + */ + +/** + * Converts an RGBA value to HSVA. Hue uses degrees as the unit. + */ +highp vec4 rgbToHsv(highp vec4 rgb) +{ + highp vec4 hsv; + hsv.a = rgb.a; + + highp float rgbMin = min(min(rgb.r, rgb.g), rgb.b); + highp float rgbMax = max(max(rgb.r, rgb.g), rgb.b); + highp float delta = rgbMax - rgbMin; + + hsv.z = rgbMax; + + if (delta < 0.00001) + { + hsv.xy = vec2(0.0); + return hsv; + } + + if (rgbMax > 0.0) + { + hsv.y = delta / rgbMax; + } + else + { + hsv.xy = vec2(0.0); + return hsv; + } + + if (rgb.r >= rgbMax) + { + hsv.x = (rgb.g - rgb.b) / delta; + } + else + { + if (rgb.g >= rgbMax) + { + hsv.x = 2.0 + (rgb.b - rgb.r) / delta; + } + else + { + hsv.x = 4.0 + (rgb.r - rgb.g) / delta; + } + } + + hsv.x *= 60.0; + if (hsv.x < 0.0) + { + hsv.x += 360.0; + } + return hsv; +} + +highp vec4 hsvToRgb(highp vec4 hsv) +{ + highp vec4 rgb; + rgb.a = hsv.a; + + if (hsv.y <= 0.0) + { + rgb.rgb = vec3(hsv.z); + return rgb; + } + + highp float hh = hsv.x; + if (hh >= 360.0) hh = 0.0; + hh /= 60.0; + + highp float ff = fract(hh); + highp float p = hsv.z * (1.0 - hsv.y); + highp float q = hsv.z * (1.0 - (hsv.y * ff)); + highp float t = hsv.z * (1.0 - (hsv.y * (1.0 - ff))); + + int i = int(hh); + + if (i == 0) + { + rgb.rgb = vec3(hsv.z, t, p); + } + else if (i == 1) + { + rgb.rgb = vec3(q, hsv.z, p); + } + else if (i == 2) + { + rgb.rgb = vec3(p, hsv.z, t); + } + else if (i == 3) + { + rgb.rgb = vec3(p, q, hsv.z); + } + else if (i == 4) + { + rgb.rgb = vec3(t, p, hsv.z); + } + else + { + rgb.rgb = vec3(hsv.z, p, q); + } + + return rgb; +}