From d03291eeb1efbb1005eba5a8a4b2c92ef66d2fd5 Mon Sep 17 00:00:00 2001 From: Mateusz Zalega Date: Thu, 16 Aug 2018 21:44:37 +0200 Subject: gui: improved FPGAViewWidget::paintGL() performance Profiling revealed that memcpy() in QOpenGLBuffer::allocate() had been taking the most time during paintGL() calls. I've been able to take the CPU usage down to about 1/4 of its previous values by caching elements in VBOs and updating them only after subsequent calls to renderGraphicElement(). Signed-off-by: Mateusz Zalega --- common/nextpnr.h | 14 ++++++ gui/fpgaviewwidget.cc | 96 ++++++++++++++++++++++++++++----------- gui/fpgaviewwidget.h | 1 + gui/lineshader.cc | 123 +++++++++++++++++++++++++++++++++----------------- gui/lineshader.h | 32 ++++++------- 5 files changed, 180 insertions(+), 86 deletions(-) diff --git a/common/nextpnr.h b/common/nextpnr.h index 29a85a10..59ae0323 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -157,11 +157,25 @@ struct GraphicElement enum style_t { + STYLE_GRID, STYLE_FRAME, // Static "frame". Contrast between STYLE_INACTIVE and STYLE_ACTIVE STYLE_HIDDEN, // Only display when object is selected or highlighted STYLE_INACTIVE, // Render using low-contrast color STYLE_ACTIVE, // Render using high-contast color + // UI highlight groups + STYLE_HIGHLIGHTED0, + STYLE_HIGHLIGHTED1, + STYLE_HIGHLIGHTED2, + STYLE_HIGHLIGHTED3, + STYLE_HIGHLIGHTED4, + STYLE_HIGHLIGHTED5, + STYLE_HIGHLIGHTED6, + STYLE_HIGHLIGHTED7, + + STYLE_SELECTED, + STYLE_HOVER, + STYLE_MAX } style = STYLE_FRAME; diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index 3b0b3df7..fb5e36b2 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -103,8 +103,21 @@ void FPGAViewWidget::initializeGL() log_error("Could not compile shader.\n"); } initializeOpenGLFunctions(); - glClearColor(colors_.background.red() / 255, colors_.background.green() / 255, colors_.background.blue() / 255, - 0.0); + glClearColor(colors_.background.red() / 255, colors_.background.green() / 255, + colors_.background.blue() / 255, 0.0); + + + { + QMutexLocker locker(&rendererDataLock_); + // Render grid. + auto grid = LineShaderData(); + for (float i = -100.0f; i < 100.0f; i += 1.0f) { + PolyLine(-100.0f, i, 100.0f, i).build(grid); + PolyLine(i, -100.0f, i, 100.0f).build(grid); + } + grid.last_render = 1; + lineShader_.update_vbos(GraphicElement::STYLE_GRID, grid); + } } float FPGAViewWidget::PickedElement::distance(Context *ctx, float wx, float wy) const @@ -185,6 +198,8 @@ void FPGAViewWidget::renderGraphicElement(LineShaderData &out, PickQuadTree::Bou bb.setY0(std::min(bb.y0(), y + el.y1)); bb.setX1(std::max(bb.x1(), x + el.x2)); bb.setY1(std::max(bb.y1(), y + el.y2)); + + out.last_render++; return; } @@ -194,6 +209,8 @@ void FPGAViewWidget::renderGraphicElement(LineShaderData &out, PickQuadTree::Bou bb.setY0(std::min(bb.y0(), y + el.y1)); bb.setX1(std::max(bb.x1(), x + el.x2)); bb.setY1(std::max(bb.y1(), y + el.y2)); + + out.last_render++; return; } } @@ -297,36 +314,40 @@ void FPGAViewWidget::paintGL() float thick11Px = mouseToWorldDimensions(1.1, 0).x(); float thick2Px = mouseToWorldDimensions(2, 0).x(); - // Render grid. - auto grid = LineShaderData(); - for (float i = -100.0f; i < 100.0f; i += 1.0f) { - PolyLine(-100.0f, i, 100.0f, i).build(grid); - PolyLine(i, -100.0f, i, 100.0f).build(grid); - } - // Flags from pipeline. - PassthroughFlags flags; - // Draw grid. - lineShader_.draw(grid, colors_.grid, thick1Px, matrix); - { QMutexLocker locker(&rendererDataLock_); + // Must be called from a thread holding the OpenGL context + update_vbos(); + } - // Render Arch graphics. - lineShader_.draw(rendererData_->gfxByStyle[GraphicElement::STYLE_FRAME], colors_.frame, thick11Px, matrix); - lineShader_.draw(rendererData_->gfxByStyle[GraphicElement::STYLE_HIDDEN], colors_.hidden, thick11Px, matrix); - lineShader_.draw(rendererData_->gfxByStyle[GraphicElement::STYLE_INACTIVE], colors_.inactive, thick11Px, - matrix); - lineShader_.draw(rendererData_->gfxByStyle[GraphicElement::STYLE_ACTIVE], colors_.active, thick11Px, matrix); - - // Draw highlighted items. - for (int i = 0; i < 8; i++) - lineShader_.draw(rendererData_->gfxHighlighted[i], colors_.highlight[i], thick11Px, matrix); + // Render the grid. + lineShader_.draw(GraphicElement::STYLE_GRID, colors_.grid, thick1Px, + matrix); + + // Render Arch graphics. + lineShader_.draw(GraphicElement::STYLE_FRAME, colors_.frame, thick11Px, + matrix); + lineShader_.draw(GraphicElement::STYLE_HIDDEN, colors_.hidden, thick11Px, + matrix); + lineShader_.draw(GraphicElement::STYLE_INACTIVE, colors_.inactive, + thick11Px, matrix); + lineShader_.draw(GraphicElement::STYLE_ACTIVE, colors_.active, thick11Px, + matrix); + + // Draw highlighted items. + for (int i = 0; i < 8; i++) { + GraphicElement::style_t style = (GraphicElement::style_t)( + GraphicElement::STYLE_HIGHLIGHTED0 + i); + lineShader_.draw(style, colors_.highlight[i], thick11Px, matrix); + } - lineShader_.draw(rendererData_->gfxSelected, colors_.selected, thick11Px, matrix); - lineShader_.draw(rendererData_->gfxHovered, colors_.hovered, thick2Px, matrix); + lineShader_.draw(GraphicElement::STYLE_SELECTED, colors_.selected, + thick11Px, matrix); + lineShader_.draw(GraphicElement::STYLE_HOVER, colors_.hovered, + thick2Px, matrix); - flags = rendererData_->flags; - } + // Flags from pipeline. + PassthroughFlags flags = rendererData_->flags; // Check flags passed through pipeline. if (flags.zoomOutbound) { @@ -799,4 +820,25 @@ void FPGAViewWidget::leaveEvent(QEvent *event) pokeRenderer(); } +void FPGAViewWidget::update_vbos() +{ + for (int style = GraphicElement::STYLE_FRAME; style + < GraphicElement::STYLE_HIGHLIGHTED0; + style++) { + lineShader_.update_vbos((enum GraphicElement::style_t)(style), + rendererData_->gfxByStyle[style]); + } + + for (int i = 0; i < 8; i++) { + GraphicElement::style_t style = (GraphicElement::style_t)( + GraphicElement::STYLE_HIGHLIGHTED0 + i); + lineShader_.update_vbos(style, rendererData_->gfxHighlighted[i]); + } + + lineShader_.update_vbos(GraphicElement::STYLE_SELECTED, + rendererData_->gfxSelected); + lineShader_.update_vbos(GraphicElement::STYLE_HOVER, + rendererData_->gfxHovered); +} + NEXTPNR_NAMESPACE_END diff --git a/gui/fpgaviewwidget.h b/gui/fpgaviewwidget.h index 4be41bf5..697ace21 100644 --- a/gui/fpgaviewwidget.h +++ b/gui/fpgaviewwidget.h @@ -304,6 +304,7 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions QVector4D mouseToWorldCoordinates(int x, int y); QVector4D mouseToWorldDimensions(float x, float y); QMatrix4x4 getProjection(void); + void update_vbos(); }; NEXTPNR_NAMESPACE_END diff --git a/gui/lineshader.cc b/gui/lineshader.cc index eba96020..0f762b38 100644 --- a/gui/lineshader.cc +++ b/gui/lineshader.cc @@ -162,75 +162,114 @@ bool LineShader::compile(void) return false; } - if (!vao_.create()) - log_abort(); - vao_.bind(); - - if (!buffers_.position.create()) - log_abort(); - if (!buffers_.normal.create()) - log_abort(); - if (!buffers_.miter.create()) - log_abort(); - if (!buffers_.index.create()) - log_abort(); - + program_->bind(); attributes_.position = program_->attributeLocation("position"); attributes_.normal = program_->attributeLocation("normal"); attributes_.miter = program_->attributeLocation("miter"); uniforms_.thickness = program_->uniformLocation("thickness"); uniforms_.projection = program_->uniformLocation("projection"); uniforms_.color = program_->uniformLocation("color"); + program_->release(); + + for (int style = 0; style < GraphicElement::STYLE_MAX; style++) { + buffers_[style].position = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); + buffers_[style].normal = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); + buffers_[style].miter = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); + buffers_[style].index = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer); + + if (!buffers_[style].vao.create()) + log_abort(); + buffers_[style].vao.bind(); + + if (!buffers_[style].position.create()) + log_abort(); + if (!buffers_[style].normal.create()) + log_abort(); + if (!buffers_[style].miter.create()) + log_abort(); + if (!buffers_[style].index.create()) + log_abort(); + + buffers_[style].position.setUsagePattern(QOpenGLBuffer::StaticDraw); + buffers_[style].normal.setUsagePattern(QOpenGLBuffer::StaticDraw); + buffers_[style].miter.setUsagePattern(QOpenGLBuffer::StaticDraw); + buffers_[style].index.setUsagePattern(QOpenGLBuffer::StaticDraw); + + buffers_[style].position.bind(); + buffers_[style].normal.bind(); + buffers_[style].miter.bind(); + buffers_[style].index.bind(); + + buffers_[style].vao.release(); + } - vao_.release(); return true; } -void LineShader::draw(const LineShaderData &line, const QColor &color, float thickness, const QMatrix4x4 &projection) +void LineShader::update_vbos(enum GraphicElement::style_t style, + const LineShaderData &line) { - auto gl = QOpenGLContext::currentContext()->functions(); - if (line.vertices.size() == 0) + if (buffers_[style].last_vbo_update == line.last_render) return; - vao_.bind(); - program_->bind(); + buffers_[style].last_vbo_update = line.last_render; + + buffers_[style].indices = line.indices.size(); + if (buffers_[style].indices == 0) { + // invalidate buffers + buffers_[style].position.allocate(nullptr, 0); + buffers_[style].normal.allocate(nullptr, 0); + buffers_[style].miter.allocate(nullptr, 0); + buffers_[style].index.allocate(nullptr, 0); + return; + } + + buffers_[style].position.bind(); + buffers_[style].position.allocate(&line.vertices[0], sizeof(Vertex2DPOD) * line.vertices.size()); - buffers_.position.bind(); - buffers_.position.allocate(&line.vertices[0], sizeof(Vertex2DPOD) * line.vertices.size()); + buffers_[style].normal.bind(); + buffers_[style].normal.allocate(&line.normals[0], sizeof(Vertex2DPOD) * line.normals.size()); - buffers_.normal.bind(); - buffers_.normal.allocate(&line.normals[0], sizeof(Vertex2DPOD) * line.normals.size()); + buffers_[style].miter.bind(); + buffers_[style].miter.allocate(&line.miters[0], sizeof(GLfloat) * line.miters.size()); - buffers_.miter.bind(); - buffers_.miter.allocate(&line.miters[0], sizeof(GLfloat) * line.miters.size()); + buffers_[style].index.bind(); + buffers_[style].index.allocate(&line.indices[0], sizeof(GLuint) * line.indices.size()); +} - buffers_.index.bind(); - buffers_.index.allocate(&line.indices[0], sizeof(GLuint) * line.indices.size()); +void LineShader::draw(enum GraphicElement::style_t style, const QColor &color, + float thickness, const QMatrix4x4 &projection) +{ + auto gl = QOpenGLContext::currentContext()->functions(); + if (buffers_[style].indices == 0) + return; + program_->bind(); + buffers_[style].vao.bind(); program_->setUniformValue(uniforms_.projection, projection); program_->setUniformValue(uniforms_.thickness, thickness); program_->setUniformValue(uniforms_.color, color.redF(), color.greenF(), color.blueF(), color.alphaF()); - buffers_.position.bind(); - program_->enableAttributeArray("position"); - gl->glVertexAttribPointer(attributes_.position, 2, GL_FLOAT, GL_FALSE, 0, (void *)0); + buffers_[style].position.bind(); + program_->enableAttributeArray(attributes_.position); + program_->setAttributeBuffer(attributes_.position, GL_FLOAT, 0, 2); - buffers_.normal.bind(); - program_->enableAttributeArray("normal"); - gl->glVertexAttribPointer(attributes_.normal, 2, GL_FLOAT, GL_FALSE, 0, (void *)0); + buffers_[style].normal.bind(); + program_->enableAttributeArray(attributes_.normal); + program_->setAttributeBuffer(attributes_.normal, GL_FLOAT, 0, 2); - buffers_.miter.bind(); - program_->enableAttributeArray("miter"); - gl->glVertexAttribPointer(attributes_.miter, 1, GL_FLOAT, GL_FALSE, 0, (void *)0); + buffers_[style].miter.bind(); + program_->enableAttributeArray(attributes_.miter); + program_->setAttributeBuffer(attributes_.miter, GL_FLOAT, 0, 1); - buffers_.index.bind(); - gl->glDrawElements(GL_TRIANGLES, line.indices.size(), GL_UNSIGNED_INT, (void *)0); + buffers_[style].index.bind(); + gl->glDrawElements(GL_TRIANGLES, buffers_[style].indices, GL_UNSIGNED_INT, (void *)0); - program_->disableAttributeArray("miter"); - program_->disableAttributeArray("normal"); - program_->disableAttributeArray("position"); + program_->disableAttributeArray(attributes_.position); + program_->disableAttributeArray(attributes_.normal); + program_->disableAttributeArray(attributes_.miter); + buffers_[style].vao.release(); program_->release(); - vao_.release(); } NEXTPNR_NAMESPACE_END diff --git a/gui/lineshader.h b/gui/lineshader.h index 3f4c4057..eb0f9e09 100644 --- a/gui/lineshader.h +++ b/gui/lineshader.h @@ -20,12 +20,14 @@ #ifndef LINESHADER_H #define LINESHADER_H +#include #include #include #include #include #include +#include "log.h" #include "nextpnr.h" NEXTPNR_NAMESPACE_BEGIN @@ -49,7 +51,7 @@ struct LineShaderData std::vector miters; std::vector indices; - LineShaderData(void) {} + int last_render = 0; void clear(void) { @@ -142,13 +144,18 @@ class LineShader } attributes_; // GL buffers - struct + struct Buffers { QOpenGLBuffer position; QOpenGLBuffer normal; QOpenGLBuffer miter; QOpenGLBuffer index; - } buffers_; + QOpenGLVertexArrayObject vao; + int indices = 0; + + int last_vbo_update = 0; + }; + std::array buffers_; // GL uniform locations. struct @@ -161,22 +168,9 @@ class LineShader GLuint color; } uniforms_; - QOpenGLVertexArrayObject vao_; - public: LineShader(QObject *parent) : parent_(parent), program_(nullptr) { - buffers_.position = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); - buffers_.position.setUsagePattern(QOpenGLBuffer::StaticDraw); - - buffers_.normal = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); - buffers_.normal.setUsagePattern(QOpenGLBuffer::StaticDraw); - - buffers_.miter = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer); - buffers_.miter.setUsagePattern(QOpenGLBuffer::StaticDraw); - - buffers_.index = QOpenGLBuffer(QOpenGLBuffer::IndexBuffer); - buffers_.index.setUsagePattern(QOpenGLBuffer::StaticDraw); } static constexpr const char *vertexShaderSource_ = @@ -200,8 +194,12 @@ class LineShader // Must be called on initialization. bool compile(void); + void update_vbos(enum GraphicElement::style_t style, + const LineShaderData &line); + // Render a LineShaderData with a given M/V/P transformation. - void draw(const LineShaderData &data, const QColor &color, float thickness, const QMatrix4x4 &projection); + void draw(enum GraphicElement::style_t style, const QColor &color, + float thickness, const QMatrix4x4 &projection); }; NEXTPNR_NAMESPACE_END -- cgit v1.2.3