Skip to content

fix: add anti-firefly pass#2028

Open
alandtse wants to merge 3 commits intodoodlum:devfrom
alandtse:fireflies
Open

fix: add anti-firefly pass#2028
alandtse wants to merge 3 commits intodoodlum:devfrom
alandtse:fireflies

Conversation

@alandtse
Copy link
Copy Markdown
Collaborator

@alandtse alandtse commented Mar 29, 2026

Meant to address this issue:

image

Summary by CodeRabbit

  • Bug Fixes
    • Further reduced extreme bright-pixel "fireflies" in bloom for cleaner highlights.
    • Softer, more natural bright-pass with luminance-aware soft-knee shaping.
    • Improved blur sampling for higher-quality bloom in high-contrast scenes and at small blur scales/dynamic resolution.
    • Minor performance stabilization by avoiding redundant luminance work during blur.

@github-actions
Copy link
Copy Markdown

No actionable suggestions for changed features.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 29, 2026

📝 Walkthrough

Walkthrough

Replaces the in-loop bright-pass with Karis-weighted 2×2 sampling and a soft‑knee bright-pass, moves avg luminance computation outside the blur loop, adds a new Color helper and Math include, and updates tests for the Karis-weighted average.

Changes

Cohort / File(s) Summary
Blur shader
package/Shaders/ISBlur.hlsl
Replaced in-loop bright-pass math with SampleKarisFireflySuppress and ApplyBrightPass; compute avgLum once before the blur loop; compute per-pixel texel size once; use dynamic-resolution-adjusted sampling when BlurScale.x < 0.5; preserve color.w = avgLum.
Color helpers
package/Shaders/Common/Color.hlsli
Added Color::KarisWeightedAverage(float3 a, float3 b, float3 c, float3 d) — luminance-weighted average that reduces influence of bright outliers.
Math include
package/Shaders/Common/Math.hlsli
New include added (supporting helper math referenced by ISBlur.hlsl).
Tests
package/Shaders/Tests/TestColor.hlsl
Replaced TestGammaConversionRoundtrip with TestKarisWeightedAverage; added assertions covering identity, firefly suppression, interpolation behavior, and luminance bounds; minor formatting changes to other test declarations.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • jiayev
  • doodlum

Poem

🐇 I nibble code in moonlit pass,

Soft knees hush a blinding mass,
Four pixels pooled to calm the light,
Fireflies fade into the night,
Bloom sleeps sound till morning's glass.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: add anti-firefly pass' directly and specifically describes the main change—implementing an anti-firefly filter for bloom rendering to address visual artifacts.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Copy link
Copy Markdown
Collaborator Author

@alandtse alandtse left a comment

Choose a reason for hiding this comment

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

@copilot can you please review the following comments and address if valid?

use the bloom firefly pass from jiaye pp
this is not really doing anything correct
you need to check other pixels basically, which you can do in the first downscale pass yeah
https://github.com/jiayev/skyrim-community-shaders/blob/pp/features/Post%20Processing/Shaders/PostProcessing/common.hlsli
DownsampleCODFirstMip if that's what u mean

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 the current code and only fix it if needed.

Inline comments:
In `@package/Shaders/ISBlur.hlsl`:
- Around line 103-106: The bright-pass branch bypasses GetImageColor so
BlurScale.y is not applied, changing HDR cutoff and bloom energy; restore
BlurScale.y influence by either (A) using BlurScale.y when computing sampleUV
(e.g., apply dynamic-resolution adjustment that incorporates BlurScale.y before
calling SampleKarisFireflySuppress) or (B, preferred if vertical blur should
affect Karis weights) extend SampleKarisFireflySuppress to accept BlurScale.y
and apply it per tap inside SampleKarisFireflySuppress (instead of averaging
then scaling) so ApplyBrightPass receives the correctly scaled HDR sample;
update the call site that currently passes sampleUV to pass the BlurScale.y
parameter if you choose option B.
🪄 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

Run ID: c7dec34b-4c03-486e-bb34-ca364d15ab18

📥 Commits

Reviewing files that changed from the base of the PR and between a9236f0 and 7f3b680.

📒 Files selected for processing (1)
  • package/Shaders/ISBlur.hlsl

Comment on lines +103 to +106
{
float2 sampleUV = (BlurScale.x < 0.5) ? FrameBuffer::GetDynamicResolutionAdjustedScreenPosition(screenPosition) : screenPosition;
imageColor.rgb = ApplyBrightPass(SampleKarisFireflySuppress(sampleUV, texelSize));
}
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 | 🟠 Major

Keep blurScale.y in the BRIGHTPASS sample path.

This branch no longer goes through GetImageColor, so any non-1 blurScale.y stops affecting the HDR sample before bright-pass. That changes the cutoff and bloom energy relative to the previous path in COLORRANGE builds.

Suggested fix
 		{
 			float2 sampleUV = (BlurScale.x < 0.5) ? FrameBuffer::GetDynamicResolutionAdjustedScreenPosition(screenPosition) : screenPosition;
-			imageColor.rgb = ApplyBrightPass(SampleKarisFireflySuppress(sampleUV, texelSize));
+			float3 hdrColor = SampleKarisFireflySuppress(sampleUV, texelSize) * blurScale.y;
+			imageColor.rgb = ApplyBrightPass(hdrColor);
 		}

If blurScale.y is supposed to influence the Karis weights too, thread it into SampleKarisFireflySuppress and apply it per tap instead of after the average.

📝 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
{
float2 sampleUV = (BlurScale.x < 0.5) ? FrameBuffer::GetDynamicResolutionAdjustedScreenPosition(screenPosition) : screenPosition;
imageColor.rgb = ApplyBrightPass(SampleKarisFireflySuppress(sampleUV, texelSize));
}
{
float2 sampleUV = (BlurScale.x < 0.5) ? FrameBuffer::GetDynamicResolutionAdjustedScreenPosition(screenPosition) : screenPosition;
float3 hdrColor = SampleKarisFireflySuppress(sampleUV, texelSize) * blurScale.y;
imageColor.rgb = ApplyBrightPass(hdrColor);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package/Shaders/ISBlur.hlsl` around lines 103 - 106, The bright-pass branch
bypasses GetImageColor so BlurScale.y is not applied, changing HDR cutoff and
bloom energy; restore BlurScale.y influence by either (A) using BlurScale.y when
computing sampleUV (e.g., apply dynamic-resolution adjustment that incorporates
BlurScale.y before calling SampleKarisFireflySuppress) or (B, preferred if
vertical blur should affect Karis weights) extend SampleKarisFireflySuppress to
accept BlurScale.y and apply it per tap inside SampleKarisFireflySuppress
(instead of averaging then scaling) so ApplyBrightPass receives the correctly
scaled HDR sample; update the call site that currently passes sampleUV to pass
the BlurScale.y parameter if you choose option B.

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.

♻️ Duplicate comments (1)
package/Shaders/ISBlur.hlsl (1)

89-93: ⚠️ Potential issue | 🟠 Major

Restore blurScale.y before ApplyBrightPass().

Line 92 still feeds unscaled HDR into the bright-pass threshold, while the non-BRIGHTPASS path applies blurScale.y in GetImageColor(). When blurScale.y != 1, that changes both the cutoff and bloom energy.

Suggested fix
 		{
 			float2 sampleUV = (BlurScale.x < 0.5) ? FrameBuffer::GetDynamicResolutionAdjustedScreenPosition(screenPosition) : screenPosition;
-			imageColor.rgb = ApplyBrightPass(SampleKarisFireflySuppress(sampleUV, texelSize));
+			float3 hdrColor = SampleKarisFireflySuppress(sampleUV, texelSize) * blurScale.y;
+			imageColor.rgb = ApplyBrightPass(hdrColor);
 		}

If blurScale.y is also supposed to influence the Karis weights, pass it into SampleKarisFireflySuppress() and scale each tap there instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package/Shaders/ISBlur.hlsl` around lines 89 - 93, The BRIGHTPASS branch is
applying ApplyBrightPass to HDR color sampled without restoring blurScale.y, so
when blurScale.y != 1 the bright-pass cutoff and bloom energy are wrong; in the
BRIGHTPASS block restore or apply blurScale.y to the sampled color before
calling ApplyBrightPass (mirroring the non-BRIGHTPASS GetImageColor path), or
alternatively modify SampleKarisFireflySuppress to accept blurScale.y and scale
each Karis tap there; update the call in the BRIGHTPASS branch
(SampleKarisFireflySuppress(...)) and/or scale imageColor by blurScale.y prior
to ApplyBrightPass so behavior matches non-BRIGHTPASS.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@package/Shaders/ISBlur.hlsl`:
- Around line 89-93: The BRIGHTPASS branch is applying ApplyBrightPass to HDR
color sampled without restoring blurScale.y, so when blurScale.y != 1 the
bright-pass cutoff and bloom energy are wrong; in the BRIGHTPASS block restore
or apply blurScale.y to the sampled color before calling ApplyBrightPass
(mirroring the non-BRIGHTPASS GetImageColor path), or alternatively modify
SampleKarisFireflySuppress to accept blurScale.y and scale each Karis tap there;
update the call in the BRIGHTPASS branch (SampleKarisFireflySuppress(...))
and/or scale imageColor by blurScale.y prior to ApplyBrightPass so behavior
matches non-BRIGHTPASS.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9818b545-942c-40c9-ac22-00fac65f3216

📥 Commits

Reviewing files that changed from the base of the PR and between 7f3b680 and 393ec75.

📒 Files selected for processing (3)
  • package/Shaders/Common/Color.hlsli
  • package/Shaders/ISBlur.hlsl
  • package/Shaders/Tests/TestColor.hlsl

return Color::KarisWeightedAverage(s0, s1, s2, s3);
}

float3 ApplyBrightPass(float3 hdrColor)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

shouldn't be needed now. probably isn't very effective

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I don't understand what you're saying.

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