diff --git a/qrenderdoc/Windows/TimelineBar.cpp b/qrenderdoc/Windows/TimelineBar.cpp index b2f3751eb..43ddee6ab 100644 --- a/qrenderdoc/Windows/TimelineBar.cpp +++ b/qrenderdoc/Windows/TimelineBar.cpp @@ -29,6 +29,13 @@ #include #include "Code/Resources.h" +QPointF aliasAlign(QPointF pt) +{ + pt.setX(int(pt.x()) + 0.5); + pt.setY(int(pt.y()) + 0.5); + return pt; +} + QMarginsF uniformMargins(qreal m) { return QMarginsF(m, m, m, m); @@ -66,10 +73,53 @@ TimelineBar::~TimelineBar() void TimelineBar::HighlightResourceUsage(ResourceId id) { + m_UsageEvents.clear(); + + TextureDescription *tex = m_Ctx.GetTexture(id); + + if(tex) + m_UsageTarget = ToQStr(tex->name); + + BufferDescription *buf = m_Ctx.GetBuffer(id); + + if(buf) + m_UsageTarget = ToQStr(buf->name); + + m_Ctx.Replay().AsyncInvoke([this, id](IReplayController *r) { + rdctype::array usage = r->GetUsage(id); + + GUIInvoke::call([this, usage]() { + for(const EventUsage &u : usage) + m_UsageEvents << u; + viewport()->update(); + }); + }); + + viewport()->update(); } void TimelineBar::HighlightHistory(ResourceId id, const QList &history) { + m_HistoryTarget = QString(); + m_HistoryEvents.clear(); + + if(id != ResourceId()) + { + TextureDescription *tex = m_Ctx.GetTexture(id); + + if(tex) + m_HistoryTarget = ToQStr(tex->name); + + BufferDescription *buf = m_Ctx.GetBuffer(id); + + if(buf) + m_HistoryTarget = ToQStr(buf->name); + + for(const PixelModification &mod : history) + m_HistoryEvents << mod; + } + + viewport()->update(); } void TimelineBar::OnLogfileClosed() @@ -103,7 +153,9 @@ void TimelineBar::OnEventChanged(uint32_t eventID) QSize TimelineBar::minimumSizeHint() const { - return QSize(margin * 4 + borderWidth * 2 + 100, margin * 4 + borderWidth * 2 + 40); + return QSize(margin * 4 + borderWidth * 2 + 100, + margin * 4 + borderWidth * 2 + m_eidAxisRect.height() * 2 + + m_highlightingRect.height() + horizontalScrollBar()->sizeHint().height()); } void TimelineBar::resizeEvent(QResizeEvent *e) @@ -124,11 +176,17 @@ void TimelineBar::layout() m_dataArea.setLeft(m_dataArea.left() + m_titleWidth); m_eidAxisRect = m_dataArea.marginsRemoved(uniformMargins(margin)); - m_eidAxisRect.setHeight(qBound(fm.height(), eidAxisHeight, (int)m_eidAxisRect.height())); + m_eidAxisRect.setHeight(qMax(fm.height(), dataBarHeight)); m_markerRect = m_dataArea.marginsRemoved(uniformMargins(margin)); m_markerRect.setTop(m_eidAxisRect.bottom() + margin); + m_highlightingRect = m_area; + m_highlightingRect.setHeight(qMax(fm.height(), dataBarHeight) + highlightingExtra); + m_highlightingRect.moveTop(m_markerRect.bottom() - m_highlightingRect.height()); + + m_markerRect.setBottom(m_highlightingRect.top()); + uint32_t maxEID = m_Draws.isEmpty() ? 0 : m_Draws.back(); int stepSize = 1; @@ -186,10 +244,51 @@ void TimelineBar::mousePressEvent(QMouseEvent *e) if(marker) { marker->expanded = !marker->expanded; + m_lastPos = QPointF(); viewport()->update(); return; } + if(m_highlightingRect.contains(m_lastPos)) + { + uint32_t eid = eventAt(x); + + m_lastPos = QPointF(); + + // history events get first crack at any selection, if they exist + if(!m_HistoryEvents.isEmpty()) + { + auto it = std::find_if(m_HistoryEvents.begin(), m_HistoryEvents.end(), + [this, eid](const PixelModification &mod) { + if(mod.eventID == eid) + return true; + + return false; + }); + + if(it != m_HistoryEvents.end()) + m_Ctx.SetEventID({}, eid, eid); + + return; + } + + if(!m_UsageEvents.isEmpty()) + { + auto it = std::find_if(m_UsageEvents.begin(), m_UsageEvents.end(), + [this, eid](const EventUsage &use) { + if(use.eventID == eid) + return true; + + return false; + }); + + if(it != m_UsageEvents.end()) + m_Ctx.SetEventID({}, eid, eid); + } + + return; + } + if(!m_Draws.isEmpty() && m_dataArea.contains(m_lastPos)) { uint32_t eid = eventAt(x); @@ -214,7 +313,7 @@ void TimelineBar::mouseReleaseEvent(QMouseEvent *e) void TimelineBar::mouseMoveEvent(QMouseEvent *e) { - if(e->buttons() == Qt::LeftButton) + if(e->buttons() == Qt::LeftButton && m_lastPos != QPointF()) { qreal x = e->localPos().x(); @@ -325,6 +424,8 @@ void TimelineBar::paintEvent(QPaintEvent *e) p.drawLine(eidAxisRect.bottomLeft(), eidAxisRect.bottomRight() + QPointF(margin, 0)); + p.drawLine(m_highlightingRect.topLeft(), m_highlightingRect.topRight()); + if(m_Draws.isEmpty()) return; @@ -429,7 +530,7 @@ void TimelineBar::paintEvent(QPaintEvent *e) currentRect.setLeft(offsetOf(curEID)); currentRect.setWidth( - qMax(m_eidAxisLabelWidth, m_eidAxisLabelTextWidth + eidAxisHeight + margin * 2)); + qMax(m_eidAxisLabelWidth, m_eidAxisLabelTextWidth + dataBarHeight + margin * 2)); // recentre currentRect.moveLeft(currentRect.left() - currentRect.width() / 2 + m_eidWidth / 2); @@ -454,7 +555,7 @@ void TimelineBar::paintEvent(QPaintEvent *e) p.drawPixmap(currentRect.topLeft() + QPointF(margin, 1), px, px.rect()); // move to where the text should be and draw it - currentRect.setLeft(currentRect.left() + margin * 2 + eidAxisHeight); + currentRect.setLeft(currentRect.left() + margin * 2 + dataBarHeight); p.drawText(currentRect, QString::number(curEID), to); // draw a line from the bottom of the shadow downwards @@ -467,6 +568,204 @@ void TimelineBar::paintEvent(QPaintEvent *e) p.drawLine(currentTop, currentBottom); } + + to.setAlignment(Qt::AlignLeft | Qt::AlignTop); + + if(!m_UsageTarget.isEmpty() || !m_HistoryTarget.isEmpty()) + { + p.setRenderHint(QPainter::Antialiasing); + + QRectF highlightLabel = m_highlightingRect.marginsRemoved(uniformMargins(margin)); + + highlightLabel.setX(highlightLabel.x() + margin); + + QString text; + + if(!m_HistoryTarget.isEmpty()) + text = tr("Pixel history for %1").arg(m_HistoryTarget); + else + text = tr("Usage for %1:").arg(m_UsageTarget); + + p.drawText(highlightLabel, text, to); + + const int triRadius = fm.averageCharWidth(); + const int triHeight = fm.ascent(); + + QPainterPath triangle; + triangle.addPolygon( + QPolygonF({QPoint(0, triHeight), QPoint(triRadius * 2, triHeight), QPoint(triRadius, 0)})); + triangle.closeSubpath(); + + enum + { + ReadUsage, + WriteUsage, + ReadWriteUsage, + ClearUsage, + BarrierUsage, + + HistoryPassed, + HistoryFailed, + + UsageCount, + }; + + const QColor colors[UsageCount] = { + // read + QColor(Qt::red), + // write + QColor(Qt::green), + // read/write + QColor(Qt::yellow), + // clear + QColor(Qt::blue), + // barrier + QColor(Qt::magenta), + + // pass + QColor(Qt::green), + // fail + QColor(Qt::red), + }; + + // draw the key + if(m_HistoryTarget.isEmpty()) + { + // advance past the first text to draw the key + highlightLabel.setLeft(highlightLabel.left() + fm.width(text)); + + text = lit(" Reads, "); + p.drawText(highlightLabel, text, to); + highlightLabel.setLeft(highlightLabel.left() + fm.width(text)); + + QPainterPath path = triangle.translated(aliasAlign(highlightLabel.topLeft())); + p.fillPath(path, colors[ReadUsage]); + p.drawPath(path); + highlightLabel.setLeft(highlightLabel.left() + triRadius * 2); + + text = lit(" Writes, "); + p.drawText(highlightLabel, text, to); + highlightLabel.setLeft(highlightLabel.left() + fm.width(text)); + + path = triangle.translated(aliasAlign(highlightLabel.topLeft())); + p.fillPath(path, colors[WriteUsage]); + p.drawPath(path); + highlightLabel.setLeft(highlightLabel.left() + triRadius * 2); + + text = lit(" Read/Write, "); + p.drawText(highlightLabel, text, to); + highlightLabel.setLeft(highlightLabel.left() + fm.width(text)); + + path = triangle.translated(aliasAlign(highlightLabel.topLeft())); + p.fillPath(path, colors[ReadWriteUsage]); + p.drawPath(path); + highlightLabel.setLeft(highlightLabel.left() + triRadius * 2); + + if(m_Ctx.CurPipelineState().SupportsBarriers()) + { + text = lit(" Barriers, "); + p.drawText(highlightLabel, text, to); + highlightLabel.setLeft(highlightLabel.left() + fm.width(text)); + + path = triangle.translated(aliasAlign(highlightLabel.topLeft())); + p.fillPath(path, colors[BarrierUsage]); + p.drawPath(path); + highlightLabel.setLeft(highlightLabel.left() + triRadius * 2); + } + + text = lit(" and Clears "); + p.drawText(highlightLabel, text, to); + highlightLabel.setLeft(highlightLabel.left() + fm.width(text)); + + path = triangle.translated(aliasAlign(highlightLabel.topLeft())); + p.fillPath(path, colors[ClearUsage]); + p.drawPath(path); + highlightLabel.setLeft(highlightLabel.left() + triRadius * 2); + } + + QPainterPath paths[UsageCount]; + + QRectF pipsRect = m_highlightingRect.marginsRemoved(uniformMargins(margin)); + + pipsRect.setX(pipsRect.x() + margin + m_titleWidth); + pipsRect.setHeight(triHeight + margin); + pipsRect.moveBottom(m_highlightingRect.bottom()); + + p.setClipRect(pipsRect); + + if(!m_HistoryEvents.isEmpty()) + { + for(const PixelModification &mod : m_HistoryEvents) + { + QPointF pos; + + pos.setX(offsetOf(mod.eventID) + m_eidWidth / 2 - triRadius); + pos.setY(pipsRect.y()); + + QPainterPath path = triangle.translated(aliasAlign(pos)); + + if(mod.passed()) + paths[HistoryPassed] = paths[HistoryPassed].united(path); + else + paths[HistoryFailed] = paths[HistoryFailed].united(path); + } + } + else + { + for(const EventUsage &use : m_UsageEvents) + { + QPointF pos; + + pos.setX(offsetOf(use.eventID) + m_eidWidth / 2 - triRadius); + pos.setY(pipsRect.y()); + + QPainterPath path = triangle.translated(aliasAlign(pos)); + + if(((int)use.usage >= (int)ResourceUsage::VS_RWResource && + (int)use.usage <= (int)ResourceUsage::All_RWResource) || + use.usage == ResourceUsage::GenMips || use.usage == ResourceUsage::Copy || + use.usage == ResourceUsage::Resolve) + { + paths[ReadWriteUsage] = paths[ReadWriteUsage].united(path); + } + else if(use.usage == ResourceUsage::StreamOut || use.usage == ResourceUsage::ResolveDst || + use.usage == ResourceUsage::ColorTarget || use.usage == ResourceUsage::CopyDst) + { + paths[WriteUsage] = paths[WriteUsage].united(path); + } + else if(use.usage == ResourceUsage::Clear) + { + paths[ClearUsage] = paths[ClearUsage].united(path); + } + else if(use.usage == ResourceUsage::Barrier) + { + paths[BarrierUsage] = paths[BarrierUsage].united(path); + } + else + { + paths[ReadUsage] = paths[ReadUsage].united(path); + } + } + } + + for(int i = 0; i < UsageCount; i++) + { + if(!paths[i].isEmpty()) + { + p.drawPath(paths[i]); + p.fillPath(paths[i], colors[i]); + } + } + } + else + { + QRectF highlightLabel = m_highlightingRect; + highlightLabel = highlightLabel.marginsRemoved(uniformMargins(margin)); + + highlightLabel.setX(highlightLabel.x() + margin); + + p.drawText(highlightLabel, tr("No resource selected for highlighting."), to); + } } TimelineBar::Marker *TimelineBar::findMarker(QVector &markers, QRectF markerRect, QPointF pos) @@ -642,6 +941,8 @@ uint32_t TimelineBar::processDraws(QVector &markers, QVector & m.eidStart = d.eventID; m.eidEnd = processDraws(m.children, m.draws, d.children); + maxEID = qMax(maxEID, m.eidEnd); + if(d.markerColor[3] > 0.0f) { m.color = QColor::fromRgb( diff --git a/qrenderdoc/Windows/TimelineBar.h b/qrenderdoc/Windows/TimelineBar.h index 52c2f376a..11566b3c4 100644 --- a/qrenderdoc/Windows/TimelineBar.h +++ b/qrenderdoc/Windows/TimelineBar.h @@ -75,10 +75,17 @@ private: QVector m_RootDraws; QVector m_Draws; + QString m_HistoryTarget; + QList m_HistoryEvents; + + QString m_UsageTarget; + QList m_UsageEvents; + const qreal margin = 2.0; const qreal borderWidth = 1.0; const QString eidAxisTitle = lit("EID:"); - const int eidAxisHeight = 18; + const int dataBarHeight = 18; + const int highlightingExtra = 12; int m_eidAxisLabelStep = 0; qreal m_eidAxisLabelTextWidth = 0; @@ -89,6 +96,7 @@ private: QRectF m_dataArea; QRectF m_eidAxisRect; QRectF m_markerRect; + QRectF m_highlightingRect; qreal m_titleWidth = 0; qreal m_zoom = 1.0;