Owns a logical Geode context over a selected GPU runtime device. More...
#include "donner/svg/renderer/geode/GeodeDevice.h"
Classes | |
| struct | ReadbackStats |
| Readback timing, completion, loss, cancellation, and resource counters. More... | |
| struct | SceneBatchBindGroupKey |
| Key identifying a scene-batch bind group by its exact buffer bindings: the shared batch uniform allocation, the geometry slab chunk, and the record span. More... | |
Public Types | |
| enum class | SnapshotReadbackPhase { WaitingForContext , ContextAcquired , MapRequested } |
| Explicit capture phases exposed only for deterministic protocol tests. | |
Public Member Functions | |
| ~GeodeDevice () | |
| Destructor releases logical resources before their runtime device and selected root. Native builds wait, bounded by lack of progress, for this context's submitted work, and a declared loss skips those waits; a Vulkan runtime device still proves its own submissions complete when it is destroyed. WebAssembly builds do not wait. | |
| GeodeDevice (const GeodeDevice &)=delete | |
| GeodeDevice & | operator= (const GeodeDevice &)=delete |
| GeodeDevice (GeodeDevice &&)=delete | |
| GeodeDevice & | operator= (GeodeDevice &&)=delete |
| uint32_t | maxTextureDimension2D () const |
| Maximum supported width or height of a 2D texture on this device. | |
| GpuWaitResult | waitForQueueIdle (std::chrono::milliseconds stallBound=kDefaultGpuWaitTimeout) const |
| Wait, bounded by lack of progress, for all submitted GPU work to complete. | |
| void | setQueueWaitResultForTesting (std::optional< GpuWaitResult > result) |
| Overrides one queue-idle result while preserving its loss side effects. Test seam. | |
| bool | isDeviceLost () const |
| True once this device has been declared lost, either by its backend or by a bounded GPU wait exceeding its deadline. Sticky: never resets. Once lost, rendering output is undefined, snapshots return empty bitmaps promptly, and this context's teardown skips its queue-idle waits; a Vulkan runtime device still proves its own submissions complete when it is destroyed. | |
| bool | isBoundToCreatingThread () const |
| Whether this device may only be used on the thread that created it: true on the browser backend, whose objects belong to the worker that made them, and false on every backend whose devices any thread may drive. A cache of idle devices hands a bound device back only to the thread that created it. | |
| void | markDeviceLost (const char *reason) const |
Declare this device lost with no wait to attribute it to. Idempotent; the first call logs reason. Called when a frame's or a filter's commands cannot be closed, submitted, or restored, and available to embedders whose own device-lost signal is not shared through the selected root. Bounded waits that give up use markDeviceLostAfterWaitTimeout instead. Const because observers treat the flag as shared diagnostic state and waits that discover a hang run through const accessors. | |
| void | markDeviceLostAfterWaitTimeout (GpuWaitSite site, std::chrono::milliseconds elapsed, const char *reason) const |
| Declare this device lost because a bounded wait exceeded its deadline, recording which wait it was and how long it actually ran. | |
| void | setSnapshotReadbackBudgetForTesting (std::chrono::milliseconds budget) |
| Override the total capture budget for deterministic cancellation/deadline tests. | |
| void | setSnapshotReadbackHookForTesting (std::function< void(SnapshotReadbackPhase)> hook) |
| Install a capture-phase test hook; it may be called concurrently by capture waiters. | |
| void | setSnapshotCaptureRuntimeHookForTesting (std::function< void(gpu::Device &)> hook) |
| Test-only access to the capture runtime while its lease is held, before it submits work. Set only while captures are quiescent; the callback must not retain the reference past the capture context's lifetime. | |
| void | recordReadback (bool usedTimedWaitAny, int pollIterations) |
| Record one readback buffer-map wait from a renderer sharing this device, whether or not the map completed. | |
| ReadbackStats | consumeReadbackStats () |
| Consume aggregate readback diagnostics for all renderers sharing this device. | |
| const std::shared_ptr< GeodeDeviceLostState > & | lostState () const |
| The sticky loss condition of the physical root this context renders through, shared with every other context over that root and with the runtime devices driving it. Retained rather than borrowed because a backend device-lost callback can outlive the context that registered it. | |
| std::shared_ptr< GeodePhysicalDeviceOwner > | physicalDeviceOwner () const |
| Shared owner of the selected backend root and the runtime device the first context over it renders through, retained by every logical context over that root. Pass it to CreateOverPhysicalDeviceOwner for a sibling context with a runtime device of its own. | |
| gpu::TextureFormat | textureFormat () const |
| Render-target texture format, chosen by the caller or defaulted to RGBA8Unorm. | |
| void | deferDestroy (gpu::BindGroup bindGroup) |
| Enqueue a bind group for deferred destruction. | |
| bool | deferDestroyTextureBacking (gpu::Texture &&texture) |
| Transfer owned texture backing to this context's thread-safe retirement mailbox. | |
| bool | deferDestroyTextureBackingAfterSerial (gpu::Texture &&texture, uint64_t serial) |
Retain a failed frame's owned texture until this context has completed serial, then destroy its backing instead of recycling it with any unsubmitted writes. | |
| void | drainFencedTextureBackings () |
| Retire completed failed-frame textures without blocking on GPU progress. | |
| std::size_t | pendingFencedTextureBackings () const |
| Number of failed-frame textures still awaiting a submission fence. | |
| void | drainDeferredTextureBackings () |
| Destroy queued texture backing on the owning rendering context or during exclusive teardown. | |
| const std::shared_ptr< GeodeHandleRetirement > & | handleRetirement () const |
| Where state kept for this context on another thread's behalf - a document's resident slabs, say - hands back this context's buffers and bind groups when it is destroyed, so they are released on this context's thread (see GeodeHandleRetirement). | |
| void | releaseRetiredHandles () |
| Releases the handles other threads retired to this context. Call on this context's thread; the renderer does at every frame boundary. | |
| GeodeHandleRetirement::HeldCounts | retiredHandleCountsForTesting () const |
| Handles retired to this context and not yet released. Test accessor. | |
| bool | retirementOutlivesOwnedRuntimeDeviceForTesting () const |
Whether this context's retirement is destroyed after the runtime device it created, as handleRetirement requires: true when handleRetirement_ is declared before ownedRuntimeDevice_. Test accessor. | |
| void | drainDeferredDestroys () |
| Drop all deferred-destroy handles, releasing their GPU resources. | |
| void | pollIdle () |
| Nonblocking owner-thread maintenance after a frame or while the renderer is idle. Drains cross-thread mailboxes, pumps callback-driven backends once, and releases completed work. No new submission or queue wait is required. | |
| bool | hasIdleWork () const |
| Whether idle maintenance can still free resources when a completion arrives. Owner-thread only; event loops may use it to schedule a short maintenance wake instead of rendering. | |
| void | setIdleWakeCallback (std::function< void()> callback) |
| Posts a cheap wake when a cross-thread retirement arrives. Callbacks must not reenter this context and must be cleared before their event loop is destroyed. | |
| std::size_t | deferredTextureDestroyCountForTesting () const |
| Number of texture backings waiting for the next frame-boundary destroy pass. Exposed to pin resource-retirement behavior in renderer regression tests. | |
| GpuWaitResult | waitForSnapshotCaptureIdleForTesting (std::chrono::milliseconds timeout=kDefaultGpuWaitTimeout) |
| Waits until the snapshot capture context's queue has no submitted work left, so a test can check what the owner's next release point frees once every capture has finished on the GPU, without polling for it. | |
| const gpu::BindGroup * | findSceneBatchBindGroup (const SceneBatchBindGroupKey &key) |
| Look up a cached scene-batch bind group. Returns a borrowed handle (valid while the cache entry lives), or an empty handle on miss. A hit refreshes the entry to most-recently-used, so eviction at the cap drops the coldest entry rather than the oldest-inserted one - with more than the cap of live groups, pure insertion order would evict the hottest steady-state entry every frame. | |
| const gpu::BindGroup & | storeSceneBatchBindGroup (const SceneBatchBindGroupKey &key, gpu::BindGroup group) |
| Store a scene-batch bind group under key, taking ownership of the +1 handle. At the cap, the OLDEST entries are evicted one at a time rather than the whole cache being dropped: the cache is device-wide and a key belongs to one document's buffers, so wholesale clearing punished the live document for documents that had already been torn down, and a steady frame then rebuilt every batch group it had just been using. Insertion order puts those dead entries first. Recorded command buffers keep their own references, so dropping the cache's handle mid-frame is safe either way. | |
| uint64_t | deviceId () const |
| Process-unique identity for this device instance, assigned at construction from a monotonic counter (never reused, starts at 1). | |
| void | setCounters (GeodeCounters *counters) |
| Install a GeodeCounters struct for this device. | |
| GeodeCounters * | counters () const |
| Non-owning pointer to the installed counters, or null. | |
| void | countBuffer () const |
| Record one buffer creation in lifetime and attached frame counters. | |
| void | countBindGroup () const |
| Record one bind-group creation in the attached frame counters. | |
| void | countTexture () const |
| Record one texture creation in lifetime and attached frame counters. | |
| uint64_t | lifetimeTextureCreates () const |
| Cumulative number of countTexture() calls since this GeodeDevice was created. Does not account for textures released back into a pool - it is an allocation-site counter, not a live-count. | |
| void | countTextureRelease () const |
| Record that this context's runtime device gave up its ownership of a texture allocation. | |
| uint64_t | lifetimeTextureReleases () const |
| Texture allocations whose ownership this context's runtime device has given up since the context was created (see gpu::DeviceObserver::onTextureReleased), on every backend. It excludes the snapshot-capture context, whose creations lifetimeTextureCreates() includes, so creates minus releases is not a live count. | |
| uint64_t | lifetimeBufferCreates () const |
| Cumulative number of countBuffer() calls since this GeodeDevice was created. Same caveat as lifetimeTextureCreates(). | |
| void | countSubmit () const |
| Record one queue submission in the attached frame counters. | |
| void | countCommandBuffers (uint64_t count) const |
| Record the command buffers one submission carried. | |
| void | countPathEncode () const |
| Record one path encoding in the attached frame counters. | |
| void | countDraws (uint64_t count) const |
| Record the draws a submission counted (see gpu::DeviceObserver::onSubmitted). | |
| void | countPipelineSwitch () const |
| Record one pipeline switch in the attached frame counters. | |
| void | countBufferWrite (uint64_t bytes) const |
| Record one runtime buffer write of bytes payload bytes. | |
| void | countTextureWrite (uint64_t bytes) const |
| Record one runtime texture write of bytes payload bytes. | |
| uint64_t | beginFrameGeneration () |
| Open a frame on this device and return its generation. | |
| void | endFrameGeneration (uint64_t generation) |
| Close a generation opened by beginFrameGeneration. Unknown values are ignored, so a renderer that never opened one (no-op mode) is safe. | |
| uint64_t | oldestOpenFrameGeneration () const |
| Oldest generation whose frame has not closed yet, or ~0 when no frame is open. Anything a generation at or after this touched may still be read by an unsubmitted draw. | |
| bool | frameStampClaimed (uint64_t stamp) const |
| True when a resource stamped with stamp (a generation minted by beginFrameGeneration) may still be read by a frame that has not submitted: some open frame is at or before the stamp, so a recorded draw in that frame can reference the stamped bytes, and every queue write in a frame lands before every draw in its submit. | |
| void | countGlyphResidencyHit () const |
| Record one glyph occurrence served from an already-resident outline. | |
| void | countGlyphResidencyUpload () const |
| Record one unique glyph outline encoded and made resident. | |
| void | countGlyphResidencyEvictions (uint64_t count) const |
| Record count cached glyph outlines dropped to stay inside the budget. | |
| const std::shared_ptr< std::atomic< int64_t > > & | residentBytesGauge () const |
| Shared live-resident-bytes gauge for GPU residence. Co-owned with each GeodeResidentSlot's buffer so resident-memory accounting stays lifetime-safe even if a document (and its ECS registry) outlives this device. Lazily created on first access. GeoEncoder bumps it when a slot gains residence; the slot decrements it on reset / destruction (geometry change or document teardown), which is the eviction signal for "many distinct
documents". | |
| int64_t | liveResidentBytesForTesting () const |
| Current live resident-geometry bytes across every GeodeResidentSlot buffer created against this device. Zero at construction and after all resident documents are torn down; used by eviction tests. | |
| bool | isVulkan () const |
| True when the active native backend is Vulkan. GeodeFilterEngine uses this to force inter-pass serialization that eliminates a nondeterministic cross-submit storage-write -> sampled-read visibility race observed on Arc Vulkan. Metal returns false and keeps the fast multi-submit path. | |
| gpu::Device & | runtimeDevice () const |
| This context's GPU runtime device: the owner of its handle tables, submission serials, and resource retirement. Renderer services that need only the runtime contract take this instead of naming the concrete backend type. | |
| const GeodeGpuContext & | gpuContext () const |
| The recording context Geode's encoders record a frame against: this device's GPU runtime device, its shared bind-slot resources, and its counter sinks. Wired once with the shared pipelines and owned here, so it lives exactly as long as this device does. | |
| gpu::DeviceObserver & | runtimeCounterObserverForTesting () const |
| The observer this context installs on its own runtime device, on every backend. | |
Shared render / compute pipelines (issue #575 fix) | |
The shared bind-slot resources - 1x1 dummy pattern texture and view, 1x1 full-coverage clip-mask texture and view, their samplers, a one-element identity instance record, and a zero-filled gradient paint block - are reached through gpuContext. RendererGeode once constructed these pipeline objects per renderer instance, exhausting the GPU memory budget in image-comparison runs (issue #575). Ownership here keeps one fixed set per GeodeDevice instead. Every renderer that talks to this device shares the same pipeline objects; their state is intentionally immutable after construction (no per-draw mutation), so concurrent use from sibling renderers is safe as long as submissions are serialized by the selected runtime device. | |
| GeodePipeline & | pipeline () const |
| Slug solid-fill render pipeline. | |
| GeodeGradientPipeline & | gradientPipeline () const |
| Slug gradient-fill render pipeline. | |
| GeodeImagePipeline & | imagePipeline () const |
| Image-blit render pipeline (used by GeoEncoder::drawImage and the pattern / layer composition path). | |
| GeodeMaskPipeline & | maskPipeline () const |
| Clip-path mask render pipeline. Built lazily on first access rather than eagerly at device creation, matching the prior per-encoder lazy path - most documents don't use <clipPath> and the production WASM path avoids the cost. | |
| GeodeFilterEngine & | filterEngine () const |
| GPU filter-graph executor. Owns ~15 compute pipelines for SVG filter primitives. | |
| GeodeCheckerboardPipeline & | checkerboardPipeline () const |
| Framebuffer checkerboard underlay pipeline used by the editor's direct presentation path. Built lazily on first access - only the editor draws it, so headless/WASM consumers never pay the compile cost. | |
| GeodeCheckerboardPipeline & | checkerboardUnderlayPipeline () const |
| Destination-over variant of checkerboardPipeline, for targets that already hold composed premultiplied content and need the checkerboard placed underneath it. Built lazily and independently of the replace-blend pipeline, so a consumer only pays for the variant it actually draws. | |
| void | buildOnDemandPipelinesAheadOfUse () const |
| Builds the browser editor's on-demand pipelines at startup: the batched fill variant, the clip-path mask, the replace-blend checkerboard under each presented document, and the snapshot readback. | |
Static Public Member Functions | |
| static std::unique_ptr< GeodeDevice > | CreateHeadless (gpu::TextureFormat textureFormat=gpu::TextureFormat::RGBA8Unorm) |
| Create a headless GeodeDevice. | |
| static std::unique_ptr< GeodeDevice > | CreateOverSelectedRoot (std::shared_ptr< GeodeGpuRoot > root, gpu::TextureFormat textureFormat) |
| Creates the first logical context over a backend root the caller already selected. | |
| static std::unique_ptr< GeodeDevice > | CreateOverPhysicalDeviceOwner (std::shared_ptr< GeodePhysicalDeviceOwner > physicalDevice, gpu::TextureFormat textureFormat) |
| Creates another logical context over the selected physical owner. | |
| static int | headlessCreationCountForTesting () |
| Number of CreateHeadless calls made so far in this process. Each headless creation stands up a full physical GPU root, so hot paths must share one device instead of re-creating; tests pin that sharing by asserting this count stays flat across repeated operations. | |
| static uint64_t | AllocateBufferId () |
| Allocate a process-unique identity for a GPU buffer handle, from a monotonic counter that starts at 1 and is never reused. | |
Friends | |
| class | svg::RendererGeodeTextureSnapshot |
Owns a logical Geode context over a selected GPU runtime device.
GeodeDevice is the entry point to the Geode rendering backend. In headless mode it selects the platform's GPU root (native Metal or Vulkan, or the browser GPU runtime in WebAssembly builds) and creates a device without window system integration.
A host that selects a presentation root creates its first context with CreateOverSelectedRoot, then creates sibling contexts with CreateOverPhysicalDeviceOwner.
Typical headless usage:
auto maybeDevice = GeodeDevice::CreateHeadless();
if (!maybeDevice) {
// No GPU available.
return;
}
| struct donner::geode::GeodeDevice::ReadbackStats |
Readback timing, completion, loss, cancellation, and resource counters.
| Class Members | ||
|---|---|---|
| uint64_t | bindgroupCreates = 0 | |
| uint64_t | bufferCreates = 0 | GPU allocation and submission work performed by isolated captures. |
| uint64_t | captureCancellations = 0 | Captures cancelled while acquiring the context, mapping, or copying pixels. |
| uint64_t | captureTimeouts = 0 | Captures whose total deadline expired. |
| uint64_t | contextCreates = 0 | Lazily created readback-only contexts; independent of rendering pipelines. |
| int | count = 0 | Readback buffer-map waits run since the last consume, including waits that were cancelled or timed out. |
| bool | deviceLost = false | True once this device has been declared lost. Sticky, so every later consume keeps reporting it: a frame that never rendered has no other evidence to carry. |
| int | pollIterations = 0 | Bounded wait iterations those readbacks ran. |
| uint64_t | poolBytes = 0 | |
| uint64_t | poolEntries = 0 | Idle pooled resource sets and their logical backing bytes after the latest capture. |
| uint64_t | sharedTextureTailBytes = 0 | Bytes of this context's textures still resident because another context registered them or an export of them is alive, after this context released its own handle. No context counts that memory as its allocation, so a working set has to add it. |
| uint64_t | submits = 0 | |
| uint64_t | textureCreates = 0 | |
| int | timedOutWaitMs = 0 | Wall time that wait spent before giving up, in milliseconds. |
| GpuWaitSite | timedOutWaitSite = GpuWaitSite::None | Bounded wait that declared that loss, or None. |
| bool | usedTimedWaitAny = false | True when any of those waits blocked on the map's completion event rather than only polling for it (gpu::MapWaitKind::CompletionEvent). |
|
staticnodiscard |
Allocate a process-unique identity for a GPU buffer handle, from a monotonic counter that starts at 1 and is never reused.
Anything that outlives a buffer and still has to answer "is this the same buffer I saw before?" must compare these ids rather than backend handle addresses. The runtime may release a handle while submitted work still retains its allocation; a later allocation can reuse an address and make unrelated buffers appear identical. deviceId() exists for the same reason one level up.
0 is reserved for "no buffer", so a default-constructed id never matches a real one.
|
inline |
Open a frame on this device and return its generation.
Generations are DEVICE-scoped and monotonic, so two renderers sharing one device cannot mint the same index. That happens routinely: an offscreen renderer built from this device renders an feImage fragment or a layer thumbnail from the same document while the outer renderer's frame is still open and has already recorded draws. With per-renderer counters the two frames alias, and a cache keyed on "was this touched in the current frame?" cannot tell the inner pass from the outer one - it would let the inner pass rewrite buffers the outer pass's recorded draws read, and every buffer write in a frame lands before every draw in that frame's submit.
Pair with endFrameGeneration. A cache that must not recycle memory an unsubmitted frame still reads compares against oldestOpenFrameGeneration() rather than against its own index.
| void donner::geode::GeodeDevice::buildOnDemandPipelinesAheadOfUse | ( | ) | const |
Builds the browser editor's on-demand pipelines at startup: the batched fill variant, the clip-path mask, the replace-blend checkerboard under each presented document, and the snapshot readback.
The destination-over checkerboard stays lazy because the editor never draws it.
The raster worker uses RGBA8Unorm targets even when the canvas uses another format. In that case, also seeds the default and batched RGBA fill pipelines on this context's runtime device and retains them for its lifetime. An RGBA context reuses its existing fill pipelines, and repeated calls reuse every seed. No extra context or draw is needed.
A pipeline built at its first use is created in order with the device's work, so on a cold shader cache every later submission waits for its compile, the next presented frame included. A backend that compiles pipelines ahead of use, such as the browser, compiles the pipelines built here off the device timeline instead, before any interaction needs them, and shares each compile with every other context over the same browser device that builds the same pipeline. That is why the snapshot readback is built on a context that never reads back: it seeds the compile the snapshot capture context finds when it first builds it.
Only the editor calls this, at startup; every other consumer keeps the lazy default.
|
inline |
Record the command buffers one submission carried.
| count | Command buffers handed to the queue together. |
|
inline |
Record the draws a submission counted (see gpu::DeviceObserver::onSubmitted).
| count | Draws counted. |
|
static |
Create a headless GeodeDevice.
Selects a GPU root with default options, so DONNER_GPU_BACKEND applies: a value that names no backend, or one this process cannot open, halts the process.
| textureFormat | Format the context's render targets and pipelines are built for. |
|
static |
Creates another logical context over the selected physical owner.
It shares the backend root and loss condition with the first context while retaining its own runtime device, handle table, submission serials, counters, and pipelines. Native and browser roots use the same shared-owner path.
| physicalDevice | Owner retained by the first context; null or lost owners are refused. |
| textureFormat | Format this context's render targets and pipelines are built for. |
|
static |
Creates the first logical context over a backend root the caller already selected.
The editor and native embed example select against their actual window surface before opening a logical device. This context takes the selected root's first runtime device.
| root | Selected backend root; must not be null. |
| textureFormat | Format the context's render targets and pipelines are built for. |
| void donner::geode::GeodeDevice::deferDestroy | ( | gpu::BindGroup | bindGroup | ) |
Enqueue a bind group for deferred destruction.
The handle is kept alive until drainDeferredDestroys() is called: a bind group evicted from a cache may still be named by draws that have been recorded but not yet replayed to the backend, and destroying it there fails those draws closed.
|
nodiscard |
Transfer owned texture backing to this context's thread-safe retirement mailbox.
Enqueuing checks only immutable device identity and never touches the resource table. The owning rendering context or exclusive teardown must drain the mailbox.
| texture | Handle consumed on success; unchanged for a null or foreign handle. |
|
nodiscard |
Retain a failed frame's owned texture until this context has completed serial, then destroy its backing instead of recycling it with any unsubmitted writes.
| texture | Owned texture handle; consumed on success. |
| serial | Last submission that could have named the texture. |
|
inline |
Process-unique identity for this device instance, assigned at construction from a monotonic counter (never reused, starts at 1).
Keys the GPU residence a document keeps per device (see GeodePerDevice), so each device that draws a document finds its own slabs and slots and never another's. Residence slots also record it, and the draw path treats a slot whose id does not match as non-resident, a defensive cross-check since GPU backends reject cross-device resources inside a render pass. A monotonic counter (rather than a raw this pointer) avoids the ABA hazard of a freed device's address being recycled by a later allocation.
| void donner::geode::GeodeDevice::drainDeferredDestroys | ( | ) |
Drop all deferred-destroy handles, releasing their GPU resources.
Called at the top of each frame (before new allocations) so resources from the previous frame's command buffer submission have had time to complete on the GPU. The runtime retires backing resources after submitted work completes.
|
inlinenodiscard |
True when a resource stamped with stamp (a generation minted by beginFrameGeneration) may still be read by a frame that has not submitted: some open frame is at or before the stamp, so a recorded draw in that frame can reference the stamped bytes, and every queue write in a frame lands before every draw in its submit.
The never-drawn sentinel ~0 used by the resident-slot stamps is explicitly NOT claimed - a naive >= would read a never-drawn slot as claimed forever, since oldestOpenFrameGeneration() is also ~0 when no frame is open.
|
inline |
Where state kept for this context on another thread's behalf - a document's resident slabs, say - hands back this context's buffers and bind groups when it is destroyed, so they are released on this context's thread (see GeodeHandleRetirement).
Shared so that state can tell whether this context still exists: it is closed when the context is destroyed.
| void donner::geode::GeodeDevice::markDeviceLost | ( | const char * | reason | ) | const |
Declare this device lost with no wait to attribute it to. Idempotent; the first call logs reason. Called when a frame's or a filter's commands cannot be closed, submitted, or restored, and available to embedders whose own device-lost signal is not shared through the selected root. Bounded waits that give up use markDeviceLostAfterWaitTimeout instead. Const because observers treat the flag as shared diagnostic state and waits that discover a hang run through const accessors.
| reason | Human-readable cause, logged once. |
| void donner::geode::GeodeDevice::markDeviceLostAfterWaitTimeout | ( | GpuWaitSite | site, |
| std::chrono::milliseconds | elapsed, | ||
| const char * | reason ) const |
Declare this device lost because a bounded wait exceeded its deadline, recording which wait it was and how long it actually ran.
Prefer this over markDeviceLost at every deadline: the attribution is what turns "rendering stopped" into a diagnosable report, and it is only available at the wait site. Loss stays sticky, and only the call that declares it records an attribution; see DeclareDeviceLostAfterWaitTimeout for why that rule is what keeps a driver-reported loss from being relabelled as a wait timeout.
| site | Which bounded wait expired. |
| elapsed | Wall time that wait spent before giving up. |
| reason | Human-readable cause, logged once like markDeviceLost. |
| void donner::geode::GeodeDevice::recordReadback | ( | bool | usedTimedWaitAny, |
| int | pollIterations ) |
Record one readback buffer-map wait from a renderer sharing this device, whether or not the map completed.
| usedTimedWaitAny | Whether any iteration of the wait blocked on the map's completion event. |
| pollIterations | Bounded wait iterations the readback ran. |
| gpu::DeviceObserver & donner::geode::GeodeDevice::runtimeCounterObserverForTesting | ( | ) | const |
The observer this context installs on its own runtime device, on every backend.
It reports what that device allocates, uploads, and submits through the count* members above, so the counters follow the same rules whichever backend renders. Texture upload bytes are what each backend hands its queue, so they can differ where a backend repacks rows.
A test that installs it on another runtime device attributes that device's work to this context as well, and removes it again before either is destroyed.
|
inline |
Install a GeodeCounters struct for this device.
Non-owning; the caller must keep the struct alive for as long as the device might increment it. Pass nullptr to disable instrumentation.
All Geode components that hold a GeodeDevice& route their per-frame hot-path allocation and submission sites through this hook.
|
inline |
Override the total capture budget for deterministic cancellation/deadline tests.
| budget | Includes context acquisition and GPU mapping. |
| void donner::geode::GeodeDevice::setSnapshotReadbackHookForTesting | ( | std::function< void(SnapshotReadbackPhase)> | hook | ) |
Install a capture-phase test hook; it may be called concurrently by capture waiters.
| hook | Hook to install; empty removes it. Set only while captures are quiescent. |
| GpuWaitResult donner::geode::GeodeDevice::waitForQueueIdle | ( | std::chrono::milliseconds | stallBound = kDefaultGpuWaitTimeout | ) | const |
Wait, bounded by lack of progress, for all submitted GPU work to complete.
Waits through this context's runtime device for its last submitted serial, rather than asking whether a queue shared with other contexts is momentarily empty. That serial completes only after everything queued ahead of it, so the wait gives up only when that work stops making progress for stallBound (see gpu::Device::waitForSerialUnlessStalled), never because a slow device's backlog is long. A stall declares the device lost, attributed to the queue-idle wait with the time the wait actually spent, and later waits return immediately. A runtime device that declares that loss itself reports it as DeviceLost rather than TimedOut.
| stallBound | Longest the work may go without progress; defaults to the shared bound. |
|
nodiscard |
Waits until the snapshot capture context's queue has no submitted work left, so a test can check what the owner's next release point frees once every capture has finished on the GPU, without polling for it.
Waits for a capture in progress to end first.
| timeout | Bound on the queue wait; a timeout is declared like any queue-idle wait. |