aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gui/designwidget.cc14
-rw-r--r--gui/fpgaviewwidget.cc390
-rw-r--r--gui/fpgaviewwidget.h207
-rw-r--r--ice40/chipdb.py2
-rw-r--r--ice40/main.cc1
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 92d52288..374922f0 100644
--- a/ice40/chipdb.py
+++ b/ice40/chipdb.py
@@ -207,7 +207,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>