Skip to content

#724 Create shared-texture exports off the render thread - #731

Merged
iamfatness merged 2 commits into
mainfrom
fix/724-export-create-off-render
Oct 1, 2026
Merged

iamfatness merged 2 commits into
mainfrom
fix/724-export-create-off-render

Conversation

@iamfatness

Copy link
Copy Markdown
Owner

Fixes the Program stall in #724 as far as the harness reproduces it.

Cause

D3DDecoupledExport owns its own D3D device. Its constructor called D3D11CreateDevice on the caller's thread, which is the render thread. That cost 13 to 21 ms every time something was exported for the first time: a clip taken to Program, a new guest, a capture device, the encoder texture at stream start. The core already logged it as [d3d-participant-create] ... cost_us=21545. Resizes reused the device; first creation did not.

Change

  • D3DDecoupledExport::Creation::Deferred: the device and textures are created on the exporter's own worker thread. Until ready(), submit() refuses and handle() is null. valid() now means "not failed", so a pending exporter is not discarded. Immediate (the default) is the old behaviour.
  • Participant tiles are created deferred. A new source's tile handle appears a frame or two late and nothing is uploaded for it until then.
  • Encoder export is created deferred as soon as a render target exists (prewarmEncoderExport, checked every render) and kept. A stream's encode path is chosen from the handle on its starting frame, so exportEncoderSharedTexture waits for readiness, bounded at 250 ms, if a stream starts inside the creation window.
  • Multiview and preview exports are unchanged.
  • CLAUDE.md section and a backlog row.

Measured

Streaming stand-in harness, node scripts/qa/youtube-same-run-av.mjs --local-standin --duration-seconds 12, this rig (RTX 4090):

runs with early missed slots slots missed participant creation on the render thread
before (cdd2e87 + #730) 5 of 5 17-18 and/or 21-22 about 17,700 to 21,500 us
after 0 of 6 none 60 to 1,500 us

Tests

  • 4 new D3DDecoupledExport cases: a deferred export is created off the caller's thread and then publishes pixels; an immediate one is still created on the caller's thread; resize while pending ends ready at the new size; destroy while pending does not hang.
  • 2 existing tests updated to allow the first frame or two without a tile handle (ParticipantBgraExportCopiesCachedPixelsAcrossDevices, SourceTextureCacheDedupsUploadsAcrossPasses).
  • Full Windows dev suite: 1365 passed, 0 failed.
  • On the changed core: validate-multiview PASS, validate-iso-record PASS. Show drill: 60.0 fps, 0 dropped, recording 60.0 fps; its source->render latency line fails the same as on main.
  • validate-gpu-encode: 3 of 5 on this branch and 3 of 5 on the cdd2e87 core. Every failure is "no [gpu-encode] path= line found" with the stream healthy; when the line is present it reads gpu-direct on this branch.

Not covered

  • The real app in a real meeting.
  • The issue reported about 100 ms and 4 to 6 slots. This harness showed two slots per event, so a longer stall may have another contributor.
  • Cost of the kept encoder export: one idle device and five Program-sized textures while nothing consumes it.
  • Multiview and preview first creation still runs on the render thread at startup.

🤖 Generated with Claude Code

claude added 2 commits October 1, 2026 17:00
D3DDecoupledExport owns its own D3D device, and its constructor created
that device on the caller's thread: the render thread, 13 to 21 ms each
time a source, the encoder texture or a tile was first exported. Program
lost two delivery slots per event.

Creation::Deferred builds the device and textures on the exporter's own
worker. Participant tiles use it and publish a frame or two late. The
encoder export is created deferred as soon as a render target exists and
kept, so a stream start finds it ready; that caller waits, bounded, only
if a stream starts inside the creation window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@iamfatness
iamfatness merged commit 43b33b9 into main Oct 1, 2026
20 checks passed
@iamfatness
iamfatness deleted the fix/724-export-create-off-render branch October 1, 2026 23:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants