+ state->vp_vpm_offset = vpm_offset;
+ vpm_offset += 2;
+
+ if (!c->vs_key->is_coord) {
+ state->zs_vpm_offset = vpm_offset++;
+ state->rcp_wc_vpm_offset = vpm_offset++;
+ } else {
+ state->zs_vpm_offset = -1;
+ state->rcp_wc_vpm_offset = -1;
+ }
+
+ if (c->vs_key->per_vertex_point_size)
+ state->psiz_vpm_offset = vpm_offset++;
+ else
+ state->psiz_vpm_offset = -1;
+
+ state->varyings_vpm_offset = vpm_offset;
+
+ c->vpm_output_size = vpm_offset + c->vs_key->num_fs_inputs;
+}
+
+static void
+v3d_nir_emit_ff_vpm_outputs(struct v3d_compile *c, nir_builder *b,
+ struct v3d_nir_lower_io_state *state)
+{
+ for (int i = 0; i < 4; i++) {
+ if (!state->pos[i])
+ state->pos[i] = nir_ssa_undef(b, 1, 32);
+ }
+
+ nir_ssa_def *rcp_wc = nir_frcp(b, state->pos[3]);
+
+ if (state->pos_vpm_offset != -1) {
+ for (int i = 0; i < 4; i++) {
+ v3d_nir_store_output(b, state->pos_vpm_offset + i,
+ state->pos[i]);
+ }
+ }
+
+ for (int i = 0; i < 2; i++) {
+ nir_ssa_def *pos;
+ nir_ssa_def *scale;
+ pos = state->pos[i];
+ if (i == 0)
+ scale = nir_load_viewport_x_scale(b);
+ else
+ scale = nir_load_viewport_y_scale(b);
+ pos = nir_fmul(b, pos, scale);
+ pos = nir_fmul(b, pos, rcp_wc);
+ pos = nir_f2i32(b, nir_fround_even(b, pos));
+ v3d_nir_store_output(b, state->vp_vpm_offset + i,
+ pos);
+ }
+
+ if (state->zs_vpm_offset != -1) {
+ nir_ssa_def *z = state->pos[2];
+ z = nir_fmul(b, z, nir_load_viewport_z_scale(b));
+ z = nir_fmul(b, z, rcp_wc);
+ z = nir_fadd(b, z, nir_load_viewport_z_offset(b));
+ v3d_nir_store_output(b, state->zs_vpm_offset, z);
+ }
+
+ if (state->rcp_wc_vpm_offset != -1)
+ v3d_nir_store_output(b, state->rcp_wc_vpm_offset, rcp_wc);
+
+ /* Store 0 to varyings requested by the FS but not stored in the VS.
+ * This should be undefined behavior, but glsl-routing seems to rely
+ * on it.
+ */
+ for (int i = 0; i < c->vs_key->num_fs_inputs; i++) {
+ if (!BITSET_TEST(state->varyings_stored, i)) {
+ v3d_nir_store_output(b, state->varyings_vpm_offset + i,
+ nir_imm_int(b, 0));
+ }
+ }