Skip to content

feat: add GPU/CPU profiling system with per-feature timing#2389

Open
doodlum wants to merge 11 commits into
devfrom
profiling
Open

feat: add GPU/CPU profiling system with per-feature timing#2389
doodlum wants to merge 11 commits into
devfrom
profiling

Conversation

@doodlum
Copy link
Copy Markdown
Collaborator

@doodlum doodlum commented May 20, 2026

Adds D3D11 timestamp query and QPC-based profiling with LegitProfiler graphs, a Profiling menu tab, and per-feature Profiling sections with GPU/CPU toggle.

SkyrimSE 2026-05-20 18-19-12_220 SkyrimSE 2026-05-20 18-39-56_189 SkyrimSE 2026-05-20 18-24-30_308

Summary by CodeRabbit

  • New Features
    • Global GPU/CPU profiler with rolling history, percentile reporting, and per-pass timing.
    • Profiling UI: timing graphs, per-feature timers, detailed statistics, and a new Profiling menu entry.
    • Performance Overlay: added "Show CS Render Passes" toggle to surface compute-pass metrics.
  • Improvements
    • Broad instrumentation: many rendering stages now emit named profiler pass markers for finer timing.
  • Chores
    • Bumped shader feature version metadata across multiple modules.

Review Change Stack

doodlum and others added 2 commits May 20, 2026 18:04
Adds D3D11 timestamp query and QPC-based profiling with LegitProfiler
graphs, a Statistics built-in menu tab, and per-feature Profiling
sections with GPU/CPU toggle. Instruments all feature compute dispatches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a D3D11 GPU/CPU profiler, wires a global profiler instance, instruments many compute/draw sites with named pass scopes, and adds ImGui visualizations (graphs, tables, per-group/per-pass aggregates, percentiles, and per-feature views).

Changes

GPU/CPU Profiling System

Layer / File(s) Summary
Profiler core: timing contracts and D3D11 implementation
src/Profiler.h, src/Profiler.cpp
Introduces RollingHistory, TimerResult, and Profiler class with D3D11 query-based GPU timing, CPU tick conversion, rolling averages, async result collection, and percentile reporting.
Profiler global instance and lifecycle
src/Globals.h, src/Globals.cpp, include/PCH.h, src/State.cpp
Forward-declares Profiler and extern in Globals.h, defines static Profiler instance and globals::profiler in Globals.cpp, includes Profiler.h in PCH, and hooks profiler init/end into State::SetupResources()/State::Reset().
Feature compute/draw dispatch profiling
src/Deferred.cpp, src/Features/... (multiple features)
Wraps compute shader dispatches and fullscreen draw calls across feature modules with globals::profiler->BeginPass()/EndPass() pairs, recording GPU/CPU timing without changing shader/dispatch logic.
Profiling graph visualization primitives
src/Utils/LegitProfiler.h
Adds legit::ProfilerTask and ImGuiUtils::ProfilerGraph to aggregate per-task timing segments across a frame history and render stacked timing graphs with legend and per-task stats.
Statistics UI: tables, graphs, color utilities
src/Menu/ProfilingRenderer.h, src/Menu/ProfilingRenderer.cpp
Implements ProfilingRenderer with stable per-group color assignment, heatmap cell rendering, frame timing graph aggregation, and grouped statistics table with CPU/GPU mode toggle and per-feature filtering.
Menu and overlay UI integration
src/Features/PerformanceOverlay.h, src/Features/PerformanceOverlay.cpp, src/Menu/FeatureListRenderer.cpp
Adds ShowCSPasses setting, inserts CS render-pass statistics into the overlay, adds "Profiling" menu entry, and embeds per-feature profiling blocks in feature settings.

Sequence Diagram

sequenceDiagram
  participant Engine as Engine
  participant State as State
  participant Profiler as globals::profiler
  participant D3D11 as D3D11
  participant Feature as Feature GPU Code
  participant UI as Profiling UI

  Engine->>State: SetupResources()
  State->>Profiler: Initialize(device, context)
  Profiler->>D3D11: Create timestamp queries\ncompute tick->ms

  loop Per frame
    Engine->>Profiler: BeginFrame()
    Feature->>Profiler: BeginPass("name")
    Profiler->>D3D11: Issue GPU timestamp begin
    Feature->>D3D11: GPU Dispatch/Draw
    Profiler->>D3D11: Issue GPU timestamp end
    Feature->>Profiler: EndPass()
    Engine->>Profiler: EndFrame()
  end

  Note over Profiler: After kFrameLatency
  Profiler->>D3D11: Read queries
  Profiler->>Profiler: Compute GPU/CPU ms, update history
  UI->>Profiler: GetResults()
  Profiler-->>UI: TimerResult list
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • alandtse
  • davo0411

"A rabbit timed each pass with care,
Hopping through shaders, here and there,
CPU and GPU now sing in rhyme,
Rolling stats reveal the time.
Hop—profiler made frame-time fair!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add GPU/CPU profiling system with per-feature timing' clearly and concisely summarizes the main change of this PR: adding a comprehensive profiling system with GPU/CPU timing and per-feature instrumentation, which is reflected across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch profiling

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 20, 2026

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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Features/Upscaling/RCAS/RCAS.cpp (1)

48-83: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the profiler/perf-event nesting order.

The profiler pass and state perf event are not properly nested. Currently:

Line 48: profiler->BeginPass
Line 49: state->BeginPerfEvent  
Line 81: profiler->EndPass      ← ends before state
Line 82: state->EndPerfEvent

This creates overlapping scopes rather than proper nesting. Compare with the pattern in Upscaling.cpp lines 1717-1777, where state->EndPerfEvent() is called before profiler->EndPass().

🔄 Proposed fix
 	context->CSSetShader(nullptr, nullptr, 0);

-	globals::profiler->EndPass();
 	state->EndPerfEvent();
+	globals::profiler->EndPass();
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Features/Upscaling/RCAS/RCAS.cpp` around lines 48 - 83, The profiler and
perf-event scopes are closed in the wrong order; move the perf-event end call
before ending the profiler pass so nesting matches BeginPass/BeginPerfEvent
order — specifically, call state->EndPerfEvent() prior to
globals::profiler->EndPass() at the end of the RCAS sharpening block (matching
the pattern used in Upscaling.cpp), leaving all other dispatch and resource
unbind calls intact.
🧹 Nitpick comments (1)
src/Features/ScreenSpaceShadows.cpp (1)

198-200: ⚡ Quick win

Avoid per-frame std::format for profiler pass names in the render hot path.

Line 198 builds a formatted string every eye/frame. Use fixed pass-name literals selected by branch to remove avoidable CPU overhead.

As per coding guidelines, "Flag potential performance impacts on rendering performance when implementing graphics features and suggest user toggles for feature control".

♻️ Proposed refactor
-auto DispatchEye = [&](const char* eyeName, ID3D11ComputeShader* shader, const float* lightProj,
-                       float invTexSizeX, float invTexSizeY) {
-    std::string timerName = eyeName ? std::format("ScreenSpaceShadows::RayMarch({})", eyeName) : "ScreenSpaceShadows::RayMarch";
-    globals::profiler->BeginPass(timerName);
+auto DispatchEye = [&](const char* eyeName, const char* profilerPassName, ID3D11ComputeShader* shader, const float* lightProj,
+                       float invTexSizeX, float invTexSizeY) {
+    globals::profiler->BeginPass(profilerPassName);
-DispatchEye(nullptr, GetComputeRaymarch(), lightProjectionF.data(), InvTexSizeX, InvTexSizeY);
+DispatchEye(nullptr, "ScreenSpaceShadows::RayMarch", GetComputeRaymarch(), lightProjectionF.data(), InvTexSizeX, InvTexSizeY);
...
-DispatchEye("Left Eye", GetComputeRaymarch(), lightProjectionF.data(), InvTexSizeX, InvTexSizeY);
+DispatchEye("Left Eye", "ScreenSpaceShadows::RayMarch(LeftEye)", GetComputeRaymarch(), lightProjectionF.data(), InvTexSizeX, InvTexSizeY);
...
-DispatchEye("Right Eye", GetComputeRaymarchRight(), lightProjectionRightF.data(), InvTexSizeX, InvTexSizeY);
+DispatchEye("Right Eye", "ScreenSpaceShadows::RayMarch(RightEye)", GetComputeRaymarchRight(), lightProjectionRightF.data(), InvTexSizeX, InvTexSizeY);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Features/ScreenSpaceShadows.cpp` around lines 198 - 200, The code
currently constructs a per-frame std::format string into timerName using eyeName
and passes it to globals::profiler->BeginPass, causing unnecessary allocations
in the render hot path; replace this with two fixed string literals (e.g.,
"ScreenSpaceShadows::RayMarch_Left" and "ScreenSpaceShadows::RayMarch_Right" or
a generic "ScreenSpaceShadows::RayMarch" when eyeName is null) and select the
appropriate literal by branching on eyeName before calling
globals::profiler->BeginPass to eliminate per-frame formatting and allocations
while preserving distinct pass names for profiling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Menu/StatisticsRenderer.cpp`:
- Around line 93-95: The code unconditionally dereferences globals::profiler
(via "auto& profiler = (*globals::profiler)") which can crash if it’s null;
update each render entry point that uses this pattern (the blocks at the diff
and the similar locations around lines 146-147 and 293-295) to first check if
globals::profiler is non-null, and if null either early-return or use a safe
fallback (e.g., skip rendering the profiler UI, set results to an empty/default
value, and log a warning), then only call profiler.GetResults() and evaluate
timingMode/TimmingMode::CPU after the null check so no dereference occurs on a
null pointer.

In `@src/Profiler.cpp`:
- Around line 188-190: activeTimers currently overwrites entries when the same
pass name appears multiple times (activeTimers[timer.name] = { ms, timer.cpuMs
};), dropping earlier samples and underreporting; change the logic in
Profiler.cpp where you populate activeTimers to check for an existing entry for
timer.name and accumulate the values (add ms to the existing elapsed ms and add
timer.cpuMs to the existing cpu ms) rather than replacing them, and continue to
increment activeTotalMs by the ms for each occurrence so total time remains
correct.
- Around line 44-55: The CreateQuery calls inside the frames loop
(device->CreateQuery for frame.disjoint and for each timer.begin/timer.end)
currently ignore HRESULTs and can leave null/invalid ComPtrs; change these to
check the returned HRESULT (use SUCCEEDED/FAILED) for each CreateQuery, log or
mark the frame as profiling-disabled on failure, release any partially-created
queries for that frame, and skip issuing Begin/End/GetData for frames whose
queries failed (or propagate an error state) so later calls don't operate on
null queries; ensure this logic covers creation of frame.disjoint, all
timer.begin/timer.end in the frame.timers array and uses safe guards around
kMaxTimers handling.

In `@src/State.cpp`:
- Around line 763-767: When frameAnnotations is false you must clear the
existing profiler callbacks so perf events stop firing; update the conditional
around globals::profiler->SetPerfEventCallbacks to set the
BeginPerfEvent/EndPerfEvent lambdas when frameAnnotations is true and to
clear/reset (pass no-op lambdas or nullptr equivalents) when false so the
profiler no longer invokes BeginPerfEvent/EndPerfEvent after annotations are
toggled off; refer to globals::profiler->SetPerfEventCallbacks, BeginPerfEvent,
EndPerfEvent, and frameAnnotations to locate the change.

In `@src/Utils/LegitProfiler.h`:
- Around line 7-11: This header is not self-contained: add the missing includes
<algorithm> (for std::sort, std::min, std::max), <cstdint> (for uint32_t) and
<cmath> (for floating-point std::abs overload) to the top of LegitProfiler.h,
and update the abs usage to std::abs to ensure the floating-point overload is
selected; also qualify calls to sort/min/max with std:: where appropriate to
avoid relying on transitive includes.

---

Outside diff comments:
In `@src/Features/Upscaling/RCAS/RCAS.cpp`:
- Around line 48-83: The profiler and perf-event scopes are closed in the wrong
order; move the perf-event end call before ending the profiler pass so nesting
matches BeginPass/BeginPerfEvent order — specifically, call
state->EndPerfEvent() prior to globals::profiler->EndPass() at the end of the
RCAS sharpening block (matching the pattern used in Upscaling.cpp), leaving all
other dispatch and resource unbind calls intact.

---

Nitpick comments:
In `@src/Features/ScreenSpaceShadows.cpp`:
- Around line 198-200: The code currently constructs a per-frame std::format
string into timerName using eyeName and passes it to
globals::profiler->BeginPass, causing unnecessary allocations in the render hot
path; replace this with two fixed string literals (e.g.,
"ScreenSpaceShadows::RayMarch_Left" and "ScreenSpaceShadows::RayMarch_Right" or
a generic "ScreenSpaceShadows::RayMarch" when eyeName is null) and select the
appropriate literal by branching on eyeName before calling
globals::profiler->BeginPass to eliminate per-frame formatting and allocations
while preserving distinct pass names for profiling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ed7f2a83-cd32-4f8f-ae2c-90b7acd8ebc6

📥 Commits

Reviewing files that changed from the base of the PR and between 57b827f and fbfa7f1.

📒 Files selected for processing (28)
  • include/PCH.h
  • src/Deferred.cpp
  • src/Features/DynamicCubemaps.cpp
  • src/Features/GrassCollision.cpp
  • src/Features/HDRDisplay.cpp
  • src/Features/IBL.cpp
  • src/Features/LightLimitFix.cpp
  • src/Features/PerformanceOverlay.cpp
  • src/Features/PerformanceOverlay.h
  • src/Features/ScreenSpaceGI.cpp
  • src/Features/ScreenSpaceShadows.cpp
  • src/Features/Skylighting.cpp
  • src/Features/SubsurfaceScattering.cpp
  • src/Features/TerrainBlending.cpp
  • src/Features/TerrainShadows.cpp
  • src/Features/Upscaling.cpp
  • src/Features/Upscaling/RCAS/RCAS.cpp
  • src/Features/VRStereoOptimizations.cpp
  • src/Features/VolumetricShadows.cpp
  • src/Globals.cpp
  • src/Globals.h
  • src/Menu/FeatureListRenderer.cpp
  • src/Menu/StatisticsRenderer.cpp
  • src/Menu/StatisticsRenderer.h
  • src/Profiler.cpp
  • src/Profiler.h
  • src/State.cpp
  • src/Utils/LegitProfiler.h

Comment thread src/Menu/ProfilingRenderer.cpp
Comment thread src/Profiler.cpp
Comment thread src/Profiler.cpp
Comment thread src/State.cpp
Comment thread src/Utils/LegitProfiler.h
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: 2

♻️ Duplicate comments (1)
src/Menu/ProfilingRenderer.cpp (1)

93-95: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard globals::profiler before dereference in all render entry points.

These paths can crash when profiler is unavailable (startup/reset edge cases).

Suggested fix
 void ProfilingRenderer::RenderGraph()
 {
+	if (!globals::profiler)
+		return;
 	auto& profiler = (*globals::profiler);
 	const auto& results = profiler.GetResults();
@@
 void ProfilingRenderer::RenderStatistics(bool showTable, bool showModeToggle)
 {
+	if (!globals::profiler) {
+		ImGui::TextDisabled("Profiler unavailable");
+		return;
+	}
 	auto& profiler = (*globals::profiler);
@@
 void ProfilingRenderer::RenderFeatureTimers(const std::string& featurePrefix)
 {
+	if (!globals::profiler) {
+		ImGui::TextDisabled("Profiler unavailable");
+		return;
+	}
 	auto& profiler = (*globals::profiler);

As per coding guidelines, "Include proper resource management and graceful degradation error handling in all DirectX code to prevent crashes from malformed configurations".

Also applies to: 147-148, 295-297

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Menu/ProfilingRenderer.cpp` around lines 93 - 95, Guard against a null
globals::profiler before dereferencing it in all render entry points: replace
the direct dereference pattern (auto& profiler = (*globals::profiler); const
auto& results = profiler.GetResults(); bool cpuMode = (timingMode ==
TimingMode::CPU);) with an initial null-check (if (!globals::profiler) { /*
early return or set safe defaults and degrade gracefully */ }) and then only
dereference globals::profiler after the check; apply the same change to the
other occurrences that access globals::profiler and call GetResults() so the
renderer returns or uses safe defaults when the profiler is unavailable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Features/PerformanceOverlay.cpp`:
- Around line 1589-1592: The code reads globals::profiler->GetTotalTimeMs()
without checking profiler, which can crash if profiler is null; update the
PerformanceOverlay calculation to null-check globals::profiler before calling
GetTotalTimeMs (e.g., use a local float csPassesTime = globals::profiler ?
globals::profiler->GetTotalTimeMs() : 0.0f) and then compute csPercent,
remainingOtherTime and remainingOtherPercent from that safe value so the overlay
gracefully degrades when profiler is uninitialized.

In `@src/Menu/ProfilingRenderer.cpp`:
- Around line 149-160: cpuMode is only set inside the showModeToggle branch so
when the toggle is hidden the UI always treats profiling as GPU; initialize
cpuMode from timingMode before the if block (e.g., set cpuMode = (timingMode ==
TimingMode::CPU) immediately after declaring cpuMode) and keep the existing
toggle logic that updates timingMode and timeSinceLastUpdate so the
hidden-toggle path respects the currently selected TimingMode.

---

Duplicate comments:
In `@src/Menu/ProfilingRenderer.cpp`:
- Around line 93-95: Guard against a null globals::profiler before dereferencing
it in all render entry points: replace the direct dereference pattern (auto&
profiler = (*globals::profiler); const auto& results = profiler.GetResults();
bool cpuMode = (timingMode == TimingMode::CPU);) with an initial null-check (if
(!globals::profiler) { /* early return or set safe defaults and degrade
gracefully */ }) and then only dereference globals::profiler after the check;
apply the same change to the other occurrences that access globals::profiler and
call GetResults() so the renderer returns or uses safe defaults when the
profiler is unavailable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b386b229-e117-4471-a111-e5f8224ea831

📥 Commits

Reviewing files that changed from the base of the PR and between fbfa7f1 and 45383a8.

📒 Files selected for processing (5)
  • src/Features/PerformanceOverlay.cpp
  • src/Features/PerformanceOverlay.h
  • src/Menu/FeatureListRenderer.cpp
  • src/Menu/ProfilingRenderer.cpp
  • src/Menu/ProfilingRenderer.h

Comment thread src/Features/PerformanceOverlay.cpp
Comment thread src/Menu/ProfilingRenderer.cpp Outdated
doodlum and others added 3 commits May 20, 2026 18:48
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Menu/ProfilingRenderer.cpp`:
- Around line 162-164: Remove the stray closing brace that prematurely ends
ProfilingRenderer::RenderStatistics: delete the extra `}` (the one that closes
the function at line 162) so that the subsequent statements (starting with the
`float currentTime = static_cast<float>(ImGui::GetTime());` and all code through
the end of the intended function) remain inside
ProfilingRenderer::RenderStatistics; verify the function's opening and closing
braces now correctly encompass all logic and recompile.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c38eafb3-6f2d-4693-bd46-ff2c0a2d3bcf

📥 Commits

Reviewing files that changed from the base of the PR and between 45383a8 and ebc9bd7.

📒 Files selected for processing (1)
  • src/Menu/ProfilingRenderer.cpp

Comment thread src/Menu/ProfilingRenderer.cpp Outdated
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.

🧹 Nitpick comments (1)
src/Features/DynamicCubemaps.cpp (1)

377-379: ⚡ Quick win

Gate profiling scopes behind the runtime profiling toggle.

Lines 377-379, 420-423, 465-477, and 512-531 now profile every dispatch unconditionally. Please short-circuit these BeginPass/EndPass calls when profiling is disabled to avoid unnecessary per-pass overhead on the render path.

As per coding guidelines, "src/Features/**/*.{h,cpp}: Flag potential performance impacts on rendering performance when implementing graphics features and suggest user toggles for feature control".

Also applies to: 420-423, 465-477, 512-531

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Features/DynamicCubemaps.cpp` around lines 377 - 379, The profiling
BeginPass/EndPass calls around the dispatches in DynamicCubemaps.cpp should be
short-circuited when runtime profiling is disabled: wrap the
globals::profiler->BeginPass(...) / globals::profiler->EndPass() pair (the block
that uses the a_reflections ? "DynamicCubemaps::CaptureReflections" :
"DynamicCubemaps::Capture" name and the other profiling strings at the other
sites) with a runtime check (e.g. verify globals::profiler is non-null and the
profiler is enabled via the profiler's runtime-enabled API) so the Dispatch(...)
calls run without the per-pass profiling overhead when profiling is off; apply
the same guard to the other BeginPass/EndPass occurrences at the noted locations
(lines ~420-423, ~465-477, ~512-531) that currently call
globals::profiler->BeginPass/EndPass unconditionally.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/Features/DynamicCubemaps.cpp`:
- Around line 377-379: The profiling BeginPass/EndPass calls around the
dispatches in DynamicCubemaps.cpp should be short-circuited when runtime
profiling is disabled: wrap the globals::profiler->BeginPass(...) /
globals::profiler->EndPass() pair (the block that uses the a_reflections ?
"DynamicCubemaps::CaptureReflections" : "DynamicCubemaps::Capture" name and the
other profiling strings at the other sites) with a runtime check (e.g. verify
globals::profiler is non-null and the profiler is enabled via the profiler's
runtime-enabled API) so the Dispatch(...) calls run without the per-pass
profiling overhead when profiling is off; apply the same guard to the other
BeginPass/EndPass occurrences at the noted locations (lines ~420-423, ~465-477,
~512-531) that currently call globals::profiler->BeginPass/EndPass
unconditionally.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ae4e8924-b5fc-4b8b-96e5-feb25e4f4c95

📥 Commits

Reviewing files that changed from the base of the PR and between ebc9bd7 and 1097bf7.

📒 Files selected for processing (18)
  • features/Dynamic Cubemaps/Shaders/Features/DynamicCubemaps.ini
  • features/Grass Collision/Shaders/Features/GrassCollision.ini
  • features/HDR Display/Shaders/Features/HDRDisplay.ini
  • features/IBL/Shaders/Features/ImageBasedLighting.ini
  • features/Light Limit Fix/Shaders/Features/LightLimitFix.ini
  • features/Performance Overlay/Shaders/Features/PerformanceOverlay.ini
  • features/Screen Space GI/Shaders/Features/ScreenSpaceGI.ini
  • features/Skylighting/Shaders/Features/Skylighting.ini
  • features/Subsurface Scattering/Shaders/Features/SubsurfaceScattering.ini
  • features/Terrain Blending/Shaders/Features/TerrainBlending.ini
  • features/Terrain Shadows/Shaders/Features/TerrainShadows.ini
  • features/Volumetric Shadows/Shaders/Features/VolumetricShadows.ini
  • src/Features/DynamicCubemaps.cpp
  • src/Menu/ProfilingRenderer.cpp
  • src/Profiler.cpp
  • src/Profiler.h
  • src/State.cpp
  • src/Utils/LegitProfiler.h
💤 Files with no reviewable changes (1)
  • src/Menu/ProfilingRenderer.cpp
✅ Files skipped from review due to trivial changes (11)
  • features/Light Limit Fix/Shaders/Features/LightLimitFix.ini
  • features/Terrain Shadows/Shaders/Features/TerrainShadows.ini
  • features/Grass Collision/Shaders/Features/GrassCollision.ini
  • features/HDR Display/Shaders/Features/HDRDisplay.ini
  • features/Performance Overlay/Shaders/Features/PerformanceOverlay.ini
  • features/Volumetric Shadows/Shaders/Features/VolumetricShadows.ini
  • features/Terrain Blending/Shaders/Features/TerrainBlending.ini
  • features/Dynamic Cubemaps/Shaders/Features/DynamicCubemaps.ini
  • features/Screen Space GI/Shaders/Features/ScreenSpaceGI.ini
  • features/Skylighting/Shaders/Features/Skylighting.ini
  • features/IBL/Shaders/Features/ImageBasedLighting.ini

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 20, 2026

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

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.

2 participants