void vogleditor_apiCallTimelineModel::AddApiCallsToTimeline(vogleditor_apiCallTreeItem *pParentCallTreeItem, vogleditor_timelineItem *pParentTimelineItem) { vogleditor_timelineItem *pNewTimelineItem; int numChildren = pParentCallTreeItem->childCount(); for (int c = 0; c < numChildren; c++) { vogleditor_apiCallTreeItem *pChildCallTreeItem = pParentCallTreeItem->child(c); float beginFloat = u64ToFloat(pChildCallTreeItem->startTime() - m_rawBaseTime); float endFloat = u64ToFloat(pChildCallTreeItem->endTime() - m_rawBaseTime); if (pChildCallTreeItem->isGroup()) { // Create a group timelineItem with group color pNewTimelineItem = new vogleditor_timelineItem(beginFloat, endFloat, m_rootItem, pChildCallTreeItem->groupItem()); QColor color; if (pChildCallTreeItem->isStateChangeGroup()) { color = Qt::green; } else if (pChildCallTreeItem->isRenderGroup()) { color = Qt::red; } pNewTimelineItem->setBrush(new QBrush(color, Qt::Dense5Pattern)); } else // (API call) { // close a timeline parent group if the tree parent group has ended if (!pChildCallTreeItem->parent()->isGroup() && pParentTimelineItem->isGroupItem()) { pParentTimelineItem = pParentTimelineItem->parent(); } // Create new timeline apicall item pNewTimelineItem = new vogleditor_timelineItem(beginFloat, endFloat, pParentTimelineItem, pChildCallTreeItem->apiCallItem()); // Add random color for debug marker group parent if (g_settings.group_debug_marker_in_use()) { // (For now) only if State/Render groups are enabled so that // default timeline display isn't affected (debug marker groups // are checked on) // TODO: remove check for state/render status if/when consensus // is the debug marker groups should be separately colored // by default (when checked on) if (g_settings.group_state_render_stat()) { if (vogl_is_marker_push_entrypoint(pChildCallTreeItem->entrypoint_id())) { pNewTimelineItem->setBrush(new QBrush(QColor(randomRGB()))); } } } } AddApiCallsToTimeline(pChildCallTreeItem, pNewTimelineItem); } }
//----------------------------------------------------------------------------- void vktraceviewer_QTimelineView::calculateRectsIfNecessary() { if (!m_hashIsDirty) { return; } if (model() == NULL) { return; } int itemHeight = m_threadHeight * 0.4; for (int threadIndex = 0; threadIndex < m_threadIdList.size(); threadIndex++) { int top = (m_threadHeight * threadIndex) + (m_threadHeight * 0.5) - itemHeight/2; this->m_threadArea[threadIndex] = QRect(0, top, viewport()->width(), itemHeight); } int numRows = model()->rowCount(); for (int row = 0; row < numRows; row++) { QRectF rect; QModelIndex item = model()->index(row, vktraceviewer_QTraceFileModel::Column_EntrypointName); vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)item.internalPointer(); // make sure item is valid size if (pHeader->entrypoint_end_time > pHeader->entrypoint_begin_time) { int threadIndex = m_threadIdList.indexOf(pHeader->thread_id); int topOffset = (m_threadHeight * threadIndex) + (m_threadHeight * 0.5); uint64_t duration = pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time; float leftOffset = u64ToFloat(pHeader->entrypoint_begin_time - m_rawStartTime); float Width = u64ToFloat(duration); // create the rect that represents this item rect.setLeft(leftOffset); rect.setTop(topOffset - (itemHeight/2)); rect.setWidth(Width); rect.setHeight(itemHeight); } m_rowToRectMap[row] = rect; } m_hashIsDirty = false; viewport()->update(); }
void vogleditor_apiCallTimelineModel::AddApiCallsToTimeline(vogleditor_apiCallTreeItem* pRoot, vogleditor_timelineItem* pDestRoot) { int numChildren = pRoot->childCount(); for (int c = 0; c < numChildren; c++) { vogleditor_apiCallTreeItem* pChild = pRoot->child(c); if (pChild->apiCallItem() != NULL) { float beginFloat = u64ToFloat(pChild->apiCallItem()->startTime() - m_rawBaseTime); float endFloat = u64ToFloat(pChild->apiCallItem()->endTime() - m_rawBaseTime); vogleditor_timelineItem* pNewItem = new vogleditor_timelineItem(beginFloat, endFloat, pDestRoot); pNewItem->setApiCallItem(pChild->apiCallItem()); pDestRoot->appendChild(pNewItem); AddApiCallsToTimeline(pChild, pNewItem); } } }
void vktraceviewer_QTimelineItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { vktrace_trace_packet_header* pHeader = (vktrace_trace_packet_header*)index.internalPointer(); if (pHeader->entrypoint_end_time <= pHeader->entrypoint_begin_time) { return; } painter->save(); { vktraceviewer_QTimelineView* pTimeline = (vktraceviewer_QTimelineView*)parent(); if (pTimeline != NULL) { QRectF rect = option.rect; if (rect.width() == 0) { rect.setWidth(1); } float duration = u64ToFloat(pHeader->entrypoint_end_time - pHeader->entrypoint_begin_time); float durationRatio = duration / pTimeline->getMaxItemDuration(); int intensity = std::min(255, (int)(durationRatio * 255.0f)); QColor color(intensity, 255-intensity, 0); // add gradient to the items better distinguish between the end of one and beginning of the next QLinearGradient linearGrad(rect.center(), rect.bottomRight()); linearGrad.setColorAt(0, color); linearGrad.setColorAt(1, color.darker(150)); painter->setBrush(linearGrad); painter->setPen(Qt::NoPen); painter->drawRect(rect); if (rect.width() >= 2) { // draw shadow and highlight around the item painter->setPen(color.darker(175)); painter->drawLine(rect.right()-1, rect.top(), rect.right()-1, rect.bottom()-1); painter->drawLine(rect.right()-1, rect.bottom()-1, rect.left(), rect.bottom()-1); painter->setPen(color.lighter()); painter->drawLine(rect.left(), rect.bottom()-1, rect.left(), rect.top()); painter->drawLine(rect.left(), rect.top(), rect.right()-1, rect.top()); } } } painter->restore(); }
//----------------------------------------------------------------------------- void vktraceviewer_QTimelineView::resizeEvent(QResizeEvent *event) { m_hashIsDirty = true; deletePixmap(); // The duration to viewport scale should allow us to map the entire timeline into the current window width. if (m_lineLength > 0) { // Calculate zoom ratio prior to the resize float ratio = m_zoomFactor / m_durationToViewportScale; // Adjust scale that fits the timeline duration to the viewport area int timelineViewportWidth = viewport()->width() - 2*m_margin - m_scrollBarWidth; m_durationToViewportScale = (float)timelineViewportWidth / u64ToFloat(m_lineLength); // Adjust the zoom factor based on the new duration to viewport scale and the previous ratio m_zoomFactor = m_durationToViewportScale * ratio; // Adjust horizontal scroll bar to maintain current view as best as possible float hRatio = (float)horizontalScrollBar()->value() / qMax(1.0f,(float)horizontalScrollBar()->maximum()); updateGeometries(); horizontalScrollBar()->setValue(hRatio * horizontalScrollBar()->maximum()); } }
//----------------------------------------------------------------------------- float vktraceviewer_QTimelineView::scaleDurationHorizontally(uint64_t value) const { float scaled = u64ToFloat(value) * m_zoomFactor; return scaled; }
//----------------------------------------------------------------------------- void vktraceviewer_QTimelineView::setModel(QAbstractItemModel* pModel) { QAbstractItemView::setModel(pModel); m_hashIsDirty = true; setItemDelegate(&m_itemDelegate); m_threadIdList.clear(); m_threadMask.clear(); m_maxItemDuration = 0; m_rawStartTime = 0; m_rawEndTime = 0; deletePixmap(); // Gather some stats from the model if (model() == NULL) { horizontalScrollBar()->setRange(0,0); verticalScrollBar()->setRange(0,0); return; } int numRows = model()->rowCount(); for (int i = 0; i < numRows; i++) { // Count number of unique thread Ids QModelIndex item = model()->index(i, vktraceviewer_QTraceFileModel::Column_ThreadId); if (item.isValid()) { uint32_t threadId = item.data().toUInt(); if (!m_threadIdList.contains(threadId)) { m_threadIdList.append(threadId); m_threadMask.insert(threadId, QVector<int>()); m_threadArea.append(QRect()); } } // Find duration of longest item item = model()->index(i, vktraceviewer_QTraceFileModel::Column_CpuDuration); if (item.isValid()) { float duration = item.data().toFloat(); if (m_maxItemDuration < duration) { m_maxItemDuration = duration; } } } // Get start time QModelIndex start = model()->index(0, vktraceviewer_QTraceFileModel::Column_BeginTime); if (start.isValid()) { m_rawStartTime = start.data().toULongLong(); } // Get end time QModelIndex end = model()->index(numRows - 1, vktraceviewer_QTraceFileModel::Column_EndTime); if (end.isValid()) { m_rawEndTime = end.data().toULongLong(); } // the duration to viewport scale should allow us to map the entire timeline into the current window width. m_lineLength = m_rawEndTime - m_rawStartTime; int initialTimelineWidth = viewport()->width() - 2*m_margin - m_scrollBarWidth; m_durationToViewportScale = (float)initialTimelineWidth / u64ToFloat(m_lineLength); m_zoomFactor = m_durationToViewportScale; verticalScrollBar()->setMaximum(1000); verticalScrollBar()->setValue(0); verticalScrollBar()->setPageStep(1); verticalScrollBar()->setSingleStep(1); }
void vogleditor_apiCallTimelineModel::refresh() { if (m_pRootApiCall == NULL) { return; } float timelineStart = 0; float timelineEnd = 1; m_rawBaseTime = timelineStart; int numChildren = m_pRootApiCall->childCount(); if (numChildren > 0) { uint64_t firstStart = 0; uint64_t firstEnd = 0; m_pRootApiCall->child(0)->frameItem()->getStartEndTimes(firstStart, firstEnd); uint64_t lastStart = 0; uint64_t lastEnd = 0; vogleditor_apiCallTreeItem* pLastChild = m_pRootApiCall->child(numChildren-1); pLastChild->frameItem()->getStartEndTimes(lastStart, lastEnd); m_rawBaseTime = firstStart; timelineStart = u64ToFloat(firstStart - m_rawBaseTime); timelineEnd = u64ToFloat(lastEnd - m_rawBaseTime); } // see if we actually have to update some of this stuff bool skipCreation = false; if (m_rootItem != NULL) { if (m_rootItem->getDuration() == timelineEnd - timelineStart && m_rootItem->childCount() == numChildren) { // no need to make a new root skipCreation = true; } } if (!skipCreation) { if (m_rootItem != NULL) { delete m_rootItem; } m_rootItem = new vogleditor_timelineItem(timelineStart, timelineEnd); // add markers for the start of each frame float frameStart = 0; for (int c = 0; c < numChildren; c++) { vogleditor_apiCallTreeItem* pFrameItem = m_pRootApiCall->child(c); if (pFrameItem->childCount() > 0) { frameStart = u64ToFloat(pFrameItem->child(0)->apiCallItem()->startTime() - m_rawBaseTime); vogleditor_timelineItem* pFrameTimelineItem = new vogleditor_timelineItem(frameStart, m_rootItem); pFrameTimelineItem->setFrameItem(pFrameItem->frameItem()); m_rootItem->appendChild(pFrameTimelineItem); } else { // if we get here, we are at a frame that had no api calls } } // recursively add each children for (int frameIndex = 0; frameIndex < numChildren; frameIndex++) { vogleditor_apiCallTreeItem* pFrameChild = m_pRootApiCall->child(frameIndex); AddApiCallsToTimeline(pFrameChild, m_rootItem); } } }