Upload spectrum ring slots by offset
This commit is contained in:
parent
b44aaac7cc
commit
07a7864944
6 changed files with 49 additions and 26 deletions
|
|
@ -156,9 +156,11 @@ a named invariant that the native runtime cannot protect yet.
|
|||
truncated and reported in the runtime UI. Lowerings use that fixed capacity
|
||||
as `ColumnStride`. Physical history slots are addressed as a ring with
|
||||
`RollingOffset`, so logical age no longer requires reshuffling every column
|
||||
before shader sampling. `MIMIR_SPECTRUM_TUBE_SUBDIVISIONS` controls the
|
||||
requested Catmull-Rom subdivision count so Fensalir's overflow report has a
|
||||
direct cost lever.
|
||||
before shader sampling. Mimir emits one Float32 resource upload per sampled
|
||||
history slot with an explicit element offset, rather than uploading the full
|
||||
fixed-capacity backing buffer every frame. `MIMIR_SPECTRUM_TUBE_SUBDIVISIONS`
|
||||
controls the requested Catmull-Rom subdivision count so Fensalir's overflow
|
||||
report has a direct cost lever.
|
||||
- `MimirFensalirFieldLowering` now emits `AquariumFieldResourceDeclaration`
|
||||
rows for live native/GPU payload views. Observation claims reference
|
||||
`mimir:resource:*` keys, and Fensalir validation/planning can reject or defer
|
||||
|
|
|
|||
|
|
@ -393,9 +393,11 @@ resize the GPU buffer while the rolling trail fills. `MIMIR_SPECTRUM_SOURCE_LANE
|
|||
sets the fixed lane budget; surplus sources are truncated and reported in the
|
||||
runtime UI. Physical history slots are now addressed as a ring with
|
||||
`RollingOffset`, so logical age no longer requires moving every column before
|
||||
shader sampling. `MIMIR_SPECTRUM_TUBE_SUBDIVISIONS` is the runtime cost lever
|
||||
for lowering Catmull-Rom subdivision count when Fensalir reports TubeField
|
||||
truncation.
|
||||
shader sampling. Mimir emits one Float32 resource upload per sampled history
|
||||
slot with an explicit element offset instead of uploading the full
|
||||
fixed-capacity backing buffer every frame. `MIMIR_SPECTRUM_TUBE_SUBDIVISIONS`
|
||||
is the runtime cost lever for lowering Catmull-Rom subdivision count when
|
||||
Fensalir reports TubeField truncation.
|
||||
|
||||
## Known Risks
|
||||
|
||||
|
|
|
|||
|
|
@ -655,8 +655,9 @@ static int RunMimirSpectrumUploadSmoke()
|
|||
|
||||
var frame = runtime.Frame;
|
||||
var field = frame.Scene.FieldEvidenceFrame;
|
||||
var upload = field.ResourceUploads.SingleOrDefault(upload =>
|
||||
string.Equals(upload.ResourceKey, "mimir:resource:spectrum:field-upload", StringComparison.Ordinal));
|
||||
var matchingUploads = field.ResourceUploads
|
||||
.Where(upload => string.Equals(upload.ResourceKey, "mimir:resource:spectrum:field-upload", StringComparison.Ordinal))
|
||||
.ToArray();
|
||||
var matchingResources = field.Resources
|
||||
.Where(resource => string.Equals(resource.ResourceKey, "mimir:resource:spectrum:field-upload", StringComparison.Ordinal))
|
||||
.ToArray();
|
||||
|
|
@ -686,19 +687,30 @@ static int RunMimirSpectrumUploadSmoke()
|
|||
.ToArray();
|
||||
|
||||
Console.WriteLine(
|
||||
$"mimir-spectrum-upload-smoke resources={field.Resources.Count} uploads={field.ResourceUploads.Count} claims={field.Claims.Count} tubeLowerings={field.TubeSplineLowerings.Count} planned={plan.Packets.Count} errors={validation.HasErrors} legacySplines={frame.Scene.SplineFrame.Splines.Count} bufferFields={frame.Scene.BufferFieldFrame.SplineTubeFields.Count + frame.Scene.BufferFieldFrame.TextureSplineFields.Count} uploadFloats={upload?.Float32Data.Count ?? 0}");
|
||||
$"mimir-spectrum-upload-smoke resources={field.Resources.Count} uploads={field.ResourceUploads.Count} claims={field.Claims.Count} tubeLowerings={field.TubeSplineLowerings.Count} planned={plan.Packets.Count} errors={validation.HasErrors} legacySplines={frame.Scene.SplineFrame.Splines.Count} bufferFields={frame.Scene.BufferFieldFrame.SplineTubeFields.Count + frame.Scene.BufferFieldFrame.TextureSplineFields.Count} uploadFloats={matchingUploads.Sum(upload => upload.Float32Data.Count)}");
|
||||
|
||||
return
|
||||
!validation.HasErrors &&
|
||||
!frame.Scene.SplineFrame.HasInput &&
|
||||
!frame.Scene.BufferFieldFrame.HasInput &&
|
||||
upload is not null &&
|
||||
matchingUploads.Length == expectedHistoryCount &&
|
||||
matchingResources.Length == 1 &&
|
||||
matchingRamps.Length == 1 &&
|
||||
matchingLowerings.Length == 4 &&
|
||||
matchingClaims.Length == matchingLowerings.Length &&
|
||||
upload.Version == resource.Version &&
|
||||
upload.Float32Data.Count == resource.DepthOrCount &&
|
||||
matchingUploads.All(upload => upload.Version == resource.Version) &&
|
||||
matchingUploads.Sum(upload => upload.Float32Data.Count) == lowering.Width * expectedSourceLaneCapacity * expectedHistoryCount &&
|
||||
matchingUploads.All(upload =>
|
||||
upload.Float32Data.Count == lowering.Width * expectedSourceLaneCapacity &&
|
||||
upload.ElementOffset >= 0 &&
|
||||
upload.ElementOffset + upload.Float32Data.Count <= resource.DepthOrCount) &&
|
||||
matchingUploads.Select(upload => upload.ElementOffset).Order().SequenceEqual(
|
||||
new[]
|
||||
{
|
||||
0,
|
||||
lowering.Width * expectedSourceLaneCapacity * (expectedHistoryCapacity - 2),
|
||||
lowering.Width * expectedSourceLaneCapacity * (expectedHistoryCapacity - 1),
|
||||
}.Order()) &&
|
||||
resource.Kind == AquariumFieldResourceKind.StructuredBuffer &&
|
||||
resource.Residency == AquariumFieldResourceResidency.GpuResident &&
|
||||
resource.Access == AquariumFieldShaderAccess.ShaderResource &&
|
||||
|
|
@ -710,7 +722,6 @@ static int RunMimirSpectrumUploadSmoke()
|
|||
string.Equals(ramp.Format, "Rgba8Unorm", StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(ramp.SourceUri, @"D:\WIP4\Projects\Aetheria\Assets\Resources\Ramps\blackbody.png", StringComparison.OrdinalIgnoreCase) &&
|
||||
resource.Height == expectedSourceLaneCapacity * expectedHistoryCapacity &&
|
||||
lowering.Width * resource.Height == upload.Float32Data.Count &&
|
||||
matchingLowerings.All(item =>
|
||||
item.ColumnCount == expectedHistoryCount &&
|
||||
item.ColumnStride == expectedSourceLaneCapacity &&
|
||||
|
|
|
|||
|
|
@ -280,10 +280,12 @@ public sealed class MimirRuntime : IAquariumRuntime
|
|||
var resourceColumnCapacity = checked(spectrumSourceLaneCapacity * SpectrumHistoryWindowCount);
|
||||
var newestHistorySlot = PositiveModulo(-historyFrames[0].Sequence, SpectrumHistoryWindowCount);
|
||||
var rollingOffset = checked(newestHistorySlot * spectrumSourceLaneCapacity);
|
||||
var samples = new float[width * resourceColumnCapacity];
|
||||
var resourceUploads = new List<AquariumFieldResourceUpload>(historyFrames.Length);
|
||||
for (var historyIndex = 0; historyIndex < historyFrames.Length; historyIndex++)
|
||||
{
|
||||
var historySlot = PositiveModulo(-historyFrames[historyIndex].Sequence, SpectrumHistoryWindowCount);
|
||||
var uploadColumnOffset = checked(historySlot * spectrumSourceLaneCapacity);
|
||||
var samples = new float[width * spectrumSourceLaneCapacity];
|
||||
foreach (var spectrum in historyFrames[historyIndex].Spectra)
|
||||
{
|
||||
if (!sourceIndexById.TryGetValue(spectrum.SourceId, out var sourceIndex))
|
||||
|
|
@ -291,9 +293,16 @@ public sealed class MimirRuntime : IAquariumRuntime
|
|||
continue;
|
||||
}
|
||||
|
||||
var physicalColumn = historySlot * spectrumSourceLaneCapacity + sourceIndex;
|
||||
WriteNormalizedSpectrum(spectrum, samples.AsSpan(physicalColumn * width, width));
|
||||
WriteNormalizedSpectrum(spectrum, samples.AsSpan(sourceIndex * width, width));
|
||||
}
|
||||
|
||||
resourceUploads.Add(new AquariumFieldResourceUpload
|
||||
{
|
||||
ResourceKey = SpectrumFieldResourceKey,
|
||||
Version = unchecked((ulong)Math.Max(0, spectrumHistorySequence)),
|
||||
ElementOffset = checked(uploadColumnOffset * width),
|
||||
Float32Data = samples,
|
||||
});
|
||||
}
|
||||
|
||||
const string domainKey = "mimir:domain:spectrum:field-upload";
|
||||
|
|
@ -323,7 +332,7 @@ public sealed class MimirRuntime : IAquariumRuntime
|
|||
Format: "Float32",
|
||||
Width: width,
|
||||
Height: resourceColumnCapacity,
|
||||
DepthOrCount: samples.Length,
|
||||
DepthOrCount: checked(width * resourceColumnCapacity),
|
||||
StrideBytes: sizeof(float),
|
||||
ValidFromNs: 0,
|
||||
ValidUntilNs: 0,
|
||||
|
|
@ -422,12 +431,7 @@ public sealed class MimirRuntime : IAquariumRuntime
|
|||
ResourceUploads =
|
||||
[
|
||||
.. frame.ResourceUploads,
|
||||
new AquariumFieldResourceUpload
|
||||
{
|
||||
ResourceKey = SpectrumFieldResourceKey,
|
||||
Version = version,
|
||||
Float32Data = samples,
|
||||
},
|
||||
.. resourceUploads,
|
||||
],
|
||||
TubeSplineLowerings = [.. frame.TubeSplineLowerings, .. lowerings],
|
||||
AccumulationWindowSeconds = frame.AccumulationWindowSeconds,
|
||||
|
|
|
|||
|
|
@ -167,3 +167,4 @@
|
|||
{"ts": "2026-05-27T11:41:00+01:00", "type": "implementation", "status": "ok", "note": "Mimir now exposes MIMIR_SPECTRUM_TUBE_SUBDIVISIONS as the first runtime cost lever for TubeField overflow. The spectrum upload smoke sets it to 2 and verifies every emitted source-lane TubeSpline lowering uses that Catmull-Rom subdivision count, so Fensalir's requested/dispatched/truncated segment report can be answered without changing code."}
|
||||
{"ts": "2026-05-27T11:47:48+01:00", "type": "implementation", "status": "ok", "note": "Mimir spectrum upload now has fixed source-lane capacity as well as fixed history capacity. MIMIR_SPECTRUM_SOURCE_LANES sets the lane budget, TubeSpline lowerings use that budget as ColumnStride, surplus sources are truncated and reported in the runtime UI, and --mimir-spectrum-upload-smoke verifies 48 bands x 8 lanes x 40 history capacity = 15360 uploaded floats while emitting only four active TubeField claims/lowerings."}
|
||||
{"ts": "2026-05-27T11:54:44+01:00", "type": "implementation", "status": "ok", "note": "Mimir spectrum history now uses true ring addressing. Physical history slots are keyed by sequence modulo the fixed history capacity, TubeSpline lowerings carry RollingOffset for newest logical age, and --mimir-spectrum-upload-smoke verifies the expected offset with the fixed 8-lane resource. This makes partial column uploads possible without changing shader sampling semantics."}
|
||||
{"ts": "2026-05-27T12:00:41+01:00", "type": "implementation", "status": "ok", "note": "Mimir now emits partial spectrum resource uploads for the active ring slots it samples. The declared GPU buffer remains fixed at 48 bands x 8 lanes x 40 history slots, but the smoke now verifies three offset uploads totaling 1152 floats for the three active ages instead of a full 15360-float backing-store upload. Headless Fensalir rendered the offset-copy path with empty stderr."}
|
||||
|
|
|
|||
|
|
@ -372,9 +372,12 @@ accepted_design:
|
|||
shared Float32 resource so each active lane renders a true age trail at
|
||||
`z = age * 0.1`. Physical history slots are addressed as a ring with
|
||||
`RollingOffset`, so logical age no longer requires moving every column in
|
||||
the upload matrix before shader sampling. `MIMIR_SPECTRUM_TUBE_SUBDIVISIONS`
|
||||
controls the Mimir-requested Catmull-Rom subdivisions so Fensalir's
|
||||
TubeField truncation report has a direct cost lever.
|
||||
the upload matrix before shader sampling. Mimir emits one Float32 resource
|
||||
upload per sampled history slot with an explicit element offset instead of
|
||||
uploading the full fixed-capacity backing buffer every frame.
|
||||
`MIMIR_SPECTRUM_TUBE_SUBDIVISIONS` controls the Mimir-requested Catmull-Rom
|
||||
subdivisions so Fensalir's TubeField truncation report has a direct cost
|
||||
lever.
|
||||
- `config/perfect-machine.example.json` declares live owners, sensors,
|
||||
reservoir views, and runtime policy.
|
||||
- `src/Mimir.Runtime/Synchronization` now includes the Perfect Machine module
|
||||
|
|
|
|||
Loading…
Reference in a new issue