Vixen
02b45cc4
csharp
public sealed class GpuVisibilityGroup

The same visibility bitset, decided by a compute dispatch instead of by the job system.

No guide page documents this yet — the page shows what the code says about itself.

Remarks

[docs/plan/06] asks for "object bounds uploaded once, frustum ... culling in compute" — this is the host half of that, and Raven/Library/Pipeline/Culling.rvn is the other. It answers exactly what VisibilityGroup answers, in exactly the same bits, which is what makes the two interchangeable at Visibility rather than merely comparable.

It is the CPU group plus a front end, not a reimplementation of it. Everything that reads an answer — Words, Hide, IsVisible — is the composed group's, and the dispatch's readback is written into that group's storage. So there is one bitset implementation in the engine and one place a consumer's assumptions can be wrong, and the fallback below costs nothing to provide.

It falls back rather than failing. A device that cannot run compute at all, no effect system, no pipeline cache, a variant that has not compiled yet, a provider that does not report layouts: each of those is a frame that still has to be drawn, and each of them culls on the CPU instead. CulledOnDevice says which happened. The first of them is what a GL or WebGL target hits on every frame, and is what doc 06 means by "the CPU path remains" — see IsSupported.

The readback is a stall, and choosing to pay it is the design. The interface promises that the bits are this frame's when Cull returns, and sorting, preparation and recording all read them within the same frame — so the alternative is not "no wait" but "an answer one frame old", which is geometry popping at the edges of a moving camera and nothing anywhere to say why. What makes it affordable is the queue it waits on: the dispatch goes to ComputeQueue, so on hardware with a real async compute queue the wait does not drain the frame being rendered. The RHI has no fence, which is why the wait is queue-wide rather than on this submission alone.

And the readback is optional. With ReadBack off there is no wait at all: the dispatch goes into the frame's own list through GpuCullingRenderer, GpuDrawArguments turns the bits into draw calls without them ever leaving the device, and what the host is handed is the conservative set — everything that could be visible — for the work list to be built from. That is the endpoint doc 06 describes, and what it trades for the stall is recording draws that turn out to be empty.

Fields and properties (12)

  • public EffectSystem? Effects

    Where the culling variant is resolved from. Null culls on the CPU.

  • public ComputePipelineCache? Pipelines

    Where the compute pipeline comes from. Null culls on the CPU.

  • public HiZPyramid? Occluders

    Last frame's depth, min-reduced, for the occlusion half of the pass. Null tests the frustum alone.

  • public bool OcclusionTested

    Whether the last Cull tested against a depth pyramid as well.

  • public bool ReadBack

    Whether the bits come back to the host, which is what the interface's promise costs.

  • public bool TwoPhase

    Whether a second dispatch is prepared each frame, to catch what the first one's stale depth could not know about.

  • public bool LatePhaseRan

    Whether the last frame's late dispatch was recorded.

  • public long ObjectBytesUploaded

    How many bytes of object records the last Cull handed to the device.

  • public int ObjectUploadRegions

    How many separate writes those bytes took.

  • public bool CulledOnDevice

    Whether the last Cull ran on the device rather than on the CPU.

  • public BufferHandle Bits

    The device-side bits, as WordSize-object words, view-major.

  • public int ViewCount

    How many views have results.

Methods (9)

  • public GpuVisibilityGroup(IGraphicsDevice device)

    Creates a group that culls on a device.

  • public bool IsVisible(int viewIndex, RenderObjectId id)

    Whether an object is visible in a view.

  • public ReadOnlySpan<ulong> Words(int viewIndex)

    The raw visibility words for a view, for a consumer that walks them itself.

  • public void Hide(int viewIndex, RenderObjectId id)

    Removes an object from a view after culling has run.

  • public int VisibleCount(int viewIndex)

    How many objects are visible in a view.

  • public void Cull(RenderObjectStore store, IReadOnlyList<RenderView> views, JobScheduler? scheduler = null)
  • public bool Record(ICommandList list)

    Records the dispatch a ReadBack-less cull left pending, into the frame's own list.

  • public bool RecordLate(ICommandList list)

    Records the second dispatch of a two-phase cull: what this frame's own depth says is visible and the main pass did not draw.

  • public void Dispose()

    Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.

Used by (9)

  • ArenaThirdPersonShooter
  • ArenaIlluminationThirdPersonShooter
  • FrameDocumentTestsThirdPersonShooter.Frame.Tests
  • CompositorAssetTestsVixen.Rendering.Tests
  • CompositorBuilderVixen.Rendering
  • GpuCullingRendererVixen.Rendering
  • GpuDrivenCompositorTestsVixen.Rendering.Tests
  • GpuVisibilityGroupTestsVixen.Rendering.Tests
  • ViewCullingDeviceTestsVixen.Graphics.Golden.Tests