// 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_CLIENT_QUERY_TRACKER_H_
#define GPU_COMMAND_BUFFER_CLIENT_QUERY_TRACKER_H_

#include <GLES2/gl2.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>

#include <bitset>
#include <deque>
#include <list>

#include "base/atomicops.h"
#include "base/containers/hash_tables.h"
#include "base/macros.h"
#include "gles2_impl_export.h"
#include "gpu/command_buffer/common/gles2_cmd_format.h"

namespace gpu {

class CommandBufferHelper;
class MappedMemoryManager;

namespace gles2 {

class GLES2Implementation;

// Manages buckets of QuerySync instances in mapped memory.
class GLES2_IMPL_EXPORT QuerySyncManager {
 public:
  static const size_t kSyncsPerBucket = 256;

  struct Bucket {
    Bucket(QuerySync* sync_mem, int32_t shm_id, uint32_t shm_offset);
    ~Bucket();
    QuerySync* syncs;
    int32_t shm_id;
    uint32_t base_shm_offset;
    std::bitset<kSyncsPerBucket> in_use_queries;
  };
  struct QueryInfo {
    QueryInfo(Bucket* bucket, int32_t id, uint32_t offset, QuerySync* sync_mem)
        : bucket(bucket), shm_id(id), shm_offset(offset), sync(sync_mem) {}

    QueryInfo()
        : bucket(NULL),
          shm_id(0),
          shm_offset(0),
          sync(NULL) {
    }

    Bucket* bucket;
    int32_t shm_id;
    uint32_t shm_offset;
    QuerySync* sync;
  };

  explicit QuerySyncManager(MappedMemoryManager* manager);
  ~QuerySyncManager();

  bool Alloc(QueryInfo* info);
  void Free(const QueryInfo& sync);
  void Shrink();

 private:
  MappedMemoryManager* mapped_memory_;
  std::deque<Bucket*> buckets_;

  DISALLOW_COPY_AND_ASSIGN(QuerySyncManager);
};

// Tracks queries for client side of command buffer.
class GLES2_IMPL_EXPORT QueryTracker {
 public:
  class GLES2_IMPL_EXPORT Query {
   public:
    enum State {
      kUninitialized,  // never used
      kActive,         // between begin - end
      kPending,        // not yet complete
      kComplete        // completed
    };

    Query(GLuint id, GLenum target, const QuerySyncManager::QueryInfo& info);

    GLenum target() const {
      return target_;
    }

    GLuint id() const {
      return id_;
    }

    int32_t shm_id() const { return info_.shm_id; }

    uint32_t shm_offset() const { return info_.shm_offset; }

    void MarkAsActive() {
      state_ = kActive;
      ++submit_count_;
      if (submit_count_ == INT_MAX)
        submit_count_ = 1;
    }

    void MarkAsPending(int32_t token) {
      token_ = token;
      state_ = kPending;
    }

    base::subtle::Atomic32 submit_count() const { return submit_count_; }

    int32_t token() const { return token_; }

    bool NeverUsed() const {
      return state_ == kUninitialized;
    }

    bool Active() const {
      return state_ == kActive;
    }

    bool Pending() const {
      return state_ == kPending;
    }

    bool CheckResultsAvailable(CommandBufferHelper* helper);

    uint64_t GetResult() const;

   private:
    friend class QueryTracker;
    friend class QueryTrackerTest;

    void Begin(GLES2Implementation* gl);
    void End(GLES2Implementation* gl);
    void QueryCounter(GLES2Implementation* gl);

    GLuint id_;
    GLenum target_;
    QuerySyncManager::QueryInfo info_;
    State state_;
    base::subtle::Atomic32 submit_count_;
    int32_t token_;
    uint32_t flush_count_;
    uint64_t client_begin_time_us_;  // Only used for latency query target.
    uint64_t result_;
  };

  QueryTracker(MappedMemoryManager* manager);
  ~QueryTracker();

  Query* CreateQuery(GLuint id, GLenum target);
  Query* GetQuery(GLuint id);
  Query* GetCurrentQuery(GLenum target);
  void RemoveQuery(GLuint id);
  void Shrink();
  void FreeCompletedQueries();

  bool BeginQuery(GLuint id, GLenum target, GLES2Implementation* gl);
  bool EndQuery(GLenum target, GLES2Implementation* gl);
  bool QueryCounter(GLuint id, GLenum target, GLES2Implementation* gl);
  bool SetDisjointSync(GLES2Implementation* gl);
  bool CheckAndResetDisjoint();

  int32_t DisjointCountSyncShmID() const {
    return disjoint_count_sync_shm_id_;
  }

  uint32_t DisjointCountSyncShmOffset() const {
    return disjoint_count_sync_shm_offset_;
  }

 private:
  typedef base::hash_map<GLuint, Query*> QueryIdMap;
  typedef base::hash_map<GLenum, Query*> QueryTargetMap;
  typedef std::list<Query*> QueryList;

  QueryIdMap queries_;
  QueryTargetMap current_queries_;
  QueryList removed_queries_;
  QuerySyncManager query_sync_manager_;

  // The shared memory used for synchronizing timer disjoint values.
  MappedMemoryManager* mapped_memory_;
  int32_t disjoint_count_sync_shm_id_;
  uint32_t disjoint_count_sync_shm_offset_;
  DisjointValueSync* disjoint_count_sync_;
  uint32_t local_disjoint_count_;

  DISALLOW_COPY_AND_ASSIGN(QueryTracker);
};

}  // namespace gles2
}  // namespace gpu

#endif  // GPU_COMMAND_BUFFER_CLIENT_QUERY_TRACKER_H_
