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
69
70
71
72
73
74
75
76
77
78
79
80
81
// src/directx/parallel_encoder.rs
//
// Send/Sync shims for parallel per-pass command-list recording. The
// render-graph executor in `directx/graph_exec.rs` fans non-composite
// passes onto rayon workers; each worker resets its assigned pass's
// allocator + cmd list, encodes its pass, and closes the cmd list. The
// main thread then submits every closed cmd list in topological pass
// order via `ExecuteCommandLists`.
//
// `DxContext` and the windows-crate COM smart pointers it stores are
// not Send/Sync in Rust's type system; Microsoft's API contract makes
// shared, read-only access to D3D12 resources thread-safe (commands
// being recorded against *separate* command lists with *separate*
// allocators is the canonical free-threaded pattern), but the windows
// crate cannot encode that without a hand claim. The wrappers below
// adopt that claim at the parallel-dispatch boundary. Workers reach
// `&DxContext` through `ParallelCtxRef::as_ctx()` for strictly
// read-only encode work; the only mutations during encode are bumps to
// `diagnostics.draw_calls_accum` (atomic), the `deformed_primed` velocity-priming
// store (atomic), and inline command-list recording into the worker's
// own dedicated cmd list (single-writer per allocator).
//
// Mirrors `metal/parallel_encoder.rs`.
use ID3D12GraphicsCommandList;
use DxContext;
// `Send` wrapper around a closed but unsubmitted command list. Workers
// in the parallel-dispatch fan-out reset their cmd list, encode their
// pass into it, close it, then hand it back to the main thread to
// submit in topological order. D3D12 command lists are free-threaded
// for "record on one thread, submit on another" once closed; the
// windows crate just lacks an auto Send impl.
pub ;
// SAFETY: Each `SendableCmdList` is owned by exactly one worker for the
// span of encoding, then moves back to the main thread for submission.
// No two threads access the inner handle simultaneously.
unsafe
// A `Send + Sync` handle to a `&DxContext` borrow. Worker closures use
// it to reach the immutable subset of `DxContext` they need while
// recording commands into their own command list.
//
// # Safety
//
// Construction takes `&'a DxContext`. 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`. D3D12 resources (root
// signatures, PSOs, descriptor heaps, mapped upload buffers, fence)
// are thread-safe for shared read per Microsoft's free-threading rules
// for ID3D12Device-derived objects.
//
// The contract is **read-only**: any mutable field access from a
// worker (RefCell::borrow_mut, Cell::set on a frame-relevant field)
// is unsound. The audited mutations during encode are limited to the
// `diagnostics.draw_calls_accum: AtomicU32` bump via `inc_draw_calls` and the
// `skinned.deformed_primed: AtomicBool` priming store in the G-buffer
// pass, both already thread-safe.
// The wrapper itself is the shared generic shim in `gfx::parallel_ctx`; this
// alias keeps the `ParallelCtxRef<'a>` spelling at the directx call sites.
pub type ParallelCtxRef<'a> = crateParallelCtxRef;
// SAFETY: see the type-level safety contract above. The contract is
// **read-only**: any mutable field access from a worker (RefCell::borrow_mut,
// Cell::set on a frame-relevant field) is unsound. The audited mutations during
// encode are limited to the `diagnostics.draw_calls_accum: AtomicU32` bump via
// `inc_draw_calls` and the `skinned.deformed_primed: AtomicBool` priming store
// in the G-buffer pass, both already thread-safe; D3D12 device-derived objects
// (root signatures, PSOs, descriptor heaps, mapped upload buffers, fence) are
// thread-safe for shared read per Microsoft's free-threading rules.
unsafe
// Index into the per-pass `pass_allocators` / `pass_cmd_lists` pools
// in `DxContext`. Layout: `frame_idx * PASS_COUNT + (PassId as usize)`.
pub