+ struct panfrost_shader_state *vs = &ctx->vs->variants[ctx->vs->active_variant];
+ struct panfrost_shader_state *fs = &ctx->fs->variants[ctx->fs->active_variant];
+ unsigned int num_gen_varyings = 0;
+
+ /* Allocate the varying descriptor */
+
+ size_t vs_size = sizeof(struct mali_attr_meta) * vs->tripipe->varying_count;
+ size_t fs_size = sizeof(struct mali_attr_meta) * fs->tripipe->varying_count;
+
+ struct panfrost_transfer trans = panfrost_allocate_transient(ctx,
+ vs_size + fs_size);
+
+ /*
+ * Assign ->src_offset now that we know about all the general purpose
+ * varyings that will be used by the fragment and vertex shaders.
+ */
+ for (unsigned i = 0; i < vs->tripipe->varying_count; i++) {
+ /*
+ * General purpose varyings have ->index set to 0, skip other
+ * entries.
+ */
+ if (vs->varyings[i].index)
+ continue;
+
+ vs->varyings[i].src_offset = 16 * (num_gen_varyings++);
+ }
+
+ for (unsigned i = 0; i < fs->tripipe->varying_count; i++) {
+ unsigned j;
+
+ /* If we have a point sprite replacement, handle that here. We
+ * have to translate location first. TODO: Flip y in shader.
+ * We're already keying ... just time crunch .. */
+
+ unsigned loc = fs->varyings_loc[i];
+ unsigned pnt_loc =
+ (loc >= VARYING_SLOT_VAR0) ? (loc - VARYING_SLOT_VAR0) :
+ (loc == VARYING_SLOT_PNTC) ? 8 :
+ ~0;
+
+ if (~pnt_loc && fs->point_sprite_mask & (1 << pnt_loc)) {
+ /* gl_PointCoord index by convention */
+ fs->varyings[i].index = 3;
+ fs->reads_point_coord = true;
+
+ /* Swizzle out the z/w to 0/1 */
+ fs->varyings[i].format = MALI_RG16F;
+ fs->varyings[i].swizzle =
+ panfrost_get_default_swizzle(2);
+
+ continue;
+ }
+
+ if (fs->varyings[i].index)
+ continue;
+
+ /*
+ * Re-use the VS general purpose varying pos if it exists,
+ * create a new one otherwise.
+ */
+ for (j = 0; j < vs->tripipe->varying_count; j++) {
+ if (fs->varyings_loc[i] == vs->varyings_loc[j])
+ break;
+ }
+
+ if (j < vs->tripipe->varying_count)
+ fs->varyings[i].src_offset = vs->varyings[j].src_offset;
+ else
+ fs->varyings[i].src_offset = 16 * (num_gen_varyings++);
+ }
+
+ memcpy(trans.cpu, vs->varyings, vs_size);
+ memcpy(trans.cpu + vs_size, fs->varyings, fs_size);
+
+ ctx->payload_vertex.postfix.varying_meta = trans.gpu;
+ ctx->payload_tiler.postfix.varying_meta = trans.gpu + vs_size;
+
+ /* Buffer indices must be in this order per our convention */
+ union mali_attr varyings[PIPE_MAX_ATTRIBS];
+ unsigned idx = 0;
+
+ panfrost_emit_varyings(ctx, &varyings[idx++], num_gen_varyings * 16,
+ invocation_count);
+
+ /* fp32 vec4 gl_Position */
+ ctx->payload_tiler.postfix.position_varying =
+ panfrost_emit_varyings(ctx, &varyings[idx++],
+ sizeof(float) * 4, invocation_count);
+
+
+ if (vs->writes_point_size || fs->reads_point_coord) {
+ /* fp16 vec1 gl_PointSize */
+ ctx->payload_tiler.primitive_size.pointer =
+ panfrost_emit_varyings(ctx, &varyings[idx++],
+ 2, invocation_count);
+ }
+
+ if (fs->reads_point_coord) {
+ /* Special descriptor */
+ panfrost_emit_point_coord(&varyings[idx++]);
+ }
+
+ mali_ptr varyings_p = panfrost_upload_transient(ctx, &varyings, idx * sizeof(union mali_attr));
+ ctx->payload_vertex.postfix.varyings = varyings_p;
+ ctx->payload_tiler.postfix.varyings = varyings_p;
+}
+
+static mali_ptr
+panfrost_vertex_buffer_address(struct panfrost_context *ctx, unsigned i)
+{
+ struct pipe_vertex_buffer *buf = &ctx->vertex_buffers[i];
+ struct panfrost_resource *rsrc = (struct panfrost_resource *) (buf->buffer.resource);
+
+ return rsrc->bo->gpu + buf->buffer_offset;