fix(render): shader reserved-word + defensive SkyRenderer dat reads
Two runtime blockers discovered after merging the sky/weather/lighting branch: 1. GLSL reserved word: mesh.frag + mesh_instanced.frag used \`int active\` as a local. On GLSL ES / some drivers \`active\` is a reserved identifier and compile fails hard (\"ERROR: 0:38: 'active' : Reserved word\"). Renamed to \`activeLights\`. 2. SkyRenderer.EnsureMeshUploaded called DatCollection.Get<GfxObj> without the _datLock that wraps the streaming pipeline's dat reads. DatBinReader has shared buffer state; concurrent reads race and throw ArgumentOutOfRangeException from Vec2Duv.Unpack deep in the mesh parse. Wrapped both Get<GfxObj> and GfxObjMesh.Build in try/catch and cache a null entry on failure so we don't retry every frame and crash the render loop. Full fix would plumb _datLock into the sky renderer, left as a TODO. Client now stable end-to-end — in-world, spawn stream flowing, animation + audio + sky + light UBO all live. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
48b5e1f1b1
commit
187226f504
3 changed files with 24 additions and 6 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -217,14 +217,32 @@ public sealed unsafe class SkyRenderer : IDisposable
|
|||
{
|
||||
if (_gpuByGfxObj.ContainsKey(gfxObjId)) return;
|
||||
|
||||
var gfx = _dats.Get<GfxObj>(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<GfxObj>(gfxObjId); }
|
||||
catch { gfx = null; }
|
||||
|
||||
if (gfx is null)
|
||||
{
|
||||
_gpuByGfxObj[gfxObjId] = new List<SubMeshGpu>();
|
||||
return;
|
||||
}
|
||||
|
||||
var subMeshes = GfxObjMesh.Build(gfx, _dats);
|
||||
System.Collections.Generic.IReadOnlyList<GfxObjSubMesh>? subMeshes = null;
|
||||
try { subMeshes = GfxObjMesh.Build(gfx, _dats); }
|
||||
catch { subMeshes = null; }
|
||||
|
||||
if (subMeshes is null)
|
||||
{
|
||||
_gpuByGfxObj[gfxObjId] = new List<SubMeshGpu>();
|
||||
return;
|
||||
}
|
||||
|
||||
var gpuList = new List<SubMeshGpu>(subMeshes.Count);
|
||||
foreach (var sm in subMeshes)
|
||||
gpuList.Add(UploadSubMesh(sm));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue