diff --git a/src/AcDream.App/Rendering/Walk/RetailFrameWalk.cs b/src/AcDream.App/Rendering/Walk/RetailFrameWalk.cs
index ed20b5ac..621db244 100644
--- a/src/AcDream.App/Rendering/Walk/RetailFrameWalk.cs
+++ b/src/AcDream.App/Rendering/Walk/RetailFrameWalk.cs
@@ -177,7 +177,10 @@ public sealed class RetailFrameWalk
// outdoor-static turn.
if (block.CellBuildings[cellIndex] is WalkBuilding building)
DrawBuilding(building, activeViews, ctx, sink);
- sink.OnLandscapeCellTurn((block.LandblockId & 0xFFFF0000u) | (uint)(cellIndex + 1));
+ sink.OnLandscapeCellTurn(
+ block.LandblockId,
+ block.SideCellCount,
+ cellIndex);
}
}
}
diff --git a/src/AcDream.App/Rendering/Walk/WalkEvents.cs b/src/AcDream.App/Rendering/Walk/WalkEvents.cs
index cae598ab..b57a1185 100644
--- a/src/AcDream.App/Rendering/Walk/WalkEvents.cs
+++ b/src/AcDream.App/Rendering/Walk/WalkEvents.cs
@@ -99,6 +99,19 @@ public interface IWalkEventSink
///
void OnLandscapeCellTurn(uint cellId) { }
+ ///
+ /// The LOD-aware sibling of .
+ /// names one cell in the block's CURRENT
+ /// ×
+ /// coarse terrain grid. Retail attaches every outdoor object's shadow to
+ /// that coarse CObjCell; acdream retains the authoritative 8×8
+ /// owner buckets, so production consumers expand this turn back to the
+ /// covered original cells. The default preserves the FW1 vocabulary and
+ /// every diagnostic sink that only needs the coarse turn.
+ void OnLandscapeCellTurn(uint landblockId, int sideCellCount, int cellIndex) =>
+ OnLandscapeCellTurn(
+ (landblockId & 0xFFFF0000u) | checked((uint)(cellIndex + 1)));
+
///
/// Fires at once retail's own
/// gate has passed — RenderDeviceD3D::DrawBuilding @0x0059f2a0
diff --git a/src/AcDream.App/Rendering/Walk/WalkFrameDriver.cs b/src/AcDream.App/Rendering/Walk/WalkFrameDriver.cs
index 12538873..5892c3ae 100644
--- a/src/AcDream.App/Rendering/Walk/WalkFrameDriver.cs
+++ b/src/AcDream.App/Rendering/Walk/WalkFrameDriver.cs
@@ -223,6 +223,13 @@ internal enum WalkFrameEventKind : byte
/// was recorded.
StreamMark,
+ /// Submits every retained translucent walk batch through
+ /// up to the
+ /// exclusive-end index in . This is a
+ /// replay-time event: collecting the complete frame must not make alpha
+ /// from a later/near cell visible to an earlier building barrier.
+ AlphaSubmitMark,
+
/// .
Sky,
@@ -351,6 +358,9 @@ internal readonly struct WalkFrameEvent
internal static WalkFrameEvent Mark(int exclusiveEnd) =>
new(WalkFrameEventKind.StreamMark, exclusiveEnd, 0, 0f, null);
+ internal static WalkFrameEvent AlphaSubmitMark(int exclusiveEnd) =>
+ new(WalkFrameEventKind.AlphaSubmitMark, exclusiveEnd, 0, 0f, null);
+
internal static WalkFrameEvent Sky() =>
new(WalkFrameEventKind.Sky, 0, 0, 0f, null);
@@ -472,6 +482,8 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
private readonly OrderedDrawStream _stream = new();
private readonly List _events = new();
private readonly List _markPositions = new();
+ private readonly List _alphaSubmissions = new();
+ private int _alphaSubmitMark;
// Campaign FW3.4a: visited-set collection, absorbed from the renderer's
// former dedicated set-collecting sink (RetailPViewRenderer's old
@@ -603,6 +615,8 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
_stream.Reset();
_events.Clear();
_markPositions.Clear();
+ _alphaSubmissions.Clear();
+ _alphaSubmitMark = 0;
VisitedCells.Clear();
LookInCellTurns.Clear();
_lookInTurns.Clear();
@@ -736,6 +750,8 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
_stream.Reset();
_events.Clear();
_markPositions.Clear();
+ _alphaSubmissions.Clear();
+ _alphaSubmitMark = 0;
VisitedCells.Clear();
LookInCellTurns.Clear();
_lookInTurns.Clear();
@@ -764,6 +780,7 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
try
{
MarkIfGrown();
+ MarkAlphaIfGrown();
}
finally
{
@@ -804,6 +821,7 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
_dispatcher.PrepareOrderedStream(frame, _stream, _viewProjection, _markPositions);
int cursor = 0;
+ int alphaCursor = 0;
for (int i = 0; i < _events.Count; i++)
{
WalkFrameEvent e = _events[i];
@@ -817,6 +835,18 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
_dispatcher.DrawOrderedRange(encoder, cursor, count);
cursor = end;
break;
+ case WalkFrameEventKind.AlphaSubmitMark:
+ int alphaEnd = e.IntArg;
+ for (; alphaCursor < alphaEnd; alphaCursor++)
+ {
+ WbDrawDispatcher.WalkClassifiedBatch batch =
+ _alphaSubmissions[alphaCursor];
+ _dispatcher.SubmitWalkAlphaInstance(
+ in batch,
+ _cameraWorldPosition,
+ _viewProjection);
+ }
+ break;
case WalkFrameEventKind.Sky:
_leafRenderer.DrawSky();
break;
@@ -883,6 +913,8 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
_stream.Reset();
_events.Clear();
_markPositions.Clear();
+ _alphaSubmissions.Clear();
+ _alphaSubmitMark = 0;
_readyToReplay = false;
_ctx = null;
}
@@ -916,6 +948,50 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
}
void IWalkEventSink.OnLandscapeCellTurn(uint cellId)
+ => HandleLandscapeCellTurn(cellId);
+
+ void IWalkEventSink.OnLandscapeCellTurn(
+ uint landblockId,
+ int sideCellCount,
+ int cellIndex)
+ {
+ if (sideCellCount is not (1 or 2 or 4 or 8))
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(sideCellCount),
+ sideCellCount,
+ "A landscape LOD grid must be 1, 2, 4, or 8 cells per side.");
+ }
+ if ((uint)cellIndex >= (uint)(sideCellCount * sideCellCount))
+ throw new ArgumentOutOfRangeException(nameof(cellIndex));
+
+ uint blockPrefix = landblockId & 0xFFFF0000u;
+ if (sideCellCount == 8)
+ {
+ HandleLandscapeCellTurn(blockPrefix | checked((uint)(cellIndex + 1)));
+ return;
+ }
+
+ // CLandBlock's coarse DrawSortCell owns the shadows from every
+ // original 24 m land cell it covers. Our render journal deliberately
+ // retains those authoritative 8x8 owner buckets, so turn all covered
+ // buckets here while preserving the coarse cell's single walk turn.
+ int span = 8 / sideCellCount;
+ int coarseX = cellIndex / sideCellCount;
+ int coarseY = cellIndex % sideCellCount;
+ int firstX = coarseX * span;
+ int firstY = coarseY * span;
+ for (int x = firstX; x < firstX + span; x++)
+ {
+ for (int y = firstY; y < firstY + span; y++)
+ {
+ HandleLandscapeCellTurn(
+ blockPrefix | checked((uint)(x * 8 + y + 1)));
+ }
+ }
+ }
+
+ private void HandleLandscapeCellTurn(uint cellId)
{
RequireOpenFrame();
if (_landscapeViewRouteIndex < 0)
@@ -930,11 +1006,18 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
_populator.PopulateOutdoorStatics(
_stream, cellId, records.Records, records.TupleLandblockId,
_cameraWorldPosition, _viewProjection,
- this, _landscapeViewRouteIndex, _outdoorDrawnThisFrame);
+ this, _landscapeViewRouteIndex, _outdoorDrawnThisFrame,
+ _alphaSubmissions);
_populator.PopulateCellDynamics(
_stream, cellId, dynamics.Records, dynamics.TupleLandblockId,
_cameraWorldPosition, _viewProjection,
- this, _landscapeViewRouteIndex, _outdoorDrawnThisFrame);
+ this, _landscapeViewRouteIndex, _outdoorDrawnThisFrame,
+ _alphaSubmissions);
+ if (_alphaSubmissions.Count != _alphaSubmitMark)
+ {
+ MarkIfGrown();
+ MarkAlphaIfGrown();
+ }
// FW4 (the #132 positional invariant): this cell's emitter owners
// submit AT THIS TURN, so nearer buildings' pre-punch barriers
// drain them against still-true depth — see
@@ -977,6 +1060,7 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
// look-ins) follows this call; the building's own shell content is
// appended only once that pass completes (OnBuildingShellTurn).
MarkIfGrown();
+ MarkAlphaIfGrown();
_events.Add(WalkFrameEvent.AlphaBarrier());
_currentDcStage = WalkDrawStage.LookInStatic;
@@ -996,7 +1080,13 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
WalkFrameStaticRecords shell = _worldData.GetBuildingShellStatics(building);
_populator.PopulateCell(
_stream, WalkDrawStage.BuildingShell, building.PositionCellId,
- shell.Records, shell.TupleLandblockId, _cameraWorldPosition, _viewProjection);
+ shell.Records, shell.TupleLandblockId, _cameraWorldPosition, _viewProjection,
+ alphaSubmissions: _alphaSubmissions);
+ if (_alphaSubmissions.Count != _alphaSubmitMark)
+ {
+ MarkIfGrown();
+ MarkAlphaIfGrown();
+ }
// FW4 (the #132 positional invariant): the building's own shell
// emitters submit at the shell turn, after the shell content
// flushes — see WalkFrameEventKind.StaticParticles.
@@ -1166,8 +1256,9 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
_populator.PopulateCell(
_stream, stage, cellId, records.Records, records.TupleLandblockId,
_cameraWorldPosition, _viewProjection,
- this, viewRouteIndex);
+ this, viewRouteIndex, _alphaSubmissions);
MarkIfGrown();
+ MarkAlphaIfGrown();
if (stage == WalkDrawStage.LookInStatic)
{
@@ -1184,8 +1275,10 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
_cameraWorldPosition,
_viewProjection,
this,
- viewRouteIndex);
+ viewRouteIndex,
+ alphaSubmissions: _alphaSubmissions);
MarkIfGrown();
+ MarkAlphaIfGrown();
if (HasAnyOwner(records) || HasAnyOwner(dynamics))
_events.Add(WalkFrameEvent.CellParticles(cellId));
}
@@ -1364,6 +1457,20 @@ internal sealed class WalkFrameDriver : IWalkEventSink, IWalkLookInViewSource
_events.Add(WalkFrameEvent.Mark(count));
}
+ /// Records translucent walk batches at the same temporal point
+ /// as their cell turn. Replay submits them only when it reaches this
+ /// marker, so an earlier building's full alpha barrier cannot see content
+ /// that the far-to-near walk has not reached yet.
+ private void MarkAlphaIfGrown()
+ {
+ int count = _alphaSubmissions.Count;
+ if (count == _alphaSubmitMark)
+ return;
+
+ _alphaSubmitMark = count;
+ _events.Add(WalkFrameEvent.AlphaSubmitMark(count));
+ }
+
private IWalkBuildingFrameContext RequireOpenFrame() =>
_ctx ?? throw new InvalidOperationException(
"WalkFrameDriver received a walk turn outside BeginFrame/EndFrame — call "
diff --git a/src/AcDream.App/Rendering/Walk/WalkStaticStreamPopulator.cs b/src/AcDream.App/Rendering/Walk/WalkStaticStreamPopulator.cs
index ef9a51ef..b1858697 100644
--- a/src/AcDream.App/Rendering/Walk/WalkStaticStreamPopulator.cs
+++ b/src/AcDream.App/Rendering/Walk/WalkStaticStreamPopulator.cs
@@ -66,7 +66,8 @@ internal sealed class WalkStaticStreamPopulator
Vector3 cameraWorldPosition,
Matrix4x4 viewProjection,
IWalkLookInViewSource? views = null,
- int viewRouteIndex = -1)
+ int viewRouteIndex = -1,
+ List? alphaSubmissions = null)
{
ArgumentNullException.ThrowIfNull(stream);
for (int i = 0; i < records.Length; i++)
@@ -74,7 +75,7 @@ internal sealed class WalkStaticStreamPopulator
ClassifyAndAppend(
stream, stage, cellId, in records[i], tupleLandblockId,
cameraWorldPosition, viewProjection,
- liveDynamic: false, views, viewRouteIndex);
+ liveDynamic: false, views, viewRouteIndex, alphaSubmissions);
}
}
@@ -98,7 +99,8 @@ internal sealed class WalkStaticStreamPopulator
Matrix4x4 viewProjection,
IWalkLookInViewSource? views = null,
int viewRouteIndex = -1,
- ISet? drawnOnce = null)
+ ISet? drawnOnce = null,
+ List? alphaSubmissions = null)
{
ArgumentNullException.ThrowIfNull(stream);
for (int i = 0; i < records.Length; i++)
@@ -108,7 +110,7 @@ internal sealed class WalkStaticStreamPopulator
ClassifyAndAppend(
stream, WalkDrawStage.OutdoorStatic, cellId, in records[i],
tupleLandblockId, cameraWorldPosition, viewProjection,
- liveDynamic: false, views, viewRouteIndex);
+ liveDynamic: false, views, viewRouteIndex, alphaSubmissions);
}
}
@@ -121,7 +123,8 @@ internal sealed class WalkStaticStreamPopulator
Matrix4x4 viewProjection,
IWalkLookInViewSource? lookInViews = null,
int lookInRouteIndex = -1,
- ISet? drawnOnce = null)
+ ISet? drawnOnce = null,
+ List? alphaSubmissions = null)
{
ArgumentNullException.ThrowIfNull(stream);
for (int i = 0; i < records.Length; i++)
@@ -138,7 +141,8 @@ internal sealed class WalkStaticStreamPopulator
viewProjection,
liveDynamic: true,
lookInViews,
- lookInRouteIndex);
+ lookInRouteIndex,
+ alphaSubmissions);
}
}
@@ -152,7 +156,8 @@ internal sealed class WalkStaticStreamPopulator
Matrix4x4 viewProjection,
bool liveDynamic = false,
IWalkLookInViewSource? lookInViews = null,
- int lookInRouteIndex = -1)
+ int lookInRouteIndex = -1,
+ List? alphaSubmissions = null)
{
_batchScratch.Clear();
_selectionScratch.Clear();
@@ -178,8 +183,15 @@ internal sealed class WalkStaticStreamPopulator
}
else
{
- _dispatcher.SubmitWalkAlphaInstance(
- in batch, cameraWorldPosition, viewProjection);
+ if (alphaSubmissions is null)
+ {
+ _dispatcher.SubmitWalkAlphaInstance(
+ in batch, cameraWorldPosition, viewProjection);
+ }
+ else
+ {
+ alphaSubmissions.Add(batch);
+ }
}
}
diff --git a/tests/AcDream.App.Tests/Rendering/Walk/WalkFrameDriverTests.cs b/tests/AcDream.App.Tests/Rendering/Walk/WalkFrameDriverTests.cs
index 608a097c..4960f4b7 100644
--- a/tests/AcDream.App.Tests/Rendering/Walk/WalkFrameDriverTests.cs
+++ b/tests/AcDream.App.Tests/Rendering/Walk/WalkFrameDriverTests.cs
@@ -40,10 +40,13 @@ public sealed class WalkFrameDriverTests
// interleave (stream flushes interleaved with sky/terrain/shell/punch/
// alpha-barrier), not just each half in isolation. ─────────────────────
- private sealed class RecordingLeafRenderer(List log) : IWalkFrameLeafRenderer
+ private sealed class RecordingLeafRenderer(
+ List log,
+ RetailAlphaQueue? alpha = null) : IWalkFrameLeafRenderer
{
public readonly List Punches = new();
public readonly List<(uint CellId, uint ClipSlot)> Shells = new();
+ public readonly List AlphaPendingAtBarrier = new();
public void DrawSky() => log.Add("SKY");
@@ -65,7 +68,18 @@ public sealed class WalkFrameDriverTests
log.Add($"PUNCH:{worldPolygon.Vertices.Length}@v{activeViewIndex}");
}
- public void AlphaBarrier() => log.Add("ALPHA");
+ public void AlphaBarrier()
+ {
+ if (alpha is null)
+ {
+ log.Add("ALPHA");
+ return;
+ }
+
+ AlphaPendingAtBarrier.Add(alpha.PendingCount);
+ log.Add($"ALPHA:{alpha.PendingCount}");
+ alpha.Flush();
+ }
public void DrawStaticParticles(IReadOnlySet ownerIds)
{
@@ -762,6 +776,89 @@ public sealed class WalkFrameDriverTests
Assert.Equal(1u, mdi[0].DrawCount);
}
+ [Fact]
+ public void CoarseLandscapeCellTurn_ExpandsToEveryCoveredAuthoritativeOwnerCell()
+ {
+ using var fx = new DispatcherFixture();
+ const ulong gfxObj = 0x0200_0022UL;
+ InjectRenderData(fx.Manager, gfxObj, MakeFlatMesh(
+ MakeBatch(0x08100022u, TranslucencyKind.Opaque, 0, 0, 3, 1)));
+
+ var worldData = new FakeWorldData();
+ worldData.OutdoorStaticsByCell[0xE43D0040u] = new WalkFrameStaticRecords(
+ new[] { MakeRecord(304, 0, Vector3.Zero, [new MeshRef((uint)gfxObj, Matrix4x4.Identity)]) },
+ 0xE43Du);
+ var driver = new WalkFrameDriver(
+ fx.Dispatcher,
+ new RecordingLeafRenderer(new List()),
+ worldData);
+ var ctx = new TestContext();
+
+ using DrawScope draw = fx.BeginDraw();
+ driver.BeginFrame(ctx, Matrix4x4.Identity, Vector3.Zero);
+ var activeViews = new WalkPortalView();
+ WalkCopyView.AppendFullViewportQuad(
+ activeViews,
+ ctx.Rays,
+ ctx.WorldViewpoint,
+ ctx.ViewportWidth,
+ ctx.ViewportHeight);
+ ((IWalkEventSink)driver).OnLandscapeViews(activeViews);
+ // A 2x2 coarse grid's cell 3 covers authoritative x/y cells 4..7,
+ // including original cell 64. The old cellIndex+1 lookup only asked
+ // for ids 1..4 and silently lost this record.
+ ((IWalkEventSink)driver).OnLandscapeCellTurn(0xE43DFFFFu, 2, 3);
+ driver.EndFrame();
+ driver.Replay(draw.Frame, draw.Pass);
+
+ Assert.Contains(0xE43D0040u, driver.VisitedLandscapeCellIds);
+ GpuRecordedMultiDrawIndirect call = Assert.Single(
+ fx.Device.Calls.OfType());
+ Assert.Equal(1u, call.DrawCount);
+ }
+
+ [Fact]
+ public void Collect_DoesNotExposeNearAlphaToEarlierBuildingBarrier()
+ {
+ using var fx = new DispatcherFixture();
+ const ulong gfxObj = 0x0200_0023UL;
+ InjectRenderData(fx.Manager, gfxObj, MakeFlatMesh(
+ MakeBatch(0x08100023u, TranslucencyKind.AlphaBlend, 0, 0, 3, 1)));
+
+ var worldData = new FakeWorldData();
+ worldData.OutdoorStaticsByCell[0xE43D0001u] = new WalkFrameStaticRecords(
+ new[] { MakeRecord(305, 0, new Vector3(0, 0, -20), [new MeshRef((uint)gfxObj, Matrix4x4.Identity)]) },
+ 0xE43Du);
+ worldData.OutdoorStaticsByCell[0xE43D0002u] = new WalkFrameStaticRecords(
+ new[] { MakeRecord(306, 0, new Vector3(0, 0, -2), [new MeshRef((uint)gfxObj, Matrix4x4.Identity)]) },
+ 0xE43Du);
+ var log = new List();
+ var leaf = new RecordingLeafRenderer(log, fx.AlphaQueue);
+ var driver = new WalkFrameDriver(fx.Dispatcher, leaf, worldData);
+ var ctx = new TestContext();
+
+ using DrawScope draw = fx.BeginDraw(beginAlpha: true);
+ driver.BeginFrame(ctx, Matrix4x4.Identity, Vector3.Zero);
+ var activeViews = new WalkPortalView();
+ WalkCopyView.AppendFullViewportQuad(
+ activeViews,
+ ctx.Rays,
+ ctx.WorldViewpoint,
+ ctx.ViewportWidth,
+ ctx.ViewportHeight);
+ ((IWalkEventSink)driver).OnLandscapeViews(activeViews);
+ ((IWalkEventSink)driver).OnLandscapeCellTurn(0xE43D0001u);
+ ((IWalkEventSink)driver).OnBuildingTurn(new WalkBuilding());
+ ((IWalkEventSink)driver).OnLandscapeCellTurn(0xE43D0002u);
+ driver.EndFrame();
+
+ Assert.Equal(0, fx.AlphaQueue.PendingCount);
+ driver.Replay(draw.Frame, draw.Pass);
+
+ Assert.Equal(new[] { 1 }, leaf.AlphaPendingAtBarrier);
+ Assert.Equal(1, fx.AlphaQueue.PendingCount);
+ }
+
// ── Fixture (mirrors WalkStaticStreamPopulatorTests' DispatcherFixture —
// FW3.2a's own referee) ─────────────────────────────────────────────────
@@ -838,12 +935,18 @@ public sealed class WalkFrameDriverTests
{
private readonly IDisposable _publication;
private readonly IGpuPassEncoder _pass;
+ private readonly RetailAlphaQueue? _alpha;
- public DrawScope(IGpuFrame frame, IGpuPassEncoder pass, IDisposable publication)
+ public DrawScope(
+ IGpuFrame frame,
+ IGpuPassEncoder pass,
+ IDisposable publication,
+ RetailAlphaQueue? alpha = null)
{
Frame = frame;
_pass = pass;
_publication = publication;
+ _alpha = alpha;
}
public IGpuFrame Frame { get; }
@@ -852,6 +955,8 @@ public sealed class WalkFrameDriverTests
public void Dispose()
{
+ if (_alpha?.IsCollecting == true)
+ _alpha.EndFrame();
_publication.Dispose();
_pass.Dispose();
}
@@ -886,7 +991,8 @@ public sealed class WalkFrameDriverTests
_meshAdapter,
entitySpawnAdapter,
new EntityClassificationCache(),
- new AcDream.Core.Rendering.TranslucencyFadeManager());
+ new AcDream.Core.Rendering.TranslucencyFadeManager(),
+ alphaQueue: AlphaQueue);
}
public RecordingGpuDevice Device { get; }
@@ -897,10 +1003,17 @@ public sealed class WalkFrameDriverTests
public WbDrawDispatcher Dispatcher { get; }
+ public RetailAlphaQueue AlphaQueue { get; } = new();
+
public ObjectMeshManager Manager => _meshAdapter.MeshManager!;
- public DrawScope BeginDraw()
+ public DrawScope BeginDraw(bool beginAlpha = false)
{
+ if (beginAlpha)
+ {
+ Dispatcher.BeginFrame(frameSlot: 0);
+ AlphaQueue.BeginFrame();
+ }
FrameLifetime.BeginFrame();
IGpuFrame frame = FrameLifetime.CurrentFrame!;
IGpuPassEncoder pass = frame.BeginPass(
@@ -908,11 +1021,17 @@ public sealed class WalkFrameDriverTests
"fw3-2b-1-walk-frame-driver-test", Vector4.Zero, sampleCount: 1));
IDisposable publication = Scope.Publish(pass);
Device.Clear();
- return new DrawScope(frame, pass, publication);
+ return new DrawScope(
+ frame,
+ pass,
+ publication,
+ beginAlpha ? AlphaQueue : null);
}
public void Dispose()
{
+ if (AlphaQueue.IsCollecting)
+ AlphaQueue.AbortFrame();
Dispatcher.Dispose();
_meshAdapter.Dispose();
_textures.Dispose();