diff --git a/src/AcDream.App/Rendering/Shaders/mesh.frag b/src/AcDream.App/Rendering/Shaders/mesh.frag index 94cc31f..7765a46 100644 --- a/src/AcDream.App/Rendering/Shaders/mesh.frag +++ b/src/AcDream.App/Rendering/Shaders/mesh.frag @@ -52,9 +52,9 @@ layout(std140, binding = 1) uniform SceneLighting { // light" look relies on crisp boundaries. vec3 accumulateLights(vec3 N, vec3 worldPos) { vec3 lit = uCellAmbient.xyz; - int active = int(uCellAmbient.w); + int activeLights = int(uCellAmbient.w); for (int i = 0; i < 8; ++i) { - if (i >= active) break; + if (i >= activeLights) break; int kind = int(uLights[i].posAndKind.w); vec3 Lcol = uLights[i].colorAndIntensity.xyz * uLights[i].colorAndIntensity.w; diff --git a/src/AcDream.App/Rendering/Shaders/mesh_instanced.frag b/src/AcDream.App/Rendering/Shaders/mesh_instanced.frag index 6199aa4..1719e2f 100644 --- a/src/AcDream.App/Rendering/Shaders/mesh_instanced.frag +++ b/src/AcDream.App/Rendering/Shaders/mesh_instanced.frag @@ -35,9 +35,9 @@ layout(std140, binding = 1) uniform SceneLighting { vec3 accumulateLights(vec3 N, vec3 worldPos) { vec3 lit = uCellAmbient.xyz; - int active = int(uCellAmbient.w); + int activeLights = int(uCellAmbient.w); for (int i = 0; i < 8; ++i) { - if (i >= active) break; + if (i >= activeLights) break; int kind = int(uLights[i].posAndKind.w); vec3 Lcol = uLights[i].colorAndIntensity.xyz * uLights[i].colorAndIntensity.w; diff --git a/src/AcDream.App/Rendering/Sky/SkyRenderer.cs b/src/AcDream.App/Rendering/Sky/SkyRenderer.cs index ba7a58b..34104d4 100644 --- a/src/AcDream.App/Rendering/Sky/SkyRenderer.cs +++ b/src/AcDream.App/Rendering/Sky/SkyRenderer.cs @@ -217,14 +217,32 @@ public sealed unsafe class SkyRenderer : IDisposable { if (_gpuByGfxObj.ContainsKey(gfxObjId)) return; - var gfx = _dats.Get(gfxObjId); + // DatCollection isn't thread-safe and the streaming loader can be + // actively reading a shared DatBinReader buffer; sky meshes are + // loaded on the render thread but GfxObj.Unpack can race with the + // streamer. Cache a null entry on any read failure so we don't + // retry every frame and crash the render loop. A future + // refactor should move all dat access behind the _datLock. + GfxObj? gfx = null; + try { gfx = _dats.Get(gfxObjId); } + catch { gfx = null; } + if (gfx is null) { _gpuByGfxObj[gfxObjId] = new List(); return; } - var subMeshes = GfxObjMesh.Build(gfx, _dats); + System.Collections.Generic.IReadOnlyList? subMeshes = null; + try { subMeshes = GfxObjMesh.Build(gfx, _dats); } + catch { subMeshes = null; } + + if (subMeshes is null) + { + _gpuByGfxObj[gfxObjId] = new List(); + return; + } + var gpuList = new List(subMeshes.Count); foreach (var sm in subMeshes) gpuList.Add(UploadSubMesh(sm));