Skip to content

fix: fix effect shader decals#1186

Merged
doodlum merged 11 commits into
devfrom
dissolve-fix
Jul 9, 2025
Merged

fix: fix effect shader decals#1186
doodlum merged 11 commits into
devfrom
dissolve-fix

Conversation

@doodlum
Copy link
Copy Markdown
Collaborator

@doodlum doodlum commented Jun 23, 2025

Summary by CodeRabbit

  • New Features

    • Improved handling of blended decal rendering for enhanced visual effects during world rendering.
  • Refactor

    • Centralized and streamlined decal rendering logic for better consistency and maintainability.
    • Updated rendering hooks to defer blended decal passes, optimizing render flow.
  • Bug Fixes

    • Enhanced reliability of world rendering state tracking, reducing potential visual inconsistencies with decals.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 23, 2025

Walkthrough

The changes refactor decal rendering control flow by centralizing "in-world" state management with a global flag, introducing a global vector for deferred blended decal render passes, and consolidating decal rendering logic into a new RenderBlendedDecals() method. Several redundant hooks and local state flags are removed, and related function signatures are updated accordingly.

Changes

File(s) Change Summary
src/Deferred.cpp, src/Deferred.h Replaced local inWorld flag with globals::state->inWorld. Added RenderBlendedDecals() method. Removed three blended decal hooks.
src/Features/TerrainBlending.cpp Replaced local deferred->inWorld check with globals::state->inWorld.
src/Hooks.cpp, src/Hooks.h Added RenderPass struct and new BSBatchRenderer_RenderPassImmediately1 hook. Updated thunk signatures and decal separation logic.
src/State.h Added blendedDecalRenderPasses vector and inWorld boolean to State class. Included <Hooks.h>.
src/TruePBR.cpp Removed conditional for separating deferred/forward blended decals in render pass thunk. No signature changes.

Sequence Diagram(s)

sequenceDiagram
    participant GameEngine
    participant Deferred
    participant State
    participant Hooks

    GameEngine->>Deferred: Start world rendering
    Deferred->>State: Set inWorld = true

    loop Render Passes
        GameEngine->>Hooks: RenderPassImmediately
        Hooks->>State: Check inWorld
        alt Blended Decal Pass
            Hooks->>State: Append RenderPass to blendedDecalRenderPasses
        else
            Hooks->>GameEngine: Call original render pass
        end
    end

    GameEngine->>Deferred: End world rendering
    Deferred->>Deferred: RenderBlendedDecals()
    Deferred->>State: For each blendedDecalRenderPass, render via hook
    Deferred->>State: Clear blendedDecalRenderPasses
    Deferred->>State: Set inWorld = false
Loading

Suggested reviewers

  • alandtse

Poem

In the world of decals, a rabbit hops with glee,
Refactoring flags for all to see!
No more confusion—global state is king,
Deferred blends now share a single ring.
With hooks trimmed down and logic anew,
The rendering burrow is streamlined through!
🐇✨

✨ 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.

pre-commit-ci Bot and others added 5 commits June 23, 2025 16:36
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 30, 2025

Using provided base ref: 3a5ff65
Using base ref: 3a5ff65
Base commit date: 2025-06-23T17:09:40-07:00 (Monday, June 23, 2025 05:09 PM)

Actionable Suggestions

  • Hair Specular: Action required; Needs version bump to 1-1-0

@doodlum doodlum marked this pull request as ready for review July 9, 2025 10:51
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: 3

🧹 Nitpick comments (2)
src/Hooks.cpp (1)

874-876: Note: Inconsistency in hook logic.

The second hook includes an additional call to UpdateRasterStateCullMode for interior sun shadows, while the other hooks don't. This appears intentional based on the feature check, but worth noting for maintainability.

Consider adding a comment explaining why only this hook handles interior sun shadows.

src/Deferred.cpp (1)

778-792: Consider defining the depth bias mode as a named constant.

The implementation correctly manages the deferred blended decal render passes. However, the magic number 10 for rasterStateDepthBiasMode should be defined as a named constant for better maintainability and clarity.

+    static constexpr int BLENDED_DECAL_DEPTH_BIAS_MODE = 10;
+
 void Deferred::RenderBlendedDecals()
 {
     if (!globals::state->blendedDecalRenderPasses.empty()) {
         auto& runtimeData = globals::game::shadowState->GetRuntimeData();
         auto runtimeDataCopy = runtimeData;
-        runtimeData.rasterStateDepthBiasMode = 10;
+        runtimeData.rasterStateDepthBiasMode = BLENDED_DECAL_DEPTH_BIAS_MODE;

         for (auto& renderPass : globals::state->blendedDecalRenderPasses)
             ::Hooks::BSBatchRenderer_RenderPassImmediately1::func(renderPass.a_pass, renderPass.a_technique, renderPass.a_alphaTest, renderPass.a_renderFlags);

         globals::state->blendedDecalRenderPasses.clear();

         runtimeData = runtimeDataCopy;
     }
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3a5ff65 and 38f0880.

📒 Files selected for processing (7)
  • src/Deferred.cpp (4 hunks)
  • src/Deferred.h (1 hunks)
  • src/Features/TerrainBlending.cpp (1 hunks)
  • src/Hooks.cpp (1 hunks)
  • src/Hooks.h (1 hunks)
  • src/State.h (2 hunks)
  • src/TruePBR.cpp (0 hunks)
💤 Files with no reviewable changes (1)
  • src/TruePBR.cpp
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-07-05T05:20:45.777Z
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/TerrainBlending.cpp (2)
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-07-05T05:20:45.777Z
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.
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/Deferred.h (3)
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.
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/Hooks.cpp (1)
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-07-05T05:20:45.777Z
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/Deferred.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/State.h (3)
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 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 (1)
src/Features/TerrainBlending.cpp (2)
src/Upscaling.h (1)
  • state (169-185)
src/FrameAnnotations.cpp (1)
  • state (224-231)
⏰ 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). (1)
  • GitHub Check: Build plugin and addons
🔇 Additional comments (12)
src/Features/TerrainBlending.cpp (1)

263-263: LGTM: Global state refactor correctly implemented.

The change from deferred->inWorld to globals::state->inWorld aligns with the centralized state management approach described in the PR summary.

src/State.h (2)

13-13: LGTM: Include added for RenderPass struct.

The include is necessary for the RenderPass struct used in the blendedDecalRenderPasses vector.


151-152: LGTM: Global state variables for decal rendering refactor.

The new members support the centralized decal rendering approach:

  • blendedDecalRenderPasses stores deferred render passes
  • inWorld centralizes world rendering state tracking

Note that RenderPass contains raw pointers (RE::BSRenderPass*, RE::BSShaderProperty*), so ensure these remain valid until the deferred passes are processed.

src/Deferred.h (1)

82-82: LGTM: New method for centralized decal rendering.

The RenderBlendedDecals() method aligns with the refactor to centralize decal rendering logic and process deferred render passes stored in the global state.

src/Hooks.h (2)

16-20: LGTM: New hook structure follows established pattern.

The BSBatchRenderer_RenderPassImmediately1 hook structure is consistent with other hooks in the file and supports the deferred decal rendering system.


22-28: LGTM: RenderPass struct appropriately designed.

The struct encapsulates the necessary parameters for deferred render pass processing, with appropriate member types for the rendering pipeline.

src/Hooks.cpp (1)

858-861: Verify blended decal identification logic in Hooks.cpp (src/Hooks.cpp:858–861)

Please confirm that using a_pass->accumulationHint == 3 together with !a_pass->shaderProperty->flags.all(RE::BSShaderProperty::EShaderPropertyFlag::kZBufferWrite) reliably captures all and only the intended decal passes (both deferred and forward). In particular:

  • Ensure that the engine’s accumulation‐hint value 3 indeed represents decal rendering.
  • Confirm that checking for absence of the kZBufferWrite flag correctly differentiates forward blended decals.

Consider extracting this into a named helper (e.g. IsBlendedDecalPass(a_pass)) or referring directly to well‐named enum constants for clarity and future maintenance.

src/Deferred.cpp (5)

16-16: LGTM!

The addition of the Hooks.h header is appropriate given the new usage of hook functions in the RenderBlendedDecals() method.


524-524: LGTM!

Correctly updated to use the global inWorld flag, maintaining the same early return logic while centralizing state management.


759-761: LGTM!

Properly manages the global inWorld state by setting it before and clearing it after the world rendering phase.


769-772: LGTM!

Consistently uses the global inWorld flag for conditional logic in the deferred rendering path.


800-800: LGTM!

The changes correctly integrate the new deferred blended decal rendering approach:

  • Uses the global inWorld flag for consistency
  • Calls the new RenderBlendedDecals() method instead of the original function, consolidating the decal rendering logic

Also applies to: 815-815

Comment thread src/Hooks.cpp
Comment on lines +896 to +901
// Separate deferred and forward blended decals
if (globals::state->inWorld && a_pass->accumulationHint == 3 && !a_pass->shaderProperty->flags.all(RE::BSShaderProperty::EShaderPropertyFlag::kZBufferWrite)) {
RenderPass call{ a_pass, a_technique, a_alphaTest, a_renderFlags };
globals::state->blendedDecalRenderPasses.push_back(call);
return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null pointer check for shader property access.

Same issue as in the other hooks - missing null check for shaderProperty.

Apply this diff to add safety:

 // Separate deferred and forward blended decals
-if (globals::state->inWorld && a_pass->accumulationHint == 3 && !a_pass->shaderProperty->flags.all(RE::BSShaderProperty::EShaderPropertyFlag::kZBufferWrite)) {
+if (globals::state->inWorld && a_pass->accumulationHint == 3 && a_pass->shaderProperty && !a_pass->shaderProperty->flags.all(RE::BSShaderProperty::EShaderPropertyFlag::kZBufferWrite)) {
🤖 Prompt for AI Agents
In src/Hooks.cpp around lines 896 to 901, the code accesses
a_pass->shaderProperty without checking if shaderProperty is null, which can
cause a crash. Add a null pointer check for a_pass->shaderProperty before
accessing its flags to ensure safety. Only proceed with the condition if
shaderProperty is not null.

Comment thread src/Hooks.cpp
Comment on lines +877 to +882
// Separate deferred and forward blended decals
if (globals::state->inWorld && a_pass->accumulationHint == 3 && !a_pass->shaderProperty->flags.all(RE::BSShaderProperty::EShaderPropertyFlag::kZBufferWrite)) {
RenderPass call{ a_pass, a_technique, a_alphaTest, a_renderFlags };
globals::state->blendedDecalRenderPasses.push_back(call);
return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null pointer check for shader property access.

Same issue as in the first hook - missing null check for shaderProperty.

Apply this diff to add safety:

 // Separate deferred and forward blended decals
-if (globals::state->inWorld && a_pass->accumulationHint == 3 && !a_pass->shaderProperty->flags.all(RE::BSShaderProperty::EShaderPropertyFlag::kZBufferWrite)) {
+if (globals::state->inWorld && a_pass->accumulationHint == 3 && a_pass->shaderProperty && !a_pass->shaderProperty->flags.all(RE::BSShaderProperty::EShaderPropertyFlag::kZBufferWrite)) {
🤖 Prompt for AI Agents
In src/Hooks.cpp around lines 877 to 882, the code accesses
a_pass->shaderProperty without checking if shaderProperty is null, which can
cause a crash. Add a null pointer check for a_pass->shaderProperty before
accessing its flags to ensure safety. Only proceed with the condition if
shaderProperty is not null.

Comment thread src/Hooks.cpp
Comment on lines +852 to +865
void BSBatchRenderer_RenderPassImmediately1::thunk(RE::BSRenderPass* a_pass, uint32_t a_technique, bool a_alphaTest, uint32_t a_renderFlags)
{
static void thunk(RE::BSRenderPass* pass, uint32_t technique, bool alphaTest, uint32_t renderFlags)
{
if (globals::features::lightLimitFix->loaded && !globals::features::lightLimitFix->CheckParticleLights(pass, technique))
return;
if (globals::features::lightLimitFix->loaded && !globals::features::lightLimitFix->CheckParticleLights(a_pass, a_technique))
return;

func(pass, technique, alphaTest, renderFlags);
// Separate deferred and forward blended decals
if (globals::state->inWorld && a_pass->accumulationHint == 3 && !a_pass->shaderProperty->flags.all(RE::BSShaderProperty::EShaderPropertyFlag::kZBufferWrite)) {
RenderPass call{ a_pass, a_technique, a_alphaTest, a_renderFlags };
globals::state->blendedDecalRenderPasses.push_back(call);
return;
}
static inline REL::Relocation<decltype(thunk)> func;
};

func(a_pass, a_technique, a_alphaTest, a_renderFlags);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null pointer check for shader property access.

The code accesses a_pass->shaderProperty->flags without checking if shaderProperty is null, which could cause crashes.

Apply this diff to add safety:

 // Separate deferred and forward blended decals
-if (globals::state->inWorld && a_pass->accumulationHint == 3 && !a_pass->shaderProperty->flags.all(RE::BSShaderProperty::EShaderPropertyFlag::kZBufferWrite)) {
+if (globals::state->inWorld && a_pass->accumulationHint == 3 && a_pass->shaderProperty && !a_pass->shaderProperty->flags.all(RE::BSShaderProperty::EShaderPropertyFlag::kZBufferWrite)) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void BSBatchRenderer_RenderPassImmediately1::thunk(RE::BSRenderPass* a_pass, uint32_t a_technique, bool a_alphaTest, uint32_t a_renderFlags)
{
static void thunk(RE::BSRenderPass* pass, uint32_t technique, bool alphaTest, uint32_t renderFlags)
{
if (globals::features::lightLimitFix->loaded && !globals::features::lightLimitFix->CheckParticleLights(pass, technique))
return;
if (globals::features::lightLimitFix->loaded && !globals::features::lightLimitFix->CheckParticleLights(a_pass, a_technique))
return;
func(pass, technique, alphaTest, renderFlags);
// Separate deferred and forward blended decals
if (globals::state->inWorld && a_pass->accumulationHint == 3 && !a_pass->shaderProperty->flags.all(RE::BSShaderProperty::EShaderPropertyFlag::kZBufferWrite)) {
RenderPass call{ a_pass, a_technique, a_alphaTest, a_renderFlags };
globals::state->blendedDecalRenderPasses.push_back(call);
return;
}
static inline REL::Relocation<decltype(thunk)> func;
};
func(a_pass, a_technique, a_alphaTest, a_renderFlags);
}
void BSBatchRenderer_RenderPassImmediately1::thunk(RE::BSRenderPass* a_pass, uint32_t a_technique, bool a_alphaTest, uint32_t a_renderFlags)
{
if (globals::features::lightLimitFix->loaded && !globals::features::lightLimitFix->CheckParticleLights(a_pass, a_technique))
return;
// Separate deferred and forward blended decals
if (globals::state->inWorld
&& a_pass->accumulationHint == 3
&& a_pass->shaderProperty
&& !a_pass->shaderProperty->flags.all(RE::BSShaderProperty::EShaderPropertyFlag::kZBufferWrite))
{
RenderPass call{ a_pass, a_technique, a_alphaTest, a_renderFlags };
globals::state->blendedDecalRenderPasses.push_back(call);
return;
}
func(a_pass, a_technique, a_alphaTest, a_renderFlags);
}
🤖 Prompt for AI Agents
In src/Hooks.cpp around lines 852 to 865, the code accesses
a_pass->shaderProperty->flags without verifying if shaderProperty is null,
risking a crash. Add a null pointer check for a_pass->shaderProperty before
accessing its flags to ensure safety. Modify the condition to first confirm
a_pass->shaderProperty is not null, then proceed with the existing flag checks.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jul 9, 2025

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

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.

Looks good to me. good work

doodlum and others added 2 commits July 9, 2025 23:56
Automated formatting by clang-format, prettier, and other hooks.
See https://pre-commit.ci for details.
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: 0

🧹 Nitpick comments (1)
src/Deferred.cpp (1)

778-805: Consider refactoring to reduce code duplication.

The RenderBlendedDecals() method correctly handles deferred blended decal rendering with proper state management and cleanup. However, the VR and non-VR paths have significant code duplication.

Consider extracting the common logic into a helper function:

+void Deferred::RenderBlendedDecalsImpl(auto& runtimeData) {
+	auto runtimeDataCopy = runtimeData;
+	runtimeData.rasterStateDepthBiasMode = 10;
+
+	for (auto& renderPass : globals::state->blendedDecalRenderPasses)
+		::Hooks::BSBatchRenderer_RenderPassImmediately1::func(renderPass.a_pass, renderPass.a_technique, renderPass.a_alphaTest, renderPass.a_renderFlags);
+
+	globals::state->blendedDecalRenderPasses.clear();
+
+	runtimeData = runtimeDataCopy;
+}
+
 void Deferred::RenderBlendedDecals()
 {
 	if (!globals::state->blendedDecalRenderPasses.empty()) {
 		if (globals::game::isVR) {
-			auto& runtimeData = globals::game::shadowState->GetVRRuntimeData();
-			auto runtimeDataCopy = runtimeData;
-			runtimeData.rasterStateDepthBiasMode = 10;
-
-			for (auto& renderPass : globals::state->blendedDecalRenderPasses)
-				::Hooks::BSBatchRenderer_RenderPassImmediately1::func(renderPass.a_pass, renderPass.a_technique, renderPass.a_alphaTest, renderPass.a_renderFlags);
-
-			globals::state->blendedDecalRenderPasses.clear();
-
-			runtimeData = runtimeDataCopy;
+			RenderBlendedDecalsImpl(globals::game::shadowState->GetVRRuntimeData());
 		} else {
-			auto& runtimeData = globals::game::shadowState->GetRuntimeData();
-			auto runtimeDataCopy = runtimeData;
-			runtimeData.rasterStateDepthBiasMode = 10;
-
-			for (auto& renderPass : globals::state->blendedDecalRenderPasses)
-				::Hooks::BSBatchRenderer_RenderPassImmediately1::func(renderPass.a_pass, renderPass.a_technique, renderPass.a_alphaTest, renderPass.a_renderFlags);
-
-			globals::state->blendedDecalRenderPasses.clear();
-
-			runtimeData = runtimeDataCopy;
+			RenderBlendedDecalsImpl(globals::game::shadowState->GetRuntimeData());
 		}
 	}
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 38f0880 and 157f02c.

📒 Files selected for processing (1)
  • src/Deferred.cpp (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-07-05T05:20:45.777Z
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/Deferred.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.
⏰ 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). (1)
  • GitHub Check: Build plugin and addons
🔇 Additional comments (6)
src/Deferred.cpp (6)

16-16: LGTM: Include addition aligns with new functionality.

The new include for Hooks.h is necessary for the RenderBlendedDecals() method that uses hook functions.


524-524: LGTM: State centralization is consistent.

The change from deferred->inWorld to globals::state->inWorld aligns with the PR objective of centralizing "in-world" state management.


759-761: LGTM: Proper global state management.

The global state is correctly set to true at the start of world rendering and reset to false at the end, ensuring proper state lifecycle management.


769-769: LGTM: Consistent with global state refactoring.

The change maintains consistency with the centralized state management approach throughout the codebase.


813-813: LGTM: Consistent state management.

The change to use globals::state->inWorld maintains consistency with the global state refactoring.


828-828: LGTM: Integration of new decal rendering method.

The call to RenderBlendedDecals() properly integrates the new centralized decal rendering logic, replacing the previous implementation.

@doodlum doodlum merged commit 89d7002 into dev Jul 9, 2025
17 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jul 19, 2025
alandtse pushed a commit to alandtse/open-shaders that referenced this pull request Jul 20, 2025
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
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