diff options
author | David Shah <davey1576@gmail.com> | 2018-06-22 18:35:18 +0200 |
---|---|---|
committer | David Shah <davey1576@gmail.com> | 2018-06-22 18:35:18 +0200 |
commit | 6a783ef94f31eb47cbf6c14b0e8793638d90dc88 (patch) | |
tree | 1c9f00927bc4260ef8041bb1b2901186e3738b4e | |
parent | 60e885d3421fb4401ebe046daf5a4a7e9ae08f87 (diff) | |
parent | 5dfe1969af330b61abdc77284ddc9cd60562c9a3 (diff) | |
download | nextpnr-6a783ef94f31eb47cbf6c14b0e8793638d90dc88.tar.gz nextpnr-6a783ef94f31eb47cbf6c14b0e8793638d90dc88.tar.bz2 nextpnr-6a783ef94f31eb47cbf6c14b0e8793638d90dc88.zip |
Merge branch 'master' of gitlab.com:SymbioticEDA/nextpnr
-rw-r--r-- | gui/designwidget.cc | 14 | ||||
-rw-r--r-- | gui/fpgaviewwidget.cc | 390 | ||||
-rw-r--r-- | gui/fpgaviewwidget.h | 207 | ||||
-rw-r--r-- | ice40/chipdb.py | 2 | ||||
-rw-r--r-- | ice40/main.cc | 1 |
5 files changed, 501 insertions, 113 deletions
diff --git a/gui/designwidget.cc b/gui/designwidget.cc index cc161c9e..0f87f06c 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -117,7 +117,7 @@ DesignWidget::DesignWidget(Context *_ctx, QWidget *parent) for (auto bel : ctx->getBels()) {
auto name = ctx->getBelName(bel);
bel_items.append(
- new BelTreeItem(name, ElementType::BEL, QString(name.c_str())));
+ new BelTreeItem(name, ElementType::BEL, QString(name.c_str(ctx))));
}
bel_root->addChildren(bel_items);
@@ -129,7 +129,7 @@ DesignWidget::DesignWidget(Context *_ctx, QWidget *parent) for (auto wire : ctx->getWires()) {
auto name = ctx->getWireName(wire);
wire_items.append(new WireTreeItem(name, ElementType::WIRE,
- QString(name.c_str())));
+ QString(name.c_str(ctx))));
}
wire_root->addChildren(wire_items);
@@ -141,7 +141,7 @@ DesignWidget::DesignWidget(Context *_ctx, QWidget *parent) for (auto pip : ctx->getPips()) {
auto name = ctx->getPipName(pip);
pip_items.append(
- new PipTreeItem(name, ElementType::PIP, QString(name.c_str())));
+ new PipTreeItem(name, ElementType::PIP, QString(name.c_str(ctx))));
}
pip_root->addChildren(pip_items);
@@ -184,7 +184,7 @@ void DesignWidget::addProperty(QtVariantProperty *property, const QString &id) {
propertyToId[property] = id;
idToProperty[id] = property;
- QtBrowserItem *item = propertyEditor->addProperty(property);
+ propertyEditor->addProperty(property);
}
void DesignWidget::clearProperties()
@@ -213,14 +213,14 @@ void DesignWidget::onItemClicked(QTreeWidgetItem *item, int pos) QtVariantProperty *topItem =
variantManager->addProperty(QVariant::String, QString("Name"));
- topItem->setValue(QString(c.c_str()));
+ topItem->setValue(QString(c.c_str(ctx)));
addProperty(topItem, QString("Name"));
} else if (type == ElementType::WIRE) {
IdString c = static_cast<WireTreeItem *>(item)->getData();
QtVariantProperty *topItem =
variantManager->addProperty(QVariant::String, QString("Name"));
- topItem->setValue(QString(c.c_str()));
+ topItem->setValue(QString(c.c_str(ctx)));
addProperty(topItem, QString("Name"));
} else if (type == ElementType::PIP) {
@@ -228,7 +228,7 @@ void DesignWidget::onItemClicked(QTreeWidgetItem *item, int pos) QtVariantProperty *topItem =
variantManager->addProperty(QVariant::String, QString("Name"));
- topItem->setValue(QString(c.c_str()));
+ topItem->setValue(QString(c.c_str(ctx)));
addProperty(topItem, QString("Name"));
}
}
diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index 8119eae3..f9cfc900 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -1,15 +1,221 @@ -#include "fpgaviewwidget.h" +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include <cstdio> +#include <math.h> + #include <QApplication> #include <QCoreApplication> #include <QMouseEvent> #include <QWidget> -#include <math.h> + +#include "fpgaviewwidget.h" +#include "log.h" #include "mainwindow.h" NEXTPNR_NAMESPACE_BEGIN +void PolyLine::buildPoint(LineShaderData *building, const QVector2D *prev, + const QVector2D *cur, const QVector2D *next) const +{ + // buildPoint emits two vertices per line point, along with normals to move + // them the right directio when rendering and miter to compensate for + // bends. + + if (cur == nullptr) { + // BUG + return; + } + + if (prev == nullptr && next == nullptr) { + // BUG + return; + } + + // TODO(q3k): fast path for vertical/horizontal lines? + + // TODO(q3k): consider moving some of the linear algebra to the GPU, + // they're better at this than poor old CPUs. + + // Build two unit vectors pointing in the direction of the two segments + // defined by (prev, cur) and (cur, next) + QVector2D dprev, dnext; + if (prev == nullptr) { + dnext = *next - *cur; + dprev = dnext; + } else if (next == nullptr) { + dprev = *cur - *prev; + dnext = dprev; + } else { + dprev = *cur - *prev; + dnext = *next - *cur; + } + dprev.normalize(); + dnext.normalize(); + + // Calculate tangent unit vector. + QVector2D tangent(dprev + dnext); + tangent.normalize(); + + // Calculate normal to tangent - this is the line on which the vectors need + // to be pushed to build a thickened line. + const QVector2D tangent_normal = QVector2D(-tangent.y(), tangent.x()); + + // Calculate normal to one of the lines. + const QVector2D dprev_normal = QVector2D(-dprev.y(), dprev.x()); + // https://people.eecs.berkeley.edu/~sequin/CS184/IMGS/Sweep_PolyLine.jpg + // (the ^-1 is performed in the shader) + const float miter = QVector2D::dotProduct(tangent_normal, dprev_normal); + + const float x = cur->x(); + const float y = cur->y(); + const float mx = tangent_normal.x(); + const float my = tangent_normal.y(); + + // Push back 'left' vertex. + building->vertices.push_back(Vertex2DPOD(x, y)); + building->normals.push_back(Vertex2DPOD(mx, my)); + building->miters.push_back(miter); + + // Push back 'right' vertex. + building->vertices.push_back(Vertex2DPOD(x, y)); + building->normals.push_back(Vertex2DPOD(mx, my)); + building->miters.push_back(-miter); +} + +void PolyLine::build(LineShaderData &target) const +{ + if (points_.size() < 2) { + return; + } + const QVector2D *first = &points_.front(); + const QVector2D *last = &points_.back(); + + // Index number of vertices, used to build the index buffer. + unsigned int startIndex = target.vertices.size(); + unsigned int index = startIndex; + + // For every point on the line, call buildPoint with (prev, point, next). + // If we're building a closed line, prev/next wrap around. Otherwise + // they are passed as nullptr and buildPoint interprets that accordinglu. + const QVector2D *prev = nullptr; + + // Loop iterator used to ensure next is valid. + unsigned int i = 0; + for (const QVector2D &point : points_) { + const QVector2D *next = nullptr; + if (++i < points_.size()) { + next = (&point + 1); + } + + // If the line is closed, wrap around. Otherwise, pass nullptr. + if (prev == nullptr && closed_) { + buildPoint(&target, last, &point, next); + } else if (next == nullptr && closed_) { + buildPoint(&target, prev, &point, first); + } else { + buildPoint(&target, prev, &point, next); + } + + // If we have a prev point relative to cur, build a pair of triangles + // to render vertices into lines. + if (prev != nullptr) { + target.indices.push_back(index); + target.indices.push_back(index + 1); + target.indices.push_back(index + 2); + + target.indices.push_back(index + 2); + target.indices.push_back(index + 1); + target.indices.push_back(index + 3); + + index += 2; + } + prev = &point; + } + + // If we're closed, build two more vertices that loop the line around. + if (closed_) { + target.indices.push_back(index); + target.indices.push_back(index + 1); + target.indices.push_back(startIndex); + + target.indices.push_back(startIndex); + target.indices.push_back(index + 1); + target.indices.push_back(startIndex + 1); + } +} + +bool LineShader::compile(void) +{ + program_ = new QOpenGLShaderProgram(parent_); + program_->addShaderFromSourceCode(QOpenGLShader::Vertex, + vertexShaderSource_); + program_->addShaderFromSourceCode(QOpenGLShader::Fragment, + fragmentShaderSource_); + if (!program_->link()) { + printf("could not link program: %s\n", + program_->log().toStdString().c_str()); + return false; + } + 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"); + + return true; +} + +void LineShader::draw(const LineShaderData &line, const QMatrix4x4 &projection) +{ + auto gl = QOpenGLContext::currentContext()->functions(); + program_->bind(); + + program_->setUniformValue(uniforms_.projection, projection); + program_->setUniformValue(uniforms_.thickness, line.thickness); + program_->setUniformValue(uniforms_.color, line.color.r, line.color.g, + line.color.b, line.color.a); + + gl->glVertexAttribPointer(attributes_.position, 2, GL_FLOAT, GL_FALSE, 0, + &line.vertices[0]); + gl->glVertexAttribPointer(attributes_.normal, 2, GL_FLOAT, GL_FALSE, 0, + &line.normals[0]); + gl->glVertexAttribPointer(attributes_.miter, 1, GL_FLOAT, GL_FALSE, 0, + &line.miters[0]); + + gl->glEnableVertexAttribArray(0); + gl->glEnableVertexAttribArray(1); + gl->glEnableVertexAttribArray(2); + + gl->glDrawElements(GL_TRIANGLES, line.indices.size(), GL_UNSIGNED_INT, + &line.indices[0]); + + gl->glDisableVertexAttribArray(2); + gl->glDisableVertexAttribArray(1); + gl->glDisableVertexAttribArray(0); + program_->release(); +} + FPGAViewWidget::FPGAViewWidget(QWidget *parent) - : QOpenGLWidget(parent), m_xMove(0), m_yMove(0), m_zDistance(1.0) + : QOpenGLWidget(parent), moveX_(0), moveY_(0), zoom_(10.0f), + lineShader_(this) { ctx = qobject_cast<BaseMainWindow *>(getMainWindow())->getContext(); } @@ -31,139 +237,127 @@ QSize FPGAViewWidget::sizeHint() const { return QSize(640, 480); } void FPGAViewWidget::setXTranslation(float t_x) { - if (t_x != m_xMove) { - m_xMove = t_x; - update(); - } + if (t_x == moveX_) + return; + + moveX_ = t_x; + update(); } void FPGAViewWidget::setYTranslation(float t_y) { - if (t_y != m_yMove) { - m_yMove = t_y; - update(); - } + if (t_y == moveY_) + return; + + moveY_ = t_y; + update(); } void FPGAViewWidget::setZoom(float t_z) { - if (t_z != m_zDistance) { - m_zDistance -= t_z; - if (m_zDistance < 0.1f) - m_zDistance = 0.1f; - if (m_zDistance > 10.0f) - m_zDistance = 10.0f; - - update(); - } + if (t_z == zoom_) + return; + zoom_ = t_z; + + if (zoom_ < 1.0f) + zoom_ = 1.0f; + if (zoom_ > 100.f) + zoom_ = 100.0f; + + update(); } void FPGAViewWidget::initializeGL() { + if (!lineShader_.compile()) { + log_error("Could not compile shader.\n"); + } initializeOpenGLFunctions(); glClearColor(1.0, 1.0, 1.0, 0.0); } -void FPGAViewWidget::drawElement(const GraphicElement &el) +void FPGAViewWidget::drawElement(LineShaderData &out, const GraphicElement &el) { - float scale = 1.0, offset = 0.0; - if (el.type == GraphicElement::G_BOX) { - glBegin(GL_LINES); - glVertex3f((offset + scale * el.x1), (offset + scale * el.y1), 0.0f); - glVertex3f((offset + scale * el.x2), (offset + scale * el.y1), 0.0f); - - glVertex3f((offset + scale * el.x2), (offset + scale * el.y1), 0.0f); - glVertex3f((offset + scale * el.x2), (offset + scale * el.y2), 0.0f); + const float scale = 1.0, offset = 0.0; - glVertex3f((offset + scale * el.x2), (offset + scale * el.y2), 0.0f); - glVertex3f((offset + scale * el.x1), (offset + scale * el.y2), 0.0f); - - glVertex3f((offset + scale * el.x1), (offset + scale * el.y2), 0.0f); - glVertex3f((offset + scale * el.x1), (offset + scale * el.y1), 0.0f); - glEnd(); + if (el.type == GraphicElement::G_BOX) { + auto line = PolyLine(true); + line.point(offset + scale * el.x1, offset + scale * el.y1); + line.point(offset + scale * el.x2, offset + scale * el.y1); + line.point(offset + scale * el.x2, offset + scale * el.y2); + line.point(offset + scale * el.x1, offset + scale * el.y2); + line.build(out); } if (el.type == GraphicElement::G_LINE) { - glBegin(GL_LINES); - glVertex3f((offset + scale * el.x1), (offset + scale * el.y1), 0.0f); - glVertex3f((offset + scale * el.x2), (offset + scale * el.y2), 0.0f); - glEnd(); + PolyLine(offset + scale * el.x1, offset + scale * el.y1, + offset + scale * el.x2, offset + scale * el.y2) + .build(out); } } void FPGAViewWidget::paintGL() { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glLoadIdentity(); - - glTranslatef(m_xMove, m_yMove, -10.0); - glScalef(m_zDistance, m_zDistance, 0.0f); - - // Grid - glColor3f(0.9, 0.9, 0.9); - glBegin(GL_LINES); - for (float i = -100; i <= 100; i += 0.1) { - glVertex3f((float)i, -100.0f, 0.0f); - glVertex3f((float)i, 100.0f, 0.0f); - glVertex3f(-100.0f, (float)i, 0.0f); - glVertex3f(100.0f, (float)i, 0.0f); - } - glColor3f(0.7, 0.7, 0.7); - for (int i = -100; i <= 100; i += 1) { - glVertex3f((float)i, -100.0f, 0.0f); - glVertex3f((float)i, 100.0f, 0.0f); - glVertex3f(-100.0f, (float)i, 0.0f); - glVertex3f(100.0f, (float)i, 0.0f); + auto gl = QOpenGLContext::currentContext()->functions(); + const qreal retinaScale = devicePixelRatio(); + gl->glViewport(0, 0, width() * retinaScale, height() * retinaScale); + gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + const float aspect = float(width()) / float(height()); + + QMatrix4x4 matrix; + matrix.ortho(QRectF(-aspect / 2.0, -0.5, aspect, 1.0f)); + matrix.translate(moveX_, moveY_, -0.5); + matrix.scale(zoom_ * 0.01f, zoom_ * 0.01f, 0); + + // Draw grid. + auto grid = LineShaderData(0.01f, QColor("#DDD")); + 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); } - glEnd(); + lineShader_.draw(grid, matrix); - glColor3f(0.1, 0.1, 0.1); - glLineWidth(0.1); - // Draw Bels + // Draw Bels. + auto bels = LineShaderData(0.02f, QColor("#b000ba")); for (auto bel : ctx->getBels()) { for (auto &el : ctx->getBelGraphics(bel)) - drawElement(el); + drawElement(bels, el); } - // Draw Frame Graphics - for (auto &el : ctx->getFrameGraphics()) - drawElement(el); -} + lineShader_.draw(bels, matrix); -void FPGAViewWidget::resizeGL(int width, int height) -{ - m_windowWidth = width; - m_windowHeight = height; - glViewport(0, 0, m_windowWidth, m_windowHeight); - - float aspect = width * 1.0 / height; - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(-1.0 * aspect, +1.0 * aspect, -1.0, +1.0, 1.0, 15.0); - glMatrixMode(GL_MODELVIEW); + // Draw Frame Graphics. + auto frames = LineShaderData(0.02f, QColor("#0066ba")); + for (auto &el : ctx->getFrameGraphics()) { + drawElement(frames, el); + } + lineShader_.draw(frames, matrix); } +void FPGAViewWidget::resizeGL(int width, int height) {} + void FPGAViewWidget::mousePressEvent(QMouseEvent *event) { - m_lastPos = event->pos(); + startDragX_ = moveX_; + startDragY_ = moveY_; + lastPos_ = event->pos(); } void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event) { - int dx = event->x() - m_lastPos.x(); - int dy = event->y() - m_lastPos.y(); - float dx_scale = dx * (1 / (float)640); - float dy_scale = -dy * (1 / (float)480); - - if (event->buttons() & Qt::LeftButton) { - float xpos = m_xMove + dx_scale; - float ypos = m_yMove + dy_scale; - if (m_xMove / m_zDistance <= 100.0 && m_xMove / m_zDistance >= -100.0) - setXTranslation(xpos); - if (m_yMove / m_zDistance <= 100.0 && m_yMove / m_zDistance >= -100.0) - setYTranslation(ypos); - } - m_lastPos = event->pos(); + const int dx = event->x() - lastPos_.x(); + const int dy = event->y() - lastPos_.y(); + + const qreal retinaScale = devicePixelRatio(); + float aspect = float(width()) / float(height()); + const float dx_scale = dx * (1 / (float)width() * retinaScale * aspect); + const float dy_scale = dy * (1 / (float)height() * retinaScale); + + float xpos = dx_scale + startDragX_; + float ypos = dy_scale + startDragY_; + + setXTranslation(xpos); + setYTranslation(ypos); } void FPGAViewWidget::wheelEvent(QWheelEvent *event) @@ -171,8 +365,8 @@ void FPGAViewWidget::wheelEvent(QWheelEvent *event) QPoint degree = event->angleDelta() / 8; if (!degree.isNull()) { - QPoint step = degree / 15; - setZoom(step.y() * -0.1f); + float steps = degree.y() / 15.0; + setZoom(zoom_ + steps); } } diff --git a/gui/fpgaviewwidget.h b/gui/fpgaviewwidget.h index fc3ca562..0cfcbb3e 100644 --- a/gui/fpgaviewwidget.h +++ b/gui/fpgaviewwidget.h @@ -1,14 +1,205 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + #ifndef MAPGLWIDGET_H #define MAPGLWIDGET_H #include <QMainWindow> +#include <QOpenGLBuffer> #include <QOpenGLFunctions> +#include <QOpenGLShaderProgram> #include <QOpenGLWidget> #include <QPainter> + #include "nextpnr.h" NEXTPNR_NAMESPACE_BEGIN +// Vertex2DPOD is a structure of X, Y coordinates that can be passed to OpenGL +// directly. +struct Vertex2DPOD +{ + GLfloat x; + GLfloat y; + + Vertex2DPOD(GLfloat X, GLfloat Y) : x(X), y(Y) {} +} __attribute__((packed)); + +// Vertex2DPOD is a structure of R, G, B, A values that can be passed to OpenGL +// directly. +struct ColorPOD +{ + GLfloat r; + GLfloat g; + GLfloat b; + GLfloat a; + + ColorPOD(GLfloat R, GLfloat G, GLfloat B, GLfloat A) + : r(R), g(G), b(B), a(A) + { + } + ColorPOD(const QColor &color) + : r(color.redF()), g(color.greenF()), b(color.blueF()), + a(color.alphaF()) + { + } +} __attribute__((packed)); + +// LineShaderData is a built set of vertices that can be rendered by the +// LineShader. +// Each LineShaderData can have its' own color and thickness. +struct LineShaderData +{ + std::vector<Vertex2DPOD> vertices; + std::vector<Vertex2DPOD> normals; + std::vector<GLfloat> miters; + std::vector<GLuint> indices; + + GLfloat thickness; + ColorPOD color; + + LineShaderData(GLfloat Thickness, QColor Color) + : thickness(Thickness), color(Color) + { + } +}; + +// PolyLine is a set of segments defined by points, that can be built to a +// ShaderLine for GPU rendering. +class PolyLine +{ + private: + std::vector<QVector2D> points_; + bool closed_; + + void buildPoint(LineShaderData *building, const QVector2D *prev, + const QVector2D *cur, const QVector2D *next) const; + + public: + // Create an empty PolyLine. + PolyLine(bool closed = false) : closed_(closed) {} + + // Create a non-closed polyline consisting of one segment. + PolyLine(float x0, float y0, float x1, float y1) : closed_(false) + { + point(x0, y0); + point(x1, y1); + } + + // Add a point to the PolyLine. + void point(float x, float y) { points_.push_back(QVector2D(x, y)); } + + // Built PolyLine to shader data. + void build(LineShaderData &target) const; + + // Set whether line is closed (ie. a loop). + void setClosed(bool closed) { closed_ = closed; } +}; + +// LineShader is an OpenGL shader program that renders LineShaderData on the +// GPU. +// The LineShader expects two vertices per line point. It will push those +// vertices along the given normal * miter. This is used to 'stretch' the line +// to be as wide as the given thickness. The normal and miter are calculated +// by the PolyLine build method in order to construct a constant thickness line +// with miter edge joints. +// +// +------+------+ +// +// | +// PolyLine.build() +// | +// V +// +// ^ ^ ^ +// | | | <--- normal vectors (x2, pointing in the same +// +/+----+/+----+/+ direction) +// +// | +// vertex shader +// | +// V +// +// +------+------+ ^ by normal * miter * thickness/2 +// | | | +// +------+------+ V by normal * miter * thickness/2 +// +// (miter is flipped for every second vertex generated) +class LineShader +{ + private: + QObject *parent_; + QOpenGLShaderProgram *program_; + + // GL attribute locations. + struct + { + // original position of line vertex + GLuint position; + // normal by which vertex should be translated + GLuint normal; + // scalar defining: + // - how stretched the normal vector should be to + // compensate for bends + // - which way the normal should be applied (+1 for one vertex, -1 + // for the other) + GLuint miter; + } attributes_; + + // GL uniform locations. + struct + { + // combines m/v/p matrix to apply + GLuint projection; + // desired thickness of line + GLuint thickness; + // color of line + GLuint color; + } uniforms_; + + public: + LineShader(QObject *parent) : parent_(parent), program_(nullptr) {} + + static constexpr const char *vertexShaderSource_ = + "attribute highp vec2 position;\n" + "attribute highp vec2 normal;\n" + "attribute highp float miter;\n" + "uniform highp float thickness;\n" + "uniform highp mat4 projection;\n" + "void main() {\n" + " vec2 p = position.xy + vec2(normal * thickness/2.0 / miter);\n" + " gl_Position = projection * vec4(p, 0.0, 1.0);\n" + "}\n"; + + static constexpr const char *fragmentShaderSource_ = + "uniform lowp vec4 color;\n" + "void main() {\n" + " gl_FragColor = color;\n" + "}\n"; + + // Must be called on initialization. + bool compile(void); + + // Render a LineShaderData with a given M/V/P transformation. + void draw(const LineShaderData &data, const QMatrix4x4 &projection); +}; + class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT @@ -35,16 +226,18 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; - void drawElement(const GraphicElement &el); + void drawElement(LineShaderData &data, const GraphicElement &el); QMainWindow *getMainWindow(); private: - int m_windowWidth; - int m_windowHeight; - float m_xMove; - float m_yMove; - float m_zDistance; - QPoint m_lastPos; + QPoint lastPos_; + float moveX_; + float moveY_; + float zoom_; + LineShader lineShader_; + + float startDragX_; + float startDragY_; Context *ctx; }; diff --git a/ice40/chipdb.py b/ice40/chipdb.py index db8730cf..623cdfce 100644 --- a/ice40/chipdb.py +++ b/ice40/chipdb.py @@ -209,7 +209,7 @@ def init_tiletypes(device): num_tile_types = 10 else: num_tile_types = 5 - tile_sizes = {_: (0, 0) for _ in range(num_tile_types)} + tile_sizes = {i: (0, 0) for i in range(num_tile_types)} tile_bits = [[] for _ in range(num_tile_types)] with open(sys.argv[1], "r") as f: diff --git a/ice40/main.cc b/ice40/main.cc index 8ae9ccf0..ae8c7705 100644 --- a/ice40/main.cc +++ b/ice40/main.cc @@ -20,6 +20,7 @@ #ifdef MAIN_EXECUTABLE #include <QApplication> +#include <QSurfaceFormat> #include <boost/filesystem/convenience.hpp> #include <boost/program_options.hpp> #include <fstream> |