// 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_TEXTURE_MANAGER_H_
#define GPU_COMMAND_BUFFER_SERVICE_TEXTURE_MANAGER_H_

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

#include <algorithm>
#include <list>
#include <memory>
#include <set>
#include <string>
#include <vector>

#include "base/containers/hash_tables.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "gpu/command_buffer/service/gl_utils.h"
#include "gpu/command_buffer/service/feature_info.h"
#include "gpu/command_buffer/service/memory_tracking.h"
#include "gpu/command_buffer/service/sampler_manager.h"
#include "gpu/gpu_export.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gl/gl_image.h"

namespace gpu {
namespace gles2 {

class GLES2Decoder;
class GLStreamTextureImage;
struct ContextState;
struct DecoderFramebufferState;
class Display;
class ErrorState;
class FeatureInfo;
class FramebufferManager;
class MailboxManager;
class ProgressReporter;
class Texture;
class TextureManager;
class TextureRef;

class GPU_EXPORT TextureBase {
 public:
  explicit TextureBase(GLuint service_id);
  virtual ~TextureBase();

  // The service side OpenGL id of the texture.
  GLuint service_id() const { return service_id_; }

 protected:
  // The id of the texture.
  GLuint service_id_;

  void DeleteFromMailboxManager();

 private:
  friend class MailboxManagerSync;
  friend class MailboxManagerImpl;

  void SetMailboxManager(MailboxManager* mailbox_manager);

  MailboxManager* mailbox_manager_;
};

// A ref-counted version of the TextureBase class that deletes the texture after
// all references have been released.
class TexturePassthrough final : public TextureBase,
                                 public base::RefCounted<TexturePassthrough> {
 public:
  explicit TexturePassthrough(GLuint service_id);

  // Notify the texture that the context is lost and it shouldn't delete the
  // native GL texture in the destructor
  void MarkContextLost();

 protected:
  ~TexturePassthrough() override;

 private:
  friend class base::RefCounted<TexturePassthrough>;

  bool have_context_;

  DISALLOW_COPY_AND_ASSIGN(TexturePassthrough);
};

// Info about Textures currently in the system.
// This class wraps a real GL texture, keeping track of its meta-data. It is
// jointly owned by possibly multiple TextureRef.
class GPU_EXPORT Texture final : public TextureBase {
 public:
  enum ImageState {
    // If an image is associated with the texture and image state is UNBOUND,
    // then sampling out of the texture or using it as a target for drawing
    // will not read/write from/to the image.
    UNBOUND,
    // If image state is BOUND, then sampling from the texture will return the
    // contents of the image and using it as a target will modify the image.
    BOUND,
    // Image state is set to COPIED if the contents of the image has been
    // copied to the texture. Sampling from the texture will be equivalent
    // to sampling out the image (assuming image has not been changed since
    // it was copied). Using the texture as a target for drawing will only
    // modify the texture and not the image.
    COPIED
  };

  struct CompatibilitySwizzle {
    GLenum format;
    GLenum dest_format;
    GLenum red;
    GLenum green;
    GLenum blue;
    GLenum alpha;
  };

  explicit Texture(GLuint service_id);

  const SamplerState& sampler_state() const {
    return sampler_state_;
  }

  GLenum min_filter() const {
    return sampler_state_.min_filter;
  }

  GLenum mag_filter() const {
    return sampler_state_.mag_filter;
  }

  GLenum wrap_r() const {
    return sampler_state_.wrap_r;
  }

  GLenum wrap_s() const {
    return sampler_state_.wrap_s;
  }

  GLenum wrap_t() const {
    return sampler_state_.wrap_t;
  }

  GLenum usage() const {
    return usage_;
  }

  GLenum compare_func() const {
    return sampler_state_.compare_func;
  }

  GLenum compare_mode() const {
    return sampler_state_.compare_mode;
  }

  GLfloat max_lod() const {
    return sampler_state_.max_lod;
  }

  GLfloat min_lod() const {
    return sampler_state_.min_lod;
  }

  GLint base_level() const {
    return base_level_;
  }

  GLint max_level() const {
    return max_level_;
  }

  GLenum swizzle_r() const { return swizzle_r_; }

  GLenum swizzle_g() const { return swizzle_g_; }

  GLenum swizzle_b() const { return swizzle_b_; }

  GLenum swizzle_a() const { return swizzle_a_; }

  int num_uncleared_mips() const {
    return num_uncleared_mips_;
  }

  uint32_t estimated_size() const { return estimated_size_; }

  bool CanRenderTo(const FeatureInfo* feature_info, GLint level) const;

  void SetServiceId(GLuint service_id) {
    DCHECK(service_id);
    DCHECK_EQ(owned_service_id_, service_id_);
    service_id_ = service_id;
    owned_service_id_ = service_id;
  }

  // Returns the target this texure was first bound to or 0 if it has not
  // been bound. Once a texture is bound to a specific target it can never be
  // bound to a different target.
  GLenum target() const {
    return target_;
  }

  bool SafeToRenderFrom() const {
    return cleared_;
  }

  // Get the width/height/depth for a particular level. Returns false if level
  // does not exist.
  // |depth| is optional and can be nullptr.
  bool GetLevelSize(
      GLint target, GLint level,
      GLsizei* width, GLsizei* height, GLsizei* depth) const;

  // Get the type of a level. Returns false if level does not exist.
  bool GetLevelType(
      GLint target, GLint level, GLenum* type, GLenum* internal_format) const;

  // Set the image for a particular level. If a GLStreamTextureImage was
  // previously set with SetLevelStreamTextureImage(), this will reset
  // |service_id_| back to |owned_service_id_|, removing the service id override
  // set by the GLStreamTextureImage.
  void SetLevelImage(GLenum target,
                     GLint level,
                     gl::GLImage* image,
                     ImageState state);

  // Set the GLStreamTextureImage for a particular level.  This is like
  // SetLevelImage, but it also makes it optional to override |service_id_| with
  // a texture bound to the stream texture, and permits
  // GetLevelStreamTextureImage to return the image. See
  // SetStreamTextureServiceId() for the details of how |service_id| is used.
  void SetLevelStreamTextureImage(GLenum target,
                                  GLint level,
                                  GLStreamTextureImage* image,
                                  ImageState state,
                                  GLuint service_id);

  // Set the ImageState for the image bound to the given level.
  void SetLevelImageState(GLenum target, GLint level, ImageState state);


  // Get the image associated with a particular level. Returns NULL if level
  // does not exist.
  gl::GLImage* GetLevelImage(GLint target,
                             GLint level,
                             ImageState* state) const;
  gl::GLImage* GetLevelImage(GLint target, GLint level) const;

  // Like GetLevelImage, but will return NULL if the image wasn't set via
  // a call to SetLevelStreamTextureImage.
  GLStreamTextureImage* GetLevelStreamTextureImage(GLint target,
                                                   GLint level) const;

  bool HasImages() const {
    return has_images_;
  }

  // Returns true of the given dimensions are inside the dimensions of the
  // level.
  bool ValidForTexture(
      GLint target,
      GLint level,
      GLint xoffset,
      GLint yoffset,
      GLint zoffset,
      GLsizei width,
      GLsizei height,
      GLsizei depth) const;

  bool IsValid() const {
    return !!target();
  }

  bool IsAttachedToFramebuffer() const {
    return framebuffer_attachment_count_ != 0;
  }

  void AttachToFramebuffer() {
    ++framebuffer_attachment_count_;
  }

  void DetachFromFramebuffer() {
    DCHECK_GT(framebuffer_attachment_count_, 0);
    --framebuffer_attachment_count_;
  }

  void SetImmutable(bool immutable) {
    immutable_ = immutable;
  }

  bool IsImmutable() const {
    return immutable_;
  }

  // Return 0 if it's not immutable.
  GLint GetImmutableLevels() const;

  // Get the cleared rectangle for a particular level. Returns an empty
  // rectangle if level does not exist.
  gfx::Rect GetLevelClearedRect(GLenum target, GLint level) const;

  // Whether a particular level/face is cleared.
  bool IsLevelCleared(GLenum target, GLint level) const;
  // Whether a particular level/face is partially cleared.
  bool IsLevelPartiallyCleared(GLenum target, GLint level) const;

  // Whether the texture has been defined
  bool IsDefined() const {
    return estimated_size() > 0;
  }

  // Initialize TEXTURE_MAX_ANISOTROPY to 1 if we haven't done so yet.
  void InitTextureMaxAnisotropyIfNeeded(GLenum target);

  void DumpLevelMemory(base::trace_event::ProcessMemoryDump* pmd,
                       uint64_t client_tracing_id,
                       const std::string& dump_name) const;

  void ApplyFormatWorkarounds(FeatureInfo* feature_info);

  bool EmulatingRGB();

 private:
  friend class MailboxManagerImpl;
  friend class MailboxManagerSync;
  friend class MailboxManagerTest;
  friend class TextureDefinition;
  friend class TextureManager;
  friend class TextureRef;
  friend class TextureTestHelper;

  ~Texture() override;
  void AddTextureRef(TextureRef* ref);
  void RemoveTextureRef(TextureRef* ref, bool have_context);
  MemoryTypeTracker* GetMemTracker();

  // Condition on which this texture is renderable. Can be ONLY_IF_NPOT if it
  // depends on context support for non-power-of-two textures (i.e. will be
  // renderable if NPOT support is in the context, otherwise not, e.g. texture
  // with a NPOT level). ALWAYS means it doesn't depend on context features
  // (e.g. complete POT), NEVER means it's not renderable regardless (e.g.
  // incomplete).
  enum CanRenderCondition {
    CAN_RENDER_ALWAYS,
    CAN_RENDER_NEVER,
    CAN_RENDER_NEEDS_VALIDATION,
  };

  struct LevelInfo {
    LevelInfo();
    LevelInfo(const LevelInfo& rhs);
    ~LevelInfo();

    gfx::Rect cleared_rect;
    GLenum target;
    GLint level;
    GLenum internal_format;
    GLsizei width;
    GLsizei height;
    GLsizei depth;
    GLint border;
    GLenum format;
    GLenum type;
    scoped_refptr<gl::GLImage> image;
    scoped_refptr<GLStreamTextureImage> stream_texture_image;
    ImageState image_state;
    uint32_t estimated_size;
    bool internal_workaround;
  };

  struct FaceInfo {
    FaceInfo();
    FaceInfo(const FaceInfo& other);
    ~FaceInfo();

    // This is relative to base_level and max_level of a texture.
    GLsizei num_mip_levels;
    // This contains slots for all levels starting at 0.
    std::vector<LevelInfo> level_infos;
  };

  // Helper for SetLevel*Image.  |stream_texture_image| may be null.
  void SetLevelImageInternal(GLenum target,
                             GLint level,
                             gl::GLImage* image,
                             GLStreamTextureImage* stream_texture_image,
                             ImageState state);

  // Returns the LevelInfo for |target| and |level| if it's set, else NULL.
  const LevelInfo* GetLevelInfo(GLint target, GLint level) const;

  // Set the info for a particular level.
  void SetLevelInfo(GLenum target,
                    GLint level,
                    GLenum internal_format,
                    GLsizei width,
                    GLsizei height,
                    GLsizei depth,
                    GLint border,
                    GLenum format,
                    GLenum type,
                    const gfx::Rect& cleared_rect);

  // Causes us to report |service_id| as our service id, but does not delete
  // it when we are destroyed.  Will rebind any OES_EXTERNAL texture units to
  // our new service id in all contexts.  If |service_id| is zero, then we
  // revert to |owned_service_id_|.
  void SetStreamTextureServiceId(GLuint service_id);

  void MarkLevelAsInternalWorkaround(GLenum target, GLint level);

  // In GLES2 "texture complete" means it has all required mips for filtering
  // down to a 1x1 pixel texture, they are in the correct order, they are all
  // the same format.
  bool texture_complete() const {
    return texture_complete_;
  }

  // In GLES2 "cube complete" means all 6 faces level 0 are defined, all the
  // same format, all the same dimensions and all width = height.
  bool cube_complete() const {
    return cube_complete_;
  }

  // Whether or not this texture is a non-power-of-two texture.
  bool npot() const {
    return npot_;
  }

  // Marks a |rect| of a particular level as cleared.
  void SetLevelClearedRect(GLenum target,
                           GLint level,
                           const gfx::Rect& cleared_rect);

  // Marks a particular level as cleared or uncleared.
  void SetLevelCleared(GLenum target, GLint level, bool cleared);

  // Updates the cleared flag for this texture by inspecting all the mips.
  void UpdateCleared();

  // Clears any renderable uncleared levels.
  // Returns false if a GL error was generated.
  bool ClearRenderableLevels(GLES2Decoder* decoder);

  // Clears the level.
  // Returns false if a GL error was generated.
  bool ClearLevel(GLES2Decoder* decoder, GLenum target, GLint level);

  // Sets a texture parameter.
  // TODO(gman): Expand to SetParameteriv,fv
  // Returns GL_NO_ERROR on success. Otherwise the error to generate.
  GLenum SetParameteri(
      const FeatureInfo* feature_info, GLenum pname, GLint param);
  GLenum SetParameterf(
      const FeatureInfo* feature_info, GLenum pname, GLfloat param);

  // Makes each of the mip levels as though they were generated.
  void MarkMipmapsGenerated();

  bool NeedsMips() const {
    return sampler_state_.min_filter != GL_NEAREST &&
           sampler_state_.min_filter != GL_LINEAR;
  }

  // True if this texture meets all the GLES2 criteria for rendering.
  // See section 3.8.2 of the GLES2 spec.
  bool CanRender(const FeatureInfo* feature_info) const;
  bool CanRenderWithSampler(const FeatureInfo* feature_info,
                            const SamplerState& sampler_state) const;

  // Returns true if mipmaps can be generated by GL.
  bool CanGenerateMipmaps(const FeatureInfo* feature_info) const;

  // Returns true if any of the texture dimensions are not a power of two.
  static bool TextureIsNPOT(GLsizei width, GLsizei height, GLsizei depth);

  // Returns true if texture face is complete relative to the first face.
  static bool TextureFaceComplete(const Texture::LevelInfo& first_face,
                                  size_t face_index,
                                  GLenum target,
                                  GLenum internal_format,
                                  GLsizei width,
                                  GLsizei height,
                                  GLsizei depth,
                                  GLenum format,
                                  GLenum type);

  // Returns true if texture mip level is complete relative to base level.
  // Note that level_diff = level - base_level.
  static bool TextureMipComplete(const Texture::LevelInfo& base_level_face,
                                 GLenum target,
                                 GLint level_diff,
                                 GLenum internal_format,
                                 GLsizei width,
                                 GLsizei height,
                                 GLsizei depth,
                                 GLenum format,
                                 GLenum type);

  static bool ColorRenderable(const FeatureInfo* feature_info,
                              GLenum internal_format,
                              bool immutable);

  static bool TextureFilterable(const FeatureInfo* feature_info,
                                GLenum internal_format,
                                GLenum type,
                                bool immutable);

  // Sets the Texture's target
  // Parameters:
  //   target: GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP or
  //           GL_TEXTURE_EXTERNAL_OES or GL_TEXTURE_RECTANGLE_ARB
  //           GL_TEXTURE_2D_ARRAY or GL_TEXTURE_3D (for GLES3)
  //   max_levels: The maximum levels this type of target can have.
  void SetTarget(GLenum target, GLint max_levels);

  // Update info about this texture.
  void Update();

  // Appends a signature for the given level.
  void AddToSignature(
      const FeatureInfo* feature_info,
      GLenum target, GLint level, std::string* signature) const;

  // Updates the unsafe textures count in all the managers referencing this
  // texture.
  void UpdateSafeToRenderFrom(bool cleared);

  // Updates the uncleared mip count in all the managers referencing this
  // texture.
  void UpdateMipCleared(LevelInfo* info,
                        GLsizei width,
                        GLsizei height,
                        const gfx::Rect& cleared_rect);

  // Computes the CanRenderCondition flag.
  CanRenderCondition GetCanRenderCondition() const;

  // Updates the unrenderable texture count in all the managers referencing this
  // texture.
  void UpdateCanRenderCondition();

  // Updates the images count in all the managers referencing this
  // texture.
  void UpdateHasImages();

  // Updates the flag that indicates whether this texture requires RGB
  // emulation.
  void UpdateEmulatingRGB();

  // Increment the framebuffer state change count in all the managers
  // referencing this texture.
  void IncAllFramebufferStateChangeCount();

  void UpdateBaseLevel(GLint base_level);
  void UpdateMaxLevel(GLint max_level);
  void UpdateNumMipLevels();

  // Increment the generation counter for all managers that have a reference to
  // this texture.
  void IncrementManagerServiceIdGeneration();

  // Return the service id of the texture that we will delete when we are
  // destroyed.
  GLuint owned_service_id() const { return owned_service_id_; }

  GLenum GetCompatibilitySwizzleForChannel(GLenum channel);
  void SetCompatibilitySwizzle(const CompatibilitySwizzle* swizzle);

  // Info about each face and level of texture.
  std::vector<FaceInfo> face_infos_;

  // The texture refs that point to this Texture.
  typedef std::set<TextureRef*> RefSet;
  RefSet refs_;

  // The single TextureRef that accounts for memory for this texture. Must be
  // one of refs_.
  TextureRef* memory_tracking_ref_;

  // The id of the texture that we are responsible for deleting.  Normally, this
  // is the same as |service_id_|, unless a GLStreamTextureImage with its own
  // service id is bound. In that case the GLStreamTextureImage service id is
  // stored in |service_id_| and overrides the owned service id for all purposes
  // except deleting the texture name.
  GLuint owned_service_id_;

  // Whether all renderable mips of this texture have been cleared.
  bool cleared_;

  int num_uncleared_mips_;
  int num_npot_faces_;

  // The target. 0 if unset, otherwise GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP.
  //             Or GL_TEXTURE_2D_ARRAY or GL_TEXTURE_3D (for GLES3).
  GLenum target_;

  // Texture parameters.
  SamplerState sampler_state_;
  GLenum usage_;
  GLint base_level_;
  GLint max_level_;
  GLenum swizzle_r_;
  GLenum swizzle_g_;
  GLenum swizzle_b_;
  GLenum swizzle_a_;

  // The maximum level that has been set.
  GLint max_level_set_;

  // Whether or not this texture is "texture complete"
  bool texture_complete_;

  // Whether mip levels have changed and should be reverified.
  bool texture_mips_dirty_;

  // Whether or not this texture is "cube complete"
  bool cube_complete_;

  // Whether or not this texture is non-power-of-two
  bool npot_;

  // Whether this texture has ever been bound.
  bool has_been_bound_;

  // The number of framebuffers this texture is attached to.
  int framebuffer_attachment_count_;

  // Whether the texture is immutable and no further changes to the format
  // or dimensions of the texture object can be made.
  bool immutable_;

  // Whether or not this texture has images.
  bool has_images_;

  // Size in bytes this texture is assumed to take in memory.
  uint32_t estimated_size_;

  // Cache of the computed CanRenderCondition flag.
  CanRenderCondition can_render_condition_;

  // Whether we have initialized TEXTURE_MAX_ANISOTROPY to 1.
  bool texture_max_anisotropy_initialized_;

  const CompatibilitySwizzle* compatibility_swizzle_;

  bool emulating_rgb_;

  DISALLOW_COPY_AND_ASSIGN(Texture);
};

// This class represents a texture in a client context group. It's mostly 1:1
// with a client id, though it can outlive the client id if it's still bound to
// a FBO or another context when destroyed.
// Multiple TextureRef can point to the same texture with cross-context sharing.
class GPU_EXPORT TextureRef : public base::RefCounted<TextureRef> {
 public:
  TextureRef(TextureManager* manager, GLuint client_id, Texture* texture);
  static scoped_refptr<TextureRef> Create(TextureManager* manager,
                                          GLuint client_id,
                                          GLuint service_id);

  void AddObserver() { num_observers_++; }
  void RemoveObserver() { num_observers_--; }

  const Texture* texture() const { return texture_; }
  Texture* texture() { return texture_; }
  GLuint client_id() const { return client_id_; }
  GLuint service_id() const { return texture_->service_id(); }
  GLint num_observers() const { return num_observers_; }

  // When the TextureRef is destroyed, it will assume that the context has been
  // lost, regardless of the state of the TextureManager.
  void ForceContextLost();

 private:
  friend class base::RefCounted<TextureRef>;
  friend class Texture;
  friend class TextureManager;

  ~TextureRef();
  const TextureManager* manager() const { return manager_; }
  TextureManager* manager() { return manager_; }
  void reset_client_id() { client_id_ = 0; }

  TextureManager* manager_;
  Texture* texture_;
  GLuint client_id_;
  GLint num_observers_;
  bool force_context_lost_;

  DISALLOW_COPY_AND_ASSIGN(TextureRef);
};

// Holds data that is per gles2_cmd_decoder, but is related to to the
// TextureManager.
struct DecoderTextureState {
  // total_texture_upload_time automatically initialized to 0 in default
  // constructor.
  explicit DecoderTextureState(const GpuDriverBugWorkarounds& workarounds)
      : tex_image_failed(false),
        texture_upload_count(0),
        texsubimage_faster_than_teximage(
            workarounds.texsubimage_faster_than_teximage),
        force_cube_map_positive_x_allocation(
            workarounds.force_cube_map_positive_x_allocation),
        force_cube_complete(workarounds.force_cube_complete),
        unpack_alignment_workaround_with_unpack_buffer(
            workarounds.unpack_alignment_workaround_with_unpack_buffer),
        unpack_overlapping_rows_separately_unpack_buffer(
            workarounds.unpack_overlapping_rows_separately_unpack_buffer),
        unpack_image_height_workaround_with_unpack_buffer(
            workarounds.unpack_image_height_workaround_with_unpack_buffer) {}

  // This indicates all the following texSubImage*D calls that are part of the
  // failed texImage*D call should be ignored. The client calls have a lock
  // around them, so it will affect only a single texImage*D + texSubImage*D
  // group.
  bool tex_image_failed;

  // Command buffer stats.
  int texture_upload_count;
  base::TimeDelta total_texture_upload_time;

  bool texsubimage_faster_than_teximage;
  bool force_cube_map_positive_x_allocation;
  bool force_cube_complete;
  bool unpack_alignment_workaround_with_unpack_buffer;
  bool unpack_overlapping_rows_separately_unpack_buffer;
  bool unpack_image_height_workaround_with_unpack_buffer;
};

// This class keeps track of the textures and their sizes so we can do NPOT and
// texture complete checking.
//
// NOTE: To support shared resources an instance of this class will need to be
// shared by multiple GLES2Decoders.
class GPU_EXPORT TextureManager : public base::trace_event::MemoryDumpProvider {
 public:
  class GPU_EXPORT DestructionObserver {
   public:
    DestructionObserver();
    virtual ~DestructionObserver();

    // Called in ~TextureManager.
    virtual void OnTextureManagerDestroying(TextureManager* manager) = 0;

    // Called via ~TextureRef.
    virtual void OnTextureRefDestroying(TextureRef* texture) = 0;

   private:
    DISALLOW_COPY_AND_ASSIGN(DestructionObserver);
  };

  enum DefaultAndBlackTextures {
    kTexture2D,
    kTexture3D,
    kTexture2DArray,
    kCubeMap,
    kExternalOES,
    kRectangleARB,
    kNumDefaultTextures
  };

  TextureManager(MemoryTracker* memory_tracker,
                 FeatureInfo* feature_info,
                 GLsizei max_texture_size,
                 GLsizei max_cube_map_texture_size,
                 GLsizei max_rectangle_texture_size,
                 GLsizei max_3d_texture_size,
                 GLsizei max_array_texture_layers,
                 bool use_default_textures,
                 ProgressReporter* progress_reporter);
  ~TextureManager() override;

  void set_framebuffer_manager(FramebufferManager* manager) {
    framebuffer_manager_ = manager;
  }

  // Init the texture manager.
  bool Initialize();

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

  // Returns the maximum number of levels.
  GLint MaxLevelsForTarget(GLenum target) const {
    switch (target) {
      case GL_TEXTURE_2D:
      case GL_TEXTURE_2D_ARRAY:
        return max_levels_;
      case GL_TEXTURE_RECTANGLE_ARB:
      case GL_TEXTURE_EXTERNAL_OES:
        return 1;
      case GL_TEXTURE_3D:
        return max_3d_levels_;
      default:
        return max_cube_map_levels_;
    }
  }

  // Returns the maximum size.
  GLsizei MaxSizeForTarget(GLenum target) const {
    switch (target) {
      case GL_TEXTURE_2D:
      case GL_TEXTURE_EXTERNAL_OES:
      case GL_TEXTURE_2D_ARRAY:
        return max_texture_size_;
      case GL_TEXTURE_RECTANGLE:
        return max_rectangle_texture_size_;
      case GL_TEXTURE_3D:
        return max_3d_texture_size_;
      default:
        return max_cube_map_texture_size_;
    }
  }

  GLsizei max_array_texture_layers() const {
    return max_array_texture_layers_;
  }

  // Returns the maxium number of levels a texture of the given size can have.
  static GLsizei ComputeMipMapCount(GLenum target,
                                    GLsizei width,
                                    GLsizei height,
                                    GLsizei depth);

  static GLenum ExtractFormatFromStorageFormat(GLenum internalformat);
  static GLenum ExtractTypeFromStorageFormat(GLenum internalformat);

  // Checks if a dimensions are valid for a given target.
  bool ValidForTarget(
      GLenum target, GLint level,
      GLsizei width, GLsizei height, GLsizei depth);

  // True if this texture meets all the GLES2 criteria for rendering.
  // See section 3.8.2 of the GLES2 spec.
  bool CanRender(const TextureRef* ref) const {
    return ref->texture()->CanRender(feature_info_.get());
  }

  bool CanRenderWithSampler(
      const TextureRef* ref, const SamplerState& sampler_state) const {
    return ref->texture()->CanRenderWithSampler(
        feature_info_.get(), sampler_state);
  }

  // Returns true if mipmaps can be generated by GL.
  bool CanGenerateMipmaps(const TextureRef* ref) const {
    return ref->texture()->CanGenerateMipmaps(feature_info_.get());
  }

  // Sets the Texture's target
  // Parameters:
  //   target: GL_TEXTURE_2D or GL_TEXTURE_CUBE_MAP
  //           GL_TEXTURE_2D_ARRAY or GL_TEXTURE_3D (for GLES3)
  //   max_levels: The maximum levels this type of target can have.
  void SetTarget(
      TextureRef* ref,
      GLenum target);

  // Set the info for a particular level in a TexureInfo.
  void SetLevelInfo(TextureRef* ref,
                    GLenum target,
                    GLint level,
                    GLenum internal_format,
                    GLsizei width,
                    GLsizei height,
                    GLsizei depth,
                    GLint border,
                    GLenum format,
                    GLenum type,
                    const gfx::Rect& cleared_rect);

  Texture* Produce(TextureRef* ref);

  // Maps an existing texture into the texture manager, at a given client ID.
  TextureRef* Consume(GLuint client_id, Texture* texture);

  // Sets |rect| of mip as cleared.
  void SetLevelClearedRect(TextureRef* ref,
                           GLenum target,
                           GLint level,
                           const gfx::Rect& cleared_rect);

  // Sets a mip as cleared.
  void SetLevelCleared(TextureRef* ref, GLenum target,
                       GLint level, bool cleared);

  // Sets a texture parameter of a Texture
  // Returns GL_NO_ERROR on success. Otherwise the error to generate.
  // TODO(gman): Expand to SetParameteriv,fv
  void SetParameteri(
      const char* function_name, ErrorState* error_state,
      TextureRef* ref, GLenum pname, GLint param);
  void SetParameterf(
      const char* function_name, ErrorState* error_state,
      TextureRef* ref, GLenum pname, GLfloat param);

  // Makes each of the mip levels as though they were generated.
  void MarkMipmapsGenerated(TextureRef* ref);

  // Clears any uncleared renderable levels.
  bool ClearRenderableLevels(GLES2Decoder* decoder, TextureRef* ref);

  // Clear a specific level.
  bool ClearTextureLevel(
      GLES2Decoder* decoder, TextureRef* ref, GLenum target, GLint level);

  // Creates a new texture info.
  TextureRef* CreateTexture(GLuint client_id, GLuint service_id);

  // Gets the texture info for the given texture.
  TextureRef* GetTexture(GLuint client_id) const;

  // Removes a texture info.
  void RemoveTexture(GLuint client_id);

  // Gets a Texture for a given service id (note: it assumes the texture object
  // is still mapped in this TextureManager).
  Texture* GetTextureForServiceId(GLuint service_id) const;

  TextureRef* GetDefaultTextureInfo(GLenum target) {
    switch (target) {
      case GL_TEXTURE_2D:
        return default_textures_[kTexture2D].get();
      case GL_TEXTURE_3D:
        return default_textures_[kTexture3D].get();
      case GL_TEXTURE_2D_ARRAY:
        return default_textures_[kTexture2DArray].get();
      case GL_TEXTURE_CUBE_MAP:
        return default_textures_[kCubeMap].get();
      case GL_TEXTURE_EXTERNAL_OES:
        return default_textures_[kExternalOES].get();
      case GL_TEXTURE_RECTANGLE_ARB:
        return default_textures_[kRectangleARB].get();
      default:
        NOTREACHED();
        return NULL;
    }
  }

  bool HaveUnsafeTextures() const {
    return num_unsafe_textures_ > 0;
  }

  bool HaveUnclearedMips() const {
    return num_uncleared_mips_ > 0;
  }

  bool HaveImages() const {
    return num_images_ > 0;
  }

  GLuint black_texture_id(GLenum target) const {
    switch (target) {
      case GL_SAMPLER_2D:
        return black_texture_ids_[kTexture2D];
      case GL_SAMPLER_3D:
        return black_texture_ids_[kTexture3D];
      case GL_SAMPLER_2D_ARRAY:
        return black_texture_ids_[kTexture2DArray];
      case GL_SAMPLER_CUBE:
        return black_texture_ids_[kCubeMap];
      case GL_SAMPLER_EXTERNAL_OES:
        return black_texture_ids_[kExternalOES];
      case GL_SAMPLER_2D_RECT_ARB:
        return black_texture_ids_[kRectangleARB];
      default:
        NOTREACHED();
        return 0;
    }
  }

  size_t mem_represented() const {
    return memory_type_tracker_->GetMemRepresented();
  }

  void SetLevelImage(TextureRef* ref,
                     GLenum target,
                     GLint level,
                     gl::GLImage* image,
                     Texture::ImageState state);

  void SetLevelStreamTextureImage(TextureRef* ref,
                                  GLenum target,
                                  GLint level,
                                  GLStreamTextureImage* image,
                                  Texture::ImageState state,
                                  GLuint service_id);

  void SetLevelImageState(TextureRef* ref,
                          GLenum target,
                          GLint level,
                          Texture::ImageState state);

  size_t GetSignatureSize() const;

  void AddToSignature(
      TextureRef* ref,
      GLenum target,
      GLint level,
      std::string* signature) const;

  void AddObserver(DestructionObserver* observer) {
    destruction_observers_.push_back(observer);
  }

  void RemoveObserver(DestructionObserver* observer) {
    for (unsigned int i = 0; i < destruction_observers_.size(); i++) {
      if (destruction_observers_[i] == observer) {
        std::swap(destruction_observers_[i], destruction_observers_.back());
        destruction_observers_.pop_back();
        return;
      }
    }
    NOTREACHED();
  }

  struct DoTexImageArguments {
    enum TexImageCommandType {
      kTexImage2D,
      kTexImage3D,
    };

    GLenum target;
    GLint level;
    GLenum internal_format;
    GLsizei width;
    GLsizei height;
    GLsizei depth;
    GLint border;
    GLenum format;
    GLenum type;
    const void* pixels;
    uint32_t pixels_size;
    uint32_t padding;
    TexImageCommandType command_type;
  };

  bool ValidateTexImage(
    ContextState* state,
    const char* function_name,
    const DoTexImageArguments& args,
    // Pointer to TextureRef filled in if validation successful.
    // Presumes the pointer is valid.
    TextureRef** texture_ref);

  void ValidateAndDoTexImage(
    DecoderTextureState* texture_state,
    ContextState* state,
    DecoderFramebufferState* framebuffer_state,
    const char* function_name,
    const DoTexImageArguments& args);

  struct DoTexSubImageArguments {
    enum TexSubImageCommandType {
      kTexSubImage2D,
      kTexSubImage3D,
    };

    GLenum target;
    GLint level;
    GLint xoffset;
    GLint yoffset;
    GLint zoffset;
    GLsizei width;
    GLsizei height;
    GLsizei depth;
    GLenum format;
    GLenum type;
    const void* pixels;
    uint32_t pixels_size;
    uint32_t padding;
    TexSubImageCommandType command_type;
  };

  bool ValidateTexSubImage(
      ContextState* state,
      const char* function_name,
      const DoTexSubImageArguments& args,
      // Pointer to TextureRef filled in if validation successful.
      // Presumes the pointer is valid.
      TextureRef** texture_ref);

  void ValidateAndDoTexSubImage(GLES2Decoder* decoder,
                                DecoderTextureState* texture_state,
                                ContextState* state,
                                DecoderFramebufferState* framebuffer_state,
                                const char* function_name,
                                const DoTexSubImageArguments& args);

  // TODO(kloveless): Make GetTexture* private once this is no longer called
  // from gles2_cmd_decoder.
  TextureRef* GetTextureInfoForTarget(ContextState* state, GLenum target);
  TextureRef* GetTextureInfoForTargetUnlessDefault(
      ContextState* state, GLenum target);

  // This function is used to validate TexImage2D and TexSubImage2D and their
  // variants. But internal_format only checked for callers of TexImage2D and
  // its variants (tex_image_call is true).
  bool ValidateTextureParameters(
    ErrorState* error_state, const char* function_name, bool tex_image_call,
    GLenum format, GLenum type, GLint internal_format, GLint level);

  // base::trace_event::MemoryDumpProvider implementation.
  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
                    base::trace_event::ProcessMemoryDump* pmd) override;

  // Returns the union of |rect1| and |rect2| if one of the rectangles is empty,
  // contains the other rectangle or shares an edge with the other rectangle.
  // Part of the public interface because texture pixel data rectangle
  // operations are also implemented in decoder at the moment.
  static bool CombineAdjacentRects(const gfx::Rect& rect1,
                                   const gfx::Rect& rect2,
                                   gfx::Rect* result);

  // Get / set the current generation number of this manager.  This generation
  // number changes whenever the service_id of one or more Textures change.
  uint32_t GetServiceIdGeneration() const;
  void IncrementServiceIdGeneration();

  static GLenum AdjustTexInternalFormat(const gles2::FeatureInfo* feature_info,
                                        GLenum format);
  static GLenum AdjustTexFormat(const gles2::FeatureInfo* feature_info,
                                GLenum format);

  void WorkaroundCopyTexImageCubeMap(
      DecoderTextureState* texture_state,
      ContextState* state,
      DecoderFramebufferState* framebuffer_state,
      TextureRef* texture_ref,
      const char* function_name,
      const DoTexImageArguments& args) {
    DoCubeMapWorkaround(texture_state, state, framebuffer_state,
                        texture_ref, function_name, args);
  }

 private:
  friend class Texture;
  friend class TextureRef;

  // Helper for Initialize().
  scoped_refptr<TextureRef> CreateDefaultAndBlackTextures(
      GLenum target,
      GLuint* black_texture);

  void DoTexImage(
      DecoderTextureState* texture_state,
      ContextState* state,
      DecoderFramebufferState* framebuffer_state,
      const char* function_name,
      TextureRef* texture_ref,
      const DoTexImageArguments& args);

  // Reserve memory for the texture and set its attributes so it can be filled
  // with TexSubImage. The image contents are undefined after this function,
  // so make sure it's subsequently filled in its entirety.
  void ReserveTexImageToBeFilled(DecoderTextureState* texture_state,
                                 ContextState* state,
                                 DecoderFramebufferState* framebuffer_state,
                                 const char* function_name,
                                 TextureRef* texture_ref,
                                 const DoTexImageArguments& args);

  void DoTexSubImageWithAlignmentWorkaround(
      DecoderTextureState* texture_state,
      ContextState* state,
      const DoTexSubImageArguments& args);

  void DoTexSubImageRowByRowWorkaround(DecoderTextureState* texture_state,
                                       ContextState* state,
                                       const DoTexSubImageArguments& args,
                                       const PixelStoreParams& unpack_params);

  void DoTexSubImageLayerByLayerWorkaround(
      DecoderTextureState* texture_state,
      ContextState* state,
      const DoTexSubImageArguments& args,
      const PixelStoreParams& unpack_params);

  void DoCubeMapWorkaround(
      DecoderTextureState* texture_state,
      ContextState* state,
      DecoderFramebufferState* framebuffer_state,
      TextureRef* texture_ref,
      const char* function_name,
      const DoTexImageArguments& args);

  void StartTracking(TextureRef* texture);
  void StopTracking(TextureRef* texture);

  void UpdateSafeToRenderFrom(int delta);
  void UpdateUnclearedMips(int delta);
  void UpdateCanRenderCondition(Texture::CanRenderCondition old_condition,
                                Texture::CanRenderCondition new_condition);
  void UpdateNumImages(int delta);
  void IncFramebufferStateChangeCount();

  // Helper function called by OnMemoryDump.
  void DumpTextureRef(base::trace_event::ProcessMemoryDump* pmd,
                      TextureRef* ref);

  MemoryTypeTracker* GetMemTracker();
  std::unique_ptr<MemoryTypeTracker> memory_type_tracker_;
  MemoryTracker* memory_tracker_;

  scoped_refptr<FeatureInfo> feature_info_;

  FramebufferManager* framebuffer_manager_;

  // Info for each texture in the system.
  typedef base::hash_map<GLuint, scoped_refptr<TextureRef> > TextureMap;
  TextureMap textures_;

  GLsizei max_texture_size_;
  GLsizei max_cube_map_texture_size_;
  GLsizei max_rectangle_texture_size_;
  GLsizei max_3d_texture_size_;
  GLsizei max_array_texture_layers_;
  GLint max_levels_;
  GLint max_cube_map_levels_;
  GLint max_3d_levels_;

  const bool use_default_textures_;

  int num_unsafe_textures_;
  int num_uncleared_mips_;
  int num_images_;

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

  bool have_context_;

  // Black (0,0,0,1) textures for when non-renderable textures are used.
  // NOTE: There is no corresponding Texture for these textures.
  // TextureInfos are only for textures the client side can access.
  GLuint black_texture_ids_[kNumDefaultTextures];

  // The default textures for each target (texture name = 0)
  scoped_refptr<TextureRef> default_textures_[kNumDefaultTextures];

  std::vector<DestructionObserver*> destruction_observers_;

  uint32_t current_service_id_generation_;

  // 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(TextureManager);
};

// This class records texture upload time when in scope.
class ScopedTextureUploadTimer {
 public:
  explicit ScopedTextureUploadTimer(DecoderTextureState* texture_state);
  ~ScopedTextureUploadTimer();

 private:
  DecoderTextureState* texture_state_;
  base::TimeTicks begin_time_;
  DISALLOW_COPY_AND_ASSIGN(ScopedTextureUploadTimer);
};

}  // namespace gles2
}  // namespace gpu

#endif  // GPU_COMMAND_BUFFER_SERVICE_TEXTURE_MANAGER_H_
