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.
|
// light" look relies on crisp boundaries.
|
||||||
vec3 accumulateLights(vec3 N, vec3 worldPos) {
|
vec3 accumulateLights(vec3 N, vec3 worldPos) {
|
||||||
vec3 lit = uCellAmbient.xyz;
|
vec3 lit = uCellAmbient.xyz;
|
||||||
int active = int(uCellAmbient.w);
|
int activeLights = int(uCellAmbient.w);
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
if (i >= active) break;
|
if (i >= activeLights) break;
|
||||||
|
|
||||||
int kind = int(uLights[i].posAndKind.w);
|
int kind = int(uLights[i].posAndKind.w);
|
||||||
vec3 Lcol = uLights[i].colorAndIntensity.xyz * uLights[i].colorAndIntensity.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 accumulateLights(vec3 N, vec3 worldPos) {
|
||||||
vec3 lit = uCellAmbient.xyz;
|
vec3 lit = uCellAmbient.xyz;
|
||||||
int active = int(uCellAmbient.w);
|
int activeLights = int(uCellAmbient.w);
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
if (i >= active) break;
|
if (i >= activeLights) break;
|
||||||
|
|
||||||
int kind = int(uLights[i].posAndKind.w);
|
int kind = int(uLights[i].posAndKind.w);
|
||||||
vec3 Lcol = uLights[i].colorAndIntensity.xyz * uLights[i].colorAndIntensity.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;
|
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)
|
if (gfx is null)
|
||||||
{
|
{
|
||||||
_gpuByGfxObj[gfxObjId] = new List<SubMeshGpu>();
|
_gpuByGfxObj[gfxObjId] = new List<SubMeshGpu>();
|
||||||
return;
|
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);
|
var gpuList = new List<SubMeshGpu>(subMeshes.Count);
|
||||||
foreach (var sm in subMeshes)
|
foreach (var sm in subMeshes)
|
||||||
gpuList.Add(UploadSubMesh(sm));
|
gpuList.Add(UploadSubMesh(sm));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue