Skip to content

feat(perfoverlay): add frame time and a/b testing stats#1250

Merged
alandtse merged 18 commits into
community-shaders:devfrom
alandtse:perf_overlay_refactor
Jul 13, 2025
Merged

feat(perfoverlay): add frame time and a/b testing stats#1250
alandtse merged 18 commits into
community-shaders:devfrom
alandtse:perf_overlay_refactor

Conversation

@alandtse
Copy link
Copy Markdown
Collaborator

@alandtse alandtse commented Jul 13, 2025

  • Add frame timing stats and per shader disabling
  • Add a/b testing
  • Use new feature type overlay and moved both weatherpicker and perfoverlay to use this
image image

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive, modular performance overlay for real-time monitoring of FPS, frame times, draw calls, VRAM usage, and per-shader performance, with advanced A/B testing and comparison tools.
    • Added an A/B Testing system for automated performance comparisons between shader configurations, including statistical analysis and settings diff display.
    • Weather Picker now supports overlay display and improved configuration options.
    • Enhanced overlay system to support multiple, independently togglable overlays.
  • Improvements

    • Added detailed per-shader frame time tracking and smoothing for more accurate performance insights.
    • Improved overlay toggle and hotkey configuration for easier access.
  • Bug Fixes

    • Corrected hotkey display for overlay toggling.
  • Refactor

    • Replaced legacy performance overlay with a modular, extensible overlay and testing framework.
    • Standardized shader type handling and improved thread safety in performance statistics.
  • Chores

    • Added utility functions for formatting, statistics, and JSON diffing to support new overlay features.
    • Updated documentation and internal structure for better maintainability.

Copilot AI review requested due to automatic review settings July 13, 2025 00:55
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jul 13, 2025

Walkthrough

This change introduces a new modular Performance Overlay feature with comprehensive real-time performance monitoring, A/B testing capabilities, and an extensible overlay system. It removes the legacy performance overlay code, refactors feature management for overlays, adds per-shader-type frame time tracking, and introduces new utility functions for formatting, statistics, and UI rendering. The WeatherPicker feature is also integrated as an overlay.

Changes

File(s) / Path(s) Change Summary
features/Performance Overlay/Shaders/Features/PerformanceOverlay.ini Added new configuration file for PerformanceOverlay feature with version info.
src/Feature.cpp, src/Globals.cpp, src/Globals.h Integrated PerformanceOverlay feature into global feature management and initialization.
src/Features/OverlayFeature.h Introduced new abstract base class OverlayFeature for modular overlay features with UI category support.
src/Features/PerformanceOverlay.cpp, src/Features/PerformanceOverlay.h Added new comprehensive PerformanceOverlay feature with real-time metrics, A/B testing, overlay UI, settings, and per-shader performance breakdowns.
src/Features/PerformanceOverlay/ABTesting/ABTestAggregator.cpp, src/Features/PerformanceOverlay/ABTesting/ABTestAggregator.h Implemented A/B test aggregator for collecting and analyzing performance data between test variants, with outlier filtering and statistics.
src/Features/PerformanceOverlay/ABTesting/ABTesting.cpp, src/Features/PerformanceOverlay/ABTesting/ABTesting.h Added A/B testing manager for automated switching, interval configuration, overlay display, and integration with aggregator.
src/Features/WeatherPicker.cpp, src/Features/WeatherPicker.h Refactored WeatherPicker to inherit from OverlayFeature, added overlay drawing and visibility logic, and direct settings access.
src/Menu.cpp, src/Menu.h Removed legacy performance overlay code and settings; integrated modular overlay system and A/B testing UI; updated overlay toggle logic and feature iteration.
src/State.cpp, src/State.h Added per-shader-type frame time tracking, smoothing, and thread safety; improved enum handling and added statistics utilities.
src/Utils/FileSystem.cpp, src/Utils/FileSystem.h Added JSON diff utility to compute and represent differences between user and test settings.
src/Utils/Format.cpp, src/Utils/Format.h Added utility functions for formatting milliseconds, microseconds, percentages, deltas, and elapsed time; calculation helpers for percentages and costs.
src/Utils/PerfUtils.h Introduced new header with inline performance/statistics utilities (frame time, FPS, mean, median).
src/Utils/UI.cpp, src/Utils/UI.h Removed legacy overlay instance; added colored multi-line tooltip rendering, threshold color helper, and generic sortable ImGui table utilities.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Menu
    participant OverlayFeature
    participant PerformanceOverlay
    participant ABTestingManager
    participant ABTestAggregator
    participant State

    User->>Menu: Press overlay toggle key / open menu
    Menu->>OverlayFeature: Iterate loaded features, call DrawOverlay()
    OverlayFeature-->>PerformanceOverlay: (if PerformanceOverlay enabled)
    PerformanceOverlay->>State: Query per-shader metrics (draw calls, frame times)
    PerformanceOverlay->>ABTestingManager: Query A/B test state
    ABTestingManager->>ABTestAggregator: On variant switch, OnFrame (collect data)
    PerformanceOverlay->>PerformanceOverlay: Render overlay UI (metrics, graphs, A/B results)
    Menu->>WeatherPicker: DrawOverlay() if enabled
Loading
sequenceDiagram
    participant User
    participant ABTestingManager
    participant PerformanceOverlay
    participant ABTestAggregator

    User->>ABTestingManager: Enable A/B testing, set interval
    ABTestingManager->>PerformanceOverlay: Save/restore test config
    ABTestingManager->>ABTestAggregator: OnABSwitch(Variant)
    loop Every frame
        PerformanceOverlay->>ABTestAggregator: OnFrame(draw call data)
        ABTestingManager->>PerformanceOverlay: Update()
    end
    User->>ABTestingManager: Disable A/B testing
    ABTestingManager->>ABTestAggregator: OnTestEnd()
    PerformanceOverlay->>ABTestAggregator: GetAggregatedResults()
    PerformanceOverlay->>PerformanceOverlay: Render A/B test results table
Loading

Possibly related PRs

Suggested reviewers

  • davo0411

Poem

In overlays bright, the rabbits delight,
With graphs and stats that dance in the night.
A/B they compare, with metrics so fair,
Frame times and calls floating on air.
Modular now, the code hops anew—
Performance revealed, in every view!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

This comment was marked as outdated.

@github-actions
Copy link
Copy Markdown

Using provided base ref: 803ba41
Using base ref: 803ba41
Base commit date: 2025-07-12T22:51:17+08:00 (Saturday, July 12, 2025 10:51 PM)
No actionable suggestions for changed features.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (9)
src/Utils/UI.cpp (1)

608-616: Consider using else-if for clarity.

The threshold logic is correct, but the code could be more readable with explicit else-if blocks.

 ImVec4 GetThresholdColor(float value, float good, float warn, ImVec4 goodColor, ImVec4 warnColor, ImVec4 badColor)
 {
-	if (value < good)
-		return goodColor;
-	else if (value < warn)
-		return warnColor;
-	else
-		return badColor;
+	if (value < good) {
+		return goodColor;
+	} else if (value < warn) {
+		return warnColor;
+	} else {
+		return badColor;
+	}
 }
src/Features/PerformanceOverlay/ABTesting/ABTesting.cpp (1)

85-95: Consider bounds checking for the slider.

While the ImGui slider should handle bounds internally, it's good practice to validate the interval value.

Add validation after the slider:

 if (ImGui::SliderInt("A/B Test Interval", reinterpret_cast<int*>(&testInterval), 0, 10)) {
+	testInterval = std::clamp(testInterval, 0u, 10u);
 	bool overlayWasEnabled = performanceOverlay->settings.ShowInOverlay;
src/Menu.h (1)

178-179: Consider making overlayVisible private with getter/setter methods.

The public overlayVisible member breaks encapsulation. Consider making it private and providing getter/setter methods to maintain consistency with the class design pattern used for other members like settings.

-	bool overlayVisible = false;
-
 
 	// Static utility functions
 	static const char* KeyIdToString(uint32_t key);
 
 private:
+	bool overlayVisible = false;
 	Settings settings;

Add public methods in the public section:

+	bool IsOverlayVisible() const { return overlayVisible; }
+	void SetOverlayVisible(bool visible) { overlayVisible = visible; }
src/Utils/Format.cpp (1)

120-131: Consider simplifying the percentage calculation logic.

The percentage calculation logic is correct but could be more readable. The current implementation calculates absolute percentage change differently based on which value is smaller, which is mathematically sound but could be clearer.

 std::string FormatDeltaWithPercent(float a, float b, float threshold)
 {
 	float delta = b - a;
 	float percentDelta = 0.0f;
-	if (a < b && a > 0.0f) {
-		percentDelta = 100.0f * (b - a) / a;
-	} else if (b < a && b > 0.0f) {
-		percentDelta = 100.0f * (a - b) / b;
-	}
-	std::string percentStr = (percentDelta >= threshold) ? std::format(" ({:+.1f}%)", (b < a ? -percentDelta : percentDelta)) : "";
+	if (a > 0.0f) {
+		percentDelta = 100.0f * delta / a;
+	}
+	std::string percentStr = (std::abs(percentDelta) >= threshold) ? std::format(" ({:+.1f}%)", percentDelta) : "";
 	return (delta > 0.0f ? "+" : "") + FormatMilliseconds(delta) + percentStr;
 }
src/Menu.cpp (1)

1724-1729: Remove redundant style variable handling.

The style variable is captured by reference but then immediately overwritten with oldStyle, making the reference capture redundant.

Apply this diff to simplify:

-    ImGuiStyle& style = ImGui::GetStyle();
-    style = oldStyle;
+    ImGui::GetStyle() = oldStyle;
 
     ImGui::Render();
     ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
src/Features/PerformanceOverlay.cpp (3)

61-91: Consider simplifying the complex template metaprogramming in MakeMetricColumn.

The nested lambda with template metaprogramming using if constexpr and std::is_same_v makes this code hard to maintain. Consider splitting this into two separate helper functions - one for optional values and one for regular values.

-auto MakeMetricColumn(const auto& theme, auto valueGetter, auto colorGetter, auto formatter, const Util::ColoredTextLines& legend, const Util::ColoredTextLines* cellLegend = nullptr)
-{
-	return [theme, valueGetter, colorGetter, formatter, legend, cellLegend](const DrawCallRow& row, int) {
-		using ValueType = decltype(valueGetter(row));
-		if constexpr (std::is_same_v<ValueType, std::optional<float>>) {
-			if (!valueGetter(row).has_value()) {
-				ImGui::TextDisabled("-");
-				return;
-			}
-			float value = *valueGetter(row);
-			ImVec4 color = colorGetter(theme, value, row);
-			std::string valueStr = formatter(value, row);
-			ImGui::PushStyleColor(ImGuiCol_Text, color);
-			ImGui::Text("%s", valueStr.c_str());
-			ImGui::PopStyleColor();
-		} else {
-			float value = valueGetter(row);
-			ImVec4 color = colorGetter(theme, value, row);
-			std::string valueStr = formatter(value, row);
-			ImGui::PushStyleColor(ImGuiCol_Text, color);
-			ImGui::Text("%s", valueStr.c_str());
-			ImGui::PopStyleColor();
-		}
-		if (ImGui::IsItemHovered()) {
-			if (auto _tt = Util::HoverTooltipWrapper()) {
-				const Util::ColoredTextLines& useLegend = cellLegend ? *cellLegend : legend;
-				Util::DrawColoredMultiLineTooltip(useLegend);
-			}
-		}
-	};
-}
+// Helper for required values
+auto MakeRequiredMetricColumn(const auto& theme, auto valueGetter, auto colorGetter, auto formatter, const Util::ColoredTextLines& legend, const Util::ColoredTextLines* cellLegend = nullptr)
+{
+	return [theme, valueGetter, colorGetter, formatter, legend, cellLegend](const DrawCallRow& row, int) {
+		float value = valueGetter(row);
+		ImVec4 color = colorGetter(theme, value, row);
+		std::string valueStr = formatter(value, row);
+		ImGui::PushStyleColor(ImGuiCol_Text, color);
+		ImGui::Text("%s", valueStr.c_str());
+		ImGui::PopStyleColor();
+		if (ImGui::IsItemHovered()) {
+			if (auto _tt = Util::HoverTooltipWrapper()) {
+				const Util::ColoredTextLines& useLegend = cellLegend ? *cellLegend : legend;
+				Util::DrawColoredMultiLineTooltip(useLegend);
+			}
+		}
+	};
+}
+
+// Helper for optional values
+auto MakeOptionalMetricColumn(const auto& theme, auto valueGetter, auto colorGetter, auto formatter, const Util::ColoredTextLines& legend, const Util::ColoredTextLines* cellLegend = nullptr)
+{
+	return [theme, valueGetter, colorGetter, formatter, legend, cellLegend](const DrawCallRow& row, int) {
+		if (!valueGetter(row).has_value()) {
+			ImGui::TextDisabled("-");
+			return;
+		}
+		float value = *valueGetter(row);
+		ImVec4 color = colorGetter(theme, value, row);
+		std::string valueStr = formatter(value, row);
+		ImGui::PushStyleColor(ImGuiCol_Text, color);
+		ImGui::Text("%s", valueStr.c_str());
+		ImGui::PopStyleColor();
+		if (ImGui::IsItemHovered()) {
+			if (auto _tt = Util::HoverTooltipWrapper()) {
+				const Util::ColoredTextLines& useLegend = cellLegend ? *cellLegend : legend;
+				Util::DrawColoredMultiLineTooltip(useLegend);
+			}
+		}
+	};
+}

1680-1843: Consider separating A/B test state management from UI rendering.

This function handles both A/B test state management and UI rendering, which violates the single responsibility principle.

Consider splitting into:

void PerformanceOverlay::UpdateABTestState(bool showCollapsibleSections)
{
    // Handle state tracking and aggregator updates (lines 1682-1719)
}

void PerformanceOverlay::RenderABTestUI(bool showCollapsibleSections)
{
    // Handle UI rendering (lines 1721-1842)
}

void PerformanceOverlay::DrawABTestSection(const std::vector<DrawCallRow>& allRows, 
                                          bool showCollapsibleSections)
{
    UpdateABTestState(showCollapsibleSections);
    RenderABTestUI(showCollapsibleSections);
}

209-211: Consider extracting magic numbers as named constants.

Buffer sizes and format strings contain magic numbers that could be named constants for better maintainability.

+    static constexpr size_t kTooltipBufferSize = 128;
+    static constexpr const char* kFrameTimeFormat = "%.2f ms (%.1f FPS)";
+    
-    char validStr[128], marginalStr[128];
+    char validStr[kTooltipBufferSize], marginalStr[kTooltipBufferSize];
     
-    snprintf(overlay_text, IM_ARRAYSIZE(overlay_text),
-        "%s%.2f ms (%.1f FPS)",
+    snprintf(overlay_text, IM_ARRAYSIZE(overlay_text),
+        "%s" kFrameTimeFormat,

Also applies to: 1119-1119

src/Features/PerformanceOverlay.h (1)

16-20: Document the rationale for using negative enum values.

Using negative values to distinguish special shader types from regular ones is clever but could be fragile if the regular shader type enum changes. Consider adding a comment explaining this design choice.

 // Special shader type enum for summary rows
+// Uses negative values to distinguish from regular shader types (which are non-negative)
 enum class SpecialShaderType
 {
 	Total = -1,
 	Other = -2
 };
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 803ba41 and 163bd95.

📒 Files selected for processing (24)
  • features/Performance Overlay/Shaders/Features/PerformanceOverlay.ini (1 hunks)
  • src/Feature.cpp (2 hunks)
  • src/Features/OverlayFeature.h (1 hunks)
  • src/Features/PerformanceOverlay.cpp (1 hunks)
  • src/Features/PerformanceOverlay.h (1 hunks)
  • src/Features/PerformanceOverlay/ABTesting/ABTestAggregator.cpp (1 hunks)
  • src/Features/PerformanceOverlay/ABTesting/ABTestAggregator.h (1 hunks)
  • src/Features/PerformanceOverlay/ABTesting/ABTesting.cpp (1 hunks)
  • src/Features/PerformanceOverlay/ABTesting/ABTesting.h (1 hunks)
  • src/Features/WeatherPicker.cpp (6 hunks)
  • src/Features/WeatherPicker.h (3 hunks)
  • src/Globals.cpp (3 hunks)
  • src/Globals.h (2 hunks)
  • src/Menu.cpp (12 hunks)
  • src/Menu.h (1 hunks)
  • src/State.cpp (10 hunks)
  • src/State.h (4 hunks)
  • src/Utils/FileSystem.cpp (2 hunks)
  • src/Utils/FileSystem.h (2 hunks)
  • src/Utils/Format.cpp (2 hunks)
  • src/Utils/Format.h (1 hunks)
  • src/Utils/PerfUtils.h (1 hunks)
  • src/Utils/UI.cpp (3 hunks)
  • src/Utils/UI.h (4 hunks)
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-06-24T07:17:36.604Z
Learning: When reviewing PRs, always clarify the scope if there are multiple related features or dependencies. WeatherPicker was a separate PR that was already merged, while this PR focuses specifically on WetnessEffects climate preset system enhancements.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-07-05T05:20:45.823Z
Learning: In the skyrim-community-shaders repository, file deletion error handling improvements that replace existence checks and try-catch blocks with std::filesystem::remove error-code-based approaches are considered bug fixes rather than refactoring, as they address inadequate error handling and misleading log messages.
features/Performance Overlay/Shaders/Features/PerformanceOverlay.ini (1)
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#1157
File: src/Feature.cpp:42-49
Timestamp: 2025-06-17T09:27:49.594Z
Learning: In src/Feature.cpp, when an obsolete feature's INI file is deleted, the feature should be silently disabled without surfacing any issues to the user. This is the intended behavior because a deleted INI file for an obsolete feature indicates that the user has properly cleaned up the obsolete feature.
src/Globals.h (1)
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-06-24T07:17:36.604Z
Learning: When reviewing PRs, always clarify the scope if there are multiple related features or dependencies. WeatherPicker was a separate PR that was already merged, while this PR focuses specifically on WetnessEffects climate preset system enhancements.
src/Feature.cpp (4)
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#1157
File: src/Feature.cpp:42-49
Timestamp: 2025-06-17T09:27:49.594Z
Learning: In src/Feature.cpp, when an obsolete feature's INI file is deleted, the feature should be silently disabled without surfacing any issues to the user. This is the intended behavior because a deleted INI file for an obsolete feature indicates that the user has properly cleaned up the obsolete feature.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#577
File: features/Wetness Effects/Shaders/WetnessEffects/WetnessEffects.hlsli:57-61
Timestamp: 2025-06-17T05:40:22.785Z
Learning: Default parameter values are supported in the HLSL compiler used by the skyrim-community-shaders project, contrary to standard HLSL (FXC/DXC) limitations.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-07-01T18:01:07.079Z
Learning: In the skyrim-community-shaders project, simple scalar constants in HLSL shaders use #define (e.g., #define NTHREADS 128), while more complex constants use static const within namespaces (e.g., Math namespace in Math.hlsli). For epsilon standardization, #define is the appropriate choice since epsilon values are simple scalar constants.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-06-24T07:17:36.604Z
Learning: When reviewing PRs, always clarify the scope if there are multiple related features or dependencies. WeatherPicker was a separate PR that was already merged, while this PR focuses specifically on WetnessEffects climate preset system enhancements.
src/Globals.cpp (4)
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#1157
File: src/Feature.cpp:42-49
Timestamp: 2025-06-17T09:27:49.594Z
Learning: In src/Feature.cpp, when an obsolete feature's INI file is deleted, the feature should be silently disabled without surfacing any issues to the user. This is the intended behavior because a deleted INI file for an obsolete feature indicates that the user has properly cleaned up the obsolete feature.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#577
File: features/Wetness Effects/Shaders/WetnessEffects/WetnessEffects.hlsli:57-61
Timestamp: 2025-06-17T05:40:22.785Z
Learning: Default parameter values are supported in the HLSL compiler used by the skyrim-community-shaders project, contrary to standard HLSL (FXC/DXC) limitations.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-07-01T18:01:07.079Z
Learning: In the skyrim-community-shaders project, simple scalar constants in HLSL shaders use #define (e.g., #define NTHREADS 128), while more complex constants use static const within namespaces (e.g., Math namespace in Math.hlsli). For epsilon standardization, #define is the appropriate choice since epsilon values are simple scalar constants.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-06-24T07:17:36.604Z
Learning: When reviewing PRs, always clarify the scope if there are multiple related features or dependencies. WeatherPicker was a separate PR that was already merged, while this PR focuses specifically on WetnessEffects climate preset system enhancements.
src/Features/WeatherPicker.h (1)
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-06-24T07:17:36.604Z
Learning: When reviewing PRs, always clarify the scope if there are multiple related features or dependencies. WeatherPicker was a separate PR that was already merged, while this PR focuses specifically on WetnessEffects climate preset system enhancements.
src/State.cpp (5)
Learnt from: davo0411
PR: doodlum/skyrim-community-shaders#1070
File: src/State.cpp:79-83
Timestamp: 2025-05-30T11:44:15.542Z
Learning: In the Skyrim Community Shaders project, the `smoothDrawCalls` array in the `State` class is declared as type `double` in `src/State.h`, which is the correct floating-point type for performing exponential moving average smoothing calculations.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#577
File: features/Wetness Effects/Shaders/WetnessEffects/WetnessEffects.hlsli:57-61
Timestamp: 2025-06-17T05:40:22.785Z
Learning: Default parameter values are supported in the HLSL compiler used by the skyrim-community-shaders project, contrary to standard HLSL (FXC/DXC) limitations.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-07-01T18:01:07.079Z
Learning: In the skyrim-community-shaders project, simple scalar constants in HLSL shaders use #define (e.g., #define NTHREADS 128), while more complex constants use static const within namespaces (e.g., Math namespace in Math.hlsli). For epsilon standardization, #define is the appropriate choice since epsilon values are simple scalar constants.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#577
File: src/Features/WetnessEffects.h:36-36
Timestamp: 2025-06-08T11:25:14.536Z
Learning: In the skyrim-community-shaders project, boolean flags in C++ structs that interface with HLSL shaders use `uint` type instead of `bool` for compatibility reasons. This ensures consistent 4-byte size, proper 16-byte alignment in constant buffers, and cross-platform compatibility when passing data between C++ and HLSL shaders.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#577
File: src/Features/WetnessEffects.h:36-36
Timestamp: 2025-06-08T11:25:14.536Z
Learning: In the skyrim-community-shaders project, boolean flags in C++ structs that interface with HLSL shaders use `uint` type instead of `bool` for compatibility reasons. This ensures consistent size, alignment, and cross-platform compatibility when passing data to shader constant buffers.
src/Features/WeatherPicker.cpp (1)
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-06-24T07:17:36.604Z
Learning: When reviewing PRs, always clarify the scope if there are multiple related features or dependencies. WeatherPicker was a separate PR that was already merged, while this PR focuses specifically on WetnessEffects climate preset system enhancements.
src/State.h (4)
Learnt from: davo0411
PR: doodlum/skyrim-community-shaders#1070
File: src/State.cpp:79-83
Timestamp: 2025-05-30T11:44:15.542Z
Learning: In the Skyrim Community Shaders project, the `smoothDrawCalls` array in the `State` class is declared as type `double` in `src/State.h`, which is the correct floating-point type for performing exponential moving average smoothing calculations.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#577
File: src/Features/WetnessEffects.h:36-36
Timestamp: 2025-06-08T11:25:14.536Z
Learning: In the skyrim-community-shaders project, boolean flags in C++ structs that interface with HLSL shaders use `uint` type instead of `bool` for compatibility reasons. This ensures consistent 4-byte size, proper 16-byte alignment in constant buffers, and cross-platform compatibility when passing data between C++ and HLSL shaders.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#577
File: src/Features/WetnessEffects.h:36-36
Timestamp: 2025-06-08T11:25:14.536Z
Learning: In the skyrim-community-shaders project, boolean flags in C++ structs that interface with HLSL shaders use `uint` type instead of `bool` for compatibility reasons. This ensures consistent size, alignment, and cross-platform compatibility when passing data to shader constant buffers.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#577
File: features/Wetness Effects/Shaders/WetnessEffects/WetnessEffects.hlsli:57-61
Timestamp: 2025-06-17T05:40:22.785Z
Learning: Default parameter values are supported in the HLSL compiler used by the skyrim-community-shaders project, contrary to standard HLSL (FXC/DXC) limitations.
src/Features/PerformanceOverlay.cpp (2)
Learnt from: davo0411
PR: doodlum/skyrim-community-shaders#1070
File: src/State.cpp:79-83
Timestamp: 2025-05-30T11:44:15.542Z
Learning: In the Skyrim Community Shaders project, the `smoothDrawCalls` array in the `State` class is declared as type `double` in `src/State.h`, which is the correct floating-point type for performing exponential moving average smoothing calculations.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-07-05T05:20:45.823Z
Learning: In the skyrim-community-shaders repository, file deletion error handling improvements that replace existence checks and try-catch blocks with std::filesystem::remove error-code-based approaches are considered bug fixes rather than refactoring, as they address inadequate error handling and misleading log messages.
src/Menu.cpp (4)
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#1157
File: src/Feature.cpp:42-49
Timestamp: 2025-06-17T09:27:49.594Z
Learning: In src/Feature.cpp, when an obsolete feature's INI file is deleted, the feature should be silently disabled without surfacing any issues to the user. This is the intended behavior because a deleted INI file for an obsolete feature indicates that the user has properly cleaned up the obsolete feature.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-06-24T07:17:36.604Z
Learning: When reviewing PRs, always clarify the scope if there are multiple related features or dependencies. WeatherPicker was a separate PR that was already merged, while this PR focuses specifically on WetnessEffects climate preset system enhancements.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#577
File: src/Features/WetnessEffects.h:36-36
Timestamp: 2025-06-08T11:25:14.536Z
Learning: In the skyrim-community-shaders project, boolean flags in C++ structs that interface with HLSL shaders use `uint` type instead of `bool` for compatibility reasons. This ensures consistent size, alignment, and cross-platform compatibility when passing data to shader constant buffers.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#577
File: src/Features/WetnessEffects.h:36-36
Timestamp: 2025-06-08T11:25:14.536Z
Learning: In the skyrim-community-shaders project, boolean flags in C++ structs that interface with HLSL shaders use `uint` type instead of `bool` for compatibility reasons. This ensures consistent 4-byte size, proper 16-byte alignment in constant buffers, and cross-platform compatibility when passing data between C++ and HLSL shaders.
🧬 Code Graph Analysis (3)
src/Utils/FileSystem.cpp (1)
src/Utils/FileSystem.h (1)
  • LoadJsonDiff (111-111)
src/Features/WeatherPicker.cpp (4)
src/Menu.cpp (4)
  • KeyIdToString (2153-2178)
  • KeyIdToString (2153-2153)
  • DrawOverlay (1615-1729)
  • DrawOverlay (1615-1615)
src/Utils/UI.h (2)
  • HoverTooltipWrapper (58-58)
  • HoverTooltipWrapper (59-59)
src/Utils/UI.cpp (2)
  • HoverTooltipWrapper (24-31)
  • HoverTooltipWrapper (33-39)
src/Features/WeatherPicker.h (1)
  • open (29-29)
src/Utils/Format.cpp (1)
src/Utils/Format.h (1)
  • FormatMilliseconds (30-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Validate shader compilation (Flatrim, .github/configs/shader-validation.yaml)
  • GitHub Check: Build plugin and addons
  • GitHub Check: Validate shader compilation (VR, .github/configs/shader-validation-vr.yaml)
🔇 Additional comments (50)
features/Performance Overlay/Shaders/Features/PerformanceOverlay.ini (1)

1-2: LGTM! Configuration follows established patterns.

The INI file correctly follows the standard format for feature configuration files with proper version marking.

src/Feature.cpp (2)

17-17: LGTM! Header include follows established pattern.

The include is properly added in alphabetical order with other feature headers.


210-210: LGTM! Feature integration follows established pattern.

The PerformanceOverlay is correctly added to the feature list, enabling it to be managed alongside other features.

src/Globals.h (2)

27-27: LGTM! Forward declaration follows established pattern.

The forward declaration is properly added in alphabetical order with other feature declarations.


82-82: LGTM! Global pointer declaration follows established pattern.

The extern pointer declaration correctly follows the same pattern as other features in the globals::features namespace.

src/Globals.cpp (3)

26-26: LGTM! Header include follows established pattern.

The include is properly added in alphabetical order with other feature headers.


81-81: LGTM! Pointer initialization follows established pattern.

The pointer is correctly initialized to nullptr following the same pattern as other features.


167-167: LGTM! Singleton assignment follows established pattern.

The singleton assignment in OnInit() correctly follows the same pattern as other features, ensuring proper initialization.

src/Utils/FileSystem.cpp (2)

2-2: LGTM! Required include for file I/O operations.

The fstream include is necessary for the ifstream usage in the new LoadJsonDiff function.


83-128: LGTM! Well-implemented JSON diff functionality with robust error handling.

The LoadJsonDiff function provides excellent support for A/B testing with:

  • Proper file existence and stream validation
  • Correct handling of all diff operation types (replace, add, remove)
  • Safe JSON pointer access within exception handling
  • Graceful error handling that logs issues without propagating exceptions
  • Appropriate return of empty vector on any failure

The implementation correctly uses nlohmann::json::diff and handles the results appropriately for the A/B testing use case.

src/Features/OverlayFeature.h (1)

12-36: LGTM! Well-designed abstract interface.

The OverlayFeature class provides a clean, well-documented interface for modular overlay features. The use of pure virtual methods, comprehensive documentation, and sensible defaults demonstrates good design principles.

src/Utils/FileSystem.h (3)

9-9: LGTM! Appropriate header inclusion for JSON functionality.

The addition of the nlohmann/json header is correct for the new JSON diff functionality.


14-19: LGTM! Clean structure design for representing JSON differences.

The SettingsDiffEntry struct provides a clear and simple way to represent differences between JSON configurations with path and both values stored as strings.


109-112: LGTM! Well-organized namespace and function signature.

The FileSystem nested namespace and LoadJsonDiff function declaration provide a clean API for JSON comparison functionality.

src/Features/WeatherPicker.h (4)

3-4: LGTM! Correct header updates for overlay integration.

The updated includes properly reflect the migration to the new overlay system architecture.


6-6: LGTM! Proper inheritance change for overlay functionality.

Changing inheritance from Feature to OverlayFeature correctly integrates WeatherPicker into the new modular overlay system.


40-46: LGTM! Well-structured settings for overlay configuration.

The WeatherDetailsWindowSettings struct provides clean configuration options with sensible defaults for overlay positioning and visibility.


121-123: LGTM! Proper implementation of OverlayFeature interface.

The addition of DrawOverlay() and IsOverlayVisible() methods correctly implements the required interface for the new overlay system.

src/Features/PerformanceOverlay/ABTesting/ABTesting.h (1)

8-37: LGTM! Well-designed A/B testing manager with clean interface.

The ABTestingManager class demonstrates good design principles:

  • Appropriate use of singleton pattern for a system manager
  • Clear separation of concerns with distinct method groups (configuration, state, UI)
  • Proper composition with ABTestAggregator
  • Good use of std::chrono for timing precision
  • Clean, well-documented interface
src/Utils/UI.cpp (1)

556-561: LGTM!

The implementation correctly iterates through the colored text lines and renders each with its specified color using ImGui.

src/Features/WeatherPicker.cpp (2)

7-12: LGTM!

Proper use of nlohmann JSON serialization macro for the WeatherDetailsWindowSettings struct.


860-871: LGTM!

The DrawOverlay implementation correctly manages the overlay visibility and window state, properly integrating with the new overlay system.

src/Utils/Format.h (1)

26-84: Well-documented utility function declarations!

The new formatting and calculation utilities are properly documented with clear descriptions of their purpose, parameters, and return values. These will provide good support for the performance overlay feature.

src/Features/PerformanceOverlay/ABTesting/ABTesting.cpp (2)

9-13: LGTM!

Standard singleton pattern implementation.


30-33: Good state preservation pattern!

The consistent preservation of the overlay enabled state across configuration switches ensures a good user experience during A/B testing.

Also applies to: 47-49, 73-73, 94-94

src/State.cpp (3)

21-21: Good addition of thread safety!

Using Lock() ensures thread-safe access to the state during the Draw operation.


83-91: Well-implemented frame time tracking!

The frame time tracking properly measures elapsed time per shader type and applies the same exponential smoothing as draw calls. The timing logic correctly handles frame boundaries.

Also applies to: 102-113


859-862: LGTM!

The utility method provides clean access to the total smoothed draw calls using magic_enum for type safety.

src/Menu.h (1)

171-173: LGTM! Good encapsulation with getter methods.

The addition of getter methods for Theme, Settings, and DXGIAdapter3 follows good encapsulation practices and provides controlled access to internal state.

src/State.h (2)

22-30: Excellent thread safety implementation.

The constructor properly initializes all arrays under mutex protection, and the Lock() method correctly returns a std::lock_guard for RAII-based locking. This ensures thread-safe access to the statistics data.


243-283: Well-designed template utility methods for shader type iteration.

The three template methods provide a clean and type-safe way to iterate over shader types with different levels of detail. The use of magic_enum for enum handling and [[maybe_unused]] attributes for unused parameters demonstrates good C++ practices.

src/Utils/Format.cpp (1)

80-106: Well-implemented formatting functions with proper edge case handling.

The formatting functions correctly handle edge cases (values < 1e-4f) and use appropriate precision for different value ranges. The consistent pattern between FormatMilliseconds and FormatMicroseconds is good for maintainability.

src/Features/PerformanceOverlay/ABTesting/ABTestAggregator.cpp (1)

108-188: Well-implemented aggregation logic with proper summary row handling.

The aggregation correctly calculates statistics per shader type and ensures that Total (-1) and Other (-2) summary rows are always present and properly sorted at the end of the results. The sorting logic correctly handles the special cases.

src/Features/PerformanceOverlay/ABTesting/ABTestAggregator.h (1)

7-18: Well-defined constants for statistical validation.

The constants for frame history, outlier detection, and statistical validation provide clear thresholds for test validity. The values chosen (30-frame baseline, 3x outlier multiplier, 100ms max frame time) are reasonable defaults for performance testing.

src/Utils/UI.h (6)

33-42: LGTM!

The ColoredTextLine struct is well-designed for representing colored text in tooltips and legends.


228-236: Good documentation improvement!

The added warning about tooltip context requirement is valuable for preventing misuse.


238-244: LGTM!

The function signature and documentation are appropriate for the colored tooltip functionality.


371-412: Well-designed adapter implementation!

The overload correctly adapts the simpler interface to the generic template version with proper bounds checking in the cell renderer adapter.


428-430: LGTM!

The threshold color helper is appropriately placed and will be useful for performance metric visualization.


319-332: Add null check for cellRender callback.

The cellRender callback is called without checking if it's null, which could cause a crash if not provided.

Apply this diff to add null checking:

 for (size_t rowIdx = 0; rowIdx < rows.size(); ++rowIdx) {
     const auto& row = rows[rowIdx];
     ImGui::TableNextRow();
     for (size_t col = 0; col < headers.size(); ++col) {
         ImGui::TableSetColumnIndex(static_cast<int>(col));
         if (cellRender) {
             cellRender(static_cast<int>(rowIdx), static_cast<int>(col), row);
-        } else if constexpr (std::is_same_v<T, std::vector<std::string>>) {
+        } else {
+            // Default rendering for vector<string> if no custom renderer provided
+            if constexpr (std::is_same_v<T, std::vector<std::string>>) {
                 if (col < row.size())
                     ImGui::TextUnformatted(row[col].c_str());
+            }
         }
     }
 }

Likely an incorrect or invalid review comment.

src/Menu.cpp (6)

26-29: LGTM!

The new includes are properly organized and necessary for the performance overlay and A/B testing features.


129-139: Verify the hover alpha reduction from 20% to 10%.

The change from hardcoded 0.2f to hoveredAlpha (0.1f) reduces hover visibility by half. This makes hover effects more subtle but might impact usability.

Please confirm this alpha reduction is intentional and has been tested for adequate visibility across different display configurations.


575-576: Good consistency improvement!

Using globals::upscaling aligns with the pattern of accessing features through global references.


1214-1228: LGTM!

The overlay toggle key UI implementation is consistent with other keybinding settings and properly integrated.


1469-1478: LGTM!

The shader type iteration with index is properly implemented and maintains backward compatibility.


2188-2196: LGTM!

The weather picker integration properly uses the singleton pattern consistent with the new overlay architecture.

src/Features/PerformanceOverlay.cpp (2)

1364-1418: Well-implemented statistical analysis for graph scaling.

The graph update logic correctly calculates mean and standard deviation, applies appropriate bounds, and uses exponential smoothing for visual stability. The documentation is excellent.


1335-1335: Confirm the VideoMemoryInfo.Budget guard is correct

Please verify whether the videoMemoryInfo.Budget > 0 check is the right approach to handle all GPU configurations (integrated GPUs, virtualized environments, unsupported drivers, etc.) or if it should be adjusted to allow a zero‐budget state rather than falling back to “Not available.”

• Location to review:
– src/Features/PerformanceOverlay.cpp around line 1335 (if (SUCCEEDED(hr) && videoMemoryInfo.Budget > 0) { … })
• Actions:

  1. Test on hardware/drivers that may return a zero budget (e.g., integrated GPUs, VMs).
  2. Consult the DXGI_QUERY_VIDEO_MEMORY_INFO documentation to confirm under what conditions Budget can validly be zero.
  3. Decide whether to:
    – Keep the guard (to avoid division by zero and invalid metrics),
    – Permit Budget == 0 and display “0 GB” usage, or
    – Add a more explicit fallback path.
src/Features/PerformanceOverlay.h (2)

193-217: Well-designed settings structure with appropriate defaults.

The PerfOverlaySettings structure is well-organized with sensible defaults, proper use of constexpr for bounds checking, and clear naming.


101-108: Excellent documentation throughout the header file.

The method documentation is comprehensive, explaining purpose, parameters, return values, and side effects clearly. This greatly improves code maintainability.

Also applies to: 161-178, 224-249

Comment thread src/Utils/PerfUtils.h
Comment thread src/Utils/PerfUtils.h
Comment thread src/Features/PerformanceOverlay/ABTesting/ABTestAggregator.cpp
Comment thread src/Features/PerformanceOverlay/ABTesting/ABTestAggregator.cpp Outdated
Comment thread src/Features/PerformanceOverlay.cpp Outdated
Comment thread src/Features/PerformanceOverlay.cpp Outdated
Comment thread src/Features/PerformanceOverlay.cpp Outdated
Comment thread src/Features/PerformanceOverlay.h
Comment thread src/Features/PerformanceOverlay.h Outdated
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jul 13, 2025

✅ A pre-release build is available for this PR:
Download

@alandtse alandtse requested a review from Copilot July 13, 2025 06:48
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the performance overlay into a standalone OverlayFeature, adds detailed per-shader frame time tracking, and introduces an A/B testing framework for automated shader performance comparisons.

  • Moved legacy performance overlay/UI code into PerformanceOverlay and OverlayFeature submodules.
  • Added real-time frame timing, per-shader metrics, and smoothing logic in State and PerfUtils.
  • Implemented A/B testing manager and aggregator with UI controls and JSON diff support.

Reviewed Changes

Copilot reviewed 24 out of 25 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Utils/UI.h Added colored tooltip types, new sortable table template, and removed old PerformanceOverlay helper.
src/Utils/UI.cpp Implemented colored tooltip and threshold color helpers.
src/Utils/PerfUtils.h New inline frame‐time, FPS, mean, and median helper functions.
src/Utils/Format.h Declared new formatting functions for time, percent, and deltas.
src/Utils/Format.cpp Defined formatting implementations (ms, us, percent, time‐ago, delta).
src/Utils/FileSystem.h Added JSON diff loader declaration and SettingsDiffEntry struct.
src/Utils/FileSystem.cpp Implemented LoadJsonDiff using nlohmann::json diff.
src/State.h / src/State.cpp Added per‐shader timing arrays, smoothing, and thread safety.
src/Menu.h / src/Menu.cpp Converted overlay and weather picker to use new OverlayFeature interface; removed monolithic overlay code.
src/Globals.h / src/Globals.cpp Registered PerformanceOverlay in globals list.
src/Features/PerformanceOverlay/** New feature directory: core overlay, A/B testing manager, aggregator and related UI.
src/Features/WeatherPicker.{h,cpp} Converted to OverlayFeature, wired ShowInOverlay settings.
Comments suppressed due to low confidence (5)

src/Utils/FileSystem.h:14

  • [nitpick] The SettingsDiffEntry struct is declared in the global namespace but used in Util::FileSystem. Consider moving it into namespace Util::FileSystem for clearer scoping.
struct SettingsDiffEntry

src/Utils/Format.h:43

  • The header uses std::chrono::steady_clock::time_point but doesn't include . Add #include <chrono> to Format.h.
	std::string TimeAgoString(std::chrono::steady_clock::time_point last);

src/Utils/Format.cpp:129

  • Uses std::format without including the header. Add #include <format> or switch to fmt::format to avoid missing symbol errors.
		std::string percentStr = (percentDelta >= threshold) ? std::format(" ({:+.1f}%)", (b < a ? -percentDelta : percentDelta)) : "";

src/Utils/FileSystem.cpp:124

  • The file references logger::warn but does not include the header that defines logger. Add the appropriate logging include (e.g., "Globals.h" or your logger header).
		logger::warn("Failed to load JSON diff: {}", e.what());

src/State.h:30

  • Returning a std::lock_guard by value is ill-formed because lock_guard is neither copyable nor movable. Consider returning a std::unique_lock<std::mutex> or locking inside the caller scope instead.
	std::lock_guard<std::mutex> Lock() { return std::lock_guard<std::mutex>(statsMutex); }

Comment thread src/Utils/UI.h
Copy link
Copy Markdown
Collaborator

@davo0411 davo0411 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent refactor, from a brief look through everything looked good. Nice comments, very helpful

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/Features/PerformanceOverlay.h (1)

123-123: Improve const-correctness for BuildDrawCallLegends

The method is declared as const but takes a non-const PerformanceOverlay* parameter. If the overlay parameter doesn't need modification, it should be const PerformanceOverlay*.

- DrawCallLegends BuildDrawCallLegends(const Menu::ThemeSettings& theme, bool anyTestData, PerformanceOverlay* overlay) const;
+ DrawCallLegends BuildDrawCallLegends(const Menu::ThemeSettings& theme, bool anyTestData, const PerformanceOverlay* overlay) const;
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 163bd95 and 0b853c6.

📒 Files selected for processing (6)
  • src/Features/PerformanceOverlay.cpp (1 hunks)
  • src/Features/PerformanceOverlay.h (1 hunks)
  • src/Features/PerformanceOverlay/ABTesting/ABTestAggregator.cpp (1 hunks)
  • src/Features/PerformanceOverlay/ABTesting/ABTestAggregator.h (1 hunks)
  • src/Menu.cpp (12 hunks)
  • src/Utils/PerfUtils.h (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/Features/PerformanceOverlay/ABTesting/ABTestAggregator.h
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/Utils/PerfUtils.h
  • src/Features/PerformanceOverlay/ABTesting/ABTestAggregator.cpp
  • src/Menu.cpp
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-06-24T07:17:36.604Z
Learning: When reviewing PRs, always clarify the scope if there are multiple related features or dependencies. WeatherPicker was a separate PR that was already merged, while this PR focuses specifically on WetnessEffects climate preset system enhancements.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-07-05T05:20:45.823Z
Learning: In the skyrim-community-shaders repository, file deletion error handling improvements that replace existence checks and try-catch blocks with std::filesystem::remove error-code-based approaches are considered bug fixes rather than refactoring, as they address inadequate error handling and misleading log messages.
src/Features/PerformanceOverlay.cpp (1)
Learnt from: davo0411
PR: doodlum/skyrim-community-shaders#1070
File: src/State.cpp:79-83
Timestamp: 2025-05-30T11:44:15.542Z
Learning: In the Skyrim Community Shaders project, the `smoothDrawCalls` array in the `State` class is declared as type `double` in `src/State.h`, which is the correct floating-point type for performing exponential moving average smoothing calculations.
src/Features/PerformanceOverlay.h (1)
Learnt from: davo0411
PR: doodlum/skyrim-community-shaders#1070
File: src/State.cpp:79-83
Timestamp: 2025-05-30T11:44:15.542Z
Learning: In the Skyrim Community Shaders project, the `smoothDrawCalls` array in the `State` class is declared as type `double` in `src/State.h`, which is the correct floating-point type for performing exponential moving average smoothing calculations.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Validate shader compilation (Flatrim, .github/configs/shader-validation.yaml)
  • GitHub Check: Validate shader compilation (VR, .github/configs/shader-validation-vr.yaml)
  • GitHub Check: Build plugin and addons
🔇 Additional comments (4)
src/Features/PerformanceOverlay.cpp (3)

511-550: Well-implemented VRAM monitoring with proper error handling

The function correctly checks for null pointers, handles DXGI query failures gracefully, and provides appropriate fallback UI.


1951-2004: Excellent statistical implementation for graph visualization

The function properly calculates mean, standard deviation, and applies exponential smoothing for stable graph scaling. The dynamic range adjustment based on variance provides an optimal viewing experience.


82-86: Consider thread safety for singleton access

The singleton pattern implementation doesn't include thread synchronization. If the overlay can be accessed from multiple threads (e.g., main thread and render thread), consider adding thread-safe initialization.

static PerformanceOverlay* GetSingleton()
{
    static std::once_flag initFlag;
    static PerformanceOverlay* singleton = nullptr;
    std::call_once(initFlag, []() {
        singleton = new PerformanceOverlay();
    });
    return singleton;
}
src/Features/PerformanceOverlay.h (1)

314-338: Well-designed settings structure with proper constraints

The PerfOverlaySettings structure properly uses constexpr for bounds checking and includes all necessary configuration options for the overlay. The enum for text size provides type safety.

Comment thread src/Features/PerformanceOverlay.cpp
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/Utils/UI.h (1)

371-434: Fix const_cast usage that violates const-correctness.

The function accepts const rows but uses const_cast to remove const qualification when sorting is not needed. This breaks the const contract and could lead to undefined behavior.

Replace the const_cast approach with a cleaner solution:

-inline void ShowSortedStringTable(
+inline void ShowSortedStringTable(
     const char* table_id,
     const std::vector<std::string>& headers,
-    const std::vector<std::vector<std::string>>& rows,
+    std::vector<std::vector<std::string>> rows,
     size_t sortColumn = 0,
     bool ascending = true,
     const std::vector<TableSortFunc>& customSorts = {},
     TableCellRenderFunc cellRender = nullptr)
 {
-    // Check if sorting is needed by looking at ImGui table sort specs
-    bool needsSorting = false;
-    if (const ImGuiTableSortSpecs* sortSpecs = ImGui::TableGetSortSpecs()) {
-        needsSorting = (sortSpecs->SpecsCount > 0);
-    }
-
-    // Only make a copy if we need to sort, otherwise use the original data
-    if (needsSorting) {
-        std::vector<std::vector<std::string>> rowsCopy = rows;
-        ShowSortedStringTable<std::vector<std::string>>(
-            table_id,
-            headers,
-            rowsCopy,
-            sortColumn,
-            ascending,
-            adaptedSorts,
-            adaptedCellRender);
-    } else {
-        // For non-sorting case, we can use the original data directly
-        // We need to const_cast because the template expects non-const, but we know it won't be modified
-        ShowSortedStringTable<std::vector<std::string>>(
-            table_id,
-            headers,
-            const_cast<std::vector<std::vector<std::string>>&>(rows),
-            sortColumn,
-            ascending,
-            adaptedSorts,
-            adaptedCellRender);
-    }
+    ShowSortedStringTable<std::vector<std::string>>(
+        table_id,
+        headers,
+        rows,
+        sortColumn,
+        ascending,
+        adaptedSorts,
+        adaptedCellRender);
 }

This change accepts the vector by value (utilizing move semantics when possible) and eliminates the dangerous const_cast.

src/Features/PerformanceOverlay.cpp (1)

282-283: Fix void return value passed to SetTextScale.

The code attempts to pass the void return value of SetTextScale() to itself, which shouldn't compile:

 float scale = this->perfOverlayState.CalculateTextScale();
-this->perfOverlayState.SetTextScale(this->perfOverlayState.SetTextScale());
+this->perfOverlayState.SetTextScale(scale);
🧹 Nitpick comments (4)
src/Utils/UI.h (1)

284-354: Consider moving complex template implementation to reduce compilation overhead.

The 70+ line template implementation increases compilation time for every file that includes this header.

Consider moving this to a separate implementation header:

// In UI.h:
template <typename T>
void ShowSortedStringTable(
    const char* table_id,
    const std::vector<std::string>& headers,
    std::vector<T>& rows,
    size_t sortColumn,
    bool ascending,
    const std::vector<std::function<bool(const T&, const T&, bool)>>& customSorts,
    std::function<void(int, int, const T&)> cellRender,
    const std::vector<T>& footerRows = {});

// In UI_impl.h or UI.inl:
#include "UI.h"
template <typename T>
void ShowSortedStringTable(...) {
    // Full implementation here
}

// Include the implementation header at the bottom of UI.h:
#include "UI_impl.h"

This keeps the main header cleaner while maintaining template functionality.

src/Features/PerformanceOverlay.h (3)

17-21: Consider using positive enum values for clarity.

The SpecialShaderType enum uses negative values which can be confusing and error-prone when used in contexts expecting positive indices or when interfacing with systems that expect unsigned types.

Consider using positive enum values or a separate enum class:

enum class SpecialShaderType
{
-	Total = -1,
-	Other = -2
+	Total = 1000,    // Use a high value to avoid conflicts with real shader types
+	Other = 1001
};

196-215: Document the derivation of performance threshold constants.

The performance threshold constants are well-organized, but some would benefit from documentation explaining how these specific values were determined, especially for thresholds that might seem arbitrary.

Consider adding comments explaining the rationale:

// Performance threshold constants
static constexpr float kSmoothingFactor = 0.15f;             // Smoothing factor: 0.1f = slow, 0.3f = fast.
static constexpr float kFrameTimeGoodThreshold = 2.0f;       // ms - Good performance threshold
static constexpr float kFrameTimeWarningThreshold = 5.0f;    // ms - Warning performance threshold
static constexpr float kCostPerCallGoodThreshold = 0.05f;    // ms/call - Good cost per call threshold
static constexpr float kCostPerCallWarningThreshold = 0.2f;  // ms/call - Warning cost per call threshold
+static constexpr float kMicrosecondThreshold = 0.01f;        // ms - Below this threshold, display in microseconds for precision
+static constexpr float kPercentDisplayThreshold = 0.01f;     // % - Minimum meaningful difference for user display
-static constexpr float kMicrosecondThreshold = 0.01f;        // ms - Threshold for showing microseconds
-static constexpr float kPercentDisplayThreshold = 0.01f;     // Minimum percent difference to display

216-278: Review the extensive getter/setter pattern for potential over-exposure.

The class provides both const getters and non-const setters for almost all private members. This extensive accessor pattern suggests that the internal state might be too tightly coupled with external code, potentially undermining the benefits of encapsulation.

Consider:

  1. Providing higher-level methods that encapsulate multiple state changes
  2. Using builder pattern for complex state updates
  3. Reducing the number of individual setters by grouping related operations

For example:

// Instead of individual setters for all frame time metrics
void UpdateFrameMetrics(float frameTimeMs, float fps, bool isPostFG = false);

// Instead of exposing raw history vectors
void AddFrameTimeToHistory(float frameTime, bool isPostFG = false);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b853c6 and 4cefff8.

📒 Files selected for processing (4)
  • src/Features/PerformanceOverlay.cpp (1 hunks)
  • src/Features/PerformanceOverlay.h (1 hunks)
  • src/State.cpp (10 hunks)
  • src/Utils/UI.h (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/State.cpp
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-06-24T07:17:36.604Z
Learning: When reviewing PRs, always clarify the scope if there are multiple related features or dependencies. WeatherPicker was a separate PR that was already merged, while this PR focuses specifically on WetnessEffects climate preset system enhancements.
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-07-05T05:20:45.823Z
Learning: In the skyrim-community-shaders repository, file deletion error handling improvements that replace existence checks and try-catch blocks with std::filesystem::remove error-code-based approaches are considered bug fixes rather than refactoring, as they address inadequate error handling and misleading log messages.
src/Features/PerformanceOverlay.cpp (1)
Learnt from: davo0411
PR: doodlum/skyrim-community-shaders#1070
File: src/State.cpp:79-83
Timestamp: 2025-05-30T11:44:15.542Z
Learning: In the Skyrim Community Shaders project, the `smoothDrawCalls` array in the `State` class is declared as type `double` in `src/State.h`, which is the correct floating-point type for performing exponential moving average smoothing calculations.
src/Features/PerformanceOverlay.h (1)
Learnt from: davo0411
PR: doodlum/skyrim-community-shaders#1070
File: src/State.cpp:79-83
Timestamp: 2025-05-30T11:44:15.542Z
Learning: In the Skyrim Community Shaders project, the `smoothDrawCalls` array in the `State` class is declared as type `double` in `src/State.h`, which is the correct floating-point type for performing exponential moving average smoothing calculations.
🧬 Code Graph Analysis (1)
src/Features/PerformanceOverlay.cpp (8)
src/Features/PerformanceOverlay.h (22)
  • theme (116-116)
  • theme (118-118)
  • theme (119-119)
  • theme (126-126)
  • theme (127-127)
  • value (256-256)
  • value (256-256)
  • value (257-257)
  • value (257-257)
  • value (258-258)
  • value (258-258)
  • value (259-259)
  • value (259-259)
  • value (260-260)
  • value (260-260)
  • value (261-261)
  • value (261-261)
  • value (262-262)
  • value (262-262)
  • value (263-263)
  • value (263-263)
  • results (117-117)
src/Utils/UI.cpp (8)
  • HoverTooltipWrapper (24-31)
  • HoverTooltipWrapper (33-39)
  • DrawColoredMultiLineTooltip (556-561)
  • DrawColoredMultiLineTooltip (556-556)
  • DrawSectionHeader (415-476)
  • DrawSectionHeader (415-415)
  • GetThresholdColor (608-616)
  • GetThresholdColor (608-608)
src/Utils/Format.cpp (16)
  • CalculateOtherFrameTime (143-146)
  • CalculateOtherFrameTime (143-143)
  • CalculatePercentage (133-136)
  • CalculatePercentage (133-133)
  • CalculateCostPerCall (138-141)
  • CalculateCostPerCall (138-138)
  • FormatMilliseconds (80-90)
  • FormatMilliseconds (80-80)
  • FormatDeltaWithPercent (120-131)
  • FormatDeltaWithPercent (120-120)
  • FormatPercent (101-106)
  • FormatPercent (101-101)
  • FormatMicroseconds (92-99)
  • FormatMicroseconds (92-92)
  • TimeAgoString (108-118)
  • TimeAgoString (108-108)
src/Menu.h (2)
  • menu (13-17)
  • dxgiAdapter3 (173-173)
src/Features/PerformanceOverlay/ABTesting/ABTesting.cpp (2)
  • GetSingleton (9-13)
  • GetSingleton (9-9)
src/Utils/PerfUtils.h (4)
  • CalcFrameTime (15-18)
  • CalcFrameTime (15-15)
  • CalcFPS (25-30)
  • CalcFPS (25-25)
src/Features/PerformanceOverlay/ABTesting/ABTesting.h (3)
  • aggregator (29-29)
  • interval (14-14)
  • abTestingEnabled (16-16)
src/Utils/FileSystem.cpp (2)
  • LoadJsonDiff (83-128)
  • LoadJsonDiff (83-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Validate shader compilation (VR, .github/configs/shader-validation-vr.yaml)
  • GitHub Check: Validate shader compilation (Flatrim, .github/configs/shader-validation.yaml)
  • GitHub Check: Build plugin and addons
🔇 Additional comments (2)
src/Features/PerformanceOverlay.h (2)

286-304: Excellent documentation for complex algorithm.

The detailed documentation for UpdateGraphValues() is exemplary. It clearly explains the multi-step algorithm for statistical analysis and graph range calculation, making the complex logic accessible to future maintainers.


83-104: Clean implementation of the overlay feature interface.

The class properly inherits from OverlayFeature and implements the required virtual methods. The singleton pattern is correctly implemented, and the method signatures are clear and consistent.

Comment thread src/Features/PerformanceOverlay.h
@alandtse alandtse merged commit 329ae0e into community-shaders:dev Jul 13, 2025
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants