diff --git a/src/Features/InverseSquareLighting.cpp b/src/Features/InverseSquareLighting.cpp index 2c8212f29f..b7ea9cfa06 100644 --- a/src/Features/InverseSquareLighting.cpp +++ b/src/Features/InverseSquareLighting.cpp @@ -92,7 +92,11 @@ float InverseSquareLighting::CalculateRadius(const float intensity, const bool s float cutoff = shadowCaster ? DefaultShadowCasterCutoff : DefaultCutoff; cutoff = cutoffOverride == 1.f ? cutoff : cutoffOverride; const float radius = std::sqrt(ScaledUnitsSq * ((2 * intensity - cutoff * size * size) / (2 * cutoff))); - return isnan(radius) ? 1.f : radius; + // Clamp any degenerate result to 1.0 so callers never divide by a bad radius: + // a negative sqrt argument yields NaN, an exact-zero argument yields 0 (which + // would drive a 0/0 attenuation fade), and a zero cutoffOverride yields +inf + // (sqrt(+inf) is not NaN, so the old isnan-only check let it through). + return std::isfinite(radius) && radius > 0.f ? radius : 1.f; } inline float InverseSquareLighting::SmoothStep(const float edge0, const float edge1, const float x) diff --git a/src/Features/LightLimitFix/ShadowCasterManager.cpp b/src/Features/LightLimitFix/ShadowCasterManager.cpp index 24978fe466..4d4c311a0b 100644 --- a/src/Features/LightLimitFix/ShadowCasterManager.cpp +++ b/src/Features/LightLimitFix/ShadowCasterManager.cpp @@ -241,7 +241,9 @@ namespace ShadowCasterManager static float ComputeFrameTimePercentile90() { - if (s_ftCount == 0) + // <= 0 (not just == 0): a negative count would drive a negative n into + // std::copy / std::nth_element below (out-of-bounds / UB). + if (s_ftCount <= 0) return 16.67f; // fallback: 60 fps target const int n = std::min(s_ftCount, kFrameWindow); float tmp[kFrameWindow]; diff --git a/src/Features/LightLimitFix/ShadowCasterManager.h b/src/Features/LightLimitFix/ShadowCasterManager.h index 6230b1c870..734912c3ff 100644 --- a/src/Features/LightLimitFix/ShadowCasterManager.h +++ b/src/Features/LightLimitFix/ShadowCasterManager.h @@ -86,8 +86,8 @@ namespace ShadowCasterManager // -- using capacity as the bound silently caps SLF at vanilla shadow // counts. Use the null sentinel instead, with a setting-derived // safety cap (ShadowLightCount + sun cascades, with a small margin). - // Per-pointer plausibility (alignment + user-mode range) handles - // non-null garbage between our prepass and this read. + // Per-pointer plausibility (low-address floor + alignment + user-mode + // range) handles non-null garbage between our prepass and this read. const std::uint32_t maxIdx = MaxShadowAccumIterationBound(); std::uint32_t idx = 0; while (idx < maxIdx) { @@ -95,7 +95,10 @@ namespace ShadowCasterManager if (!light) break; const auto raw = reinterpret_cast(light); - if (raw >= 0x0000800000000000ull || (raw & 0x7) != 0) + // Reject the low 64 KiB too: the Windows x64 null-guard region is + // never a valid user mapping, so a near-null garbage value (e.g. + // 0x8) would otherwise pass and get dereferenced (AV/CTD). + if (raw < 0x10000ull || raw >= 0x0000800000000000ull || (raw & 0x7) != 0) break; fn(light); const std::uint32_t step = light->shadowMapCount;