+static bool
+find_clipvertex_and_position_outputs(nir_shader *shader,
+ nir_variable **clipvertex,
+ nir_variable **position)
+{
+ nir_foreach_shader_out_variable(var, shader) {
+ switch (var->data.location) {
+ case VARYING_SLOT_POS:
+ *position = var;
+ break;
+ case VARYING_SLOT_CLIP_VERTEX:
+ *clipvertex = var;
+ break;
+ case VARYING_SLOT_CLIP_DIST0:
+ case VARYING_SLOT_CLIP_DIST1:
+ /* if shader is already writing CLIPDIST, then
+ * there should be no user-clip-planes to deal
+ * with.
+ *
+ * We assume nir_remove_dead_variables has removed the clipdist
+ * variables if they're not written.
+ */
+ return false;
+ }
+ }
+
+ return *clipvertex || *position;
+}
+
+static nir_ssa_def *
+get_ucp(nir_builder *b, int plane,
+ const gl_state_index16 clipplane_state_tokens[][STATE_LENGTH])
+{
+ if (clipplane_state_tokens) {
+ char tmp[100];
+ snprintf(tmp, ARRAY_SIZE(tmp), "gl_ClipPlane%dMESA", plane);
+ nir_variable *var = nir_variable_create(b->shader,
+ nir_var_uniform,
+ glsl_vec4_type(),
+ tmp);
+
+ var->num_state_slots = 1;
+ var->state_slots = ralloc_array(var, nir_state_slot, 1);
+ memcpy(var->state_slots[0].tokens,
+ clipplane_state_tokens[plane],
+ sizeof(var->state_slots[0].tokens));
+ return nir_load_var(b, var);
+ } else
+ return nir_load_user_clip_plane(b, plane);
+}
+
+
+static void
+lower_clip_outputs(nir_builder *b, nir_variable *position,
+ nir_variable *clipvertex, nir_variable **out,
+ unsigned ucp_enables, bool use_vars,
+ bool use_clipdist_array,
+ const gl_state_index16 clipplane_state_tokens[][STATE_LENGTH])
+{
+ nir_ssa_def *clipdist[MAX_CLIP_PLANES];
+ nir_ssa_def *cv;
+
+ if (use_vars) {
+ cv = nir_load_var(b, clipvertex ? clipvertex : position);
+
+ if (clipvertex) {
+ clipvertex->data.mode = nir_var_shader_temp;
+ nir_fixup_deref_modes(b->shader);
+ }
+ } else {
+ if (clipvertex)
+ cv = find_output(b->shader, clipvertex->data.driver_location);
+ else {
+ assert(position);
+ cv = find_output(b->shader, position->data.driver_location);
+ }
+ }
+
+ for (int plane = 0; plane < MAX_CLIP_PLANES; plane++) {
+ if (ucp_enables & (1 << plane)) {
+ nir_ssa_def *ucp = get_ucp(b, plane, clipplane_state_tokens);
+
+ /* calculate clipdist[plane] - dot(ucp, cv): */
+ clipdist[plane] = nir_fdot4(b, ucp, cv);
+ } else {
+ /* 0.0 == don't-clip == disabled: */
+ clipdist[plane] = nir_imm_float(b, 0.0);
+ }
+ if (use_clipdist_array && plane < util_last_bit(ucp_enables)) {
+ assert(use_vars);
+ nir_deref_instr *deref;
+ deref = nir_build_deref_array_imm(b,
+ nir_build_deref_var(b, out[0]),
+ plane);
+ nir_store_deref(b, deref, clipdist[plane], 1);
+ }
+ }
+
+ if (!use_clipdist_array) {
+ if (use_vars) {
+ if (ucp_enables & 0x0f)
+ nir_store_var(b, out[0], nir_vec(b, clipdist, 4), 0xf);
+ if (ucp_enables & 0xf0)
+ nir_store_var(b, out[1], nir_vec(b, &clipdist[4], 4), 0xf);
+ } else {
+ if (ucp_enables & 0x0f)
+ store_clipdist_output(b, out[0], &clipdist[0]);
+ if (ucp_enables & 0xf0)
+ store_clipdist_output(b, out[1], &clipdist[4]);
+ }
+ }
+}
+