/*
 * Copyright 2024 Valve Corporation
 * SPDX-License-Identifier: MIT
 */

#pragma once

#include <stdint.h>
#include "agx_pack.h"
#include "shader_enums.h"

struct nir_shader;

/* Matches the hardware order */
enum uvs_group {
   UVS_POSITION,
   UVS_VARYINGS,
   UVS_PSIZ,
   UVS_LAYER_VIEWPORT,
   UVS_CLIP_DIST,
   UVS_NUM_GROUP,
};

/**
 * Represents an "unlinked" UVS layout. This is computable from an unlinked
 * vertex shader without knowing the associated fragment shader. The various UVS
 * groups have fixed offsets, but the varyings within the varying group have
 * indeterminate order since we don't yet know the fragment shader interpolation
 * qualifiers.
 */
struct agx_unlinked_uvs_layout {
   /* Bit i set <===> components[i] != 0 && i != POS && i != PSIZ. For fast
    * iteration of user varyings.
    */
   uint64_t written;

   /* Fully packed data structure */
   struct agx_vdm_state_vertex_outputs_packed vdm;

   /* Partial data structure, must be merged with FS selects */
   struct agx_output_select_packed osel;

   /* Offset of each group in the UVS in words. */
   uint8_t group_offs[UVS_NUM_GROUP];

   /* Size of the UVS allocation in words. >= last group_offs element */
   uint8_t size;

   /* Size of the UVS_VARYINGS */
   uint8_t user_size;

   uint8_t pad;

   /* Number of 32-bit components written for each slot. TODO: Model 16-bit.
    *
    * Invariant: sum_{slot} (components[slot]) =
    *            group_offs[PSIZ] - group_offs[VARYINGS]
    */
   uint8_t components[VARYING_SLOT_MAX];
};
static_assert(sizeof(struct agx_unlinked_uvs_layout) == 88, "packed");

bool agx_nir_lower_uvs(struct nir_shader *s,
                       struct agx_unlinked_uvs_layout *layout);

/**
 * Represents a linked UVS layout.
 */
struct agx_varyings_vs {
   /* Associated linked hardware data structures */
   struct agx_varying_counts_packed counts_32, counts_16;

   /* If the user varying slot is written, this is the base index that the first
    * component of the slot is written to. The next components are found in the
    * next indices. Otherwise 0, aliasing position.
    */
   unsigned slots[VARYING_SLOT_MAX];
};

void agx_assign_uvs(struct agx_varyings_vs *varyings,
                    struct agx_unlinked_uvs_layout *layout, uint64_t flat_mask,
                    uint64_t linear_mask);

struct agx_varyings_fs;

void agx_link_varyings_vs_fs(void *out, struct agx_varyings_vs *vs,
                             unsigned nr_user_indices,
                             struct agx_varyings_fs *fs,
                             unsigned provoking_vertex,
                             uint8_t sprite_coord_enable,
                             bool *generate_primitive_id);
