[Gpu] Keep fallback occlusion results coherent and conservatively visible

This commit is contained in:
kuba 2026-10-04 07:32:42 +02:00
parent aaed66bf83
commit 428c03eb32
2 changed files with 14 additions and 4 deletions

View File

@ -435,15 +435,21 @@ public sealed partial class GpuCommandInterpreter
throw _host.Fatal($"The occlusion-counter dump is invalid: index=0x{eventIndex:X8} address=0x{eventAddress:X16}.");
}
// Publish visible occlusion results for each depth block.
// Until real sample queries are implemented, conservatively report visibility.
// A one-sample delta can disappear when the guest scales or reduces the counts.
const ulong visibleCounterStep = 0x2FFFFFF;
const ulong readyBit = 1UL << 63;
var result = readyBit | SyntheticOcclusionCounter;
for (var depthBlock = 0u; depthBlock < 16u; depthBlock++)
{
WriteQword(eventAddress + ((ulong)depthBlock * 2 * sizeof(ulong)), result);
// Query reducers can read a GPU-cached copy of this range. Use the same
// ordered transfer path as clears instead of changing only CPU backing.
var destination = eventAddress + ((ulong)depthBlock * 2 * sizeof(ulong));
_host.FillBuffer(destination, sizeof(uint), (uint)result, false);
_host.FillBuffer(destination + sizeof(uint), sizeof(uint), (uint)(result >> 32), false);
}
SyntheticOcclusionCounter = (SyntheticOcclusionCounter + 1) & (readyBit - 1);
SyntheticOcclusionCounter = (SyntheticOcclusionCounter + visibleCounterStep) & (readyBit - 1);
break;
}

View File

@ -325,12 +325,16 @@ public sealed class GpuCommandInterpreterLabelTests
Assert.Contains("event type is unknown", runner.RunExpectingFatal(StreamRunner.Packet(PacketOpcode.EventWrite, 0x3F)).Message);
runner.Run(StreamRunner.Packet(PacketOpcode.EventWrite, 0x39 | (1u << 8), StreamRunner.Low(Label), StreamRunner.High(Label)));
// CPU-only stores leave GPU query-reduction inputs stale. Require the
// host transfer path for both words of every counter, including ready.
Assert.Equal(32, runner.Host.Calls.Count);
Assert.All(runner.Host.Calls, call => Assert.StartsWith("fill ", call));
Assert.Equal(1UL << 63, runner.Host.ReadQword(Label));
Assert.Equal(1UL << 63, runner.Host.ReadQword(Label + 15 * 16));
Assert.Equal(0UL, runner.Host.ReadQword(Label + 8));
runner.Run(StreamRunner.Packet(PacketOpcode.EventWrite, 0x39 | (1u << 8), StreamRunner.Low(Label), StreamRunner.High(Label)));
Assert.Equal((1UL << 63) | 1, runner.Host.ReadQword(Label));
Assert.Equal((1UL << 63) | 0x2FFFFFFUL, runner.Host.ReadQword(Label));
}
[Fact]