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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! Uniform stream builders for the coordinate and vertex shaders.
//!
//! Grounded directly in Mesa's QIR and scheduled QPU disassembly, as
//! dumped by `VC4_DEBUG=qir,qpu` on a Pi 3. QIR tags every uniform
//! read with its index (`unif[N]`) — information the final QPU
//! disassembly doesn't preserve — while the scheduled listing is what
//! shows the order those reads are actually issued in.
//!
//! The stream is the matrix reordered into the sequence the shader
//! actually reads its uniforms in — *not* natural column-major order —
//! followed by four compiler-injected viewport values.
//!
//! The QPU reads uniforms through `uni`, a sequential FIFO: the Nth
//! read pops stream slot N. So the stream order is whatever order the
//! *scheduled* code issues its `uni` reads in — which is neither plain
//! matrix order nor QIR's instruction order, since scheduling reorders
//! both.
//!
//! Both permutations below are therefore derived from the scheduled
//! QPU disassembly directly, by tracing every `uni`
//! read to the accumulator it feeds and identifying each accumulator
//! from what the shader ultimately does with it. Two landmarks make
//! that unambiguous without having to trust any earlier reading:
//!
//! - The value moved to `sfu_recip` is clip `W`, so its four uniforms
//! are `mvp[3]`, `mvp[7]`, `mvp[11]`, `mvp[15]`.
//! - The three accumulators later multiplied by `vp_x_scale`,
//! `vp_y_scale` and `vp_z_scale` are clip `X`, `Y` and `Z`, giving
//! the remaining three rows.
//!
//! Worth stating plainly, since this module got it wrong twice: the
//! `unif[N]` tags printed in the *pre-scheduling* QIR are element
//! identities, not stream slots, and QIR instruction order is not
//! stream order either. Only the scheduled listing settles it. The
//! failure mode is also worth recording — a wrong permutation puts the
//! wrong element where the shader expects `mvp[15]`, which makes clip
//! `W` come out `0`, `rcp(0)` infinite, and every vertex land at
//! infinite screen coordinates. That shows up not as distorted
//! geometry but as the binner discarding every primitive as "outside
//! the viewport" (V3D performance counter source `10`), with every
//! control-list register still reporting a clean, successful bin.
//!
//! The four trailing uniforms (QIR names them
//! `vp_x_scale`/`vp_y_scale`/`vp_z_scale`/`vp_z_offset`) are not part
//! of the GL uniform array at all. They must equal the same numbers
//! [`crate::v3d::bcl`]'s `clipper_xy_scaling`/
//! `clipper_z_scale_and_offset` write into the binning control list for
//! the same frame — computed here with the same formula rather than
//! taken as a separate parameter, so the two can't drift out of sync.
/// Number of `f32` uniforms each of [`build_coordinate_shader_uniforms`]
/// and [`build_vertex_shader_uniforms`] writes: 16 matrix elements
/// plus 4 viewport values.
pub const UNIFORM_COUNT: usize = 20;
/// Builds the coordinate shader's uniform stream (`prog 0/4`'s fetch
/// order) into `cl`, returning the number of bytes written
/// (`UNIFORM_COUNT * 4`). `mvp` is the MVP matrix as a plain
/// column-major array (`glUniformMatrix4fv`'s own layout — column 0 in
/// `mvp[0..4]`, column 1 in `mvp[4..8]`, and so on). `width_px`/
/// `height_px` must match the render target
/// [`crate::v3d::bcl::BclParams`] uses for the same frame, since the
/// viewport values derived from them must agree with what the binning
/// control list's own clipper packets use.
///
/// Stream order, in terms of `mvp`'s column-major indices: `0, 4, 1,
/// 7, 5, 3, 6, 2, 8, 11, 9, 10, 15, 12, 13, 14`, then `vp_x_scale`,
/// `vp_y_scale`, `vp_z_scale`, `vp_z_offset` — see this module's
/// documentation for how this was traced out of the scheduled code.
/// Builds the vertex (render-pass) shader's uniform stream (`prog
/// 0/3`'s fetch order) into `cl`, returning the number of bytes
/// written. Same parameters and viewport-value derivation as
/// [`build_coordinate_shader_uniforms`], but *both* the matrix order
/// and the viewport-value order differ — Mesa scheduled this stage's
/// uniform reads into a different sequence.
///
/// Stream order, in terms of `mvp`'s column-major indices: `3, 7, 4,
/// 0, 5, 1, 6, 2, 11, 8, 9, 10, 15, 12, 13, 14`, then `vp_y_scale`,
/// `vp_x_scale`, `vp_z_scale`, `vp_z_offset` — note the first two are
/// swapped relative to the coordinate shader.
///
/// That swap is not a guess. In this stage's scheduled code the packed
/// screen-coordinate word is built as `ftoi ra0.16a` from
/// `ra2 * U18 / W` and `ftoi ra0.16b` from `r0 * U17 / W`; `.16a` is
/// bits 15:0, which Figure 10 defines as `Xs`, and `.16b` is bits
/// 31:16, `Ys`. So the 18th uniform scales X and the 17th scales Y.
/// The coordinate shader is the other way round — there, `U17`
/// multiplies `rb6`, which is the first VPM write and therefore `Xc`.
///
/// Getting this wrong exchanges the rendered X and Y, which mirrors
/// the image about its diagonal: a cube spinning left-to-right renders
/// as one spinning top-to-bottom. It also corrupts the depth values,
/// so early-Z rejects most fragments and the cube comes out in pieces.
/// `(vp_x_scale, vp_y_scale, vp_z_scale, vp_z_offset)` — must match
/// [`crate::v3d::bcl`]'s `clipper_xy_scaling`/
/// `clipper_z_scale_and_offset`, so computed with the identical
/// formula rather than passed in separately.
/// Raw serialization cursor, same shape as this crate's other `v3d`
/// builders.