Finish bounded lighting-observer cleanup and record verification
This commit is contained in:
parent
2f192570a2
commit
0ae2666ef0
11 changed files with 70 additions and 582 deletions
|
|
@ -814,6 +814,13 @@ room fixtures now reach every drawn cell (7+40product, unchanged ambient.2).
|
|||
of the temporary observer is now active, followed by final observer-free
|
||||
re-gate. FPS deferred; owner G4 still FAIL/unpassed.
|
||||
|
||||
**#478 observer cleanup:** reviewed scratch `ed2c8b12c` integrates seven
|
||||
paths with unchanged retained blobs; temporary observer and only its tests
|
||||
removed. Full scratch Release0W0E, App180/Core69/native2 PASS, lead independent
|
||||
106/106. Permanent RHI47/ordinary8/native witnesses remain. Evidence:
|
||||
`478-observer-removal-verification.md`. Fresh integrated Release and unchanged
|
||||
Town/control re-gate next; FPS deferred, no owner acceptance or main merge.
|
||||
|
||||
## 14. Risk register (condensed)
|
||||
|
||||
- **R1 second scene graph.** Graph records own only topology/membership facts;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
# #478 temporary lighting observer removal
|
||||
|
||||
ACTIVE after candidatea5debaca2: unchanged Town High/off/High and0125/8C04
|
||||
IMPLEMENTED and lead-reviewed at scratch ed2c8b12c; integration's fresh Release
|
||||
and final graphical repeat are pending. See `478-observer-removal-verification.md`.
|
||||
Activated after candidatea5debaca2: unchanged Town High/off/High and0125/8C04
|
||||
control PNGs inspected, eight provisional lighting/reveal PASS frames,
|
||||
`478-repaired-lighting-graphical.md`. Both own clients exited0/gracefully.
|
||||
This is not owner acceptance. A failed final visual check returns to
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
# #478 observer removal — lead verification
|
||||
|
||||
The eight candidate PNGs at `a5debaca2` passed the narrow lighting/reveal
|
||||
smoke provisionally (`478-repaired-lighting-graphical.md`). Under
|
||||
`478-observer-removal-contract.md`, OpenAI James removed only the temporary
|
||||
observer and its corresponding tests. Lead committed scratch
|
||||
`ed2c8b12c1ecd31dbc0069419db8104ed0e2bd49`, base `1772c1a5a`, and verified
|
||||
the six retained integration blobs against that commit; the seventh path is
|
||||
the deleted `EnvCellRenderer.LightingObservation.cs`.
|
||||
|
||||
## Scope review
|
||||
|
||||
Exactly seven paths, 1 insertion / 581 deletions. No lighting selection,
|
||||
ambient, shader, generated module, package, geometry or FPS changes. Four
|
||||
composition/DTO/test files restore the exact pre-observer (`59b0b6e72^`) Git
|
||||
blobs; snapshot-source restoration differs only by one blank line. Remaining
|
||||
EnvCell test changes remove only observer tests and unused helpers/imports.
|
||||
The permanent two-cell 47-index RHI upload and next-generation overwrite test,
|
||||
its required helpers, ordinary 8-index upload and both compiled shader pixel
|
||||
witnesses remain. Observer symbol search over `src` and `tests` returns none.
|
||||
Unrelated lifecycle and current-render-scene checkpoint evidence remains.
|
||||
|
||||
Lead checked the complete diff and all six raw source hashes against the
|
||||
frozen implementer return. No new retail algorithm or deviation is introduced;
|
||||
the preceding independent retail and production/ABI reviews still cover the
|
||||
unchanged repair. No additional retail re-port or review round was needed.
|
||||
The removed temporary source remains recoverable from Git history.
|
||||
|
||||
## Verification before integration
|
||||
|
||||
Artifacts under scratch `s5-478-observer-cleanup/artifacts/478-observer-cleanup/`:
|
||||
|
||||
- `release-build-r2/console.log`: full solution Release, 0 warnings/errors.
|
||||
First build caught a removed Wb import also needed by `GpuMemoryTracker`;
|
||||
that import was restored before the final build and freeze.
|
||||
- `app-focused/app-focused.trx`: 180/180 affected/permanent tests.
|
||||
- `core-lighting/core-lighting.trx`: 69/69, including installed Town DAT.
|
||||
- `shader-pixels/shader-pixels.trx`: both committed-module witnesses pass.
|
||||
- Lead independent `root-removal/root-removal.trx`: 106/106 affected
|
||||
lifecycle/composition/EnvCell/ordinary-upload tests plus both native pixels.
|
||||
|
||||
Lead inspected actual build output and all TRX counters. Both client names
|
||||
were absent before the independent execution. These are focused correctness
|
||||
tests, not the full solution test lane or an FPS benchmark.
|
||||
|
||||
## Final graphical gate
|
||||
|
||||
Pending fresh Release on the integrated observer-free commit and the unchanged
|
||||
Town High/off/High plus 0125/8C04/Holtburg control routes. Candidate consumed-
|
||||
light measurements remain evidence only for `a5debaca2`, not for this binary.
|
||||
Owner acceptance remains owed; G4 FAIL/unpassed, no main merge. FPS deferred.
|
||||
|
|
@ -4982,3 +4982,11 @@ requested but its live module check missed the already-exited process; no
|
|||
stronger claim. `478-repaired-lighting-graphical.md` seals paths/hashes/limits.
|
||||
Observer removal contract activated; final observer-free re-gate still owed.
|
||||
No FPS work, no owner acceptance, G4FAIL/no main merge.
|
||||
|
||||
Observer cleanup reviewed and integrated from scratch `ed2c8b12c`: exactly
|
||||
seven paths, temporary capture/DTO/wiring and observer-only tests removed.
|
||||
Permanent RHI47/ordinary8/native-pixel coverage retained; six retained blobs
|
||||
match. Full scratch Release0W0E, App180/Core69/native2 PASS; lead independent
|
||||
affected/native106/106. `478-observer-removal-verification.md` records scope
|
||||
and actual reports. No new deviation, retail algorithm or FPS edit. Fresh
|
||||
integrated Release and unchanged final graphical repeats remain next.
|
||||
|
|
|
|||
|
|
@ -596,8 +596,6 @@ internal sealed class FrameRootCompositionPhase
|
|||
foundation.MeshAdapter,
|
||||
foundation.TextureCache,
|
||||
live.DrawDispatcher,
|
||||
live.EnvCellRenderer,
|
||||
d.Lighting,
|
||||
d.FrameProfiler,
|
||||
content.Dats,
|
||||
foundation.Residency,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ using System.Text.Json;
|
|||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Rendering.Residency;
|
||||
using AcDream.App.Rendering.Scene;
|
||||
using AcDream.App.Rendering.Wb;
|
||||
using AcDream.App.Streaming;
|
||||
using AcDream.App.UI.Testing;
|
||||
using AcDream.Core.Physics;
|
||||
|
|
@ -97,8 +96,7 @@ internal sealed record WorldLifecycleResourceSnapshot(
|
|||
ResidencySnapshot Residency,
|
||||
double Fps,
|
||||
double FrameMilliseconds,
|
||||
string? LastFrameProfile,
|
||||
EnvCellLightingConsumptionSnapshot? EnvCellLighting = null);
|
||||
string? LastFrameProfile);
|
||||
|
||||
internal sealed record WorldLifecycleCheckpoint(
|
||||
int Sequence,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ using AcDream.App.Streaming;
|
|||
using AcDream.App.World;
|
||||
using AcDream.Content;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Core.Lighting;
|
||||
using AcDream.Core.Vfx;
|
||||
using DatReaderWriter.Lib.IO;
|
||||
|
||||
|
|
@ -40,8 +39,6 @@ internal sealed class WorldLifecycleResourceSnapshotSource
|
|||
private readonly WbMeshAdapter? _meshes;
|
||||
private readonly TextureCache _textures;
|
||||
private readonly WbDrawDispatcher? _dispatcher;
|
||||
private readonly EnvCellRenderer? _envCellRenderer;
|
||||
private readonly LightManager _lighting;
|
||||
private readonly FrameProfiler _frameProfiler;
|
||||
private readonly IDatReaderWriter _dats;
|
||||
private readonly ResidencyManager _residency;
|
||||
|
|
@ -65,8 +62,6 @@ internal sealed class WorldLifecycleResourceSnapshotSource
|
|||
WbMeshAdapter? meshes,
|
||||
TextureCache textures,
|
||||
WbDrawDispatcher? dispatcher,
|
||||
EnvCellRenderer? envCellRenderer,
|
||||
LightManager lighting,
|
||||
FrameProfiler frameProfiler,
|
||||
IDatReaderWriter dats,
|
||||
ResidencyManager residency,
|
||||
|
|
@ -93,8 +88,6 @@ internal sealed class WorldLifecycleResourceSnapshotSource
|
|||
_meshes = meshes;
|
||||
_textures = textures ?? throw new ArgumentNullException(nameof(textures));
|
||||
_dispatcher = dispatcher;
|
||||
_envCellRenderer = envCellRenderer;
|
||||
_lighting = lighting ?? throw new ArgumentNullException(nameof(lighting));
|
||||
_frameProfiler = frameProfiler
|
||||
?? throw new ArgumentNullException(nameof(frameProfiler));
|
||||
_dats = dats ?? throw new ArgumentNullException(nameof(dats));
|
||||
|
|
@ -163,10 +156,6 @@ internal sealed class WorldLifecycleResourceSnapshotSource
|
|||
ParticleOwners: _particleSink.TrackedOwnerCount,
|
||||
EffectOwners: _effects.ReadyOwnerCount,
|
||||
LightOwners: _lights.TrackedOwnerCount,
|
||||
EnvCellLighting: CaptureEnvCellLighting(
|
||||
outcome,
|
||||
_envCellRenderer,
|
||||
_lighting),
|
||||
ScriptOwners: _scripts.ActiveOwnerCount,
|
||||
ActiveScripts: _scripts.ActiveScriptCount,
|
||||
MeshRenderData: mesh.RenderData,
|
||||
|
|
@ -215,16 +204,4 @@ internal sealed class WorldLifecycleResourceSnapshotSource
|
|||
LastFrameProfile: _frameProfiler.LastReport);
|
||||
}
|
||||
|
||||
internal static EnvCellLightingConsumptionSnapshot? CaptureEnvCellLighting(
|
||||
RenderFrameOutcome outcome,
|
||||
EnvCellRenderer? renderer,
|
||||
LightManager lighting)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(lighting);
|
||||
return renderer?.CaptureLightingConsumption(
|
||||
lighting,
|
||||
currentWorldFrame:
|
||||
!outcome.SkippedZeroArea
|
||||
&& outcome.World.NormalWorldDrawn);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,178 +0,0 @@
|
|||
using AcDream.Core.Lighting;
|
||||
|
||||
namespace AcDream.App.Rendering.Wb;
|
||||
|
||||
internal sealed record EnvCellLightingVectorBits(
|
||||
uint X,
|
||||
uint Y,
|
||||
uint Z);
|
||||
|
||||
internal sealed record EnvCellLightingAmbientSnapshot(
|
||||
EnvCellLightingVectorBits AmbientColor,
|
||||
EnvCellLightingVectorBits SunColor,
|
||||
EnvCellLightingVectorBits SunDirection);
|
||||
|
||||
internal sealed record EnvCellLightingSourceSnapshot(
|
||||
int Index,
|
||||
uint OwnerId,
|
||||
uint CellId,
|
||||
int Kind,
|
||||
bool IsDynamic,
|
||||
bool IsLit,
|
||||
bool TracksOwnerPose,
|
||||
EnvCellLightingVectorBits RankingOrigin,
|
||||
EnvCellLightingVectorBits Position,
|
||||
EnvCellLightingVectorBits Forward,
|
||||
EnvCellLightingVectorBits Color,
|
||||
uint Intensity,
|
||||
uint Range,
|
||||
uint ConeAngle,
|
||||
uint DistSq,
|
||||
uint[] LocalPose);
|
||||
|
||||
internal sealed record EnvCellLightingCellSetSnapshot(
|
||||
uint CellId,
|
||||
int[] Indices);
|
||||
|
||||
/// <summary>
|
||||
/// Checkpoint-only copy of the point-light inputs the EnvCell RHI submission
|
||||
/// already consumed. PackedGlobalLightBits is the authoritative binding-4
|
||||
/// payload; Sources records its identity/provenance and CellSets records the
|
||||
/// exact cached 47-index values copied to binding 5.
|
||||
/// </summary>
|
||||
internal sealed record EnvCellLightingConsumptionSnapshot(
|
||||
bool HasCurrentEnvCellInput,
|
||||
int FrameGeneration,
|
||||
int FrameSlot,
|
||||
int RegisteredLightCount,
|
||||
int LogicalGlobalLightCount,
|
||||
int UploadedGlobalLightCount,
|
||||
uint[] PackedGlobalLightBits,
|
||||
EnvCellLightingSourceSnapshot[] Sources,
|
||||
EnvCellLightingCellSetSnapshot[] CellSets,
|
||||
EnvCellLightingAmbientSnapshot Ambient);
|
||||
|
||||
public sealed partial class EnvCellRenderer
|
||||
{
|
||||
/// <summary>
|
||||
/// Copies the retained inputs from this frame's completed EnvCell submission
|
||||
/// for an explicitly requested lifecycle checkpoint. This method never
|
||||
/// rebuilds a light snapshot or invokes either cell selector.
|
||||
/// </summary>
|
||||
internal EnvCellLightingConsumptionSnapshot CaptureLightingConsumption(
|
||||
LightManager lighting,
|
||||
bool currentWorldFrame)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(lighting);
|
||||
|
||||
EnvCellLightingAmbientSnapshot ambient = CaptureAmbient(
|
||||
lighting.CurrentAmbient);
|
||||
if (!currentWorldFrame)
|
||||
return EmptyLightingConsumption(lighting.RegisteredCount, ambient);
|
||||
|
||||
List<KeyValuePair<uint, CachedCellLightSet>> currentCells = [];
|
||||
foreach (KeyValuePair<uint, CachedCellLightSet> entry in _cellLightSetCache)
|
||||
{
|
||||
if (entry.Value.FrameGeneration == _lightFrameGeneration)
|
||||
currentCells.Add(entry);
|
||||
}
|
||||
|
||||
currentCells.Sort(static (left, right) => left.Key.CompareTo(right.Key));
|
||||
if (currentCells.Count == 0)
|
||||
return EmptyLightingConsumption(lighting.RegisteredCount, ambient);
|
||||
|
||||
IReadOnlyList<LightSource>? pointSnapshot = _pointSnapshot;
|
||||
int logicalCount = pointSnapshot?.Count ?? 0;
|
||||
int uploadedCount = Math.Max(logicalCount, 1);
|
||||
int packedFloatCount = checked(
|
||||
uploadedCount * GlobalLightPacker.FloatsPerLight);
|
||||
var packedBits = new uint[packedFloatCount];
|
||||
for (int index = 0; index < packedFloatCount; index++)
|
||||
packedBits[index] = Bits(_globalLightData[index]);
|
||||
|
||||
var sources = new EnvCellLightingSourceSnapshot[logicalCount];
|
||||
for (int index = 0; index < logicalCount; index++)
|
||||
sources[index] = CaptureSource(index, pointSnapshot![index]);
|
||||
|
||||
var cellSets = new EnvCellLightingCellSetSnapshot[currentCells.Count];
|
||||
for (int index = 0; index < currentCells.Count; index++)
|
||||
{
|
||||
KeyValuePair<uint, CachedCellLightSet> entry = currentCells[index];
|
||||
cellSets[index] = new EnvCellLightingCellSetSnapshot(
|
||||
entry.Key,
|
||||
(int[])entry.Value.Indices.Clone());
|
||||
}
|
||||
|
||||
return new EnvCellLightingConsumptionSnapshot(
|
||||
HasCurrentEnvCellInput: true,
|
||||
FrameGeneration: _lightFrameGeneration,
|
||||
FrameSlot: _dynamicFrameSlot,
|
||||
RegisteredLightCount: lighting.RegisteredCount,
|
||||
LogicalGlobalLightCount: logicalCount,
|
||||
UploadedGlobalLightCount: uploadedCount,
|
||||
PackedGlobalLightBits: packedBits,
|
||||
Sources: sources,
|
||||
CellSets: cellSets,
|
||||
Ambient: ambient);
|
||||
}
|
||||
|
||||
private EnvCellLightingConsumptionSnapshot EmptyLightingConsumption(
|
||||
int registeredLightCount,
|
||||
EnvCellLightingAmbientSnapshot ambient) =>
|
||||
new(
|
||||
HasCurrentEnvCellInput: false,
|
||||
FrameGeneration: _lightFrameGeneration,
|
||||
FrameSlot: _dynamicFrameSlot,
|
||||
RegisteredLightCount: registeredLightCount,
|
||||
LogicalGlobalLightCount: 0,
|
||||
UploadedGlobalLightCount: 0,
|
||||
PackedGlobalLightBits: [],
|
||||
Sources: [],
|
||||
CellSets: [],
|
||||
Ambient: ambient);
|
||||
|
||||
private static EnvCellLightingSourceSnapshot CaptureSource(
|
||||
int index,
|
||||
LightSource source) =>
|
||||
new(
|
||||
Index: index,
|
||||
OwnerId: source.OwnerId,
|
||||
CellId: source.CellId,
|
||||
Kind: (int)source.Kind,
|
||||
IsDynamic: source.IsDynamic,
|
||||
IsLit: source.IsLit,
|
||||
TracksOwnerPose: source.TracksOwnerPose,
|
||||
RankingOrigin: CaptureVector(source.RankingOrigin),
|
||||
Position: CaptureVector(source.WorldPosition),
|
||||
Forward: CaptureVector(source.WorldForward),
|
||||
Color: CaptureVector(source.ColorLinear),
|
||||
Intensity: Bits(source.Intensity),
|
||||
Range: Bits(source.Range),
|
||||
ConeAngle: Bits(source.ConeAngle),
|
||||
DistSq: Bits(source.DistSq),
|
||||
LocalPose:
|
||||
[
|
||||
Bits(source.LocalPose.M11), Bits(source.LocalPose.M12),
|
||||
Bits(source.LocalPose.M13), Bits(source.LocalPose.M14),
|
||||
Bits(source.LocalPose.M21), Bits(source.LocalPose.M22),
|
||||
Bits(source.LocalPose.M23), Bits(source.LocalPose.M24),
|
||||
Bits(source.LocalPose.M31), Bits(source.LocalPose.M32),
|
||||
Bits(source.LocalPose.M33), Bits(source.LocalPose.M34),
|
||||
Bits(source.LocalPose.M41), Bits(source.LocalPose.M42),
|
||||
Bits(source.LocalPose.M43), Bits(source.LocalPose.M44),
|
||||
]);
|
||||
|
||||
private static EnvCellLightingAmbientSnapshot CaptureAmbient(
|
||||
CellAmbientState ambient) =>
|
||||
new(
|
||||
CaptureVector(ambient.AmbientColor),
|
||||
CaptureVector(ambient.SunColor),
|
||||
CaptureVector(ambient.SunDirection));
|
||||
|
||||
private static EnvCellLightingVectorBits CaptureVector(
|
||||
System.Numerics.Vector3 value) =>
|
||||
new(Bits(value.X), Bits(value.Y), Bits(value.Z));
|
||||
|
||||
private static uint Bits(float value) =>
|
||||
BitConverter.SingleToUInt32Bits(value);
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ using AcDream.App.Rendering.Wb;
|
|||
using AcDream.App.Tests.Architecture;
|
||||
using AcDream.App.Update;
|
||||
using AcDream.App.World;
|
||||
using AcDream.Core.Lighting;
|
||||
|
||||
namespace AcDream.App.Tests.Composition;
|
||||
|
||||
|
|
@ -78,52 +77,6 @@ public sealed class FrameRootCompositionTests
|
|||
Assert.True(secondTransfer > firstTransfer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProductionLightingCheckpointReceivesCanonicalRendererAndManager()
|
||||
{
|
||||
MethodInfo compose = typeof(FrameRootCompositionPhase).GetMethod(
|
||||
"ComposeCore",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic)
|
||||
?? throw new MissingMethodException(
|
||||
typeof(FrameRootCompositionPhase).FullName,
|
||||
"ComposeCore");
|
||||
IReadOnlyList<CompiledCall> calls = CompiledCallGraph.Read(compose);
|
||||
|
||||
int constructor = CallIndex(
|
||||
calls,
|
||||
typeof(WorldLifecycleResourceSnapshotSource),
|
||||
".ctor");
|
||||
int renderer = calls
|
||||
.Select((call, index) => (call, index))
|
||||
.Last(pair => pair.index < constructor
|
||||
&& pair.call.Target.DeclaringType == typeof(LivePresentationResult)
|
||||
&& pair.call.Target.Name == "get_EnvCellRenderer")
|
||||
.index;
|
||||
int lighting = calls
|
||||
.Select((call, index) => (call, index))
|
||||
.Last(pair => pair.index < constructor
|
||||
&& pair.call.Target.DeclaringType == typeof(FrameRootDependencies)
|
||||
&& pair.call.Target.Name == "get_Lighting")
|
||||
.index;
|
||||
int dispatcher = calls
|
||||
.Select((call, index) => (call, index))
|
||||
.Last(pair => pair.index < constructor
|
||||
&& pair.call.Target.DeclaringType == typeof(LivePresentationResult)
|
||||
&& pair.call.Target.Name == "get_DrawDispatcher")
|
||||
.index;
|
||||
int profiler = calls
|
||||
.Select((call, index) => (call, index))
|
||||
.Last(pair => pair.index < constructor
|
||||
&& pair.call.Target.DeclaringType == typeof(FrameRootDependencies)
|
||||
&& pair.call.Target.Name == "get_FrameProfiler")
|
||||
.index;
|
||||
|
||||
Assert.Equal(dispatcher + 1, renderer);
|
||||
Assert.Equal(renderer + 1, lighting);
|
||||
Assert.Equal(lighting + 1, profiler);
|
||||
Assert.True(profiler < constructor);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProductionWiresTheOptionalScreenshotAdapterIntoTheOuterRenderOwner()
|
||||
{
|
||||
|
|
@ -196,19 +149,6 @@ public sealed class FrameRootCompositionTests
|
|||
BindingFlags.Instance | BindingFlags.NonPublic),
|
||||
field => field.FieldType == typeof(GameWindow));
|
||||
|
||||
FieldInfo[] snapshotFields = typeof(WorldLifecycleResourceSnapshotSource)
|
||||
.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
Assert.Contains(snapshotFields, field => field.FieldType == typeof(EnvCellRenderer));
|
||||
Assert.Contains(snapshotFields, field => field.FieldType == typeof(LightManager));
|
||||
ParameterInfo[] snapshotParameters = typeof(WorldLifecycleResourceSnapshotSource)
|
||||
.GetConstructors().Single().GetParameters();
|
||||
Assert.Contains(snapshotParameters,
|
||||
parameter => parameter.ParameterType == typeof(EnvCellRenderer)
|
||||
&& !parameter.HasDefaultValue);
|
||||
Assert.Contains(snapshotParameters,
|
||||
parameter => parameter.ParameterType == typeof(LightManager)
|
||||
&& !parameter.HasDefaultValue);
|
||||
|
||||
MethodInfo capture = typeof(WorldLifecycleResourceSnapshotSource)
|
||||
.GetMethod(nameof(WorldLifecycleResourceSnapshotSource.Capture))!;
|
||||
Assert.Equal(
|
||||
|
|
@ -227,12 +167,6 @@ public sealed class FrameRootCompositionTests
|
|||
calls,
|
||||
call => call.Target.DeclaringType == typeof(FrameProfiler)
|
||||
&& call.Target.Name == "get_LastReport");
|
||||
Assert.Contains(
|
||||
calls,
|
||||
call => call.Target.DeclaringType
|
||||
== typeof(WorldLifecycleResourceSnapshotSource)
|
||||
&& call.Target.Name == nameof(
|
||||
WorldLifecycleResourceSnapshotSource.CaptureEnvCellLighting));
|
||||
Assert.Contains(
|
||||
calls,
|
||||
call => call.Target.DeclaringType == typeof(WorldRenderFrameOutcome)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ using AcDream.App.Rendering;
|
|||
using AcDream.App.Rendering.Packs;
|
||||
using AcDream.App.Rendering.Residency;
|
||||
using AcDream.App.Rendering.Scene;
|
||||
using AcDream.App.Rendering.Wb;
|
||||
using AcDream.App.Streaming;
|
||||
using AcDream.App.UI.Testing;
|
||||
using AcDream.Runtime;
|
||||
|
|
@ -413,62 +412,6 @@ public sealed class WorldLifecycleAutomationControllerTests
|
|||
{
|
||||
LoadedLandblocks = 1,
|
||||
WorldEntities = 42,
|
||||
EnvCellLighting = new EnvCellLightingConsumptionSnapshot(
|
||||
HasCurrentEnvCellInput: true,
|
||||
FrameGeneration: 31,
|
||||
FrameSlot: 2,
|
||||
RegisteredLightCount: 214,
|
||||
LogicalGlobalLightCount: 1,
|
||||
UploadedGlobalLightCount: 1,
|
||||
PackedGlobalLightBits:
|
||||
[
|
||||
0x3F800000u, 0x40000000u, 0x40400000u, 0x3F800000u,
|
||||
0u, 0u, 0x3F800000u, 0x41200000u,
|
||||
0x3DCCCCCDu, 0x3E4CCCCDu, 0x3E99999Au, 0x40000000u,
|
||||
0u, 0x3F800000u, 0u, 0u,
|
||||
],
|
||||
Sources:
|
||||
[
|
||||
new EnvCellLightingSourceSnapshot(
|
||||
Index: 0,
|
||||
OwnerId: 0x5000000Au,
|
||||
CellId: 0x00070143u,
|
||||
Kind: 1,
|
||||
IsDynamic: true,
|
||||
IsLit: true,
|
||||
TracksOwnerPose: true,
|
||||
RankingOrigin: new EnvCellLightingVectorBits(
|
||||
0x3F000000u, 0x3F800000u, 0x40000000u),
|
||||
Position: new EnvCellLightingVectorBits(
|
||||
0x3F800000u, 0x40000000u, 0x40400000u),
|
||||
Forward: new EnvCellLightingVectorBits(
|
||||
0u, 0u, 0x3F800000u),
|
||||
Color: new EnvCellLightingVectorBits(
|
||||
0x3DCCCCCDu, 0x3E4CCCCDu, 0x3E99999Au),
|
||||
Intensity: 0x40000000u,
|
||||
Range: 0x41200000u,
|
||||
ConeAngle: 0u,
|
||||
DistSq: 0x42F60000u,
|
||||
LocalPose:
|
||||
[
|
||||
0x3F800000u, 0u, 0u, 0u,
|
||||
0u, 0x3F800000u, 0u, 0u,
|
||||
0u, 0u, 0x3F800000u, 0u,
|
||||
0u, 0u, 0u, 0x3F800000u,
|
||||
]),
|
||||
],
|
||||
CellSets:
|
||||
[
|
||||
new EnvCellLightingCellSetSnapshot(
|
||||
0x00070143u,
|
||||
[0, .. Enumerable.Repeat(-1, 46)]),
|
||||
],
|
||||
Ambient: new EnvCellLightingAmbientSnapshot(
|
||||
new EnvCellLightingVectorBits(
|
||||
0x3E4CCCCDu, 0x3E4CCCCDu, 0x3E4CCCCDu),
|
||||
new EnvCellLightingVectorBits(0u, 0u, 0u),
|
||||
new EnvCellLightingVectorBits(
|
||||
0xBF800000u, 0x3F000000u, 0x3E800000u))),
|
||||
RenderSceneOracle = new CurrentRenderSceneOracleSnapshot(
|
||||
Enabled: true,
|
||||
CompletedFrameSequence: 19,
|
||||
|
|
@ -587,28 +530,6 @@ public sealed class WorldLifecycleAutomationControllerTests
|
|||
Assert.True(json.RootElement.GetProperty("render").GetProperty("presentation")
|
||||
.GetProperty("screenshotCaptured").GetBoolean());
|
||||
Assert.Equal(42, json.RootElement.GetProperty("resources").GetProperty("worldEntities").GetInt32());
|
||||
JsonElement envCellLighting = json.RootElement
|
||||
.GetProperty("resources")
|
||||
.GetProperty("envCellLighting");
|
||||
Assert.True(envCellLighting
|
||||
.GetProperty("hasCurrentEnvCellInput").GetBoolean());
|
||||
Assert.Equal(31, envCellLighting.GetProperty("frameGeneration").GetInt32());
|
||||
Assert.Equal(214, envCellLighting
|
||||
.GetProperty("registeredLightCount").GetInt32());
|
||||
Assert.Equal(0x3F800000u, envCellLighting
|
||||
.GetProperty("sources")[0]
|
||||
.GetProperty("position")
|
||||
.GetProperty("x").GetUInt32());
|
||||
Assert.Equal(0x00070143u, envCellLighting
|
||||
.GetProperty("cellSets")[0]
|
||||
.GetProperty("cellId").GetUInt32());
|
||||
Assert.Equal(-1, envCellLighting
|
||||
.GetProperty("cellSets")[0]
|
||||
.GetProperty("indices")[7].GetInt32());
|
||||
Assert.Equal(0xBF800000u, envCellLighting
|
||||
.GetProperty("ambient")
|
||||
.GetProperty("sunDirection")
|
||||
.GetProperty("x").GetUInt32());
|
||||
JsonElement renderSceneOracle = json.RootElement
|
||||
.GetProperty("resources")
|
||||
.GetProperty("renderSceneOracle");
|
||||
|
|
|
|||
|
|
@ -18,13 +18,11 @@
|
|||
// over the same RecordingGpuDevice, plus a no-op IPreparedAssetSource.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Diagnostics;
|
||||
using AcDream.App.Rendering.Gpu;
|
||||
using AcDream.App.Rendering.Gpu.Vk;
|
||||
using AcDream.App.Rendering.Wb;
|
||||
|
|
@ -40,206 +38,6 @@ namespace AcDream.App.Tests.Rendering.Wb;
|
|||
|
||||
public class EnvCellRendererTests
|
||||
{
|
||||
[Fact]
|
||||
public void LightingCheckpoint_ReportsActualBoundInputsAndRejectsStaleFrames()
|
||||
{
|
||||
const uint landblockId = 0x8C04FFFFu;
|
||||
const uint cellA = 0x8C040101u;
|
||||
const uint cellB = 0x8C040102u;
|
||||
|
||||
using var device = new RecordingGpuDevice();
|
||||
using var meshManager = CreateMeshManager(device);
|
||||
var frameLifetime = new GpuDeviceFrameLifetime(device);
|
||||
var scope = new VulkanWorldPassScope(sampleCount: 1);
|
||||
using var renderer = new EnvCellRenderer(
|
||||
device,
|
||||
frameLifetime,
|
||||
scope,
|
||||
meshManager,
|
||||
new WbFrustum());
|
||||
|
||||
renderer.CommitLandblock(new EnvCellLandblockBuild(
|
||||
landblockId,
|
||||
[],
|
||||
[
|
||||
LightingShell(cellA, 1, new Vector3(-1f), new Vector3(1f)),
|
||||
LightingShell(cellB, 2, new Vector3(99f, -1f, -1f),
|
||||
new Vector3(101f, 1f, 1f)),
|
||||
]));
|
||||
|
||||
var lighting = new LightManager
|
||||
{
|
||||
CurrentAmbient = new CellAmbientState(
|
||||
new Vector3(0.2f, 0.3f, 0.4f),
|
||||
new Vector3(0.5f, 0.6f, 0.7f),
|
||||
new Vector3(-0.1f, 0.8f, -0.9f)),
|
||||
};
|
||||
var dynamicLight = new LightSource
|
||||
{
|
||||
Kind = LightKind.Spot,
|
||||
WorldPosition = new Vector3(50f, 60f, 70f),
|
||||
RankingOrigin = new Vector3(5f, 6f, 7f),
|
||||
WorldForward = new Vector3(0.25f, -0.5f, 0.75f),
|
||||
ColorLinear = new Vector3(0.11f, 0.22f, 0.33f),
|
||||
Intensity = 4.5f,
|
||||
Range = 1.25f,
|
||||
ConeAngle = 0.625f,
|
||||
OwnerId = 0x50000001u,
|
||||
CellId = cellA,
|
||||
IsDynamic = true,
|
||||
IsLit = true,
|
||||
TracksOwnerPose = true,
|
||||
DistSq = 123.5f,
|
||||
LocalPose = new Matrix4x4(
|
||||
1f, 2f, 3f, 4f,
|
||||
5f, 6f, 7f, 8f,
|
||||
9f, 10f, 11f, 12f,
|
||||
13f, 14f, 15f, 16f),
|
||||
};
|
||||
var staticA = new LightSource
|
||||
{
|
||||
Kind = LightKind.Point,
|
||||
WorldPosition = Vector3.Zero,
|
||||
RankingOrigin = Vector3.Zero,
|
||||
WorldForward = Vector3.UnitX,
|
||||
ColorLinear = new Vector3(0.9f, 0.1f, 0.2f),
|
||||
Intensity = 2f,
|
||||
Range = 4f,
|
||||
OwnerId = 0x50000002u,
|
||||
CellId = cellA,
|
||||
IsLit = true,
|
||||
};
|
||||
var staticB = new LightSource
|
||||
{
|
||||
Kind = LightKind.Point,
|
||||
WorldPosition = new Vector3(100f, 0f, 0f),
|
||||
RankingOrigin = new Vector3(100f, 0f, 0f),
|
||||
WorldForward = Vector3.UnitY,
|
||||
ColorLinear = new Vector3(0.2f, 0.8f, 0.4f),
|
||||
Intensity = 3f,
|
||||
Range = 4f,
|
||||
OwnerId = 0x50000003u,
|
||||
CellId = cellB,
|
||||
IsLit = true,
|
||||
};
|
||||
lighting.Register(dynamicLight);
|
||||
lighting.Register(staticA);
|
||||
lighting.Register(staticB);
|
||||
lighting.BuildPointLightSnapshot(Vector3.Zero);
|
||||
renderer.SetPointSnapshot(lighting.PointSnapshot);
|
||||
|
||||
frameLifetime.BeginFrame();
|
||||
IGpuFrame frame = frameLifetime.CurrentFrame!;
|
||||
using IGpuPassEncoder pass = frame.BeginPass(
|
||||
GpuPassDescription.BackbufferClear(
|
||||
"envcell-lighting-observation",
|
||||
Vector4.Zero,
|
||||
sampleCount: 1));
|
||||
using IDisposable publication = scope.Publish(pass);
|
||||
renderer.BeginFrame(frame.SlotIndex);
|
||||
SeedLightingSubmission(renderer, instanceCount: 2);
|
||||
|
||||
// Deliberately submit in the opposite order from deterministic JSON's
|
||||
// outer cell-id ordering. Each exported set must still match the exact
|
||||
// binding-5 block associated with its submitted cell.
|
||||
var instances = new List<InstanceData>
|
||||
{
|
||||
new() { Transform = Matrix4x4.Identity, CellId = cellB },
|
||||
new() { Transform = Matrix4x4.Identity, CellId = cellA },
|
||||
};
|
||||
device.Clear();
|
||||
InvokeSubmitRhi(renderer, instances, instanceCount: 2);
|
||||
|
||||
EnvCellLightingConsumptionSnapshot snapshot =
|
||||
renderer.CaptureLightingConsumption(lighting, currentWorldFrame: true);
|
||||
Assert.True(snapshot.HasCurrentEnvCellInput);
|
||||
Assert.Equal(frame.SlotIndex, snapshot.FrameSlot);
|
||||
Assert.Equal(3, snapshot.RegisteredLightCount);
|
||||
Assert.Equal(3, snapshot.LogicalGlobalLightCount);
|
||||
Assert.Equal(3, snapshot.UploadedGlobalLightCount);
|
||||
Assert.Equal([cellA, cellB], snapshot.CellSets.Select(set => set.CellId));
|
||||
|
||||
GpuRecordedStorageBind globalBind = Assert.Single(
|
||||
device.Calls.OfType<GpuRecordedStorageBind>(),
|
||||
bind => bind.Binding == GpuBindingModel.StorageGlobalLights);
|
||||
uint[] actualGlobalBits = MemoryMarshal.Cast<byte, uint>(
|
||||
device.RingBytes.Slice(
|
||||
(int)globalBind.OffsetBytes,
|
||||
(int)globalBind.SizeBytes)).ToArray();
|
||||
Assert.Equal(actualGlobalBits, snapshot.PackedGlobalLightBits);
|
||||
|
||||
GpuRecordedStorageBind setBind = Assert.Single(
|
||||
device.Calls.OfType<GpuRecordedStorageBind>(),
|
||||
bind => bind.Binding == GpuBindingModel.StorageInstanceLightSets);
|
||||
int[] actualSets = MemoryMarshal.Cast<byte, int>(
|
||||
device.RingBytes.Slice(
|
||||
(int)setBind.OffsetBytes,
|
||||
(int)setBind.SizeBytes)).ToArray();
|
||||
Assert.Equal(
|
||||
actualSets.AsSpan(0, LightManager.MaxLightsPerEnvCell).ToArray(),
|
||||
Assert.Single(snapshot.CellSets, set => set.CellId == cellB).Indices);
|
||||
Assert.Equal(
|
||||
actualSets.AsSpan(
|
||||
LightManager.MaxLightsPerEnvCell,
|
||||
LightManager.MaxLightsPerEnvCell).ToArray(),
|
||||
Assert.Single(snapshot.CellSets, set => set.CellId == cellA).Indices);
|
||||
int[] expectedSet = [0, 1, 2, .. Enumerable.Repeat(-1, 44)];
|
||||
Assert.Equal(expectedSet,
|
||||
Assert.Single(snapshot.CellSets, set => set.CellId == cellA).Indices);
|
||||
Assert.Equal(expectedSet,
|
||||
Assert.Single(snapshot.CellSets, set => set.CellId == cellB).Indices);
|
||||
|
||||
EnvCellLightingSourceSnapshot source = snapshot.Sources[0];
|
||||
Assert.Equal(0, source.Index);
|
||||
Assert.Equal(dynamicLight.OwnerId, source.OwnerId);
|
||||
Assert.Equal(dynamicLight.CellId, source.CellId);
|
||||
Assert.Equal((int)LightKind.Spot, source.Kind);
|
||||
Assert.True(source.IsDynamic);
|
||||
Assert.True(source.IsLit);
|
||||
Assert.True(source.TracksOwnerPose);
|
||||
Assert.Equal(BitConverter.SingleToUInt32Bits(5f), source.RankingOrigin.X);
|
||||
Assert.Equal(BitConverter.SingleToUInt32Bits(50f), source.Position.X);
|
||||
Assert.Equal(BitConverter.SingleToUInt32Bits(-0.5f), source.Forward.Y);
|
||||
Assert.Equal(BitConverter.SingleToUInt32Bits(0.33f), source.Color.Z);
|
||||
Assert.Equal(BitConverter.SingleToUInt32Bits(16f), source.LocalPose[15]);
|
||||
|
||||
RenderFrameOutcome nonWorld = new(
|
||||
new WorldRenderFrameOutcome(0, 0, NormalWorldDrawn: false),
|
||||
default);
|
||||
AssertNoCurrentLighting(WorldLifecycleResourceSnapshotSource
|
||||
.CaptureEnvCellLighting(nonWorld, renderer, lighting)!);
|
||||
AssertNoCurrentLighting(WorldLifecycleResourceSnapshotSource
|
||||
.CaptureEnvCellLighting(RenderFrameOutcome.ZeroArea, renderer, lighting)!);
|
||||
|
||||
// A real zero-light submission still binds one all-zero dummy light.
|
||||
var emptyLighting = new LightManager();
|
||||
renderer.BeginFrame(frame.SlotIndex);
|
||||
renderer.SetPointSnapshot(emptyLighting.PointSnapshot);
|
||||
device.Clear();
|
||||
InvokeSubmitRhi(renderer, [instances[0]], instanceCount: 1);
|
||||
EnvCellLightingConsumptionSnapshot empty =
|
||||
renderer.CaptureLightingConsumption(emptyLighting, currentWorldFrame: true);
|
||||
Assert.True(empty.HasCurrentEnvCellInput);
|
||||
Assert.Equal(0, empty.LogicalGlobalLightCount);
|
||||
Assert.Equal(1, empty.UploadedGlobalLightCount);
|
||||
Assert.Equal(GlobalLightPacker.FloatsPerLight, empty.PackedGlobalLightBits.Length);
|
||||
Assert.All(empty.PackedGlobalLightBits, bits => Assert.Equal(0u, bits));
|
||||
GpuRecordedStorageBind emptyGlobalBind = Assert.Single(
|
||||
device.Calls.OfType<GpuRecordedStorageBind>(),
|
||||
bind => bind.Binding == GpuBindingModel.StorageGlobalLights);
|
||||
Assert.Equal(
|
||||
MemoryMarshal.Cast<byte, uint>(device.RingBytes.Slice(
|
||||
(int)emptyGlobalBind.OffsetBytes,
|
||||
(int)emptyGlobalBind.SizeBytes)).ToArray(),
|
||||
empty.PackedGlobalLightBits);
|
||||
|
||||
// BeginFrame advances the existing cache generation. Without a draw,
|
||||
// the following checkpoint cannot reuse the preceding frame's cell set.
|
||||
renderer.BeginFrame(frame.SlotIndex);
|
||||
AssertNoCurrentLighting(
|
||||
renderer.CaptureLightingConsumption(emptyLighting, currentWorldFrame: true));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubmitRhi_BindsTwoCompleteFortySevenIndexCellSetsInCurrentGeneration()
|
||||
{
|
||||
|
|
@ -850,23 +648,6 @@ public class EnvCellRendererTests
|
|||
Assert.Empty(b);
|
||||
}
|
||||
|
||||
private static EnvCellShellPlacement LightingShell(
|
||||
uint cellId,
|
||||
ulong geometryId,
|
||||
Vector3 minimum,
|
||||
Vector3 maximum) =>
|
||||
new(
|
||||
CellId: cellId,
|
||||
GeometryId: geometryId,
|
||||
EnvironmentId: 0x0D000001u,
|
||||
CellStructure: 1,
|
||||
Surfaces: ImmutableArray<ushort>.Empty,
|
||||
WorldPosition: Vector3.Zero,
|
||||
Rotation: Quaternion.Identity,
|
||||
Transform: Matrix4x4.Identity,
|
||||
LocalBounds: new WbBoundingBox(minimum, maximum),
|
||||
WorldBounds: new WbBoundingBox(minimum, maximum));
|
||||
|
||||
private static void SeedLightingSubmission(
|
||||
EnvCellRenderer renderer,
|
||||
int instanceCount)
|
||||
|
|
@ -918,17 +699,6 @@ public class EnvCellRendererTests
|
|||
});
|
||||
}
|
||||
|
||||
private static void AssertNoCurrentLighting(
|
||||
EnvCellLightingConsumptionSnapshot snapshot)
|
||||
{
|
||||
Assert.False(snapshot.HasCurrentEnvCellInput);
|
||||
Assert.Equal(0, snapshot.LogicalGlobalLightCount);
|
||||
Assert.Equal(0, snapshot.UploadedGlobalLightCount);
|
||||
Assert.Empty(snapshot.PackedGlobalLightBits);
|
||||
Assert.Empty(snapshot.Sources);
|
||||
Assert.Empty(snapshot.CellSets);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Prepare gate (2026-07-24) — pure camera-tolerance half.
|
||||
// The tolerance must swallow the ~36 µm eye rest jitter (RetailPViewRenderer
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue