public int UpdateSize(string label, int x, int y, int padding, int height, int width = -1) { _visible = true; _label = label; if (width == -1) { // Dummy draw to measure size width = (int)_fontService.DrawText(label, 0, 0, height, false); } UpdateSize(x, y, padding, width, height); return(_right - _left); }
/// <summary> /// Profile Render Loop /// </summary> /// <remarks>There is no need to call the base implementation.</remarks> public void Draw() { if (!_visible || !_initComplete) { return; } // Update viewport if (_viewportUpdated) { GL.Viewport(0, 0, Width, Height); GL.MatrixMode(MatrixMode.Projection); GL.LoadIdentity(); GL.Ortho(0, Width, 0, Height, 0.0, 4.0); _fontService.UpdateScreenHeight(Height); _viewportUpdated = false; _redrawPending = true; } if (!_redrawPending) { return; } // Frame setup GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.ClearColor(Color.Black); _fontService.fontColor = Color.White; int verticalIndex = 0; float width; float maxWidth = 0; float yOffset = _scrollPos - TitleHeight; float xOffset = 10; float timingDataLeft; float timingWidth; // Background lines to make reading easier #region Background Lines GL.Enable(EnableCap.ScissorTest); GL.Scissor(0, BottomBarHeight, Width, Height - TitleHeight - BottomBarHeight); GL.Begin(PrimitiveType.Triangles); GL.Color3(0.2f, 0.2f, 0.2f); for (int i = 0; i < _sortedProfileData.Count; i += 2) { float top = GetLineY(yOffset, LineHeight, LinePadding, false, i - 1); float bottom = GetLineY(yOffset, LineHeight, LinePadding, false, i); // Skip rendering out of bounds bars if (top < 0 || bottom > Height) { continue; } GL.Vertex2(0, bottom); GL.Vertex2(0, top); GL.Vertex2(Width, top); GL.Vertex2(Width, top); GL.Vertex2(Width, bottom); GL.Vertex2(0, bottom); } GL.End(); _maxScroll = (LineHeight + LinePadding) * (_sortedProfileData.Count - 1); #endregion lock (_profileDataLock) { // Display category #region Category verticalIndex = 0; foreach (var entry in _sortedProfileData) { if (entry.Key.Category == null) { verticalIndex++; continue; } float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex++); width = _fontService.DrawText(entry.Key.Category, xOffset, y, LineHeight); if (width > maxWidth) { maxWidth = width; } } GL.Disable(EnableCap.ScissorTest); width = _fontService.DrawText("Category", xOffset, Height - TitleFontHeight, TitleFontHeight); if (width > maxWidth) { maxWidth = width; } xOffset += maxWidth + ColumnSpacing; #endregion // Display session group #region Session Group maxWidth = 0; verticalIndex = 0; GL.Enable(EnableCap.ScissorTest); foreach (var entry in _sortedProfileData) { if (entry.Key.SessionGroup == null) { verticalIndex++; continue; } float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex++); width = _fontService.DrawText(entry.Key.SessionGroup, xOffset, y, LineHeight); if (width > maxWidth) { maxWidth = width; } } GL.Disable(EnableCap.ScissorTest); width = _fontService.DrawText("Group", xOffset, Height - TitleFontHeight, TitleFontHeight); if (width > maxWidth) { maxWidth = width; } xOffset += maxWidth + ColumnSpacing; #endregion // Display session item #region Session Item maxWidth = 0; verticalIndex = 0; GL.Enable(EnableCap.ScissorTest); foreach (var entry in _sortedProfileData) { if (entry.Key.SessionItem == null) { verticalIndex++; continue; } float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex++); width = _fontService.DrawText(entry.Key.SessionItem, xOffset, y, LineHeight); if (width > maxWidth) { maxWidth = width; } } GL.Disable(EnableCap.ScissorTest); width = _fontService.DrawText("Item", xOffset, Height - TitleFontHeight, TitleFontHeight); if (width > maxWidth) { maxWidth = width; } xOffset += maxWidth + ColumnSpacing; _buttons[(int)ButtonIndex.TagTitle].UpdateSize(0, Height - TitleFontHeight, 0, (int)xOffset, TitleFontHeight); #endregion // Timing data timingWidth = Width - xOffset - 370; timingDataLeft = xOffset; GL.Scissor((int)xOffset, BottomBarHeight, (int)timingWidth, Height - TitleHeight - BottomBarHeight); if (_displayGraph) { DrawGraph(xOffset, yOffset, timingWidth); } else { DrawBars(xOffset, yOffset, timingWidth); } GL.Scissor(0, BottomBarHeight, Width, Height - TitleHeight - BottomBarHeight); if (!_displayGraph) { _fontService.DrawText("Blue: Instant, Green: Avg, Red: Total", xOffset, Height - TitleFontHeight, TitleFontHeight); } xOffset = Width - 360; // Display timestamps #region Timestamps verticalIndex = 0; long totalInstant = 0; long totalAverage = 0; long totalTime = 0; long totalCount = 0; GL.Enable(EnableCap.ScissorTest); foreach (var entry in _sortedProfileData) { float y = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex++); _fontService.DrawText($"{GetTimeString(entry.Value.Instant)} ({entry.Value.InstantCount})", xOffset, y, LineHeight); _fontService.DrawText(GetTimeString(entry.Value.AverageTime), 150 + xOffset, y, LineHeight); _fontService.DrawText(GetTimeString(entry.Value.TotalTime), 260 + xOffset, y, LineHeight); totalInstant += entry.Value.Instant; totalAverage += entry.Value.AverageTime; totalTime += entry.Value.TotalTime; totalCount += entry.Value.InstantCount; } GL.Disable(EnableCap.ScissorTest); float yHeight = Height - TitleFontHeight; _fontService.DrawText("Instant (Count)", xOffset, yHeight, TitleFontHeight); _buttons[(int)ButtonIndex.InstantTitle].UpdateSize((int)xOffset, (int)yHeight, 0, 130, TitleFontHeight); _fontService.DrawText("Average", 150 + xOffset, yHeight, TitleFontHeight); _buttons[(int)ButtonIndex.AverageTitle].UpdateSize((int)(150 + xOffset), (int)yHeight, 0, 130, TitleFontHeight); _fontService.DrawText("Total (ms)", 260 + xOffset, yHeight, TitleFontHeight); _buttons[(int)ButtonIndex.TotalTitle].UpdateSize((int)(260 + xOffset), (int)yHeight, 0, Width, TitleFontHeight); // Totals yHeight = FilterHeight + 3; int textHeight = LineHeight - 2; _fontService.fontColor = new Color(100, 100, 255, 255); float tempWidth = _fontService.DrawText($"Host {GetTimeString(_timingFlagsLast[(int)TimingFlagType.SystemFrame])} " + $"({GetTimeString(_timingFlagsAverages[(int)TimingFlagType.SystemFrame])})", 5, yHeight, textHeight); _fontService.fontColor = Color.Red; _fontService.DrawText($"Game {GetTimeString(_timingFlagsLast[(int)TimingFlagType.FrameSwap])} " + $"({GetTimeString(_timingFlagsAverages[(int)TimingFlagType.FrameSwap])})", 15 + tempWidth, yHeight, textHeight); _fontService.fontColor = Color.White; _fontService.DrawText($"{GetTimeString(totalInstant)} ({totalCount})", xOffset, yHeight, textHeight); _fontService.DrawText(GetTimeString(totalAverage), 150 + xOffset, yHeight, textHeight); _fontService.DrawText(GetTimeString(totalTime), 260 + xOffset, yHeight, textHeight); #endregion } #region Bottom bar // Show/Hide Inactive float widthShowHideButton = _buttons[(int)ButtonIndex.ShowHideInactive].UpdateSize($"{(_showInactive ? "Hide" : "Show")} Inactive", 5, 5, 4, 16); // Play/Pause float widthPlayPauseButton = _buttons[(int)ButtonIndex.Pause].UpdateSize(_paused ? "Play" : "Pause", 15 + (int)widthShowHideButton, 5, 4, 16) + widthShowHideButton; // Step float widthStepButton = widthPlayPauseButton; if (_paused) { widthStepButton += _buttons[(int)ButtonIndex.Step].UpdateSize("Step", (int)(25 + widthPlayPauseButton), 5, 4, 16) + 10; _buttons[(int)ButtonIndex.Step].Draw(); } // Change display float widthChangeDisplay = _buttons[(int)ButtonIndex.ChangeDisplay].UpdateSize($"View: {(_displayGraph ? "Graph" : "Bars")}", 25 + (int)widthStepButton, 5, 4, 16) + widthStepButton; width = widthChangeDisplay; if (_displayGraph) { width += _buttons[(int)ButtonIndex.ToggleFlags].UpdateSize($"{(_displayFlags ? "Hide" : "Show")} Flags", 35 + (int)widthChangeDisplay, 5, 4, 16) + 10; _buttons[(int)ButtonIndex.ToggleFlags].Draw(); } // Filter bar _fontService.DrawText($"{(_regexEnabled ? "Regex " : "Filter")}: {_filterText}", 35 + width, 7, 16); _buttons[(int)ButtonIndex.FilterBar].UpdateSize((int)(45 + width), 0, 0, Width, FilterHeight); #endregion // Draw buttons for (int i = 0; i < (int)ButtonIndex.Autodraw; i++) { _buttons[i].Draw(); } // Dividing lines #region Dividing lines GL.Color3(Color.White); GL.Begin(PrimitiveType.Lines); // Top divider GL.Vertex2(0, Height - TitleHeight); GL.Vertex2(Width, Height - TitleHeight); // Bottom divider GL.Vertex2(0, FilterHeight); GL.Vertex2(Width, FilterHeight); GL.Vertex2(0, BottomBarHeight); GL.Vertex2(Width, BottomBarHeight); // Bottom vertical dividers GL.Vertex2(widthShowHideButton + 10, 0); GL.Vertex2(widthShowHideButton + 10, FilterHeight); GL.Vertex2(widthPlayPauseButton + 20, 0); GL.Vertex2(widthPlayPauseButton + 20, FilterHeight); if (_paused) { GL.Vertex2(widthStepButton + 20, 0); GL.Vertex2(widthStepButton + 20, FilterHeight); } if (_displayGraph) { GL.Vertex2(widthChangeDisplay + 30, 0); GL.Vertex2(widthChangeDisplay + 30, FilterHeight); } GL.Vertex2(width + 30, 0); GL.Vertex2(width + 30, FilterHeight); // Column dividers float timingDataTop = Height - TitleHeight; GL.Vertex2(timingDataLeft, FilterHeight); GL.Vertex2(timingDataLeft, timingDataTop); GL.Vertex2(timingWidth + timingDataLeft, FilterHeight); GL.Vertex2(timingWidth + timingDataLeft, timingDataTop); GL.End(); #endregion _redrawPending = false; SwapBuffers(); }