From bbb418504e2352a564bded5137ea7dc9dd8ab357 Mon Sep 17 00:00:00 2001 From: ravi688 Date: Mon, 30 Dec 2024 03:16:20 +0530 Subject: [PATCH] [SUTK] Fixed inconsistent Renderable::update() calls and UIDriver::render() which led to high GPU usage --- sutk/include/sutk/Activatable.hpp | 2 +- sutk/include/sutk/NotebookView.hpp | 18 +++++++++++++----- sutk/include/sutk/Renderable.hpp | 27 +++++++++++++++++---------- sutk/include/sutk/Text.hpp | 1 - sutk/source/RenderRect.cpp | 4 +++- sutk/source/RenderRectArray.cpp | 4 +++- sutk/source/Renderable.cpp | 17 +++++++++++++++++ sutk/source/SmallText.cpp | 14 +++++++++----- sutk/source/Text.cpp | 6 ++++-- sutk/source/UIDriver.cpp | 8 ++++---- 10 files changed, 71 insertions(+), 30 deletions(-) diff --git a/sutk/include/sutk/Activatable.hpp b/sutk/include/sutk/Activatable.hpp index 7d61ba9f..6ba94dd7 100644 --- a/sutk/include/sutk/Activatable.hpp +++ b/sutk/include/sutk/Activatable.hpp @@ -21,8 +21,8 @@ namespace SUTK public: ~Activatable() noexcept; - // mandatory to be called in overriding method com::Bool isActive() const noexcept { return m_isActive; } + // mandatory to be called in overriding method virtual void setActive(com::Bool isActive) noexcept; // if called for the first time then it allocates memory for OnActiveEvent object diff --git a/sutk/include/sutk/NotebookView.hpp b/sutk/include/sutk/NotebookView.hpp index 3663a976..e565972d 100644 --- a/sutk/include/sutk/NotebookView.hpp +++ b/sutk/include/sutk/NotebookView.hpp @@ -219,21 +219,29 @@ namespace SUTK NotebookPage* getRootPage() noexcept { return getTabBar()->getRootTab() ? getTabBar()->getRootTab()->getPage() : com::null_pointer(); } NotebookPage* getCurrentPage() noexcept { return m_currentPage; } - template - T* createPage(const std::string_view labelStr, NotebookPage* afterPage = com::null_pointer()) noexcept; + template + T* createPage(const std::string_view labelStr, Args&&... args) noexcept; + template + T* createPageAfter(const std::string_view labelStr, NotebookPage* afterPage, Args&&... args) noexcept; void viewPage(NotebookPage* page) noexcept; void removePage(NotebookPage* page) noexcept; void dump() noexcept; }; + + template + T* NotebookView::createPage(const std::string_view labelStr, Args&&... args) noexcept + { + return createPageAfter(labelStr, com::null_pointer(), std::forward(args)...); + } - template - T* NotebookView::createPage(const std::string_view labelStr, NotebookPage* afterPage) noexcept + template + T* NotebookView::createPageAfter(const std::string_view labelStr, NotebookPage* afterPage, Args&&... args) noexcept { // Create Container for the page Container* container = createPageContainer(); // Create Page - T* page = getUIDriver().createObject(container); + T* page = getUIDriver().createObject(container, std::forward(args)...); // Create Tab for the page Tab* tab = createTab(labelStr, page, afterPage ? afterPage->getTab() : com::null_pointer()); // Subscribe to tab select and deselect events to activate or deactivate the associate page diff --git a/sutk/include/sutk/Renderable.hpp b/sutk/include/sutk/Renderable.hpp index 149bcefa..7ca8866c 100644 --- a/sutk/include/sutk/Renderable.hpp +++ b/sutk/include/sutk/Renderable.hpp @@ -21,6 +21,8 @@ namespace SUTK RenderableContainer* m_container; u32 m_drawOrder; bool m_isDrawOrderDirty; + // Initially it is set to false + com::Bool m_isActiveDirty { }; com::Bool m_isRedraw; // Called by UIDriver to reset the redraw flag @@ -49,12 +51,25 @@ namespace SUTK Renderable(UIDriver& driver, RenderableContainer* container = NULL) noexcept; virtual ~Renderable() noexcept; + // Override of Activatable::setActive() + virtual void setActive(com::Bool isActive) noexcept; + // returns true, if GPU side data is out of sync with respect to the CPU side data, otherwise false - virtual bool isDirty() = 0; + // Must be called in the overriding method + virtual bool isDirty(); // updates (copies CPU side data to) GPU side data, and it may also create or recreate exisiting GPU Driver objects - virtual void update() = 0; + // Must be called in the overriding method + virtual void update(); bool isDrawOrderDirty() const noexcept { return m_isDrawOrderDirty; } + com::Bool isActiveDirty() const noexcept { return m_isActiveDirty; } + // Example: + // In Frame 0: + // setActive(com::False) + // isActiveForThisFrame() -> returns com::True + // In Frame 1: + // isActiveForThisFrame() -> returns com::False, given that update() has been called + com::Bool isActiveForThisFrame() const noexcept { return isActiveDirty() || isActive(); } u32 getDrawOrder() const noexcept { return m_drawOrder; } @@ -87,10 +102,6 @@ namespace SUTK GfxDriverRenderable(UIDriver& driver, RenderableContainer* container = NULL) noexcept : Renderable(driver, container), m_handle(GFX_DRIVER_OBJECT_NULL_HANDLE) { } virtual ~GfxDriverRenderable() = default; - // Implementation of Renderable - virtual bool isDirty() = 0; - virtual void update() = 0; - virtual void destroy() { } void setClipRectGlobalCoords(const Rect2Df rect) noexcept; @@ -110,9 +121,5 @@ namespace SUTK GeometryRenderable(UIDriver& driver, RenderableContainer* container = NULL, RenderMode renderMode = RenderMode::Opaque) noexcept : GfxDriverRenderable(driver, container), m_geometry(driver, renderMode) { } virtual ~GeometryRenderable() = default; Geometry& getGeometry() noexcept { return m_geometry; } - - // Implementation of GfxDriverRenderable - virtual bool isDirty() = 0; - virtual void update() = 0; }; } \ No newline at end of file diff --git a/sutk/include/sutk/Text.hpp b/sutk/include/sutk/Text.hpp index 9b79d09f..c8e4c9be 100644 --- a/sutk/include/sutk/Text.hpp +++ b/sutk/include/sutk/Text.hpp @@ -100,7 +100,6 @@ namespace SUTK // distance between two consecutive base lines in centimeters f32 m_baselineHeight; - bool m_isDirty; bool m_isClippingEnabled; // data and rendering attributes diff --git a/sutk/source/RenderRect.cpp b/sutk/source/RenderRect.cpp index 654b549f..1a9d1dae 100644 --- a/sutk/source/RenderRect.cpp +++ b/sutk/source/RenderRect.cpp @@ -37,11 +37,13 @@ namespace SUTK bool RenderRect::isDirty() noexcept { - return isRectDirty() || m_isColorDirty || m_isGeometryDirty; + return GeometryRenderable::isDirty() || isRectDirty() || m_isColorDirty || m_isGeometryDirty; } void RenderRect::update() noexcept { + // Mandatory to be called in the overriding method + GeometryRenderable::update(); if(m_isPosDirty) { // auto& gfxDriver = getGfxDriver(); diff --git a/sutk/source/RenderRectArray.cpp b/sutk/source/RenderRectArray.cpp index a21829be..fefda8b9 100644 --- a/sutk/source/RenderRectArray.cpp +++ b/sutk/source/RenderRectArray.cpp @@ -36,11 +36,13 @@ namespace SUTK bool RenderRectFillArray::isDirty() { - return m_isRectsDirty || m_isColorDirty || m_isActiveDirty; + return GeometryRenderable::isDirty() || m_isRectsDirty || m_isColorDirty || m_isActiveDirty; } void RenderRectFillArray::update() { + // Mandatory to be called in the overriding method + GeometryRenderable::update(); if(m_isActiveDirty) { auto handle = getGfxDriverObjectHandle(); diff --git a/sutk/source/Renderable.cpp b/sutk/source/Renderable.cpp index 5a3f7b92..37326d42 100644 --- a/sutk/source/Renderable.cpp +++ b/sutk/source/Renderable.cpp @@ -33,6 +33,23 @@ namespace SUTK setDrawOrder(getContainer()->getDepth()); } + void Renderable::setActive(com::Bool isActive) noexcept + { + if(Activatable::isActive() != isActive) + m_isActiveDirty = com::True; + Activatable::setActive(isActive); + } + + bool Renderable::isDirty() + { + return static_cast(m_isActiveDirty); + } + + void Renderable::update() + { + m_isActiveDirty = com::False; + } + void GfxDriverRenderable::setClipRectGlobalCoords(const Rect2Df rect) noexcept { auto handle = getGfxDriverObjectHandle(); diff --git a/sutk/source/SmallText.cpp b/sutk/source/SmallText.cpp index 907694b4..91d4cf14 100644 --- a/sutk/source/SmallText.cpp +++ b/sutk/source/SmallText.cpp @@ -35,10 +35,13 @@ namespace SUTK bool SmallText::isDirty() { - return m_isPosDirty || m_isDataDirty || m_isPointSizeDirty || m_isColorRangesDirty || m_isColorDirty; + return GfxDriverRenderable::isDirty() || m_isPosDirty || m_isDataDirty || m_isPointSizeDirty || m_isColorRangesDirty || m_isColorDirty; } void SmallText::update() { + // Mandatory to be called in the overriding method + GfxDriverRenderable::update(); + // NOTE: Order of function calls are important here // should be updated before any updates to character array @@ -64,8 +67,6 @@ namespace SUTK if(m_isPosDirty) { - // This must be set false before calling getAlignedPosition() to avoid non-terminating recursion - m_isPosDirty = false; Vec2Df pos = m_pos; if(getContainer() != NULL) { @@ -73,6 +74,7 @@ namespace SUTK pos = getContainer()->getLocalCoordsToScreenCoords(pos); } getGfxDriver().setTextPosition(getGfxDriverObjectHandle(), { pos.x, pos.y, 0.0f }); + m_isPosDirty = false; } // should be updated after character array has been updated with setTextData(). @@ -122,14 +124,16 @@ namespace SUTK LineCountType SmallText::getColPosFromCoord(f32 coord) noexcept { // Ensure GPU side data is updated so that we get correct output from getTextGlyphIndexFromCoord() - if(isDirty()) + // NOTE: Data (chars) and PointSize both affect the value returned by getTextGlyphIndexFromCoord() + if(m_isDataDirty || m_isPointSizeDirty) update(); return getGfxDriver().getTextGlyphIndexFromCoord(getGfxDriverObjectHandle(), coord); } f32 SmallText::getCoordFromColPos(LineCountType col) noexcept { // Ensure GPU side data is updated so that we get correct output from getTextGlyphIndexFromCoord() - if(isDirty()) + // NOTE: Data (chars) and PointSize both affect the value returned by getTextGlyphIndexFromCoord() + if(m_isDataDirty || m_isPointSizeDirty) update(); return getGfxDriver().getTextCoordFromGlyphIndex(getGfxDriverObjectHandle(), col); } diff --git a/sutk/source/Text.cpp b/sutk/source/Text.cpp index 28b7ff8a..78b0b120 100644 --- a/sutk/source/Text.cpp +++ b/sutk/source/Text.cpp @@ -18,7 +18,7 @@ namespace SUTK template<> CursorPosition CursorPosition::EndOfText() { return { END_OF_TEXT, END_OF_LINE }; } template<> CursorPosition CursorPosition::EndOfLine(LineCountType line) { return { line, END_OF_LINE }; } - Text::Text(UIDriver& driver, RenderableContainer* container) noexcept : Renderable(driver, container), m_textGroup(GFX_DRIVER_OBJECT_NULL_HANDLE), m_baselineHeight(0), m_isDirty(false), m_isClippingEnabled(false), m_color(SUTK::Color4::yellow()), m_pointSize(12) + Text::Text(UIDriver& driver, RenderableContainer* container) noexcept : Renderable(driver, container), m_textGroup(GFX_DRIVER_OBJECT_NULL_HANDLE), m_baselineHeight(0), m_isClippingEnabled(false), m_color(SUTK::Color4::yellow()), m_pointSize(12) { m_textGroup = getGfxDriver().createTextGroup(RenderMode::Transparent); m_baselineHeight = getGfxDriver().getTextGroupBaselineHeightInCentimeters(m_textGroup, m_pointSize); @@ -50,7 +50,7 @@ namespace SUTK bool Text::isDirty() { - if(m_isDirty) + if(Renderable::isDirty()) return true; for(std::size_t i = 0; i < m_lines.size(); i++) @@ -62,6 +62,8 @@ namespace SUTK void Text::update() { + // Mandatory to be called in the overriding method + Renderable::update(); for(std::size_t i = 0; i < m_lines.size(); i++) { LineText* line = m_lines[i]; diff --git a/sutk/source/UIDriver.cpp b/sutk/source/UIDriver.cpp index 7dde044d..57d164ec 100644 --- a/sutk/source/UIDriver.cpp +++ b/sutk/source/UIDriver.cpp @@ -95,17 +95,17 @@ namespace SUTK for(auto it = m_renderables.begin(); it != m_renderables.end(); it++) { Renderable* renderable = *it; - if(!isRerender && renderable->isRedraw()) + if(!isRerender && renderable->isActiveForThisFrame() && renderable->isRedraw()) { isRerender = com::True; renderable->setRedraw(com::False); } // Only update Renderables which are active and dirty - if(renderable->isActive() && renderable->isDirty()) + if(renderable->isActiveForThisFrame() && renderable->isDirty()) renderable->update(); // If at least Renderable's draw order is dirty then normalized draw orders need to be re-calculated // and passed to the GPU driver - if(!isRecalculateDrawOrder && renderable->isDrawOrderDirty()) + if(!isRecalculateDrawOrder && renderable->isActiveForThisFrame() && renderable->isDrawOrderDirty()) isRecalculateDrawOrder = com::True; // Keep track of minimum and maximum draw order values u32 drawOrder = renderable->getDrawOrder(); @@ -134,7 +134,7 @@ namespace SUTK for(auto it = m_renderables.begin(); it != m_renderables.end(); it++) { Renderable* renderable = (*it); - if(isDrawOrderRangeChanged || renderable->isDrawOrderDirty()) + if(isDrawOrderRangeChanged || (renderable->isActiveForThisFrame() && renderable->isDrawOrderDirty())) { auto normDrawOrder = drawOrderRange ? ((1.0f - static_cast(renderable->getDrawOrder() - minDrawOrder) * normalizeFactor) * 99.0f) : 0; renderable->updateNormalizedDrawOrder(normDrawOrder);