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 <mateusz@appliedsourcery.com>
This commit is contained in:
Mateusz Zalega 2018-08-16 21:44:37 +02:00
parent b5faa7ad10
commit d03291eeb1
5 changed files with 184 additions and 90 deletions

View File

@ -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;

View File

@ -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,37 +314,41 @@ 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_);
// 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);
lineShader_.draw(rendererData_->gfxSelected, colors_.selected, thick11Px, matrix);
lineShader_.draw(rendererData_->gfxHovered, colors_.hovered, thick2Px, matrix);
flags = rendererData_->flags;
// Must be called from a thread holding the OpenGL context
update_vbos();
}
// 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(GraphicElement::STYLE_SELECTED, colors_.selected,
thick11Px, matrix);
lineShader_.draw(GraphicElement::STYLE_HOVER, colors_.hovered,
thick2Px, matrix);
// Flags from pipeline.
PassthroughFlags flags = rendererData_->flags;
// Check flags passed through pipeline.
if (flags.zoomOutbound) {
// If we're doing init zoomOutbound, make sure we're actually drawing
@ -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

View File

@ -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

View File

@ -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)
{
if (buffers_[style].last_vbo_update == line.last_render)
return;
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_[style].normal.bind();
buffers_[style].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_[style].index.bind();
buffers_[style].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 (line.vertices.size() == 0)
if (buffers_[style].indices == 0)
return;
vao_.bind();
program_->bind();
buffers_.position.bind();
buffers_.position.allocate(&line.vertices[0], sizeof(Vertex2DPOD) * line.vertices.size());
buffers_.normal.bind();
buffers_.normal.allocate(&line.normals[0], sizeof(Vertex2DPOD) * line.normals.size());
buffers_.miter.bind();
buffers_.miter.allocate(&line.miters[0], sizeof(GLfloat) * line.miters.size());
buffers_.index.bind();
buffers_.index.allocate(&line.indices[0], sizeof(GLuint) * line.indices.size());
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

View File

@ -20,12 +20,14 @@
#ifndef LINESHADER_H
#define LINESHADER_H
#include <array>
#include <QOpenGLBuffer>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLWidget>
#include "log.h"
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
@ -49,7 +51,7 @@ struct LineShaderData
std::vector<GLfloat> miters;
std::vector<GLuint> 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, GraphicElement::STYLE_MAX> 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