Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: 5fec87731f0c6d4370f2dc4cc96bc637ccb3fe61
      
https://github.com/WebKit/WebKit/commit/5fec87731f0c6d4370f2dc4cc96bc637ccb3fe61
  Author: Jure Triglav <[email protected]>
  Date:   2026-08-17 (Mon, 17 Aug 2026)

  Changed paths:
    M Source/WebGPU/WebGPU/Queue.h
    M Source/WebGPU/WebGPU/Queue.mm
    M Source/WebGPU/WebGPU/Queue.swift

  Log Message:
  -----------
  WebGPU: stage writeBuffer uploads on the compute channel, not standalone blit 
command buffers
https://bugs.webkit.org/show_bug.cgi?id=321676
rdar://184823819

Reviewed by Mike Wyrzykowski

GPUQueue.writeBuffer stages uploads by encoding copies into a queue-owned,
blit-only MTLCommandBuffer that is committed standalone at each flush point
(Queue::finalizeBlitCommandEncoder, called from submit()). A page that
interleaves writeBuffer bursts with compute submits therefore commits a
per-frame pattern of C,SB,C,SB,C,SB,C,R (SB = staging blit CB). On Apple
silicon the AGX driver intermittently deadlocks the blit/DMA channel under
this cross-channel interleave: the newest SB completes with
kIOGPUCommandBufferCallbackErrorHang (IOGPUCommandQueueError Code=3, encoder
state Affected, Metal validation clean, queue drained between frames) and its
neighbors are discarded as innocent victims, losing the device. Traced 4/4 in
a worker+OffscreenCanvas repro (48 writes + 4 compute submits + present per
frame); coalescing the app's writes to one blit per frame measured 0/7 deaths
against an 8/10 control, implicating the standalone staging-blit interleave.

The staging command buffer's commit points cannot move: burst-k copies must be
GPU-ordered after the submit that precedes them (anti-hazard on the
destination) and before the submit that follows (true dependency), and user
command buffers arrive pre-encoded, so the copies cannot be prepended into
them either. What can move is the engine the copies run on. This change
encodes staged writeBuffer copies as dispatches of a trivial word-copy compute
kernel in the same queue-owned staging command buffer, at the same commit
points, with the same (tracked) hazard semantics - taking the per-frame hot
path off the blit/DMA channel entirely. writeBuffer's validation guarantees
4-byte alignment of offset and size and the staging suballocator is 64-byte
aligned, so the kernel covers every spec-reachable write.

The blit encoder path remains for writeTexture, clearBuffer, texture clears,
ICB trims, managed-buffer synchronization, and as a fallback if the copy
pipeline cannot be created; mixed bursts share one staging command buffer,
switching encoder types in place. Both writeBuffer backends share the channel
decision: the C++ path reaches it through Queue::stageBufferWrite, and the
Swift path, which allocates its own staging buffer, hands the resulting buffer
and byte offsets to the same Queue::encodeStagedCopy. Passing scalars rather
than a std::span keeps the Swift call free of span interop, so it introduces no
new use of 'unsafe'.

Two safety properties the staging path did not previously hold. Switching
encoder types ends the open encoder before creating the replacement, so a null
result left both encoder pointers nil while the staging command buffer still
held encoded work; finalizeBlitCommandEncoder() keys off the encoders and would
have dropped it. Encoder creation failure now commits that command buffer
instead of orphaning it, finalize commits a live staging buffer with no encoder
open, and a null compute encoder falls through to the blit encoder rather than
returning and leaving the destination stale.

The copy kernel's loop is bounded by wordCount alone, so its argument block is a
hand-maintained ABI with no compile-time link to the MSL struct. It is now a
named StagedCopyArguments with static_asserts pinning its size and every field
offset against the MSL declaration beside it, and encodeStagedCopy bounds-checks
both source and destination ranges by subtraction before dispatching.

Measured on that repro (30 s exposures, trunk f3f75e99, M3 Max, macOS 26.3):
device loss falls from 8/10 and 4/4 in controls to 2 in 21 exposures. The
remaining failures blame the staging compute command buffer with the identical
signature, so this is a mitigation and not a fix: the hazard is the standalone
per-burst staging command buffer itself, on either channel. Removing it
entirely needs deferred encoding of staged copies into the next user command
buffer, or per-resource idle tracking to widen the existing direct-memcpy fast
path. writeTexture still mints per-burst staging blits and is unchanged here.

Canonical link: https://commits.webkit.org/319291@main



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications

Reply via email to