Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/Features/InverseSquareLighting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion src/Features/LightLimitFix/ShadowCasterManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
9 changes: 6 additions & 3 deletions src/Features/LightLimitFix/ShadowCasterManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,19 @@ 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) {
RE::BSShadowLight* light = accum[idx];
if (!light)
break;
const auto raw = reinterpret_cast<std::uintptr_t>(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;
Expand Down
Loading