/*
 * Copyright © 2012 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#ifndef BLORP_PRIV_H
#define BLORP_PRIV_H

#include <stdint.h>
#include <string.h>

#include "common/intel_measure.h"
#include "compiler/nir/nir.h"

#include "blorp.h"

#ifdef __cplusplus
extern "C" {
#endif


#define BLORP_INLINE_PARAM_THREAD_GROUP_ID_Z_DIMENSION (0)

void blorp_init(struct blorp_context *blorp, void *driver_ctx,
                struct isl_device *isl_dev, const struct blorp_config *config);

struct blorp_compiler {
   const struct brw_compiler *brw;
   const struct elk_compiler *elk;

   const nir_shader_compiler_options *(*nir_options)(struct blorp_context *blorp,
                                                     mesa_shader_stage stage);

   struct blorp_program (*compile_fs)(struct blorp_context *blorp, void *mem_ctx,
                                      struct nir_shader *nir,
                                      bool multisample_fbo,
                                      bool is_fast_clear,
                                      bool use_repclear,
                                      const void *key, uint32_t key_size);
   struct blorp_program (*compile_vs)(struct blorp_context *blorp, void *mem_ctx,
                                      struct nir_shader *nir,
                                      const void *key, uint32_t key_size);

   struct blorp_program (*compile_cs)(struct blorp_context *blorp, void *mem_ctx,
                                      struct nir_shader *nir,
                                      const void *key, uint32_t key_size);

   bool (*ensure_sf_program)(struct blorp_batch *batch,
                             struct blorp_params *params);

   bool (*params_get_layer_offset_vs)(struct blorp_batch *batch,
                                      struct blorp_params *params);
};

/**
 * Binding table indices used by BLORP.
 */
enum {
   BLORP_RENDERBUFFER_BT_INDEX,
   BLORP_TEXTURE_BT_INDEX,
   BLORP_TEXBUF_BT_INDEX,
   BLORP_NUM_BT_ENTRIES
};

#define BLORP_SAMPLER_INDEX 0

struct blorp_surface_info
{
   bool enabled;

   /* Should we unpack the last few rows using a texel buffer? */
   bool buffer;
   uint32_t buffer_rows;

   struct isl_surf surf;
   struct blorp_address addr;

   /* Inferred page boundaries of the surface address */
   uint64_t page_base, page_limit;

   struct isl_surf aux_surf;
   struct blorp_address aux_addr;
   enum isl_aux_usage aux_usage;
   enum isl_format aux_format;

   union isl_color_value clear_color;
   struct blorp_address clear_color_addr;
   bool has_replicated_pixel;

   struct isl_view view;

   /* Z offset into a 3-D texture or slice of a 2-D array texture. */
   float z_offset;

   uint32_t tile_x_sa, tile_y_sa;
};

void
blorp_surface_info_init(struct blorp_batch *batch,
                        struct blorp_surface_info *info,
                        const struct blorp_surf *surf,
                        unsigned int level, float layer,
                        enum isl_format format, bool is_dest);
void
blorp_surf_convert_to_single_slice(const struct isl_device *isl_dev,
                                   struct blorp_surface_info *info);
void
surf_fake_rgb_with_red(const struct isl_device *isl_dev,
                       struct blorp_surface_info *info);
void
blorp_surf_convert_to_uncompressed(const struct isl_device *isl_dev,
                                   struct blorp_surface_info *info,
                                   uint32_t *x, uint32_t *y,
                                   uint32_t *width, uint32_t *height);
void
blorp_surf_fake_interleaved_msaa(const struct isl_device *isl_dev,
                                 struct blorp_surface_info *info);
void
blorp_surf_retile_w_to_y(const struct isl_device *isl_dev,
                         struct blorp_surface_info *info);


struct blorp_coord_transform
{
   float multiplier;
   float offset;
};

/**
 * Bounding rectangle telling pixel discard which pixels are to be touched.
 * This is needed in when surfaces are configured as something else what they
 * really are:
 *
 *    - writing W-tiled stencil as Y-tiled
 *    - writing interleaved multisampled as single sampled.
 *
 * See blorp_check_in_bounds().
 */
struct blorp_bounds_rect
{
   uint32_t x0;
   uint32_t x1;
   uint32_t y0;
   uint32_t y1;
};

/**
 * Grid needed for blended and scaled blits of integer formats, see
 * blorp_nir_manual_blend_bilinear().
 */
struct blorp_rect_grid
{
   float x1;
   float y1;
   float pad[2];
};

struct blorp_surf_offset {
   uint32_t x;
   uint32_t y;
};

/* Parameters used in blorp_blit.c. */
struct blorp_wm_inputs_blit
{
   struct blorp_bounds_rect bounds_rect;
   struct blorp_rect_grid rect_grid;
   struct blorp_coord_transform coord_transform[2];

   struct blorp_surf_offset src_offset;
   struct blorp_surf_offset dst_offset;

   /* (1/width, 1/height) for the source surface */
   float src_inv_size[2];

   uint32_t src_buffer_first_row;
   uint32_t src_buffer_row_pitch;

   /* Minimum layer setting works for all the textures types but texture_3d
    * for which the setting has no effect. Use the z-coordinate instead.
    */
   float src_z;
};

/* Parameters used in blorp_clear.c. */
struct blorp_wm_inputs_clear {
   uint32_t clear_color[4];
   struct blorp_bounds_rect bounds_rect;
};

/* Parameters using in blorp_indirect_copy.c */
struct blorp_wm_inputs_indirect {
   /* The address of the indirect buffer containing the information about the
    * indirect copy.
    */
   uint64_t indirect_buf_addr;

   /* How far apart the information about each copy is inside the indirect
    * buffer.
    */
   uint64_t indirect_buf_stride;

   /* How many copies we have to do. */
   uint32_t copy_count;

   /* For memory to image copies, we do a single copy per shader. This
    * represents the index of the copy to be done.
    */
   uint32_t copy_idx;

   /* How many dimensions does our image have? 1, 2 or 3. */
   uint32_t dimensions;

   /* The maximum array layer of the image. */
   uint32_t max_layer;

   /* When compressed formats are used, we pretend they are a non-compressed
    * format, of the same bpb. Since we can't maintain the exact same layout
    * of mipmap and layer offsets, we're forced to make adjustments to where X
    * and Y actually start, and are also forced to copy only one layer (or Z
    * axis position) per shader invocation.
    */
   uint32_t x_offset;
   uint32_t y_offset;
   int forced_layer_or_z;
};

struct blorp_wm_inputs
{
   union {
      struct blorp_wm_inputs_blit blit;
      struct blorp_wm_inputs_clear clear;
      struct blorp_wm_inputs_indirect indirect;
   };

   /* Note: Pad out to an integral number of registers when extending, but
    * make sure subgroup_id is the last 32-bit item.
    */
   uint32_t pad[1];
   uint32_t subgroup_id;
};

static inline nir_variable *
blorp_create_nir_input(struct nir_shader *nir,
                       const char *name,
                       const struct glsl_type *type,
                       unsigned int offset)
{
   nir_variable *input;
   if (nir->info.stage == MESA_SHADER_COMPUTE) {
      input = nir_variable_create(nir, nir_var_uniform, type, name);
      input->data.driver_location = offset;
      input->data.location = offset;
   } else {
      input = nir_variable_create(nir, nir_var_shader_in, type, name);
      input->data.location = VARYING_SLOT_VAR0 + offset / (4 * sizeof(float));
      input->data.location_frac = (offset / sizeof(float)) % 4;
   }
   if (nir->info.stage == MESA_SHADER_FRAGMENT)
      input->data.interpolation = INTERP_MODE_FLAT;
   return input;
}

#define BLORP_CREATE_NIR_INPUT(shader, name, type) \
   blorp_create_nir_input((shader), #name, (type), \
                          offsetof(struct blorp_wm_inputs, name))

struct blorp_vs_inputs {
   uint32_t base_layer;
   uint32_t _instance_id; /* Set in hardware by SGVS */
   uint32_t pad[2];
};

enum blorp_shader_type {
   BLORP_SHADER_TYPE_COPY,
   BLORP_SHADER_TYPE_BLIT,
   BLORP_SHADER_TYPE_CLEAR,
   BLORP_SHADER_TYPE_MCS_PARTIAL_RESOLVE,
   BLORP_SHADER_TYPE_LAYER_OFFSET_VS,
   BLORP_SHADER_TYPE_GFX4_SF,
   BLORP_SHADER_TYPE_COPY_INDIRECT,
};

enum blorp_shader_pipeline {
   BLORP_SHADER_PIPELINE_RENDER,
   BLORP_SHADER_PIPELINE_COMPUTE,
};

struct blorp_params
{
   enum blorp_op op;

   uint32_t x0;
   uint32_t y0;
   uint32_t x1;
   uint32_t y1;
   float z;

   struct blorp_surface_info src;
   struct blorp_surface_info dst;
   struct blorp_surface_info depth;
   struct blorp_surface_info stencil;

   uint32_t depth_format;
   uint8_t stencil_mask;
   uint8_t stencil_ref;

   bool full_surface_hiz_op;
   uint8_t color_write_disable;
   enum isl_aux_op fast_clear_op;
   enum isl_aux_op hiz_op;

   struct blorp_wm_inputs wm_inputs;
   struct blorp_vs_inputs vs_inputs;

   unsigned num_samples;
   unsigned num_draw_buffers;
   unsigned num_layers;
   bool dst_clear_color_as_input;

   bool use_pre_baked_binding_table;
   uint32_t pre_baked_binding_table_offset;

   uint32_t vs_prog_kernel;
   uint32_t sf_prog_kernel;
   uint32_t wm_prog_kernel;
   uint32_t cs_prog_kernel;

   /* These are pointers to struct {brw,elk}_stage_prog_data. */
   void *vs_prog_data;
   void *sf_prog_data;
   void *fs_prog_data;
   void *cs_prog_data;

   enum blorp_shader_type shader_type;
   enum blorp_shader_pipeline shader_pipeline;
};

enum intel_measure_snapshot_type
blorp_op_to_intel_measure_snapshot(enum blorp_op op);

const char *blorp_op_to_name(enum blorp_op op);

void blorp_params_init(struct blorp_params *params);

#pragma pack(push, 1)
struct blorp_base_key
{
   char name[8];
   enum blorp_shader_type shader_type;
   enum blorp_shader_pipeline shader_pipeline;
};
#pragma pack(pop)


/* Since keys get memcmp()'d as part of the shader cache lookup, we really
 * want to ensure that all their bytes - not just fields, but also holes and
 * padding - get properly initialized. That's why we do a memset() here.
 */
#define BLORP_KEY_INIT(_key, _shader_type, _pipeline) do { \
   __typeof(_key) *_k = &(_key); \
   memset(_k, 0, sizeof(*_k)); \
   _k->base = (struct blorp_base_key) { \
      .name = "blorp", \
      .shader_type = (_shader_type), \
      .shader_pipeline = (_pipeline), \
   }; \
} while(0)

/**
 * \name BLORP internals
 * \{
 *
 * Used internally by gfx6_blorp_exec() and gfx7_blorp_exec().
 */

bool blorp_blitter_supports_aux(const struct intel_device_info *devinfo,
                                enum isl_aux_usage aux_usage);

const char *blorp_shader_type_to_name(enum blorp_shader_type type);
const char *blorp_shader_pipeline_to_name(enum blorp_shader_pipeline pipe);

struct blorp_program {
   const void *kernel;
   uint32_t    kernel_size;

   const void *prog_data;
   uint32_t    prog_data_size;
};

static inline struct blorp_program
blorp_compile_fs(struct blorp_context *blorp, void *mem_ctx,
                 struct nir_shader *nir,
                 bool multisample_fbo,
                 bool is_fast_clear,
                 bool use_repclear,
                 const void *key, uint32_t key_size)
{
   return blorp->compiler->compile_fs(blorp, mem_ctx, nir, multisample_fbo,
                                      is_fast_clear, use_repclear,
                                      key, key_size);
}

static inline struct blorp_program
blorp_compile_vs(struct blorp_context *blorp, void *mem_ctx,
                 struct nir_shader *nir, const void *key, uint32_t key_size)
{
   return blorp->compiler->compile_vs(blorp, mem_ctx, nir, key, key_size);
}

static inline bool
blorp_ensure_sf_program(struct blorp_batch *batch,
                        struct blorp_params *params)
{
   struct blorp_compiler *c = batch->blorp->compiler;
   /* Absence of callback indicates it is not needed.  This is the case for
    * brw, which is Gfx9+.
    */
   return !c->ensure_sf_program || c->ensure_sf_program(batch, params);
}

static inline uint8_t
blorp_get_cs_local_y(struct blorp_params *params)
{
   uint32_t height = params->y1 - params->y0;
   uint32_t or_ys = params->y0 | params->y1;
   if (height > 32 || util_is_aligned(or_ys, 4)) {
      return 4;
   } else if (util_is_aligned(or_ys, 2)) {
      return 2;
   } else {
      return 1;
   }
}

static inline void
blorp_set_cs_dims(struct nir_shader *nir, uint8_t local_y)
{
   assert(local_y != 0 && (16 % local_y == 0));
   nir->info.workgroup_size[0] = 16 / local_y;
   nir->info.workgroup_size[1] = local_y;
   nir->info.workgroup_size[2] = 1;
}

static inline struct blorp_program
blorp_compile_cs(struct blorp_context *blorp, void *mem_ctx,
                 struct nir_shader *nir, const void *key, uint32_t key_size)
{
   return blorp->compiler->compile_cs(blorp, mem_ctx, nir, key, key_size);
}

static inline bool
blorp_params_get_layer_offset_vs(struct blorp_batch *batch,
                                 struct blorp_params *params)
{
   return batch->blorp->compiler->params_get_layer_offset_vs(batch, params);
}

/* This means: blorp_params->wm_inputs.blit should be used. */
static inline bool
blorp_op_type_is_blit(enum blorp_op op)
{
   switch (op) {
   case BLORP_OP_BLIT:
   case BLORP_OP_COPY:
      return true;
   default:
      return false;
   }
}

/* This means: blorp_params->wm_inputs.clear should be used. */
static inline bool
blorp_op_type_is_clear(enum blorp_op op)
{
   switch (op) {
   case BLORP_OP_CCS_AMBIGUATE:
   case BLORP_OP_CCS_COLOR_CLEAR:
   case BLORP_OP_CCS_PARTIAL_RESOLVE:
   case BLORP_OP_CCS_RESOLVE:
   case BLORP_OP_FAST_STENCIL_CLEAR:
   case BLORP_OP_HIZ_AMBIGUATE:
   case BLORP_OP_HIZ_CLEAR:
   case BLORP_OP_HIZ_RESOLVE:
   case BLORP_OP_HIZ_STENCIL_CLEAR:
   case BLORP_OP_MCS_AMBIGUATE:
   case BLORP_OP_MCS_COLOR_CLEAR:
   case BLORP_OP_MCS_PARTIAL_RESOLVE:
   case BLORP_OP_LINEAR_SURFACE_CLEAR:
   case BLORP_OP_SLOW_COLOR_CLEAR:
   case BLORP_OP_SLOW_STENCIL_CLEAR:
   case BLORP_OP_SLOW_DEPTH_STENCIL_CLEAR:
   case BLORP_OP_SLOW_DEPTH_CLEAR:
      return true;
   default:
      return false;
   }
}

/* This means: blorp->wm_inputs.indirect should be used. */
static inline bool
blorp_op_type_is_indirect(enum blorp_op op)
{
   switch (op) {
   case BLORP_OP_COPY_INDIRECT:
   case BLORP_OP_COPY_IMAGE_INDIRECT:
      return true;
   default:
      return false;
   }
}

/* Asserts unless the surface is a buffer to image copy */
#define blorp_assert_is_buffer(surf, view)                     \
   do {                                                        \
      assert((surf).dim == ISL_SURF_DIM_2D);                   \
      assert((surf).tiling == ISL_TILING_LINEAR);              \
      assert((surf).logical_level0_px.d == 1);                 \
      assert((surf).logical_level0_px.array_len == 1);         \
      assert((surf).samples == 1);                             \
      assert((surf).levels == 1);                              \
      UNUSED const struct isl_format_layout *fmtl =            \
         isl_format_get_layout((view).format);                 \
      assert((surf).row_pitch_B % (fmtl->bpb / 8) == 0);       \
   } while (false)

/** \} */

#ifdef __cplusplus
} /* end extern "C" */
#endif /* __cplusplus */

#endif /* BLORP_PRIV_H */
