feat: order independent transparency - #2564
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a configurable Order Independent Transparency feature with fragment capture, adaptive/rasterizer-ordered and weighted-blended resolve paths, GPU resource management, shader permutation integration, rendering hooks, water/reflection updates, and localized settings. ChangesOrder Independent Transparency
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
No actionable suggestions for changed features. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
3010804 to
b66b953
Compare
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new rendering feature—Order Independent Transparency (OIT)—to improve blending correctness for overlapping transparent surfaces (water, hair, particles, etc.) by capturing Skyrim’s transparency pass into OIT resources and compositing the result after the engine’s alpha pass. This integrates into the existing feature system, shader compilation pipeline, and several shader stages (including water refraction/SSR interactions and HDR blend-state interoperability).
Changes:
- Introduces
OrderIndependentTransparencyfeature with multiple OIT methods (weighted blended, fragment list / adaptive transparency, and ROV-based AOIT), including resource setup, capture, and resolve/composite. - Extends shader descriptor/define plumbing so Lighting/Effect/Particle shaders can compile OIT permutations and include OIT capture paths.
- Adds engine-level hooks/guards for alpha-pass bracketing, water integration (alpha-only reprojection input), and HDR blend-state compatibility during OIT capture.
Reviewed changes
Copilot reviewed 50 out of 50 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/State.h | Adds PerfEvent RAII helper and OIT-related extra descriptor flags. |
| src/State.cpp | Integrates OIT into draw flow and shader-lookup descriptor mutations; adds wide perf-marker helpers. |
| src/ShaderCache.h | Adds OIT shader flags (Lighting/Effect) and particle OIT flag bit. |
| src/ShaderCache.cpp | Injects OIT shader defines into Lighting/Effect/Particle compilation define lists. |
| src/Menu/ThemeManager.h | Updates theme JSON documentation comment (status palette). |
| src/Menu/ThemeManager.cpp | Updates disabled text alpha comment. |
| src/Hooks.cpp | Adds OIT feature include and calls OIT pre-dirty-state hook; updates render target hook relocation offsets. |
| src/Globals.h | Registers OrderIndependentTransparency in global feature declarations. |
| src/Globals.cpp | Instantiates global OrderIndependentTransparency feature instance. |
| src/FrameAnnotations.cpp | Removes shader setup/restore annotation hooks and renames an annotation scope label. |
| src/Features/OrderIndependentTransparency.h | New feature interface, settings, buffers, and capture/resolve API. |
| src/Features/OrderIndependentTransparency.cpp | New feature implementation: hooks, resource creation, capture bracketing, resolve/composite, water SRV binding. |
| src/Features/LightLimitFix.h | Renames a LightLimitFix flag constant. |
| src/Features/InverseSquareLighting.cpp | Updates usage of the renamed LightLimitFix flag. |
| src/Features/HDRDisplay.cpp | Adds an OIT guard to avoid HDR blend-state rewrite during OIT alpha pass. |
| src/Features/ExtendedTranslucency.h | Renames enum constants/comments related to “Disabled”. |
| src/Features/ExtendedTranslucency.cpp | Updates translucency alpha detection and enum usage. |
| src/FeatureBuffer.cpp | Adds OIT feature constant-buffer payload to the shared feature buffer. |
| src/Feature.cpp | Registers OIT in the global feature list ordering. |
| src/Deferred.cpp | Hooks water boundary to bind alpha-only SRV for refraction/SSR reprojection. |
| package/SKSE/Plugins/CommunityShaders/Translations/zh_CN.json | Adds OIT UI strings (Chinese). |
| package/SKSE/Plugins/CommunityShaders/Translations/ru.json | Adds OIT UI strings (Russian). |
| package/SKSE/Plugins/CommunityShaders/Translations/pl.json | Adds OIT UI strings (Polish). |
| package/SKSE/Plugins/CommunityShaders/Translations/nl.json | Adds OIT UI strings (Dutch). |
| package/SKSE/Plugins/CommunityShaders/Translations/ja.json | Adds OIT UI strings (Japanese). |
| package/SKSE/Plugins/CommunityShaders/Translations/fr.json | Adds OIT UI strings (French). |
| package/SKSE/Plugins/CommunityShaders/Translations/es.json | Adds OIT UI strings (Spanish). |
| package/SKSE/Plugins/CommunityShaders/Translations/en.json | Adds OIT UI strings (English). |
| package/SKSE/Plugins/CommunityShaders/Translations/de.json | Adds OIT UI strings (German). |
| package/Shaders/Water.hlsl | Adds alpha-only reprojection sampling for water refraction/reflect; adjusts depth texture type and motion-vector usage. |
| package/Shaders/Particle.hlsl | Adds OIT capture include and outputs for blended OIT; enables early depth-stencil for OIT permutations. |
| package/Shaders/Lighting.hlsl | Adds OIT capture include/outputs and early depth-stencil attribute for OIT permutations. |
| package/Shaders/ISReflectionsRayTracing.hlsl | Adjusts depth texture type and updates camera motion vector helper; tweaks SSR alpha integration. |
| package/Shaders/Effect.hlsl | Adds OIT capture include/outputs and early depth-stencil attribute for OIT permutations. |
| package/Shaders/Common/SharedData.hlsli | Adds OIT settings struct into shared shader constant buffer layout. |
| package/Shaders/Common/MotionBlur.hlsli | Adds GetSSMotionVector2 helper for camera motion vector computation. |
| features/Order Independent Transparency/Shaders/OIT/WBOITWeight.hlsli | New WBOIT weight function. |
| features/Order Independent Transparency/Shaders/OIT/WBOITResolve.hlsli | New WBOIT resolve function (with optional depth write). |
| features/Order Independent Transparency/Shaders/OIT/WBOIT.hlsli | New WBOIT capture implementation. |
| features/Order Independent Transparency/Shaders/OIT/Quad.hlsl | New (licensed) fullscreen quad VS helper from Intel sample. |
| features/Order Independent Transparency/Shaders/OIT/OITResolve.ps.hlsl | New pixel-shader resolve/composite entry point selecting OIT method resolve. |
| features/Order Independent Transparency/Shaders/OIT/OITResolve.cs.hlsl | New compute-shader resolve/composite entry point (A-buffer resolve path). |
| features/Order Independent Transparency/Shaders/OIT/OITCommon.hlsli | New shared OIT constants/flags for shader capture/resolve. |
| features/Order Independent Transparency/Shaders/OIT/OITCapture.hlsli | New unified capture wrapper for fragment list / AOIT / WBOIT. |
| features/Order Independent Transparency/Shaders/OIT/FragmentList.hlsli | New fragment-list (linked list) capture implementation. |
| features/Order Independent Transparency/Shaders/OIT/DXAOITResolve.hlsli | New resolve implementation for fragment list (and debug/weighted reference). |
| features/Order Independent Transparency/Shaders/OIT/DXAOIT.hlsli | New AOIT visibility-function implementation (Intel sample-derived). |
| features/Order Independent Transparency/Shaders/OIT/AOITResolve.hlsli | New AOIT resolve implementation (ROV path). |
| features/Order Independent Transparency/Shaders/OIT/AOIT.hlsli | New AOIT capture implementation (ROV/structured buffers). |
| features/Order Independent Transparency/Shaders/Features/OrderIndependentTransparency.ini | New feature shader-version ini for OIT. |
| struct PerfEvent | ||
| { | ||
| PerfEvent(const wchar_t* title); | ||
| PerfEvent(const PerfEvent&) = delete; | ||
| PerfEvent(PerfEvent&&) = default; | ||
| ~PerfEvent(); |
| // Need to capture pre-water depth | ||
| auto& preWaterDepth = renderer->GetDepthStencilData().depthStencils[RE::RENDER_TARGETS_DEPTHSTENCIL::kPOST_ZPREPASS_COPY]; | ||
| dsv = preWaterDepth.readOnlyViews[0]; | ||
| static constexpr UINT uavcounters[2] = { 1, 1 }; |
| #if defined(OIT) | ||
| #if OIT == 3 && !defined(DEFERRED) | ||
| WBOITResult oit = OIT_CaptureWBOIT(int2(input.Position.xy), psout.Diffuse, input.Position.z); | ||
| psout.OITFrontAccumalation = oit.accumFront; | ||
| psout.OITAccumalation = oit.accumAll; | ||
| psout.OITRevealage = oit.revealage; | ||
| #else | ||
| psout.Diffuse = OIT_Capture(int2(input.Position.xy), psout.Diffuse, input.Position.z); | ||
| #if !defined(MOTIONVECTORS_NORMALS) | ||
| psout.Color2 = psout.Diffuse; | ||
| #endif | ||
| #endif | ||
| #endif |
| // blend alpha only colors with existing color | ||
| float4 ecolor = RT_ALPHA[DTid.xy]; | ||
| ecolor.xyz = ecolor.xyz * ecolor.w + wcolor.xyz; | ||
| ecolor.w = ecolor.w * wcolor.w; | ||
| wcolor.w = 1.0 - wcolor.w; | ||
| if (wcolor.w > 0.f) wcolor.xyz /= wcolor.w; | ||
| RT_ALPHA[DTid.xy] = wcolor; |
There was a problem hiding this comment.
Actionable comments posted: 15
🧹 Nitpick comments (3)
src/Features/OrderIndependentTransparency.cpp (1)
956-960: 📐 Maintainability & Code Quality | 🔵 TrivialResolve or remove the unfinished implementation placeholder.
Complete the geometry consolidation, or move the deferred work to a tracked issue and retain only an explanatory comment.
As per coding guidelines,
src/**/*.{h,cpp}must “Provide complete, fully functional code without TODO/FIXME placeholders in feature implementations”.Would you like me to draft the implementation or issue text?
🤖 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/OrderIndependentTransparency.cpp` around lines 956 - 960, Remove the TODO placeholder in the geometry handling section of OrderIndependentTransparency.cpp. Either implement the deferred Z-Buffer Write geometry consolidation into a single draw call, or replace the TODO block with a concise explanatory comment referencing a tracked issue for the deferred work; do not leave TODO/FIXME markers in the feature implementation.Source: Coding guidelines
package/Shaders/Lighting.hlsl (1)
306-310: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueTypo in variable name. The variable names
OITFrontAccumalationandOITAccumalationcontain a typo ("Accumalation" instead of "Accumulation"). Consider fixing this typo across all shaders to improve maintainability.
package/Shaders/Lighting.hlsl#L306-L310: RenameOITFrontAccumalationtoOITFrontAccumulationandOITAccumalationtoOITAccumulation.package/Shaders/Effect.hlsl#L413-L417: RenameOITFrontAccumalationtoOITFrontAccumulationandOITAccumalationtoOITAccumulation.package/Shaders/Particle.hlsl#L192-L196: RenameOITFrontAccumalationtoOITFrontAccumulationandOITAccumalationtoOITAccumulation.🤖 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 `@package/Shaders/Lighting.hlsl` around lines 306 - 310, Rename OITFrontAccumalation to OITFrontAccumulation and OITAccumalation to OITAccumulation in the declarations at package/Shaders/Lighting.hlsl:306-310, package/Shaders/Effect.hlsl:413-417, and package/Shaders/Particle.hlsl:192-196, updating all corresponding references in those shaders.package/Shaders/Particle.hlsl (1)
5-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueTypo in macro definition.
The macro
OIT_CAPTURE_IGNORE_ALPHA_THRESHOULDcontains a typo ("THRESHOULD" instead of "THRESHOLD"). Consider fixing this here and inOITCapture.hlslito maintain correctness and readability.🤖 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 `@package/Shaders/Particle.hlsl` around lines 5 - 9, Rename the misspelled macro OIT_CAPTURE_IGNORE_ALPHA_THRESHOULD to OIT_CAPTURE_IGNORE_ALPHA_THRESHOLD in Particle.hlsl and update the matching definition or references in OITCapture.hlsli so all conditional checks use the corrected symbol consistently.
🤖 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 `@features/Order` Independent Transparency/Shaders/OIT/AOIT.hlsli:
- Around line 160-165: Update the overflow merge in AOIT's [flatten] block to
guard the division by trans[OIT_NODE_COUNT - 2] when that preceding
transmittance is zero. Skip the color contribution or otherwise preserve the
opaque-layer result in that case, while retaining the existing transmittance
update and normal merge behavior for nonzero values.
In `@features/Order` Independent Transparency/Shaders/OIT/FragmentList.hlsli:
- Around line 57-67: Update FL_UnpackDepthAndFlags to detect the packed float’s
sign bit rather than comparing packedDepthAndFlags numerically, so negative zero
from depth 1.0 restores OIT_FLAGS_DEPTH_WRITE while preserving the existing
depth unpacking.
In `@features/Order` Independent Transparency/Shaders/OIT/OITResolve.cs.hlsl:
- Around line 24-30: Update the alpha-only resolve logic to write the composed
ecolor result to RT_ALPHA instead of wcolor. After combining the existing target
color and opacity, convert ecolor to the target’s opacity representation as
needed, then store ecolor so successive dispatches preserve blending with the
existing RT_ALPHA contents.
In `@features/Order` Independent Transparency/Shaders/OIT/WBOITWeight.hlsli:
- Around line 4-8: Update WBOITComputeWeight so the minimum depth factor does
not make ordinary alpha values clamp to the maximum weight; preserve meaningful
depth-based variation for fragments across the normal alpha range. Adjust the
weighting constants or final clamp bounds in this function, ensuring only
genuinely high-weight fragments reach 1.0 while retaining the existing
lower-bound protection.
In `@package/Shaders/Effect.hlsl`:
- Around line 391-396: Update the OIT output declarations and main shader output
logic in Effect.hlsl so DEFERRED with OIT == 3 initializes OITRevealage and
OITFrontAccum despite the existing non-deferred guard. In the final `#else`
branch, avoid assigning psout.Color2 when DEFERRED is defined, and ensure every
PS_OUTPUT member is initialized without referencing undefined members.
- Around line 965-977: Guard the psout.Color2 assignment in the OIT Capture
branch with a DEFERRED check in addition to MOTIONVECTORS_NORMALS, so it only
runs for non-deferred scenarios. Leave the OIT_Capture and psout.Diffuse
handling unchanged.
In `@package/Shaders/ISReflectionsRayTracing.hlsl`:
- Around line 117-119: Update the alpha texture sampling in the reflection
composition to retain the full float4 result from AlphaTex, including its
original w channel, instead of reconstructing it with a hardcoded alpha of 1.0.
Keep the existing reflectionColor blend using alpha.w unchanged.
In `@package/SKSE/Plugins/CommunityShaders/Translations/es.json`:
- Line 129: Localize the final distance-unit sentence in the distance threshold
tooltip at package/SKSE/Plugins/CommunityShaders/Translations/es.json:129-129,
package/SKSE/Plugins/CommunityShaders/Translations/fr.json:145-145,
package/SKSE/Plugins/CommunityShaders/Translations/nl.json:27-27, and
package/SKSE/Plugins/CommunityShaders/Translations/pl.json:111-111, translating
“1 unit = 1.428 cm or 0.5625 in.” into each resource’s language while preserving
the numeric values and tooltip formatting.
In `@src/Features/OrderIndependentTransparency.cpp`:
- Around line 755-838: Update CreateStructBuffer and SetupPixelBuffers to
perform all GPU buffer size, stride, and element-count multiplications in
uint64_t, checking each result against the supported UINT/D3D11 limits before
converting to UINT. Reject overflow or unsupported dimensions before
constructing buffers, including color/depth strides, byte widths, and
fragment-list BufferSize/count calculations, and preserve the existing
resource-failure handling through DisableForResourceFailure().
- Around line 291-297: Update the Disabled radio button in the Method section of
OrderIndependentTransparency’s settings UI to propagate its RadioButton return
value into dirtied.ShaderDefines, matching the Visualize and other method
options. Preserve the existing tooltip and selection behavior while ensuring
selecting Method::OIT_DISABLED invalidates the OIT shader state.
- Around line 674-712: The CompileShaders method must report whether the
selected OIT resolve shader compiled successfully and disable the active OIT
method when it does not. Track compilation failure for the shader corresponding
to the configured method, have CompileShaders return that status, and update the
caller or OIT state so capture and EndAlphaGroup do not proceed with a null
resolve shader.
- Around line 635-650: Update the post-water depth-copy allocation block in
SetupResources() to route failures from CreateTexture2D,
CreateShaderResourceView, and CreateDepthStencilView through the existing
resource-failure handling, invoking DisableForResourceFailure() instead of
allowing ThrowIfFailed exceptions to escape. Preserve the current resource
creation order and descriptors.
- Around line 897-903: Move the distance-gating and drawWriteDepth evaluation
from SetupGeometry() into the descriptor computation performed before
PreDrawHack(), ensuring the resulting OITDisabledDescriptor and depth-write
state are available when PreDrawHack() consumes them. Update the related logic
at the descriptor paths around the shown sections, and preserve the existing
reset behavior after each draw.
In `@src/Globals.h`:
- Line 39: Update the pull request metadata rather than the declaration in
Globals.h: use a conventional commit title such as feat(oit): add order
independent transparency, and include applicable issue references such as Fixes
`#123` in the PR description.
In `@src/State.h`:
- Around line 194-210: The wide-string overload State::BeginPerfEvent(const
wchar_t*) must also create the Tracy zone expected by EndPerfEvent(). Route it
through the existing State::BeginPerfEvent(std::string_view) implementation, or
otherwise add equivalent Tracy setup while preserving the D3D annotation
behavior used by wide titles.
---
Nitpick comments:
In `@package/Shaders/Lighting.hlsl`:
- Around line 306-310: Rename OITFrontAccumalation to OITFrontAccumulation and
OITAccumalation to OITAccumulation in the declarations at
package/Shaders/Lighting.hlsl:306-310, package/Shaders/Effect.hlsl:413-417, and
package/Shaders/Particle.hlsl:192-196, updating all corresponding references in
those shaders.
In `@package/Shaders/Particle.hlsl`:
- Around line 5-9: Rename the misspelled macro
OIT_CAPTURE_IGNORE_ALPHA_THRESHOULD to OIT_CAPTURE_IGNORE_ALPHA_THRESHOLD in
Particle.hlsl and update the matching definition or references in
OITCapture.hlsli so all conditional checks use the corrected symbol
consistently.
In `@src/Features/OrderIndependentTransparency.cpp`:
- Around line 956-960: Remove the TODO placeholder in the geometry handling
section of OrderIndependentTransparency.cpp. Either implement the deferred
Z-Buffer Write geometry consolidation into a single draw call, or replace the
TODO block with a concise explanatory comment referencing a tracked issue for
the deferred work; do not leave TODO/FIXME markers in the feature
implementation.
🪄 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: 7c4184ec-85d7-4e09-986b-44bfda9d5527
📒 Files selected for processing (50)
features/Order Independent Transparency/Shaders/Features/OrderIndependentTransparency.inifeatures/Order Independent Transparency/Shaders/OIT/AOIT.hlslifeatures/Order Independent Transparency/Shaders/OIT/AOITResolve.hlslifeatures/Order Independent Transparency/Shaders/OIT/DXAOIT.hlslifeatures/Order Independent Transparency/Shaders/OIT/DXAOITResolve.hlslifeatures/Order Independent Transparency/Shaders/OIT/FragmentList.hlslifeatures/Order Independent Transparency/Shaders/OIT/OITCapture.hlslifeatures/Order Independent Transparency/Shaders/OIT/OITCommon.hlslifeatures/Order Independent Transparency/Shaders/OIT/OITResolve.cs.hlslfeatures/Order Independent Transparency/Shaders/OIT/OITResolve.ps.hlslfeatures/Order Independent Transparency/Shaders/OIT/Quad.hlslfeatures/Order Independent Transparency/Shaders/OIT/WBOIT.hlslifeatures/Order Independent Transparency/Shaders/OIT/WBOITResolve.hlslifeatures/Order Independent Transparency/Shaders/OIT/WBOITWeight.hlslipackage/SKSE/Plugins/CommunityShaders/Translations/de.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/en.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/es.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/fr.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/ja.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/nl.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/pl.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/ru.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/zh_CN.jsonpackage/Shaders/Common/MotionBlur.hlslipackage/Shaders/Common/SharedData.hlslipackage/Shaders/Effect.hlslpackage/Shaders/ISReflectionsRayTracing.hlslpackage/Shaders/Lighting.hlslpackage/Shaders/Particle.hlslpackage/Shaders/Water.hlslsrc/Deferred.cppsrc/Feature.cppsrc/FeatureBuffer.cppsrc/Features/ExtendedTranslucency.cppsrc/Features/ExtendedTranslucency.hsrc/Features/HDRDisplay.cppsrc/Features/InverseSquareLighting.cppsrc/Features/LightLimitFix.hsrc/Features/OrderIndependentTransparency.cppsrc/Features/OrderIndependentTransparency.hsrc/FrameAnnotations.cppsrc/Globals.cppsrc/Globals.hsrc/Hooks.cppsrc/Menu/ThemeManager.cppsrc/Menu/ThemeManager.hsrc/ShaderCache.cppsrc/ShaderCache.hsrc/State.cppsrc/State.h
| float FL_PackDepthAndFlags(in float depth, in uint flags) | ||
| { | ||
| float packedDepth = saturate(1.0 - depth); | ||
| return (flags & OIT_FLAGS_DEPTH_WRITE) ? -packedDepth : packedDepth; | ||
| } | ||
|
|
||
| void FL_UnpackDepthAndFlags(in float packedDepthAndFlags, out float depth, out uint flags) | ||
| { | ||
| depth = 1.0 - abs(packedDepthAndFlags); | ||
| flags = packedDepthAndFlags < 0 ? OIT_FLAGS_DEPTH_WRITE : 0; | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Read the sign bit so depth 1.0 preserves the write flag.
A depth-writing fragment at exactly 1.0 packs as -0.0; packedDepthAndFlags < 0 is false for negative zero, so the flag is lost.
Proposed fix
void FL_UnpackDepthAndFlags(in float packedDepthAndFlags, out float depth, out uint flags)
{
depth = 1.0 - abs(packedDepthAndFlags);
- flags = packedDepthAndFlags < 0 ? OIT_FLAGS_DEPTH_WRITE : 0;
+ flags = (asuint(packedDepthAndFlags) & 0x80000000UL) != 0
+ ? OIT_FLAGS_DEPTH_WRITE
+ : 0;
}📝 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.
| float FL_PackDepthAndFlags(in float depth, in uint flags) | |
| { | |
| float packedDepth = saturate(1.0 - depth); | |
| return (flags & OIT_FLAGS_DEPTH_WRITE) ? -packedDepth : packedDepth; | |
| } | |
| void FL_UnpackDepthAndFlags(in float packedDepthAndFlags, out float depth, out uint flags) | |
| { | |
| depth = 1.0 - abs(packedDepthAndFlags); | |
| flags = packedDepthAndFlags < 0 ? OIT_FLAGS_DEPTH_WRITE : 0; | |
| } | |
| float FL_PackDepthAndFlags(in float depth, in uint flags) | |
| { | |
| float packedDepth = saturate(1.0 - depth); | |
| return (flags & OIT_FLAGS_DEPTH_WRITE) ? -packedDepth : packedDepth; | |
| } | |
| void FL_UnpackDepthAndFlags(in float packedDepthAndFlags, out float depth, out uint flags) | |
| { | |
| depth = 1.0 - abs(packedDepthAndFlags); | |
| flags = (asuint(packedDepthAndFlags) & 0x80000000UL) != 0 | |
| ? OIT_FLAGS_DEPTH_WRITE | |
| : 0; | |
| } |
🤖 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 `@features/Order` Independent Transparency/Shaders/OIT/FragmentList.hlsli
around lines 57 - 67, Update FL_UnpackDepthAndFlags to detect the packed float’s
sign bit rather than comparing packedDepthAndFlags numerically, so negative zero
from depth 1.0 restores OIT_FLAGS_DEPTH_WRITE while preserving the existing
depth unpacking.
| // blend alpha only colors with existing color | ||
| float4 ecolor = RT_ALPHA[DTid.xy]; | ||
| ecolor.xyz = ecolor.xyz * ecolor.w + wcolor.xyz; | ||
| ecolor.w = ecolor.w * wcolor.w; | ||
| wcolor.w = 1.0 - wcolor.w; | ||
| if (wcolor.w > 0.f) wcolor.xyz /= wcolor.w; | ||
| RT_ALPHA[DTid.xy] = wcolor; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Store the combined alpha-target color instead of discarding it.
ecolor receives the existing-target composition, but the shader normalizes and writes wcolor. Consequently, every dispatch replaces RT_ALPHA rather than blending with it. Convert the combined value to the target’s opacity representation and store ecolor.
Proposed fix
float4 ecolor = RT_ALPHA[DTid.xy];
ecolor.xyz = ecolor.xyz * ecolor.w + wcolor.xyz;
ecolor.w = ecolor.w * wcolor.w;
- wcolor.w = 1.0 - wcolor.w;
- if (wcolor.w > 0.f) wcolor.xyz /= wcolor.w;
- RT_ALPHA[DTid.xy] = wcolor;
+ ecolor.w = 1.0 - ecolor.w;
+ if (ecolor.w > 0.f) ecolor.xyz /= ecolor.w;
+ RT_ALPHA[DTid.xy] = ecolor;📝 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.
| // blend alpha only colors with existing color | |
| float4 ecolor = RT_ALPHA[DTid.xy]; | |
| ecolor.xyz = ecolor.xyz * ecolor.w + wcolor.xyz; | |
| ecolor.w = ecolor.w * wcolor.w; | |
| wcolor.w = 1.0 - wcolor.w; | |
| if (wcolor.w > 0.f) wcolor.xyz /= wcolor.w; | |
| RT_ALPHA[DTid.xy] = wcolor; | |
| // blend alpha only colors with existing color | |
| float4 ecolor = RT_ALPHA[DTid.xy]; | |
| ecolor.xyz = ecolor.xyz * ecolor.w + wcolor.xyz; | |
| ecolor.w = ecolor.w * wcolor.w; | |
| ecolor.w = 1.0 - ecolor.w; | |
| if (ecolor.w > 0.f) ecolor.xyz /= ecolor.w; | |
| RT_ALPHA[DTid.xy] = ecolor; |
🤖 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 `@features/Order` Independent Transparency/Shaders/OIT/OITResolve.cs.hlsl
around lines 24 - 30, Update the alpha-only resolve logic to write the composed
ecolor result to RT_ALPHA instead of wcolor. After combining the existing target
color and opacity, convert ecolor to the target’s opacity representation as
needed, then store ecolor so successive dispatches preserve blending with the
existing RT_ALPHA contents.
| # endif | ||
| # if defined(OIT) && OIT == 3 && !defined(MOTIONVECTORS_NORMALS) | ||
| float4 OITRevealage: SV_Target1; | ||
| # endif | ||
| # if defined(OIT) && OIT == 3 && !defined(MOTIONVECTORS_NORMALS) && !defined(NORMALS) | ||
| float4 OITFrontAccum: SV_Target2; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | 🏗️ Heavy lift
Compilation failure when DEFERRED and OIT == 3 are defined.
When DEFERRED and OIT == 3 are both defined, OITRevealage and OITFrontAccum are added to the PS_OUTPUT struct but are never initialized in the main function (because the main function logic requires !defined(DEFERRED) to write to OIT outputs). In HLSL, failing to initialize all SV_Target struct members results in a compilation error.
Additionally, in the #else block at the end of the shader, psout.Color2 is assigned, but Color2 is not a member of PS_OUTPUT when DEFERRED is defined, which will cause another compilation error.
Please review the OIT capture logic for deferred effect shaders to ensure all struct members are properly initialized and no undefined members are accessed.
🤖 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 `@package/Shaders/Effect.hlsl` around lines 391 - 396, Update the OIT output
declarations and main shader output logic in Effect.hlsl so DEFERRED with OIT ==
3 initializes OITRevealage and OITFrontAccum despite the existing non-deferred
guard. In the final `#else` branch, avoid assigning psout.Color2 when DEFERRED is
defined, and ensure every PS_OUTPUT member is initialized without referencing
undefined members.
| void OrderIndependentTransparency::CompileShaders() | ||
| { | ||
| // OIT_NODE_COUNT = MaxLayers | ||
| uint nodes = GetNodeCount(); | ||
| char nodesStr[4] = { 0 }; | ||
| std::to_chars(nodesStr, nodesStr + 4, nodes); | ||
| const char* writeDepthDefine = settings.WriteDepth ? "1" : "0"; | ||
|
|
||
| logger::info("[OIT] Compiling Order Independent Transparency shaders, OIT_NODE_COUNT={}, OIT_WRITE_DEPTH={}...", nodesStr, writeDepthDefine); | ||
|
|
||
| if (/*settings.Method == OIT_VISUALIZE && */!psVisualize) { | ||
| if (auto rawPtr = reinterpret_cast<ID3D11PixelShader*>(Util::CompileShader(L"Data\\Shaders\\OIT\\OITResolve.ps.hlsl", { { "OIT_DEBUG", "1" } }, "ps_5_0"))) { | ||
| psVisualize.attach(rawPtr); | ||
| } else { | ||
| logger::error("Failed to compile Order Independent Transparency debug pixel shader."); | ||
| } | ||
| } | ||
| if (/*settings.Method == OIT_AT && */!psAT) { | ||
| if (auto rawPtr = reinterpret_cast<ID3D11PixelShader*>(Util::CompileShader(L"Data\\Shaders\\OIT\\OITResolve.ps.hlsl", { { "OIT_AT", "1" }, { "OIT_NODE_COUNT", nodesStr }, { "OIT_WRITE_DEPTH", writeDepthDefine } }, "ps_5_0"))) { | ||
| psAT.attach(rawPtr); | ||
| } else { | ||
| logger::error("Failed to compile Order Independent Transparency resolve pixel shader."); | ||
| } | ||
| } | ||
| if (/*settings.Method == OIT_BLENDED && */!psBlend) { | ||
| if (auto rawPtr = reinterpret_cast<ID3D11PixelShader*>(Util::CompileShader(L"Data\\Shaders\\OIT\\OITResolve.ps.hlsl", { { "OIT_BLENDED", "1" }, { "OIT_WRITE_DEPTH", writeDepthDefine } }, "ps_5_0"))) { | ||
| psBlend.attach(rawPtr); | ||
| } else { | ||
| logger::error("Failed to compile Order Independent Transparency blend pixel shader."); | ||
| } | ||
| } | ||
| if (/*settings.Method == OIT_RVO && */!psROV) { | ||
| if (auto rawPtr = reinterpret_cast<ID3D11PixelShader*>(Util::CompileShader(L"Data\\Shaders\\OIT\\OITResolve.ps.hlsl", { { "OIT_ROV", "1" }, { "OIT_NODE_COUNT", nodesStr }, { "OIT_WRITE_DEPTH", writeDepthDefine } }, "ps_5_0"))) { | ||
| psROV.attach(rawPtr); | ||
| } else { | ||
| logger::error("Failed to compile Order Independent Transparency ROV resolve pixel shader."); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Disable the active method when its resolve shader fails to compile.
Compilation only logs failures. Capture still runs, then EndAlphaGroup() draws with a null shader, causing transparent geometry to disappear rather than shutting OIT down safely. Make compilation report success and disable OIT when the selected shader is unavailable.
🤖 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/OrderIndependentTransparency.cpp` around lines 674 - 712, The
CompileShaders method must report whether the selected OIT resolve shader
compiled successfully and disable the active OIT method when it does not. Track
compilation failure for the shader corresponding to the configured method, have
CompileShaders return that status, and update the caller or OIT state so capture
and EndAlphaGroup do not proceed with a null resolve shader.
| static bool CreateStructBuffer(std::optional<Buffer>& buffer, std::string_view name, uint elements, uint size, bool counter = false) | ||
| { | ||
| CD3D11_BUFFER_DESC bufferDesc( | ||
| elements * size, | ||
| D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS, | ||
| D3D11_USAGE_DEFAULT, | ||
| 0, | ||
| D3D11_RESOURCE_MISC_BUFFER_STRUCTURED, | ||
| size); | ||
| try { | ||
| buffer.emplace(bufferDesc); | ||
| } catch (const DX::com_exception& e) { | ||
| logger::error("Failed to create {} buffer: {}", name, e.what()); | ||
| return false; | ||
| } | ||
| buffer->resource->SetPrivateData(WKPDID_D3DDebugObjectName, (UINT)name.size(), name.data()); | ||
|
|
||
| CD3D11_SHADER_RESOURCE_VIEW_DESC srvDesc( | ||
| D3D11_SRV_DIMENSION_BUFFER, | ||
| DXGI_FORMAT_UNKNOWN, | ||
| 0, elements); | ||
|
|
||
| try { | ||
| buffer->CreateSRV(srvDesc); | ||
| } catch (const DX::com_exception& e) { | ||
| logger::error("Failed to create {} SRV: {}", name, e.what()); | ||
| buffer.reset(); | ||
| return false; | ||
| } | ||
| if (!buffer->srv) { | ||
| logger::error("Failed to create {} SRV", name); | ||
| buffer.reset(); | ||
| return false; | ||
| } | ||
|
|
||
| D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc{}; | ||
| uavDesc.Format = DXGI_FORMAT_UNKNOWN; | ||
| uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; | ||
| uavDesc.Buffer.FirstElement = 0; | ||
| uavDesc.Buffer.NumElements = elements; | ||
| uavDesc.Buffer.Flags = counter ? D3D11_BUFFER_UAV_FLAG_COUNTER : 0; | ||
| try { | ||
| buffer->CreateUAV(uavDesc); | ||
| } catch (const DX::com_exception& e) { | ||
| logger::error("Failed to create {} UAV: {}", name, e.what()); | ||
| buffer.reset(); | ||
| return false; | ||
| } | ||
| if (!buffer->uav) | ||
| { | ||
| logger::error("Failed to create {} UAV", name); | ||
| buffer.reset(); | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| bool OrderIndependentTransparency::SetupPixelBuffers(uint numElem) | ||
| { | ||
| logger::info("[OIT] Requested Setup Pixel Buffers {}", numElem); | ||
| if (settings.Method == Method::OIT_RVO) | ||
| { | ||
| uint NodeCount = GetNodeCount(); | ||
| uint colorBufferStride = sizeof(uint32_t) * 2 * NodeCount; | ||
| uint depthBufferStride = sizeof(float) * 4 * NodeCount; | ||
| uint colorBufferBytes = colorBufferStride * numElem; | ||
| if (!colorBuffer.has_value() || colorBuffer->desc.ByteWidth != colorBufferBytes || colorBuffer->desc.StructureByteStride != colorBufferStride) | ||
| { | ||
| logger::info("[OIT] Creating AOIT Buffers {} * {}", numElem, NodeCount); | ||
| if (!CreateStructBuffer(colorBuffer, "OIT Color", numElem, colorBufferStride) || | ||
| !CreateStructBuffer(depthBuffer, "OIT Depth", numElem, depthBufferStride)) { | ||
| DisableForResourceFailure(); | ||
| return false; | ||
| } | ||
| } | ||
| } | ||
| else if (settings.Method == Method::OIT_AT || settings.Method == Method::OIT_VISUALIZE) | ||
| { | ||
| uint BufferSize = settings.BufferSize * numElem; | ||
| if (!nodesBuffer.has_value() || nodesBuffer->desc.ByteWidth != BufferSize * sizeof(FragmentListNode)) | ||
| { | ||
| logger::info("[OIT] Creating Fragment List Buffers {} * {}", numElem, settings.BufferSize); | ||
| featureCB.MaxListNodes = BufferSize; | ||
| if (!CreateStructBuffer(nodesBuffer, "OIT Nodes", BufferSize, sizeof(FragmentListNode), true)) { |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Use checked arithmetic for GPU buffer dimensions.
elements * size, layer strides, and fragment-list counts are calculated in 32-bit uint. For example, a 4096×2160 depth buffer with 32 layers exceeds UINT_MAX, wraps ByteWidth, and can create an undersized resource or trigger device failure. Calculate in uint64_t, reject unsupported sizes, then cast to UINT.
🤖 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/OrderIndependentTransparency.cpp` around lines 755 - 838, Update
CreateStructBuffer and SetupPixelBuffers to perform all GPU buffer size, stride,
and element-count multiplications in uint64_t, checking each result against the
supported UINT/D3D11 limits before converting to UINT. Reject overflow or
unsupported dimensions before constructing buffers, including color/depth
strides, byte widths, and fragment-list BufferSize/count calculations, and
preserve the existing resource-failure handling through
DisableForResourceFailure().
|
✅ A pre-release build is available for this PR: |
The alpha value in alpha texture was incorrectly discarded (becasue its RGB was pre multiplied).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
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/Features/OrderIndependentTransparency.cpp`:
- Around line 1105-1108: Guard the OIT resolve draw near GetUpscaleVS() and
upscaleRasterizerState against uninitialized or disabled upscaling. Before
issuing the draw, validate that the required upscaling resources are ready;
otherwise skip the resolve or use the established non-upscaling fallback,
ensuring no null rasterizer state is bound.
🪄 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: 29b72a9d-45e1-4eda-92b3-e6eb441c94d8
📒 Files selected for processing (11)
package/SKSE/Plugins/CommunityShaders/Translations/de.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/en.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/es.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/fr.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/ja.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/nl.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/pl.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/ru.jsonpackage/SKSE/Plugins/CommunityShaders/Translations/zh_CN.jsonsrc/Features/OrderIndependentTransparency.cppsrc/Features/OrderIndependentTransparency.h
💤 Files with no reviewable changes (10)
- package/SKSE/Plugins/CommunityShaders/Translations/ru.json
- package/SKSE/Plugins/CommunityShaders/Translations/en.json
- package/SKSE/Plugins/CommunityShaders/Translations/ja.json
- package/SKSE/Plugins/CommunityShaders/Translations/nl.json
- package/SKSE/Plugins/CommunityShaders/Translations/fr.json
- package/SKSE/Plugins/CommunityShaders/Translations/pl.json
- package/SKSE/Plugins/CommunityShaders/Translations/zh_CN.json
- package/SKSE/Plugins/CommunityShaders/Translations/es.json
- src/Features/OrderIndependentTransparency.h
- package/SKSE/Plugins/CommunityShaders/Translations/de.json
| context->VSSetShader(globals::features::upscaling.GetUpscaleVS(), nullptr, 0); | ||
|
|
||
| // Set up rasterizer and blend states | ||
| context->RSSetState(globals::features::upscaling.upscaleRasterizerState.get()); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect upscaling VS/rasterizer lifecycle and any load-order guarantees vs OIT.
fd -t f -e cpp -e h -i upscal | sort
echo "---- GetUpscaleVS / upscaleRasterizerState definitions ----"
rg -nP -C3 '\b(GetUpscaleVS|upscaleRasterizerState)\b' --type=cpp --type=cpp
echo "---- upscaling feature loaded/guard usage ----"
rg -nP -C3 'features::upscaling' --type=cppRepository: community-shaders/skyrim-community-shaders
Length of output: 292
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== file sizes =="
wc -l src/Features/Upscaling.h src/Features/Upscaling.cpp src/Features/OrderIndependentTransparency.cpp
echo "== outlines =="
ast-grep outline src/Features/Upscaling.h --view expanded || true
echo "----"
ast-grep outline src/Features/Upscaling.cpp --view expanded || true
echo "----"
ast-grep outline src/Features/OrderIndependentTransparency.cpp --view expanded || true
echo "== targeted reads =="
sed -n '1,260p' src/Features/Upscaling.h
echo "---- CPP 1 ----"
sed -n '1,340p' src/Features/Upscaling.cpp
echo "---- OIT slice ----"
sed -n '1060,1135p' src/Features/OrderIndependentTransparency.cppRepository: community-shaders/skyrim-community-shaders
Length of output: 31837
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== GetUpscaleVS implementation and resource setup =="
rg -n -C4 'GetUpscaleVS\(|upscaleVS|upscaleRasterizerState|upscaleBlendState|upscaleDepthStencilState' src/Features/Upscaling.cpp src/Features/Upscaling.h
echo "== OIT setup/guards around resolve pass =="
rg -n -C4 'OrderIndependentTransparency::SetupResources|DisableForResourceFailure|SetupGeometry|RestoreGeometry|BeginAlphaGroup|EndAlphaGroup|Resolve|GetUpscaleVS|upscaleRasterizerState' src/Features/OrderIndependentTransparency.cpp
echo "== feature enable/load order references =="
rg -n -C3 'globals::features::upscaling|upscaling\.loaded|upscaling\.SetupResources|OrderIndependentTransparency' src/Features/*.cpp src/Features/*.hRepository: community-shaders/skyrim-community-shaders
Length of output: 46989
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Feature base load semantics =="
rg -n -C4 'bool loaded|loaded =|void Load\(|PostPostLoad\(|SetupResources\(' src/Features/Feature.h src/Features/*.cpp
echo "== upscaling load/init paths =="
rg -n -C4 'Upscaling::Load\(|Upscaling::PostPostLoad\(|Upscaling::SetupResources\(|CheckResources\(' src/Features/Upscaling.cpp
echo "== OIT activation and load dependencies =="
rg -n -C4 'orderIndependentTransparency\.loaded|OrderIndependentTransparency::Load\(|OrderIndependentTransparency::PostPostLoad\(|OrderIndependentTransparency::SetupResources\(' src/Features/OrderIndependentTransparency.cpp src/Features/OrderIndependentTransparency.h
echo "== feature manager/registry ordering =="
rg -n -C4 'features::upscaling|features::orderIndependentTransparency|LoadFeatures|SetupResources' srcRepository: community-shaders/skyrim-community-shaders
Length of output: 25994
Guard the OIT resolve draw against missing upscaling state. GetUpscaleVS() compiles on demand, but upscaleRasterizerState is only created in Upscaling::SetupResources(). If OIT can run before that feature initializes or when upscaling is disabled, this pass will bind a null rasterizer state and misrender. Add a fallback or skip the draw when the dependency isn’t ready.
🤖 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/OrderIndependentTransparency.cpp` around lines 1105 - 1108,
Guard the OIT resolve draw near GetUpscaleVS() and upscaleRasterizerState
against uninitialized or disabled upscaling. Before issuing the draw, validate
that the required upscaling resources are ready; otherwise skip the resolve or
use the established non-upscaling fallback, ensuring no null rasterizer state is
bound.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| static void WINAPI thunk(ID3D11DeviceContext* This, ID3D11BlendState* pBlendState, const FLOAT BlendFactor[4], UINT SampleMask) | ||
| { | ||
| if (pBlendState) { | ||
| if (pBlendState && !globals::features::orderIndependentTransparency.inAlphaPass) { |
There was a problem hiding this comment.
does this need to be specific to OIT?
| int blockedKeyIndex = -1; // index in shaderMap; negative value indicates disabled | ||
| enum class ParticleShaderFlags : uint32_t | ||
| { | ||
| OIT = 1UL << 15 // TODO: Find a proper bit for OIT flag, current set by trail & error |
There was a problem hiding this comment.
Looks fine so far, I haven't notice any mis rendered particles. The concern is I haven't RE for particle shader flags.
| float4 color; | ||
| float4 wcolor; | ||
|
|
||
| //uint firstNodeOffset = FL_GetFirstNodeOffset(screenAddress); |
|
|
||
| std::span<const D3D_SHADER_MACRO> OrderIndependentTransparency::GetShaderDefines() const | ||
| { | ||
| // OIT = OIT_METHOD_DEFINES[settings.Method] |
|
|
||
| logger::info("[OIT] Hooking Main_RenderWorld_RenderTransparency"); | ||
|
|
||
| // std::uintptr_t address = REL::RelocationID(100424, 107142).address() + REL::Relocate(0x3E4, 0x3FE); |
There was a problem hiding this comment.
commented out code? needed for dev work?
| auto& depthStencilDepthMode = shadowState->GetRuntimeData().depthStencilDepthMode; | ||
|
|
||
| // Setup depth write descriptor, depth write will be skipped in capture pass | ||
| //using enum RE::BSGraphics::DepthStencilDepthMode; |
|
|
||
| if (pass->shaderProperty && pass->shaderProperty->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kZBufferWrite)) | ||
| { | ||
| // TODO: |
There was a problem hiding this comment.
does this need to be done?
There was a problem hiding this comment.
No, I found the 'segmented' draw calls are not directly related to alpha pass, but coming from skin clusters of SMP bones and outfit studio >_< A separate problem
There was a problem hiding this comment.
No, I found the 'segmented' draw calls are not directly related to alpha pass, but coming from skin clusters of SMP bones and outfit studio >_< A separate problem
| auto* renderer = globals::game::renderer; | ||
| ID3D11DeviceContext* context = globals::d3d::context; | ||
| ID3D11UnorderedAccessView* headerUAV = headerBuffer->uav.get(); | ||
| // context->OMGetRenderTargets(0, NULL, sceneDSV.put()); |
| auto& alpha = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN_ONLY_ALPHA]; | ||
| ID3D11ShaderResourceView* srv[] = { alpha.SRV }; | ||
| context->PSSetShaderResources(66, ARRAYSIZE(srv), srv); | ||
| // context->PSSetSamplers(66, 1, &globals::deferred->linearSampler); |
|
This looks extremely impressive, just needs minor cleanup and performance/bug testing. |
|
Thanks for the review 😀 Will do the cleanup and push a new test build around the weekend |
Summary
Adds Order Independent Transparency (OIT) for overlapping transparent surfaces such as water, hair, glass, foliage, smoke, and particles. Rather than relying on Skyrim's transparent-geometry ordering, the feature captures the alpha pass into OIT-specific resources and composites the result after the engine has rendered that pass.
Demonstration
Integration overview
OIT implementations
Engine integration
Main::RenderWorld::RenderTransparency, which owns Skyrim's alpha-blended geometry pass. The hook initializes OIT collection before the original pass, lets the engine render its normal transparent geometry, then resolves and composites the captured result immediately afterward.SetupGeometryandRestoreGeometrywrappers (vtable slots0x6and0x7) for Lighting, Effect, Water, Utility, Particle, Grass, Distant Tree, Blood Splatter, and Sky shaders. These wrappers apply and clear the per-draw OIT descriptors without depending on the optional Frame Annotations feature.Water refraction and reflection
The engine's water shaders sample prior-frame scene data for their refraction/reflection path. Without OIT-aware handling, semi-transparent geometry captured outside the normal scene color was absent from that sample, making it disappear through water and in water reflections.
kMAIN_ONLY_ALPHAas a shader-readable target and writes the composed transparent color and coverage there alongside the main/TAA targets.BeginWater()binds that target att66;Water.hlslreconstructs the refraction world position, derives the camera motion vector, and samples the alpha-only target at the previous-frame, dynamic-resolution-adjusted UV.lerp(refractionColor, alphaOnly.rgb, alphaOnly.a)before calculating its refraction result. The transparent scene content therefore participates in the water refraction/reflection data rather than being treated as invisible.HDR blend-state interaction
HDR Display globally detours
ID3D11DeviceContext::OMSetBlendStateto repair alpha blending for its separate UI capture buffer. That broad rewrite is an existing HDR-feature bug for non-UI rendering: it changes the alpha factors requested by an active render pass, including OIT's specialized accumulation states.This branch contains an interoperability guard: HDR skips its blend-state patch while
OrderIndependentTransparency::inAlphaPassis true, preserving OIT collection and composition blend semantics. The underlying HDR hook remains overly broad and should be constrained to its UI-composition scope in follow-up work.References
Validation
cmake --build --preset Devpython tools/extract-i18n.py --checkpython tools/extract-i18n.py --orphanspython tools/sort-i18n.py --checkNotes
Quality/AOIT is production-ready but carries a higher performance cost than the other methods. This PR remains a draft while the in-game validation matrix is completed.
Summary by CodeRabbit