diff options
| -rw-r--r-- | bba/main.cc | 2 | ||||
| -rw-r--r-- | common/nextpnr.h | 28 | ||||
| -rw-r--r-- | gui/fpgaviewwidget.cc | 500 | ||||
| -rw-r--r-- | gui/fpgaviewwidget.h | 228 | ||||
| -rw-r--r-- | gui/lineshader.cc | 236 | ||||
| -rw-r--r-- | gui/lineshader.h | 209 | ||||
| -rw-r--r-- | ice40/arch.cc | 28 | ||||
| -rw-r--r-- | ice40/gfx.cc | 6 | ||||
| -rw-r--r-- | ice40/main.cc | 4 | 
9 files changed, 659 insertions, 582 deletions
diff --git a/bba/main.cc b/bba/main.cc index dd0caf1a..263cf39e 100644 --- a/bba/main.cc +++ b/bba/main.cc @@ -351,7 +351,7 @@ int main(int argc, char **argv)                      break;                  case TOK_REF:                      if (s.tokenComments[i].empty()) -                        printf("ref %s %s\n", labelNames[v].c_str()); +                        printf("ref %s\n", labelNames[v].c_str());                      else                          printf("ref %-26s %s\n", labelNames[v].c_str(), s.tokenComments[i].c_str());                      break; diff --git a/common/nextpnr.h b/common/nextpnr.h index e9e491f4..5e32d5b6 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -145,21 +145,25 @@ struct GraphicElement  {      enum type_t      { -        G_NONE, -        G_LINE, -        G_ARROW, -        G_BOX, -        G_CIRCLE, -        G_LABEL -    } type = G_NONE; +        TYPE_NONE, +        TYPE_LINE, +        TYPE_ARROW, +        TYPE_BOX, +        TYPE_CIRCLE, +        TYPE_LABEL, + +        TYPE_MAX +    } type = TYPE_NONE;      enum style_t      { -        G_FRAME,    // Static "frame". Contrast between G_INACTIVE and G_ACTIVE -        G_HIDDEN,   // Only display when object is selected or highlighted -        G_INACTIVE, // Render using low-contrast color -        G_ACTIVE,   // Render using high-contast color -    } style = G_FRAME; +        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 + +        STYLE_MAX +    } style = STYLE_FRAME;      float x1 = 0, y1 = 0, x2 = 0, y2 = 0, z = 0;      std::string text; diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc index 7ee2b4f9..ed3a0bce 100644 --- a/gui/fpgaviewwidget.cc +++ b/gui/fpgaviewwidget.cc @@ -31,220 +31,11 @@  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; -    } - -    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(); - -    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"); - -    vao_.release(); -    return true; -} - -void LineShader::draw(const LineShaderData &line, const QColor &color, float thickness, const QMatrix4x4 &projection) -{ -    auto gl = QOpenGLContext::currentContext()->functions(); -    if (line.vertices.size() == 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()); - -    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_.normal.bind(); -    program_->enableAttributeArray("normal"); -    gl->glVertexAttribPointer(attributes_.normal, 2, GL_FLOAT, GL_FALSE, 0, (void *)0); - -    buffers_.miter.bind(); -    program_->enableAttributeArray("miter"); -    gl->glVertexAttribPointer(attributes_.miter, 1, GL_FLOAT, GL_FALSE, 0, (void *)0); - -    buffers_.index.bind(); -    gl->glDrawElements(GL_TRIANGLES, line.indices.size(), GL_UNSIGNED_INT, (void *)0); - -    program_->disableAttributeArray("miter"); -    program_->disableAttributeArray("normal"); -    program_->disableAttributeArray("position"); - -    program_->release(); -    vao_.release(); -} - -FPGAViewWidget::FPGAViewWidget(QWidget *parent) -        : QOpenGLWidget(parent), lineShader_(this), zoom_(500.f), ctx_(nullptr), paintTimer_(this), -          rendererData_(new FPGAViewWidget::RendererData), rendererArgs_(new FPGAViewWidget::RendererArgs) +FPGAViewWidget::FPGAViewWidget(QWidget *parent) : +        QOpenGLWidget(parent), ctx_(nullptr), paintTimer_(this), +        lineShader_(this), zoom_(500.0f), +        rendererData_(new FPGAViewWidget::RendererData), +        rendererArgs_(new FPGAViewWidget::RendererArgs)  {      colors_.background = QColor("#000000");      colors_.grid = QColor("#333"); @@ -311,68 +102,49 @@ void FPGAViewWidget::initializeGL()                   0.0);  } -void FPGAViewWidget::drawDecal(LineShaderData &out, const DecalXY &decal) +void FPGAViewWidget::drawGraphicElement(LineShaderData &out, const GraphicElement &el, float x, float y)  {      const float scale = 1.0; + +    if (el.type == GraphicElement::TYPE_BOX) { +        auto line = PolyLine(true); +        line.point(x + scale * el.x1, y + scale * el.y1); +        line.point(x + scale * el.x2, y + scale * el.y1); +        line.point(x + scale * el.x2, y + scale * el.y2); +        line.point(x + scale * el.x1, y + scale * el.y2); +        line.build(out); +    } + +    if (el.type == GraphicElement::TYPE_LINE || el.type == GraphicElement::TYPE_ARROW) { +        PolyLine(x + scale * el.x1, y + scale * el.y1, x + scale * el.x2, y + scale * el.y2) +            .build(out); +    } +} + +void FPGAViewWidget::drawDecal(LineShaderData &out, const DecalXY &decal) +{      float offsetX = decal.x;      float offsetY = decal.y;      for (auto &el : ctx_->getDecalGraphics(decal.decal)) { - -        if (el.type == GraphicElement::G_BOX) { -            auto line = PolyLine(true); -            line.point(offsetX + scale * el.x1, offsetY + scale * el.y1); -            line.point(offsetX + scale * el.x2, offsetY + scale * el.y1); -            line.point(offsetX + scale * el.x2, offsetY + scale * el.y2); -            line.point(offsetX + scale * el.x1, offsetY + scale * el.y2); -            line.build(out); -        } - -        if (el.type == GraphicElement::G_LINE || el.type == GraphicElement::G_ARROW) { -            PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2, offsetY + scale * el.y2) -                    .build(out); -        } +        drawGraphicElement(out, el, offsetX, offsetY);      }  } -void FPGAViewWidget::drawDecal(LineShaderData out[], const DecalXY &decal) +void FPGAViewWidget::drawArchDecal(LineShaderData out[GraphicElement::STYLE_MAX], const DecalXY &decal)  { -    const float scale = 1.0; -    float offsetX = 0.0, offsetY = 0.0; +    float offsetX = decal.x; +    float offsetY = decal.y;      for (auto &el : ctx_->getDecalGraphics(decal.decal)) { -        offsetX = decal.x; -        offsetY = decal.y; - -        if (el.type == GraphicElement::G_BOX) { -            auto line = PolyLine(true); -            line.point(offsetX + scale * el.x1, offsetY + scale * el.y1); -            line.point(offsetX + scale * el.x2, offsetY + scale * el.y1); -            line.point(offsetX + scale * el.x2, offsetY + scale * el.y2); -            line.point(offsetX + scale * el.x1, offsetY + scale * el.y2); -            switch (el.style) { -            case GraphicElement::G_FRAME: -            case GraphicElement::G_INACTIVE: -            case GraphicElement::G_ACTIVE: -                line.build(out[el.style]); -                break; -            default: -                break; -            } -        } - -        if (el.type == GraphicElement::G_LINE || el.type == GraphicElement::G_ARROW) { -            auto line = PolyLine(offsetX + scale * el.x1, offsetY + scale * el.y1, offsetX + scale * el.x2, -                                 offsetY + scale * el.y2); -            switch (el.style) { -            case GraphicElement::G_FRAME: -            case GraphicElement::G_INACTIVE: -            case GraphicElement::G_ACTIVE: -                line.build(out[el.style]); -                break; -            default: -                break; -            } +        switch (el.style) { +        case GraphicElement::STYLE_FRAME: +        case GraphicElement::STYLE_INACTIVE: +        case GraphicElement::STYLE_ACTIVE: +            drawGraphicElement(out[el.style], el, offsetX, offsetY); +            break; +        default: +            break;          }      }  } @@ -402,24 +174,28 @@ void FPGAViewWidget::paintGL()      float thick1Px = mouseToWorldDimensions(1, 0).x();      float thick11Px = mouseToWorldDimensions(1.1, 0).x(); -    // Draw grid. +    // 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);      } +    // Draw grid.      lineShader_.draw(grid, colors_.grid, thick1Px, matrix);      rendererDataLock_.lock(); -    lineShader_.draw(rendererData_->decals[0], colors_.frame, thick11Px, matrix); -    lineShader_.draw(rendererData_->decals[1], colors_.hidden, thick11Px, matrix); -    lineShader_.draw(rendererData_->decals[2], colors_.inactive, thick11Px, matrix); -    lineShader_.draw(rendererData_->decals[3], colors_.active, thick11Px, matrix); +    // 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_->highlighted[i], colors_.highlight[i], thick11Px, matrix); +        lineShader_.draw(rendererData_->gfxHighlighted[i], colors_.highlight[i], thick11Px, matrix); -    lineShader_.draw(rendererData_->selected, colors_.selected, thick11Px, matrix); +    lineShader_.draw(rendererData_->gfxSelected, colors_.selected, thick11Px, matrix);      rendererDataLock_.unlock();  } @@ -430,125 +206,151 @@ void FPGAViewWidget::renderLines(void)      if (ctx_ == nullptr)          return; -    ctx_->lock_ui(); - -    // For now, collapse any decal changes into change of all decals. -    // TODO(q3k): fix this -    bool decalsChanged = false; -    if (ctx_->allUiReload) { -        ctx_->allUiReload = false; -        decalsChanged = true; -    } -    if (ctx_->frameUiReload) { -        ctx_->frameUiReload = false; -        decalsChanged = true; -    } -    if (ctx_->belUiReload.size() > 0) { -        ctx_->belUiReload.clear(); -        decalsChanged = true; -    } -    if (ctx_->wireUiReload.size() > 0) { -        ctx_->wireUiReload.clear(); -        decalsChanged = true; -    } -    if (ctx_->pipUiReload.size() > 0) { -        ctx_->pipUiReload.clear(); -        decalsChanged = true; -    } -    if (ctx_->groupUiReload.size() > 0) { -        ctx_->groupUiReload.clear(); -        decalsChanged = true; -    } - -    // Local copy of decals, taken as fast as possible to not block the P&R. -    std::vector<std::pair<BelId, DecalXY>> belDecals; +    // Data from Context needed to render all decals. +    std::vector<DecalXY> belDecals;      std::vector<DecalXY> wireDecals;      std::vector<DecalXY> pipDecals;      std::vector<DecalXY> groupDecals; - -    if (decalsChanged) { -        for (auto bel : ctx_->getBels()) { -            belDecals.push_back(std::pair<BelId, DecalXY>(bel, ctx_->getBelDecal(bel))); +    bool decalsChanged = false; +    { +        // Take the UI/Normal mutex on the Context, copy over all we need as +        // fast as we can. +        std::lock_guard<std::mutex> lock_ui(ctx_->ui_mutex); +        std::lock_guard<std::mutex> lock(ctx_->mutex); + +        // For now, collapse any decal changes into change of all decals. +        // TODO(q3k): fix this +        if (ctx_->allUiReload) { +            ctx_->allUiReload = false; +            decalsChanged = true;          } -        for (auto wire : ctx_->getWires()) { -            wireDecals.push_back(ctx_->getWireDecal(wire)); +        if (ctx_->frameUiReload) { +            ctx_->frameUiReload = false; +            decalsChanged = true;          } -        for (auto pip : ctx_->getPips()) { -            pipDecals.push_back(ctx_->getPipDecal(pip)); +        if (ctx_->belUiReload.size() > 0) { +            ctx_->belUiReload.clear(); +            decalsChanged = true;          } -        for (auto group : ctx_->getGroups()) { -            groupDecals.push_back(ctx_->getGroupDecal(group)); +        if (ctx_->wireUiReload.size() > 0) { +            ctx_->wireUiReload.clear(); +            decalsChanged = true; +        } +        if (ctx_->pipUiReload.size() > 0) { +            ctx_->pipUiReload.clear(); +            decalsChanged = true; +        } +        if (ctx_->groupUiReload.size() > 0) { +            ctx_->groupUiReload.clear(); +            decalsChanged = true; +        } + +        // Local copy of decals, taken as fast as possible to not block the P&R. +        if (decalsChanged) { +            for (auto bel : ctx_->getBels()) { +                belDecals.push_back(ctx_->getBelDecal(bel)); +            } +            for (auto wire : ctx_->getWires()) { +                wireDecals.push_back(ctx_->getWireDecal(wire)); +            } +            for (auto pip : ctx_->getPips()) { +                pipDecals.push_back(ctx_->getPipDecal(pip)); +            } +            for (auto group : ctx_->getGroups()) { +                groupDecals.push_back(ctx_->getGroupDecal(group)); +            }          }      } -    ctx_->unlock_ui(); -    rendererArgsLock_.lock(); -    auto selectedItems = rendererArgs_->selectedItems; -    auto highlightedItems = rendererArgs_->highlightedItems; -    auto highlightedOrSelectedChanged = rendererArgs_->highlightedOrSelectedChanged; -    rendererArgs_->highlightedOrSelectedChanged = false; -    rendererArgsLock_.unlock(); +    // Arguments from the main UI thread on what we should render. +    std::vector<DecalXY> selectedDecals; +    std::vector<DecalXY> highlightedDecals[8]; +    bool highlightedOrSelectedChanged; +    { +        // Take the renderer arguments lock, copy over all we need. +        QMutexLocker lock(&rendererArgsLock_); +        selectedDecals = rendererArgs_->selectedDecals; +        for (int i = 0; i < 8; i++) +            highlightedDecals[i] = rendererArgs_->highlightedDecals[i]; +        highlightedOrSelectedChanged = rendererArgs_->highlightedOrSelectedChanged; +        rendererArgs_->highlightedOrSelectedChanged = false; +    }      QuadTreeBels::BoundingBox globalBB = QuadTreeBels::BoundingBox(-1000, -1000, 1000, 1000); +    // Render decals if necessary.      if (decalsChanged) {          auto data = std::unique_ptr<FPGAViewWidget::RendererData>(new FPGAViewWidget::RendererData);          // Draw Bels.          data->qtBels = std::unique_ptr<QuadTreeBels>(new QuadTreeBels(globalBB));          for (auto const &decal : belDecals) { -            drawDecal(data->decals, decal.second); -            commitToQuadtree(data->qtBels.get(), decal.second, decal.first); +            drawArchDecal(data->gfxByStyle, decal);          }          // Draw Wires.          for (auto const &decal : wireDecals) { -            drawDecal(data->decals, decal); +            drawArchDecal(data->gfxByStyle, decal);          }          // Draw Pips.          for (auto const &decal : pipDecals) { -            drawDecal(data->decals, decal); +            drawArchDecal(data->gfxByStyle, decal);          }          // Draw Groups.          for (auto const &decal : groupDecals) { -            drawDecal(data->decals, decal); +            drawArchDecal(data->gfxByStyle, decal);          }          // Swap over. -        rendererDataLock_.lock(); -        rendererData_ = std::move(data); -        rendererDataLock_.unlock(); +        { +            QMutexLocker lock(&rendererDataLock_); + +            // If we're not re-rendering any highlights/selections, let's +            // copy them over from teh current object. +            if (!highlightedOrSelectedChanged) { +                data->gfxSelected = rendererData_->gfxSelected; +                for (int i = 0; i < 8; i++) +                    data->gfxHighlighted[i] = rendererData_->gfxHighlighted[i]; +            } + +            rendererData_ = std::move(data); +        }      } -    rendererDataLock_.lock(); -    if (decalsChanged || highlightedOrSelectedChanged) { -        rendererData_->selected.clear(); -        for (auto &decal : selectedItems) { -            drawDecal(rendererData_->selected, decal); +    if (highlightedOrSelectedChanged) { +        QMutexLocker locker(&rendererDataLock_); + +        // Render selected. +        rendererData_->gfxSelected.clear(); +        for (auto &decal : selectedDecals) { +            drawDecal(rendererData_->gfxSelected, decal);          } + +        // Render highlighted.          for (int i = 0; i < 8; i++) { -            rendererData_->highlighted[i].clear(); -            for (auto &decal : highlightedItems[i]) { -                drawDecal(rendererData_->highlighted[i], decal); +            rendererData_->gfxHighlighted[i].clear(); +            for (auto &decal : highlightedDecals[i]) { +                drawDecal(rendererData_->gfxHighlighted[i], decal);              }          }      } -    rendererDataLock_.unlock();  }  void FPGAViewWidget::onSelectedArchItem(std::vector<DecalXY> decals)  { -    rendererArgsLock_.lock(); -    rendererArgs_->selectedItems = decals; -    rendererArgs_->highlightedOrSelectedChanged = true; -    rendererArgsLock_.unlock(); +    { +        QMutexLocker locker(&rendererArgsLock_); +        rendererArgs_->selectedDecals = decals; +        rendererArgs_->highlightedOrSelectedChanged = true; +    }      pokeRenderer();  }  void FPGAViewWidget::onHighlightGroupChanged(std::vector<DecalXY> decals, int group)  { -    rendererArgsLock_.lock(); -    rendererArgs_->highlightedItems[group] = decals; -    rendererArgs_->highlightedOrSelectedChanged = true; -    rendererArgsLock_.unlock(); +    { +        QMutexLocker locker(&rendererArgsLock_); +        rendererArgs_->highlightedDecals[group] = decals; +        rendererArgs_->highlightedOrSelectedChanged = true; +    }      pokeRenderer();  } @@ -557,7 +359,7 @@ void FPGAViewWidget::resizeGL(int width, int height) {}  void FPGAViewWidget::mousePressEvent(QMouseEvent *event)  {      if (event->buttons() & Qt::RightButton || event->buttons() & Qt::MidButton) { -        lastPos_ = event->pos(); +        lastDragPos_ = event->pos();      }      if (event->buttons() & Qt::LeftButton) {          int x = event->x(); @@ -609,9 +411,9 @@ QVector4D FPGAViewWidget::mouseToWorldDimensions(int x, int y)  void FPGAViewWidget::mouseMoveEvent(QMouseEvent *event)  {      if (event->buttons() & Qt::RightButton || event->buttons() & Qt::MidButton) { -        const int dx = event->x() - lastPos_.x(); -        const int dy = event->y() - lastPos_.y(); -        lastPos_ = event->pos(); +        const int dx = event->x() - lastDragPos_.x(); +        const int dy = event->y() - lastDragPos_.y(); +        lastDragPos_ = event->pos();          auto world = mouseToWorldDimensions(dx, dy);          viewMove_.translate(world.x(), -world.y()); diff --git a/gui/fpgaviewwidget.h b/gui/fpgaviewwidget.h index c1165ef3..a360eea7 100644 --- a/gui/fpgaviewwidget.h +++ b/gui/fpgaviewwidget.h @@ -34,183 +34,10 @@  #include "nextpnr.h"  #include "quadtree.h" +#include "lineshader.h"  NEXTPNR_NAMESPACE_BEGIN -// Vertex2DPOD is a structure of X, Y coordinates that can be passed to OpenGL -// directly. -NPNR_PACKED_STRUCT(struct Vertex2DPOD { -    GLfloat x; -    GLfloat y; - -    Vertex2DPOD(GLfloat X, GLfloat Y) : x(X), y(Y) {} -}); - -// 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; - -    LineShaderData(void) {} - -    void clear(void) -    { -        vertices.clear(); -        normals.clear(); -        miters.clear(); -        indices.clear(); -    } -}; - -// 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 buffers -    struct -    { -        QOpenGLBuffer position; -        QOpenGLBuffer normal; -        QOpenGLBuffer miter; -        QOpenGLBuffer index; -    } buffers_; - -    // 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_; - -    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_ = -            "#version 110\n" -            "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_ = "#version 110\n" -                                                         "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 QColor &color, float thickness, const QMatrix4x4 &projection); -}; -  class PeriodicRunner : public QThread  {      Q_OBJECT @@ -268,18 +95,17 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions      FPGAViewWidget(QWidget *parent = 0);      ~FPGAViewWidget(); -    QSize minimumSizeHint() const override; -    QSize sizeHint() const override; -    protected: +    // Qt callbacks.      void initializeGL() Q_DECL_OVERRIDE;      void paintGL() Q_DECL_OVERRIDE;      void resizeGL(int width, int height) Q_DECL_OVERRIDE;      void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;      void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;      void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; -    void drawDecal(LineShaderData &data, const DecalXY &decal); -    void drawDecal(LineShaderData out[], const DecalXY &decal); +    QSize minimumSizeHint() const override; +    QSize sizeHint() const override; +    public Q_SLOTS:      void newContext(Context *ctx); @@ -295,26 +121,13 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions      void clickedBel(BelId bel);    private: -    void renderLines(void); -    void zoom(int level); - -    QPoint lastPos_; -    LineShader lineShader_; -    QMatrix4x4 viewMove_; -    float zoom_; -    QMatrix4x4 getProjection(void); -    QVector4D mouseToWorldCoordinates(int x, int y); -    QVector4D mouseToWorldDimensions(int x, int y); -      const float zoomNear_ = 1.0f;    // do not zoom closer than this      const float zoomFar_ = 10000.0f; // do not zoom further than this -      const float zoomLvl1_ = 100.0f;      const float zoomLvl2_ = 50.0f;      Context *ctx_;      QTimer paintTimer_; -      std::unique_ptr<PeriodicRunner> renderRunner_;      using QuadTreeBels = QuadTree<float, BelId>; @@ -326,12 +139,17 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions          float offsetY = decal.y;          for (auto &el : ctx_->getDecalGraphics(decal.decal)) { -            if (el.type == GraphicElement::G_BOX) { +            if (el.type == GraphicElement::TYPE_BOX) {                  tree->insert(typename T::BoundingBox(offsetX + el.x1, offsetY + el.y1, offsetX + el.x2, offsetY + el.y2), bel);              }          }      } +    QPoint lastDragPos_; +    LineShader lineShader_; +    QMatrix4x4 viewMove_; +    float zoom_; +      struct      {          QColor background; @@ -346,23 +164,31 @@ class FPGAViewWidget : public QOpenGLWidget, protected QOpenGLFunctions      struct RendererData      { -        LineShaderData decals[4]; -        LineShaderData selected; -        LineShaderData highlighted[8]; +        LineShaderData gfxByStyle[GraphicElement::STYLE_MAX]; +        LineShaderData gfxSelected; +        LineShaderData gfxHighlighted[8];          std::unique_ptr<QuadTreeBels> qtBels;      }; +    std::unique_ptr<RendererData> rendererData_; +    QMutex rendererDataLock_;      struct RendererArgs      { -        std::vector<DecalXY> selectedItems; -        std::vector<DecalXY> highlightedItems[8]; +        std::vector<DecalXY> selectedDecals; +        std::vector<DecalXY> highlightedDecals[8];          bool highlightedOrSelectedChanged;      }; - -    std::unique_ptr<RendererData> rendererData_; -    QMutex rendererDataLock_;      std::unique_ptr<RendererArgs> rendererArgs_;      QMutex rendererArgsLock_; + +    void zoom(int level); +    void renderLines(void); +    void drawGraphicElement(LineShaderData &out, const GraphicElement &el, float x, float y); +    void drawDecal(LineShaderData &out, const DecalXY &decal); +    void drawArchDecal(LineShaderData out[GraphicElement::STYLE_MAX], const DecalXY &decal); +    QVector4D mouseToWorldCoordinates(int x, int y); +    QVector4D mouseToWorldDimensions(int x, int y); +    QMatrix4x4 getProjection(void);  };  NEXTPNR_NAMESPACE_END diff --git a/gui/lineshader.cc b/gui/lineshader.cc new file mode 100644 index 00000000..94a7a010 --- /dev/null +++ b/gui/lineshader.cc @@ -0,0 +1,236 @@ +/* + *  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 "log.h" +#include "lineshader.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; +    } + +    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(); + +    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"); + +    vao_.release(); +    return true; +} + +void LineShader::draw(const LineShaderData &line, const QColor &color, float thickness, const QMatrix4x4 &projection) +{ +    auto gl = QOpenGLContext::currentContext()->functions(); +    if (line.vertices.size() == 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()); + +    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_.normal.bind(); +    program_->enableAttributeArray("normal"); +    gl->glVertexAttribPointer(attributes_.normal, 2, GL_FLOAT, GL_FALSE, 0, (void *)0); + +    buffers_.miter.bind(); +    program_->enableAttributeArray("miter"); +    gl->glVertexAttribPointer(attributes_.miter, 1, GL_FLOAT, GL_FALSE, 0, (void *)0); + +    buffers_.index.bind(); +    gl->glDrawElements(GL_TRIANGLES, line.indices.size(), GL_UNSIGNED_INT, (void *)0); + +    program_->disableAttributeArray("miter"); +    program_->disableAttributeArray("normal"); +    program_->disableAttributeArray("position"); + +    program_->release(); +    vao_.release(); +} + +NEXTPNR_NAMESPACE_END diff --git a/gui/lineshader.h b/gui/lineshader.h new file mode 100644 index 00000000..3f4c4057 --- /dev/null +++ b/gui/lineshader.h @@ -0,0 +1,209 @@ +/* + *  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 LINESHADER_H +#define LINESHADER_H + +#include <QOpenGLBuffer> +#include <QOpenGLFunctions> +#include <QOpenGLShaderProgram> +#include <QOpenGLVertexArrayObject> +#include <QOpenGLWidget> + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +// Vertex2DPOD is a structure of X, Y coordinates that can be passed to OpenGL +// directly. +NPNR_PACKED_STRUCT(struct Vertex2DPOD { +    GLfloat x; +    GLfloat y; + +    Vertex2DPOD(GLfloat X, GLfloat Y) : x(X), y(Y) {} +}); + +// 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; + +    LineShaderData(void) {} + +    void clear(void) +    { +        vertices.clear(); +        normals.clear(); +        miters.clear(); +        indices.clear(); +    } +}; + +// 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 buffers +    struct +    { +        QOpenGLBuffer position; +        QOpenGLBuffer normal; +        QOpenGLBuffer miter; +        QOpenGLBuffer index; +    } buffers_; + +    // 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_; + +    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_ = +            "#version 110\n" +            "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_ = "#version 110\n" +                                                         "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 QColor &color, float thickness, const QMatrix4x4 &projection); +}; + +NEXTPNR_NAMESPACE_END + +#endif diff --git a/ice40/arch.cc b/ice40/arch.cc index d08463d2..3803f842 100644 --- a/ice40/arch.cc +++ b/ice40/arch.cc @@ -654,8 +654,8 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const          if (type == GroupId::TYPE_FRAME) {              GraphicElement el; -            el.type = GraphicElement::G_LINE; -            el.style = GraphicElement::G_FRAME; +            el.type = GraphicElement::TYPE_LINE; +            el.style = GraphicElement::STYLE_FRAME;              el.x1 = x + 0.01, el.x2 = x + 0.02, el.y1 = y + 0.01, el.y2 = y + 0.01;              ret.push_back(el); @@ -680,8 +680,8 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const          if (type == GroupId::TYPE_MAIN_SW) {              GraphicElement el; -            el.type = GraphicElement::G_BOX; -            el.style = GraphicElement::G_FRAME; +            el.type = GraphicElement::TYPE_BOX; +            el.style = GraphicElement::STYLE_FRAME;              el.x1 = x + main_swbox_x1;              el.x2 = x + main_swbox_x2; @@ -692,8 +692,8 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const          if (type == GroupId::TYPE_LOCAL_SW) {              GraphicElement el; -            el.type = GraphicElement::G_BOX; -            el.style = GraphicElement::G_FRAME; +            el.type = GraphicElement::TYPE_BOX; +            el.style = GraphicElement::STYLE_FRAME;              el.x1 = x + local_swbox_x1;              el.x2 = x + local_swbox_x2; @@ -707,7 +707,7 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const          int n = chip_info->wire_data[decal.index].num_segments;          const WireSegmentPOD *p = chip_info->wire_data[decal.index].segments.get(); -        GraphicElement::style_t style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE; +        GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;          for (int i = 0; i < n; i++)              gfxTileWire(ret, p[i].x, p[i].y, GfxTileWireId(p[i].index), style); @@ -715,7 +715,7 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const      if (decal.type == DecalId::TYPE_PIP) {          const PipInfoPOD &p = chip_info->pip_data[decal.index]; -        GraphicElement::style_t style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_HIDDEN; +        GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_HIDDEN;          gfxTilePip(ret, p.x, p.y, GfxTileWireId(p.src_seg), GfxTileWireId(p.dst_seg), style);      } @@ -727,8 +727,8 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const          if (bel_type == TYPE_ICESTORM_LC) {              GraphicElement el; -            el.type = GraphicElement::G_BOX; -            el.style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE; +            el.type = GraphicElement::TYPE_BOX; +            el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;              el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;              el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;              el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 + @@ -740,8 +740,8 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const          if (bel_type == TYPE_SB_IO) {              GraphicElement el; -            el.type = GraphicElement::G_BOX; -            el.style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE; +            el.type = GraphicElement::TYPE_BOX; +            el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;              el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;              el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;              el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 + @@ -754,8 +754,8 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const          if (bel_type == TYPE_ICESTORM_RAM) {              for (int i = 0; i < 2; i++) {                  GraphicElement el; -                el.type = GraphicElement::G_BOX; -                el.style = decal.active ? GraphicElement::G_ACTIVE : GraphicElement::G_INACTIVE; +                el.type = GraphicElement::TYPE_BOX; +                el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;                  el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;                  el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;                  el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 + i; diff --git a/ice40/gfx.cc b/ice40/gfx.cc index 0a583e8e..0798862a 100644 --- a/ice40/gfx.cc +++ b/ice40/gfx.cc @@ -24,7 +24,7 @@ NEXTPNR_NAMESPACE_BEGIN  void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style)  {      GraphicElement el; -    el.type = GraphicElement::G_LINE; +    el.type = GraphicElement::TYPE_LINE;      el.style = style;      // Horizontal Span-4 Wires @@ -647,7 +647,7 @@ void pipGfx(std::vector<GraphicElement> &g, int x, int y, float x1, float y1, fl      float ty = 0.5 * (y1 + y2);      GraphicElement el; -    el.type = GraphicElement::G_ARROW; +    el.type = GraphicElement::TYPE_ARROW;      el.style = style;      if (fabsf(x1 - swx1) < 0.001 && fabsf(x2 - swx1) < 0.001) { @@ -704,7 +704,7 @@ void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src,      if (src == TILE_WIRE_CARRY_IN && dst == TILE_WIRE_CARRY_IN_MUX) {          GraphicElement el; -        el.type = GraphicElement::G_ARROW; +        el.type = GraphicElement::TYPE_ARROW;          el.style = style;          el.x1 = x + logic_cell_x1 + 0.005 * 3;          el.x2 = el.x1; diff --git a/ice40/main.cc b/ice40/main.cc index 6201831a..32815b26 100644 --- a/ice40/main.cc +++ b/ice40/main.cc @@ -52,13 +52,13 @@ void svg_dump_decal(const Context *ctx, const DecalXY &decal)      const std::string style = "stroke=\"black\" stroke-width=\"0.1\" fill=\"none\"";      for (auto &el : ctx->getDecalGraphics(decal.decal)) { -        if (el.type == GraphicElement::G_BOX) { +        if (el.type == GraphicElement::TYPE_BOX) {              std::cout << "<rect x=\"" << (offset + scale * (decal.x + el.x1)) << "\" y=\""                        << (offset + scale * (decal.y + el.y1)) << "\" height=\"" << (scale * (el.y2 - el.y1))                        << "\" width=\"" << (scale * (el.x2 - el.x1)) << "\" " << style << "/>\n";          } -        if (el.type == GraphicElement::G_LINE) { +        if (el.type == GraphicElement::TYPE_LINE) {              std::cout << "<line x1=\"" << (offset + scale * (decal.x + el.x1)) << "\" y1=\""                        << (offset + scale * (decal.y + el.y1)) << "\" x2=\"" << (offset + scale * (decal.x + el.x2))                        << "\" y2=\"" << (offset + scale * (decal.y + el.y2)) << "\" " << style << "/>\n";  | 
