/* -*- mesa-c++  -*-
 * Copyright 2022 Collabora LTD
 * Author: Gert Wollny <gert.wollny@collabora.com>
 * SPDX-License-Identifier: MIT
 */

#ifndef R600_SFN_SHADER_FS_H
#define R600_SFN_SHADER_FS_H

#include "sfn_shader.h"

namespace r600 {

class FragmentShader : public Shader {
public:
   FragmentShader(const r600_shader_key& key);
   bool load_input(nir_intrinsic_instr *intr) override;
   bool store_output(nir_intrinsic_instr *intr) override;

   bool process_stage_intrinsic(nir_intrinsic_instr *intr) override;

   unsigned image_size_const_offset() override { return m_image_size_const_offset;}

protected:
   static const int s_max_interpolators = 6;
   bool interpolators_used(int i) const { return m_interpolators_used.test(i); }

private:
   bool load_interpolated_input(nir_intrinsic_instr *intr);

   virtual int allocate_interpolators_or_inputs() = 0;
   virtual bool load_input_hw(nir_intrinsic_instr *intr) = 0;
   virtual bool process_stage_intrinsic_hw(nir_intrinsic_instr *intr) = 0;
   virtual bool load_interpolated_input_hw(nir_intrinsic_instr *intr) = 0;

   bool do_scan_instruction(nir_instr *instr) override;
   int do_allocate_reserved_registers() override;

   void do_get_shader_info(r600_shader *sh_info) override;

   bool scan_input(nir_intrinsic_instr *instr, int index_src_id);

   bool emit_export_pixel(nir_intrinsic_instr& intr);
   bool emit_load_sample_mask_in(nir_intrinsic_instr *instr);
   bool emit_load_helper_invocation(nir_intrinsic_instr *instr);
   bool emit_load_sample_pos(nir_intrinsic_instr *instr);
   void do_finalize() override;

   bool read_prop(std::istream& is) override;

   void do_print_properties(std::ostream& os) const override;

   bool m_dual_source_blend{false};
   unsigned m_max_color_exports{0};
   unsigned m_export_highest{0};
   unsigned m_num_color_exports{0};
   unsigned m_color_export_mask{0};
   unsigned m_color_export_written_mask{0};
   ExportInstr *m_last_pixel_export{nullptr};

   std::bitset<s_max_interpolators> m_interpolators_used;
   RegisterVec4 m_pos_input;
   Register *m_face_input{nullptr};
   bool m_fs_write_all{false};
   bool m_uses_discard{false};
   bool m_gs_prim_id_input{false};
   Register *m_sample_id_reg{nullptr};
   Register *m_sample_mask_reg{nullptr};
   Register *m_helper_invocation{nullptr};
   int m_nsys_inputs{0};
   bool m_apply_sample_mask{false};
   int m_rat_base{0};
   int m_pos_driver_loc{0};
   int m_face_driver_loc{0};
   int m_image_size_const_offset{0};
};

class FragmentShaderR600 : public FragmentShader {
public:
   using FragmentShader::FragmentShader;

private:
   int allocate_interpolators_or_inputs() override;
   bool load_input_hw(nir_intrinsic_instr *intr) override;
   bool process_stage_intrinsic_hw(nir_intrinsic_instr *intr) override;
   bool load_interpolated_input_hw(nir_intrinsic_instr *intr) override;

   IOMap<RegisterVec4> m_interpolated_inputs;
};

class FragmentShaderEG : public FragmentShader {
public:
   using FragmentShader::FragmentShader;

private:
   class Interpolator {
   public:
      Interpolator();
      bool enabled : 1;
      unsigned ij_index : 4;
      PRegister i;
      PRegister j;
   };

   struct InterpolateParams {
      PVirtualValue i, j;
      int base;
   };

   int allocate_interpolators_or_inputs() override;
   bool load_input_hw(nir_intrinsic_instr *intr) override;
   bool process_stage_intrinsic_hw(nir_intrinsic_instr *intr) override;
   bool load_interpolated_input_hw(nir_intrinsic_instr *intr) override;

   bool load_barycentric_pixel(nir_intrinsic_instr *intr);
   bool load_barycentric_at_sample(nir_intrinsic_instr *instr);
   bool load_barycentric_at_offset(nir_intrinsic_instr *instr);
   bool load_interpolated(RegisterVec4& dest,
                          const InterpolateParams& params,
                          int num_dest_comp,
                          int start_comp);

   bool load_interpolated_one_comp(RegisterVec4& dest,
                                   const InterpolateParams& params,
                                   EAluOp op);
   bool load_interpolated_two_comp(RegisterVec4& dest,
                                   const InterpolateParams& params,
                                   EAluOp op,
                                   int writemask);
   bool load_interpolated_two_comp_for_one(RegisterVec4& dest,
                                           const InterpolateParams& params,
                                           EAluOp op,
                                           int dest_slot);

   std::array<Interpolator, s_max_interpolators> m_interpolator;
};

} // namespace r600

#endif
