1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//! Send/Sync shims for parallel per-pass command-buffer recording. The
//! render-graph executor in `metal/graph_exec.rs` fans non-composite
//! passes onto rayon workers; each worker mints its own
//! `MTLCommandBuffer`, encodes its pass, and hands the
//! encoded-but-uncommitted buffer back through a per-pass slot. The main
//! thread then commits the slots in topological pass order, so the single
//! command queue's FIFO commit order is the GPU execution order; no
//! `MTLEvent` wait/signal pairs are involved.
//!
//! `MtlContext` and the objc2 protocol objects it stores are not Send/Sync
//! in Rust's type system: Apple's API contract makes shared, read-only
//! access to Metal resources thread-safe, but objc2 cannot encode that
//! without a hand claim. The wrappers below adopt that claim at the
//! parallel-dispatch boundary. Workers reach `&MtlContext` through
//! `ParallelCtxRef::as_ctx()` for strictly read-only encode work; the
//! lone `&mut self` mutations (`diagnostics.frame_stats.draw_calls`,
//! `particle.last_elapsed`, `particle.frame_index`, the per-emitter
//! `spawn_state`) all happen on the main thread before the fan-out.
use parallel_ctx;
use Retained;
use ProtocolObject;
use MTLCommandBuffer;
use MtlContext;
// `Send` wrapper around an encoded-but-uncommitted `MTLCommandBuffer`.
// Workers in the parallel-dispatch fan-out create + encode their cmd buf,
// then hand it back to the main thread to commit in topological order.
// Apple's command buffer is safe to transfer across thread boundaries as
// long as only one thread drives the encoder at a time; objc2 just lacks
// an auto Send impl.
pub ;
// SAFETY: Each `SendableCmdBuf` is owned by exactly one worker for the
// span of encoding, then moves back to the main thread for the commit.
// No two threads access the inner `Retained` simultaneously.
unsafe
// A `Send + Sync` handle to a `&MtlContext` borrow. Worker closures use it
// to reach the immutable subset of `MtlContext` they need while encoding
// commands into their own command buffer.
//
// # Safety
//
// Construction takes `&'a MtlContext`. The wrapper is only used inside the
// parallel-encoder fan-out in `graph_exec.rs`, which joins all workers
// before the outer borrow returns. Workers perform strictly read-only field
// access; the encode helpers they call (`encode_main_pass`,
// `encode_shadow_pass`, …) all take `&self`. Apple's Metal device, queue,
// buffers, textures, and pipeline states are thread-safe for shared read.
// The wrapper itself is the shared generic shim in `gfx::parallel_ctx`; this
// alias keeps the `ParallelCtxRef<'a>` spelling at the metal call sites.
pub type ParallelCtxRef<'a> = ParallelCtxRef;
// SAFETY: see the type-level safety contract above. Workers reach `&MtlContext`
// for strictly read-only encode work; the lone `&mut self` mutations
// (`diagnostics.frame_stats.draw_calls`, `particle.last_elapsed`, `particle.frame_index`,
// the per-emitter `spawn_state`) all happen on the main thread before the
// fan-out, and Apple's Metal device, queue, buffers, textures, and pipeline
// states are thread-safe for shared read. Two pieces of state are not
// thread-safe and are never touched from a worker: `state.model_history` is a
// `RefCell` reached only through `get_mut` from `&mut self` before the fan-out,
// so a worker must never `borrow`/`borrow_mut` it; and the device allocator's
// `Rc<RefCell<Inner>>`, which creating, cloning or dropping a pooled resource
// mutates, so a pass that changes a pooled resource's lifetime must stay off
// the fan-out.
unsafe