Repository navigation
Hot Path PGRAPH Bulk Packet Processing
Lifecycle: Performance - Implemented · Type: Guarded batch shortcut
State: Present in currentmain.
| Field | State at reviewed main
|
|---|---|
| Underlying optimization | Implemented |
| Advanced control |
Implemented, Process vertex packets in bulk
|
| Default | On |
| Apply boundary | Live; next eligible packet |
| Reference route | Scalar method-word processing |
| Evidence counters | No shared #94 shortcut artifact at reviewed main |
| Qualification | Valid/malformed focused coverage; historical performance evidence |
| Reviewed source |
322986f4c5502b3fa887a9605ac8cd88e9b2c579, 2026-09-14 |
| Primary history | PR #10; control integration PR #37 |
The NV2A guest submits method packets through PFIFO/PGRAPH. Some array methods contain many adjacent values whose meaning and destination are already known from the packet header. The bulk route validates the complete packet and processes its values as one bounded unit instead of repeatedly returning through the one-word dispatch path.
The optimization does not reorder guest methods and does not make malformed input acceptable. Current main first calculates whether the complete ARRAY_ELEMENT16, ARRAY_ELEMENT32, or INLINE_ARRAY packet can fit. An oversized packet is consumed and rejected before draw/index state is mutated.
The expected saving is approximately:
packet values
× avoided repeated method-dispatch/bookkeeping per value
It is most useful when titles send long non-incrementing inline vertex/index packets. Small packets, incrementing packets handled one word at a time, or workloads dominated by renderer/GPU work may show little end-to-end movement.
Bulk handling can also improve cache locality because packet metadata, destination selection, and pending draw state are resolved once for a run of related values. The hardening preflight itself is a correctness cost, not a speed claim.
PR #10 preserved the valid bulk path while moving complete capacity validation ahead of expansion or writes. Focused OpenGL/Vulkan ARRAY_ELEMENT16 and ARRAY_ELEMENT32 tests passed, scalar and bulk metadata/framebuffer results matched, and accepted-packet mean/p95 rows stayed within the defined regression gate. The PR also documented that malformed packets previously could enter assertion-backed mutation paths before the whole packet was known to fit.
This is measured xemu evidence for focused packet workloads. It is not proof that every retail title is PFIFO-dispatch limited.
Related emulator pattern: RPCS3 has a dedicated RSX FIFO parser and exposes accuracy/ordering policies because flattening or reordering command streams can improve throughput only while command boundaries remain correct: RPCS3 RSXFIFO.cpp. This is not a drop-in analogue; the PS3 command format and reservation model differ.
General batching precedent: Khronos' command-buffer performance guidance warns that many tiny command buffers add CPU cost and recommends grouping a reasonable amount of work while preserving ownership/order: Command buffer usage. xemu's packet batching occurs before host command recording, but the amortization principle is similar.
Xbox guest
PFIFO method packet and ordered values
↓
xemu PGRAPH semantics
method, width, count, pending draw/index state
↓
host implementation choice
bulk loop or scalar dispatch
↓
required result
same ordered PGRAPH state and draw input
Bulk packet processing is not an Xbox hardware mode. It is an xemu host dispatch optimization around the same guest-visible method sequence.
| Location | Role | Owner |
|---|---|---|
hw/xbox/nv2a/pgraph/pgraph.c |
Packet admission, capacity preflight, bulk/scalar dispatch | PFIFO/PGRAPH worker under existing locks |
hw/xbox/nv2a/pgraph/inline-elements.h |
Checked planning/capacity helpers | PGRAPH worker |
ui/xemu-tweaks.h |
Atomic effective-bit reader | UI publishes; worker reads |
ui/xemu-tweaks.c |
Converts saved boolean to active snapshot | UI/control thread |
| focused packet tests | Boundary, equivalence, refusal behavior | Test process |
flowchart TD
A[PFIFO dispatch unit] --> B[Decode method, count and increment mode]
B --> C{ARRAY_ELEMENT16, ARRAY_ELEMENT32 or INLINE_ARRAY?}
C -->|No| S[Normal method path]
C -->|Yes| D{Bulk control effective?}
D -->|No| R[Scalar one-word path]
D -->|Yes| E[Determine words represented by this dispatch]
E --> F[Plan pending draw values and destination capacity]
F --> G{Entire packet fits?}
G -->|No| H[Consume/drop packet and report guest error]
H --> I[No partial draw or index mutation]
G -->|Yes| J[Run method-specific bulk loop]
J --> K[Preserve value order and trace semantics]
K --> L[Publish equivalent PGRAPH state]
Incrementing packets may represent only one word at this point; non-incrementing packets can expose the remaining words as one bulk unit. The exact current helper decides the safe amount rather than assuming every header is bulkable.
flowchart TD
A[Eligible array packet] --> B[Bulk control disabled]
B --> C[Dispatch one method word]
C --> D[Run existing scalar semantic handler]
D --> E{More packet words?}
E -->|Yes| C
E -->|No| F[Equivalent final state]
The reference route intentionally pays repeated dispatch/bookkeeping. It must remain semantically useful for isolating batch-boundary defects.
| Invariant | Why required | Failure if weakened |
|---|---|---|
| Complete packet capacity known before writes | Rejection must be atomic | Partial inline/index mutation before failure |
Pending DRAW_ARRAYS work included |
Existing values consume destination capacity | Overflow or lost pending draw state |
ARRAY_ELEMENT16 accounts for two values/word |
Packet width differs from word count | Underestimated capacity |
| Guest method order preserved | PGRAPH state is ordered | Different primitive/index stream |
| Scalar/bulk handlers share semantics | Control must isolate dispatch only | Mode-dependent rendering |
| Malformed packet is consumed consistently | PFIFO GET/progress must remain coherent | Repeated fault or FIFO desynchronization |
| Trace/log sequence remains interpretable | Diagnostics compare modes | False differences from missing values |
Capacity and destination selection are not user-controllable guards. The toggle chooses bulk versus scalar only after safe admission.
The route consumes the current dispatch unit; it does not retain a reusable packet object across frames. Relevant lifetime is the pending primitive/draw state and destination buffer capacity at the moment of admission.
If the full packet cannot fit, current main takes the controlled rejection path rather than falling back after partial mutation. Valid packets can use scalar mode when the control is off. No renderer restart is required because the next packet reads the published effective bit.
A workload that benefits should show:
same guest packet/value count
+ fewer scalar method dispatches
+ larger average values per bulk dispatch
+ lower PFIFO/PGRAPH CPU bookkeeping
+ unchanged renderer draw/output
The hypothesis is weakened when packet sizes are mostly one word, the target methods are rare, bulk admission is frequently refused, or renderer/GPU cost dominates the selected window.
A hotter pgraph_method_array_bulk() after enabling bulk can be expected: work moved into the bulk loop. Compare total packet-processing CPU per fixed values, not only function rank.
A future #94 source should define at least:
| Counter | Unit | Meaning |
|---|---|---|
pgraph.array_packets |
packets | Eligible method packet headers seen |
pgraph.array_words |
words | Values represented by those packets |
pgraph.array_bulk_dispatches |
dispatches | Packets/segments handled by bulk route |
pgraph.array_scalar_dispatches |
dispatches | Words handled by reference route |
pgraph.array_capacity_rejections |
packets | Complete packets refused before mutation |
pgraph.array_element16_values |
values | Expanded 16-bit indices |
For a fixed workload, record guest packet/value totals so a lower dispatch count is not caused by less guest progress.
- Under-counting pending values or ELEMENT16 expansion can overflow the destination.
- Preparing a destination before full admission can leave partial state on rejection.
- A bulk loop can accidentally skip per-value side effects or trace order.
- Faster dispatch can expose downstream bottlenecks and increase host CPU because more guest work completes.
- Oversized packet handling is a correctness path, not a benchmark shortcut.
- RPCS3's FIFO misalignment reports are a useful warning: command flattening/reordering optimizations fail badly when packet boundaries are inferred incorrectly, even though its hardware model differs.
See PR #10 and Pull Request Lessons.
Minimum focused gates:
- Boundary cases immediately below, at, and above capacity for all three methods.
- Pending-draw plus incoming-packet capacity.
- ELEMENT16 odd/even counts and two-values-per-word accounting.
- Bulk On/Off state, framebuffer, and metadata equivalence for valid packets.
- Controlled malformed packet consumption with no destination mutation.
- Trace sequence comparison where tracing is enabled.
- OpenGL and Vulkan focused workloads, because PGRAPH feeds both.
- Release same-binary route evidence and CPU per fixed packet/value count.
- Full applicable XISO/retail correctness before changing default behavior.
A default change requires repeatable end-to-end benefit beyond the synthetic packet tests and no new FIFO, assertion, or rendering divergence.
DSP tooling guide and navigation updated November 2, 2026 for experimental PR #384, source 8f51d237. Capture/replay usage and limits.
The earlier wiki-wide review was published November 1, 2026. Emulator source: b7140848. Reviewed wiki content: 1d2b671. Current status · Release availability · Publication process.
- Home
- Current status
- Stable versus main
- Implemented mechanisms
- Controls and defaults
- Measured results and limits
- Complete change register
- System
- CPU and vCPU
- Memory and DMA
- Graphics / NV2A
- APU and audio
- Timing and timers
- Platform and I/O
- Hardware observations
- Fidelity boundaries
- Methodology
- PGR2 routes
- Debugging lag / hot paths
- Tools and commands
- DSP capture, replay and fuzzing
- HLE readiness and public XISO proof
- Concurrent DSP shadow and OpenAL proof
- DSP HLE operation inventory
- DSP HLE fix examples and testing
- Development
- Upstream shortlist
- Wiki maintenance
- Research / retired work
- Optimization catalog
- Hot-path shortcuts
- Researched experiments
- Unproven proposal directory
- PR lessons
- September26 snapshot
Repository · PRs · Issues