// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef GPU_COMMAND_BUFFER_SERVICE_PROGRAM_MANAGER_H_
#define GPU_COMMAND_BUFFER_SERVICE_PROGRAM_MANAGER_H_

#include <stddef.h>
#include <stdint.h>

#include <map>
#include <memory>
#include <string>
#include <vector>

#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "gpu/command_buffer/service/common_decoder.h"
#include "gpu/command_buffer/service/gl_utils.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/shader_manager.h"
#include "gpu/gpu_export.h"

namespace gpu {

struct GpuPreferences;

namespace gles2 {

class FeatureInfo;
class ProgramCache;
class ProgramManager;
class ProgressReporter;
class Shader;
class ShaderManager;

// This is used to track which attributes a particular program needs
// so we can verify at glDrawXXX time that every attribute is either disabled
// or if enabled that it points to a valid source.
class GPU_EXPORT Program : public base::RefCounted<Program> {
 public:
  static const int kMaxAttachedShaders = 2;

  enum VaryingsPackingOption {
    kCountOnlyStaticallyUsed,
    kCountAll
  };

  enum UniformApiType {
    kUniformNone = 0,
    kUniform1i = 1 << 0,
    kUniform2i = 1 << 1,
    kUniform3i = 1 << 2,
    kUniform4i = 1 << 3,
    kUniform1f = 1 << 4,
    kUniform2f = 1 << 5,
    kUniform3f = 1 << 6,
    kUniform4f = 1 << 7,
    kUniformMatrix2f = 1 << 8,
    kUniformMatrix3f = 1 << 9,
    kUniformMatrix4f = 1 << 10,
    kUniform1ui = 1 << 11,
    kUniform2ui = 1 << 12,
    kUniform3ui = 1 << 13,
    kUniform4ui = 1 << 14,
    kUniformMatrix2x3f = 1 << 15,
    kUniformMatrix2x4f = 1 << 16,
    kUniformMatrix3x2f = 1 << 17,
    kUniformMatrix3x4f = 1 << 18,
    kUniformMatrix4x2f = 1 << 19,
    kUniformMatrix4x3f = 1 << 20,
  };
  struct FragmentInputInfo {
    FragmentInputInfo(GLenum _type, GLuint _location)
        : type(_type), location(_location) {}
    FragmentInputInfo() : type(GL_NONE), location(0) {}
    GLenum type;
    GLuint location;
  };
  struct ProgramOutputInfo {
    ProgramOutputInfo(GLuint _color_name,
                      GLuint _index,
                      const std::string& _name)
        : color_name(_color_name), index(_index), name(_name) {}
    ProgramOutputInfo() : color_name(0), index(0) {}
    GLuint color_name;
    GLuint index;
    std::string name;
  };

  struct UniformInfo {
    UniformInfo();
    UniformInfo(const UniformInfo& other);
    UniformInfo(const std::string& client_name,
                GLint client_location_base,
                GLenum _type,
                bool _is_array,
                const std::vector<GLint>& service_locations);
    ~UniformInfo();
    bool IsSampler() const {
      switch (type) {
        case GL_SAMPLER_2D:
        case GL_SAMPLER_2D_RECT_ARB:
        case GL_SAMPLER_CUBE:
        case GL_SAMPLER_EXTERNAL_OES:
        case GL_SAMPLER_3D:
        case GL_SAMPLER_2D_SHADOW:
        case GL_SAMPLER_2D_ARRAY:
        case GL_SAMPLER_2D_ARRAY_SHADOW:
        case GL_SAMPLER_CUBE_SHADOW:
        case GL_INT_SAMPLER_2D:
        case GL_INT_SAMPLER_3D:
        case GL_INT_SAMPLER_CUBE:
        case GL_INT_SAMPLER_2D_ARRAY:
        case GL_UNSIGNED_INT_SAMPLER_2D:
        case GL_UNSIGNED_INT_SAMPLER_3D:
        case GL_UNSIGNED_INT_SAMPLER_CUBE:
        case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
          return true;
        default:
          return false;
      }
    }

    GLsizei size;
    GLenum type;
    uint32_t accepts_api_type;
    GLint fake_location_base;
    bool is_array;
    std::string name;
    std::vector<GLint> element_locations;
    std::vector<GLuint> texture_units;
  };
  struct VertexAttrib {
    VertexAttrib(GLsizei _size, GLenum _type, const std::string& _name,
                 GLint _location)
        : size(_size),
          type(_type),
          location(_location),
          name(_name) {
    }
    GLsizei size;
    GLenum type;
    GLint location;
    std::string name;
  };
  struct UniformBlockSizeInfo {
    uint32_t binding;
    uint32_t data_size;
  };

  template <typename T>
  class ShaderVariableLocationEntry {
   public:
    ShaderVariableLocationEntry()
        : shader_variable_(nullptr), inactive_(false) {}
    bool IsUnused() const { return !shader_variable_ && !inactive_; }
    bool IsInactive() const { return inactive_; }
    bool IsActive() const { return shader_variable_ != nullptr; }
    void SetInactive() {
      shader_variable_ = nullptr;
      inactive_ = true;
    }
    void SetActive(T* shader_variable) {
      DCHECK(shader_variable);
      shader_variable_ = shader_variable;
      inactive_ = false;
    }
    const T* shader_variable() const {
      DCHECK(IsActive());
      return shader_variable_;
    }
    T* shader_variable() {
      DCHECK(IsActive());
      return shader_variable_;
    }

   private:
    T* shader_variable_;  // Pointer to *_info_ vector entry.
    bool inactive_;
  };

  typedef std::vector<UniformInfo> UniformInfoVector;
  typedef std::vector<ShaderVariableLocationEntry<UniformInfo>>
      UniformLocationVector;
  typedef std::vector<VertexAttrib> AttribInfoVector;
  typedef std::vector<FragmentInputInfo> FragmentInputInfoVector;
  typedef std::vector<ShaderVariableLocationEntry<FragmentInputInfo>>
      FragmentInputLocationVector;
  typedef std::vector<ProgramOutputInfo> ProgramOutputInfoVector;
  typedef std::vector<int> SamplerIndices;
  typedef std::map<std::string, GLint> LocationMap;
  typedef std::map<std::string, std::pair<GLuint, GLuint>> LocationIndexMap;
  typedef std::vector<std::string> StringVector;

  Program(ProgramManager* manager, GLuint service_id);

  GLuint service_id() const {
    return service_id_;
  }

  const SamplerIndices& sampler_indices() {
    return sampler_indices_;
  }

  const AttribInfoVector& GetAttribInfos() const {
    return attrib_infos_;
  }

  const VertexAttrib* GetAttribInfo(GLint index) const {
    return (static_cast<size_t>(index) < attrib_infos_.size()) ?
       &attrib_infos_[index] : NULL;
  }

  GLint GetAttribLocation(const std::string& original_name) const;

  const VertexAttrib* GetAttribInfoByLocation(GLuint location) const {
    if (location < attrib_location_to_index_map_.size()) {
      GLint index = attrib_location_to_index_map_[location];
      if (index >= 0) {
        return &attrib_infos_[index];
      }
    }
    return NULL;
  }

  const UniformInfo* GetUniformInfo(GLint index) const;

  // If the original name is not found, return NULL.
  const std::string* GetAttribMappedName(
      const std::string& original_name) const;

  // If the original name is not found, return NULL.
  const std::string* GetUniformMappedName(
      const std::string& original_name) const;

  // If the hashed name is not found, return NULL.
  const std::string* GetOriginalNameFromHashedName(
      const std::string& hashed_name) const;

  const FragmentInputInfo* GetFragmentInputInfoByFakeLocation(
      GLint fake_location) const;

  bool IsInactiveFragmentInputLocationByFakeLocation(GLint fake_location) const;

  // Gets the fake location of a uniform by name.
  GLint GetUniformFakeLocation(const std::string& name) const;

  // Gets the UniformInfo of a uniform by location.
  const UniformInfo* GetUniformInfoByFakeLocation(
      GLint fake_location, GLint* real_location, GLint* array_index) const;

  // Returns true if |fake_location| is a location for an inactive uniform,
  // -1 for bound, non-existing uniform.
  bool IsInactiveUniformLocationByFakeLocation(GLint fake_location) const;

  // Gets the ProgramOutputInfo of a fragment output by name.
  const ProgramOutputInfo* GetProgramOutputInfo(
      const std::string& original_name) const;

  // Gets all the program info.
  void GetProgramInfo(
      ProgramManager* manager, CommonDecoder::Bucket* bucket) const;

  // Gets all the UniformBlock info.
  // Return false on overflow.
  bool GetUniformBlocks(CommonDecoder::Bucket* bucket) const;

  // Gets all the TransformFeedbackVarying info.
  // Return false on overflow.
  bool GetTransformFeedbackVaryings(CommonDecoder::Bucket* bucket) const;

  // Gather all info through glGetActiveUniformsiv, except for size, type,
  // name_length, which we gather through glGetActiveUniform in
  // glGetProgramInfoCHROMIUM.
  bool GetUniformsES3(CommonDecoder::Bucket* bucket) const;

  // Returns the fragment shader output variable color name binding.
  // Returns -1 if |original_name| is not an out variable or error.
  GLint GetFragDataLocation(const std::string& original_name) const;

  // Returns the fragment shader output variable color index binding.
  // Returns -1 if |original_name| is not an out variable or error.
  GLint GetFragDataIndex(const std::string& original_name) const;

  // Sets the sampler values for a uniform.
  // This should be called only for valid fake location pointing to
  // an active uniform.
  // If the location is not a sampler uniform nothing will happen.
  // Returns false if fake_location is a sampler and any value is >=
  // num_texture_units. Returns true otherwise.
  bool SetSamplers(
      GLint num_texture_units, GLint fake_location,
      GLsizei count, const GLint* value);

  bool IsDeleted() const {
    return deleted_;
  }

  void GetProgramiv(GLenum pname, GLint* params);

  bool IsValid() const {
    return valid_;
  }

  bool IsShaderAttached(Shader* shader);
  bool AttachShader(ShaderManager* manager, Shader* shader);
  void DetachShader(ShaderManager* manager, Shader* shader);

  void CompileAttachedShaders();
  bool AttachedShadersExist() const;
  bool CanLink() const;

  // Performs glLinkProgram and related activities.
  bool Link(ShaderManager* manager,
            VaryingsPackingOption varyings_packing_option,
            const ShaderCacheCallback& shader_callback);

  // Performs glValidateProgram and related activities.
  void Validate();

  const std::string* log_info() const {
    return log_info_.get();
  }

  bool InUse() const {
    DCHECK_GE(use_count_, 0);
    return use_count_ != 0;
  }

  // Sets attribute-location binding from a glBindAttribLocation() call.
  void SetAttribLocationBinding(const std::string& attrib, GLint location) {
    bind_attrib_location_map_[attrib] = location;
  }

  // Sets uniform-location binding from a glBindUniformLocationCHROMIUM call.
  // returns false if error.
  bool SetUniformLocationBinding(const std::string& name, GLint location);

  // Detects if the shader version combination is not valid.
  bool DetectShaderVersionMismatch() const;

  // Sets fragment input-location binding from a
  // glBindFragmentInputLocationCHROMIUM() call.
  void SetFragmentInputLocationBinding(const std::string& name, GLint location);

  // Sets program output variable location. Also sets color index to zero.
  void SetProgramOutputLocationBinding(const std::string& name,
                                       GLuint colorName);

  // Sets program output variable location and color index.
  void SetProgramOutputLocationIndexedBinding(const std::string& name,
                                              GLuint colorName,
                                              GLuint index);

  // Detects if there are attribute location conflicts from
  // glBindAttribLocation() calls.
  // We only consider the declared attributes in the program.
  bool DetectAttribLocationBindingConflicts() const;

  // Detects if there are uniform location conflicts from
  // glBindUniformLocationCHROMIUM() calls.
  // We only consider the statically used uniforms in the program.
  bool DetectUniformLocationBindingConflicts() const;

  // Detects if there are uniforms of the same name but different type
  // or precision in vertex/fragment shaders.
  // Return true and set the first found conflicting hashed name to
  // conflicting_name if such cases are detected.
  bool DetectUniformsMismatch(std::string* conflicting_name) const;

  // Detects if there are interface blocks of the same name but different
  // layouts.
  bool DetectInterfaceBlocksMismatch(std::string* conflicting_name) const;

  // Return true if a varying is statically used in fragment shader, but it
  // is not declared in vertex shader.
  bool DetectVaryingsMismatch(std::string* conflicting_name) const;

  // Detects if there are fragment input location conflicts from
  // glBindFragmentInputLocationCHROMIUM() calls.
  // We only consider the statically used fragment inputs in the program.
  bool DetectFragmentInputLocationBindingConflicts() const;

  // Detects if there are program output location conflicts from
  // glBindFragDataLocation and ..LocationIndexedEXT calls.
  // We only consider the statically used program outputs in the program.
  bool DetectProgramOutputLocationBindingConflicts() const;

  // Return true if any built-in invariant matching rules are broken as in
  // GLSL ES spec 1.00.17, section 4.6.4, Invariance and Linkage.
  bool DetectBuiltInInvariantConflicts() const;

  // Return true if an uniform and an attribute share the same name.
  bool DetectGlobalNameConflicts(std::string* conflicting_name) const;

  // Return false if varyings can't be packed into the max available
  // varying registers.
  bool CheckVaryingsPacking(VaryingsPackingOption option) const;

  void TransformFeedbackVaryings(GLsizei count, const char* const* varyings,
      GLenum buffer_mode);

  // Visible for testing
  const LocationMap& bind_attrib_location_map() const {
    return bind_attrib_location_map_;
  }

  const std::vector<std::string>& effective_transform_feedback_varyings()
      const {
    return effective_transform_feedback_varyings_;
  }

  GLenum effective_transform_feedback_buffer_mode() const {
    return effective_transform_feedback_buffer_mode_;
  }

  // See member declaration for details.
  // The data are only valid after a successful link.
  uint32_t fragment_output_type_mask() const {
    return fragment_output_type_mask_;
  }
  uint32_t fragment_output_written_mask() const {
    return fragment_output_written_mask_;
  }

  // The data are only valid after a successful link.
  const std::vector<uint32_t>& vertex_input_base_type_mask() const {
    return vertex_input_base_type_mask_;
  }
  const std::vector<uint32_t>& vertex_input_active_mask() const {
    return vertex_input_active_mask_;
  }

  // Update uniform block binding after a successful glUniformBlockBinding().
  void SetUniformBlockBinding(GLuint index, GLuint binding);

  const std::vector<UniformBlockSizeInfo>& uniform_block_size_info() const {
    return uniform_block_size_info_;
  }

  // Return the transform feedback varying sizes (per vertex).
  // Note that if the bufferMode is GL_INTERLEAVED_ATTRIBS, then there is only
  // one entry and it is the sum of all varying sizes.
  const std::vector<GLsizeiptr>& GetTransformFeedbackVaryingSizes() const {
    return transform_feedback_data_size_per_vertex_;
  }

 private:
  friend class base::RefCounted<Program>;
  friend class ProgramManager;

  ~Program();

  void set_log_info(const char* str) {
    log_info_.reset(str ? new std::string(str) : NULL);
  }

  void ClearLinkStatus() {
    link_status_ = false;
  }

  void IncUseCount() {
    ++use_count_;
  }

  void DecUseCount() {
    --use_count_;
    DCHECK_GE(use_count_, 0);
  }

  void MarkAsDeleted() {
    DCHECK(!deleted_);
    deleted_ =  true;
  }

  // Resets the program.
  void Reset();

  // Updates the program info after a successful link.
  void Update();
  void UpdateUniforms();
  void UpdateFragmentInputs();
  void UpdateProgramOutputs();
  void UpdateFragmentOutputBaseTypes();
  void UpdateVertexInputBaseTypes();
  void UpdateUniformBlockSizeInfo();
  void UpdateTransformFeedbackInfo();

  // Process the program log, replacing the hashed names with original names.
  std::string ProcessLogInfo(const std::string& log);

  // Updates the program log info from GL
  void UpdateLogInfo();

  // Clears all the uniforms.
  void ClearUniforms(std::vector<uint8_t>* zero_buffer);

  // If long attribate names are mapped during shader translation, call
  // glBindAttribLocation() again with the mapped names.
  // This is called right before the glLink() call, but after shaders are
  // translated.
  void ExecuteBindAttribLocationCalls();

  // The names of transform feedback varyings need to be hashed just
  // like bound attributes' locations, just before the link call.
  // Returns false upon failure.
  bool ExecuteTransformFeedbackVaryingsCall();

  void ExecuteProgramOutputBindCalls();

  // Query VertexAttrib data returned by ANGLE translator by the mapped name.
  void GetVertexAttribData(
      const std::string& name, std::string* original_name, GLenum* type) const;

  void DetachShaders(ShaderManager* manager);

  static inline size_t GetUniformLocationIndexFromFakeLocation(
      GLint fake_location) {
    return static_cast<size_t>(fake_location & 0xFFFF);
  }

  static inline size_t GetArrayElementIndexFromFakeLocation(
      GLint fake_location) {
    return static_cast<size_t>((fake_location >> 16) & 0xFFFF);
  }

  const FeatureInfo& feature_info() const;

  void ClearVertexInputMasks();

  ProgramManager* manager_;

  int use_count_;

  GLsizei max_attrib_name_length_;

  // Attrib by index.
  AttribInfoVector attrib_infos_;

  // Attrib by location to index.
  std::vector<GLint> attrib_location_to_index_map_;

  GLsizei max_uniform_name_length_;

  // Uniform info by index.
  UniformInfoVector uniform_infos_;
  UniformLocationVector uniform_locations_;

  // The indices of the uniforms that are samplers.
  SamplerIndices sampler_indices_;

  FragmentInputInfoVector fragment_input_infos_;
  FragmentInputLocationVector fragment_input_locations_;

  ProgramOutputInfoVector program_output_infos_;

  // The program this Program is tracking.
  GLuint service_id_;

  // Shaders by type of shader.
  scoped_refptr<Shader> attached_shaders_[kMaxAttachedShaders];

  // True if this program is marked as deleted.
  bool deleted_;

  // This is true if glLinkProgram was successful at least once.
  bool valid_;

  // This is true if glLinkProgram was successful last time it was called.
  bool link_status_;

  // True if the uniforms have been cleared.
  bool uniforms_cleared_;

  // Log info
  std::unique_ptr<std::string> log_info_;

  // attribute-location binding map from glBindAttribLocation() calls.
  LocationMap bind_attrib_location_map_;

  // uniform-location binding map from glBindUniformLocationCHROMIUM() calls.
  LocationMap bind_uniform_location_map_;

  // Set by glTransformFeedbackVaryings().
  std::vector<std::string> transform_feedback_varyings_;
  GLenum transform_feedback_buffer_mode_;

  // After a successful link.
  std::vector<std::string> effective_transform_feedback_varyings_;
  GLenum effective_transform_feedback_buffer_mode_;
  // If buffer mode is INTERLEVED, there is only one entry; otherwise there
  // might be multiple entries, one per transform feedback varying.
  // The size requirement is per vertex. Total minimum buffer size requirment
  // is calculated at DrawArrays{Instanced} time by multiplying vertex count.
  std::vector<GLsizeiptr> transform_feedback_data_size_per_vertex_;

  // Fragment input-location binding map from
  // glBindFragmentInputLocationCHROMIUM() calls.
  LocationMap bind_fragment_input_location_map_;

  // output variable - (location,index) binding map from
  // glBindFragDataLocation() and ..IndexedEXT() calls.
  LocationIndexMap bind_program_output_location_index_map_;

  // It's stored in the order of uniform block indices, i.e., the first
  // entry is the info about UniformBlock with index 0, etc.
  std::vector<UniformBlockSizeInfo> uniform_block_size_info_;

  // Fragment output variable base types: FLOAT, INT, or UINT.
  // We have up to 16 outputs, each is encoded into 2 bits, total 32 bits:
  // the lowest 2 bits for location 0, the highest 2 bits for location 15.
  uint32_t fragment_output_type_mask_;
  // Same layout as above, 2 bits per location, 0x03 if a location is occupied
  // by an output variable, 0x00 if not.
  uint32_t fragment_output_written_mask_;

  // Vertex input attrib base types: FLOAT, INT, or UINT.
  // Each base type is encoded into 2 bits, the lowest 2 bits for location 0,
  // the highest 2 bits for location (max_vertex_attribs - 1).
  std::vector<uint32_t> vertex_input_base_type_mask_;
  // Same layout as above, 2 bits per location, 0x03 if a location is set
  // by vertexAttrib API, 0x00 if not.
  std::vector<uint32_t> vertex_input_active_mask_;
};

// Tracks the Programs.
//
// NOTE: To support shared resources an instance of this class will
// need to be shared by multiple GLES2Decoders.
class GPU_EXPORT ProgramManager {
 public:
  ProgramManager(ProgramCache* program_cache,
                 uint32_t max_varying_vectors,
                 uint32_t max_draw_buffers,
                 uint32_t max_dual_source_draw_buffers,
                 uint32_t max_vertex_attribs,
                 const GpuPreferences& gpu_preferences,
                 FeatureInfo* feature_info,
                 ProgressReporter* progress_reporter);
  ~ProgramManager();

  // Must call before destruction.
  void Destroy(bool have_context);

  // Creates a new program.
  Program* CreateProgram(GLuint client_id, GLuint service_id);

  // Gets a program.
  Program* GetProgram(GLuint client_id);

  // Gets a client id for a given service id.
  bool GetClientId(GLuint service_id, GLuint* client_id) const;

  // Gets the shader cache
  ProgramCache* program_cache() const;

  // Marks a program as deleted. If it is not used the program will be deleted.
  void MarkAsDeleted(ShaderManager* shader_manager, Program* program);

  // Marks a program as used.
  void UseProgram(Program* program);

  // Makes a program as unused. If deleted the program will be removed.
  void UnuseProgram(ShaderManager* shader_manager, Program* program);

  // Clears the uniforms for this program.
  void ClearUniforms(Program* program);

  // Returns true if |name| has a prefix that is intended for GL built-in shader
  // variables.
  static bool HasBuiltInPrefix(const std::string& name);

  // Check if a Program is owned by this ProgramManager.
  bool IsOwned(Program* program) const;

  static int32_t MakeFakeLocation(int32_t index, int32_t element);

  uint32_t max_varying_vectors() const { return max_varying_vectors_; }

  uint32_t max_draw_buffers() const { return max_draw_buffers_; }

  uint32_t max_dual_source_draw_buffers() const {
    return max_dual_source_draw_buffers_;
  }

  uint32_t max_vertex_attribs() const { return max_vertex_attribs_; }

 private:
  friend class Program;

  void StartTracking(Program* program);
  void StopTracking(Program* program);

  void RemoveProgramInfoIfUnused(
      ShaderManager* shader_manager, Program* program);

  // Info for each "successfully linked" program by service side program Id.
  // TODO(gman): Choose a faster container.
  typedef std::map<GLuint, scoped_refptr<Program> > ProgramMap;
  ProgramMap programs_;

  // Counts the number of Program allocated with 'this' as its manager.
  // Allows to check no Program will outlive this.
  unsigned int program_count_;

  bool have_context_;

  // Used to clear uniforms.
  std::vector<uint8_t> zero_;

  ProgramCache* program_cache_;

  uint32_t max_varying_vectors_;
  uint32_t max_draw_buffers_;
  uint32_t max_dual_source_draw_buffers_;
  uint32_t max_vertex_attribs_;

  const GpuPreferences& gpu_preferences_;
  scoped_refptr<FeatureInfo> feature_info_;

  // Used to notify the watchdog thread of progress during destruction,
  // preventing time-outs when destruction takes a long time. May be null when
  // using in-process command buffer.
  ProgressReporter* progress_reporter_;

  DISALLOW_COPY_AND_ASSIGN(ProgramManager);
};

inline const FeatureInfo& Program::feature_info() const {
  return *manager_->feature_info_.get();
}

}  // namespace gles2
}  // namespace gpu

#endif  // GPU_COMMAND_BUFFER_SERVICE_PROGRAM_MANAGER_H_
