text: // 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_QUERY_MANAGER_H_
  #define GPU_COMMAND_BUFFER_SERVICE_QUERY_MANAGER_H_
  
  #include <stdint.h>
  
  #include <memory>
  #include <vector>
  
  #include "base/atomicops.h"
  #include "base/callback_forward.h"
  #include "base/containers/circular_deque.h"
  #include "base/macros.h"
  #include "base/memory/ref_counted.h"
  #include "gpu/command_buffer/common/buffer.h"
  #include "gpu/command_buffer/common/common_cmd_format.h"
  #include "gpu/command_buffer/common/gl2_types.h"
  #include "gpu/gpu_gles2_export.h"
  
  namespace gpu {
  
  // This class keeps track of the queries and their state
  // As Queries are not shared there is one QueryManager per context.
  class GPU_GLES2_EXPORT QueryManager {
   public:
    class GPU_GLES2_EXPORT Query : public base::RefCounted<Query> {
     public:
      Query(QueryManager* manager,
            GLenum target,
            scoped_refptr<gpu::Buffer> buffer,
            QuerySync* sync);
  
      GLenum target() const {
        return target_;
      }
  
      bool IsDeleted() const {
        return deleted_;
      }
  
      bool IsValid() const {
        return target() && !IsDeleted();
      }
  
      bool IsActive() const {
        return query_state_ == kQueryState_Active;
      }
  
      bool IsPaused() const {
        return query_state_ == kQueryState_Paused;
      }
  
      bool IsPending() const {
        return query_state_ == kQueryState_Pending;
      }
  
      bool IsFinished() const {
        return query_state_ == kQueryState_Finished;
      }
  
      const QuerySync* sync() const { return sync_; }
  
      virtual void Begin() = 0;
  
      virtual void End(base::subtle::Atomic32 submit_count) = 0;
  
      virtual void QueryCounter(base::subtle::Atomic32 submit_count) = 0;
  
      virtual void Process(bool did_finish) = 0;
  
      // Pauses active query to be resumed later.
      virtual void Pause() = 0;
  
      // Resume from a paused active query.
      virtual void Resume() = 0;
  
      virtual void Destroy(bool have_context) = 0;
  
      void AddCallback(base::OnceClosure callback);
  
     protected:
      virtual ~Query();
  
      QueryManager* manager() const {
        return manager_;
      }
  
      void MarkAsDeleted() {
        deleted_ = true;
      }
  
      void MarkAsActive() {
        DCHECK(query_state_ == kQueryState_Initialize ||
               query_state_ == kQueryState_Paused ||
               query_state_ == kQueryState_Finished);
        query_state_ = kQueryState_Active;
      }
  
      void MarkAsPaused() {
        DCHECK(query_state_ == kQueryState_Active);
        query_state_ = kQueryState_Paused;
      }
  
      void MarkAsPending(base::subtle::Atomic32 submit_count) {
        DCHECK(query_state_ == kQueryState_Active);
        query_state_ = kQueryState_Pending;
        submit_count_ = submit_count;
      }
  
      void MarkAsCompleted(uint64_t result);
  
      void UnmarkAsPending() {
        DCHECK(query_state_ == kQueryState_Pending);
        query_state_ = kQueryState_Finished;
      }
  
      void AddToPendingQueue(base::subtle::Atomic32 submit_count) {
        manager_->AddPendingQuery(this, submit_count);
      }
  
      void BeginQueryHelper(GLenum target, GLuint id) {
        manager_->BeginQueryHelper(target, id);
      }
  
      void EndQueryHelper(GLenum target) {
        manager_->EndQueryHelper(target);
      }
  
      base::subtle::Atomic32 submit_count() const { return submit_count_; }
  
     private:
      friend class QueryManager;
      friend class QueryManagerTest;
      friend class base::RefCounted<Query>;
  
      void RunCallbacks();
  
      // The manager that owns this Query.
      QueryManager* manager_;
  
      // The type of query.
      GLenum target_;
  
      // The shared memory used with this Query. We keep a reference to the Buffer
      // to ensure it doesn't get released until we wrote results. sync_ points to
      // memory inside buffer_.
      scoped_refptr<gpu::Buffer> buffer_;
      QuerySync* sync_;
  
      // Count to set process count do when completed.
      base::subtle::Atomic32 submit_count_;
  
      // Current state of the query.
      enum QueryState {
        kQueryState_Initialize, // Has not been queried yet.
        kQueryState_Active, // Query began but has not ended.
        kQueryState_Paused, // Query was active but is now paused.
        kQueryState_Pending, // Query ended, waiting for result.
        kQueryState_Finished, // Query received result.
      } query_state_;
  
      // True if deleted.
      bool deleted_;
  
      // List of callbacks to run when result is available.
      std::vector<base::OnceClosure> callbacks_;
    };
  
    QueryManager();
    virtual ~QueryManager();
  
    // Must call before destruction.
    void Destroy(bool have_context);
  
    // Creates a Query for the given query.
    virtual Query* CreateQuery(GLenum target,
                               GLuint client_id,
                               scoped_refptr<gpu::Buffer> buffer,
                               QuerySync* sync);
  
    // Gets the query info for the given query.
    Query* GetQuery(GLuint client_id);
  
    // Gets the currently active query for a target.
    Query* GetActiveQuery(GLenum target);
  
    // Removes a query info for the given query.
    void RemoveQuery(GLuint client_id);
  
    // Returns false if any query is pointing to invalid shared memory.
    void BeginQuery(Query* query);
  
    // Returns false if any query is pointing to invalid shared memory.
    void EndQuery(Query* query, base::subtle::Atomic32 submit_count);
  
    // Returns false if any query is pointing to invalid shared memory.
    void QueryCounter(Query* query, base::subtle::Atomic32 submit_count);
  
    void PauseQueries();
    void ResumeQueries();
  
    // Processes pending queries. Returns false if any queries are pointing
    // to invalid shared memory. |did_finish| is true if this is called as
    // a result of calling glFinish().
    void ProcessPendingQueries(bool did_finish);
  
    // True if there are pending queries.
    bool HavePendingQueries();
  
    void GenQueries(GLsizei n, const GLuint* queries);
    bool IsValidQuery(GLuint id);
  
   protected:
    void StartTracking(Query* query);
    void StopTracking(Query* query);
  
    // Wrappers for BeginQueryARB and EndQueryARB to hide differences between
    // ARB_occlusion_query2 and EXT_occlusion_query_boolean.
    void BeginQueryHelper(GLenum target, GLuint id);
    void EndQueryHelper(GLenum target);
  
    // Adds to queue of queries waiting for completion.
    // Returns false if any query is pointing to invalid shared memory.
    void AddPendingQuery(Query* query, base::subtle::Atomic32 submit_count);
  
    // Removes a query from the queue of pending queries.
    // Returns false if any query is pointing to invalid shared memory.
    void RemovePendingQuery(Query* query);
  
    // Returns a target used for the underlying GL extension
    // used to emulate a query.
    virtual GLenum AdjustTargetForEmulation(GLenum target);
  
    // Counts the number of Queries allocated with 'this' as their manager.
    // Allows checking no Query will outlive this.
    unsigned query_count_;
  
    // Info for each query in the system.
    using QueryMap = base::hash_map<GLuint, scoped_refptr<Query>>;
    QueryMap queries_;
  
    using GeneratedQueryIds = base::hash_set<GLuint>;
    GeneratedQueryIds generated_query_ids_;
  
    // A map of targets -> Query for current active queries.
    using ActiveQueryMap = std::map<GLenum, scoped_refptr<Query>>;
    ActiveQueryMap active_queries_;
  
    // Queries waiting for completion.
    using QueryQueue = base::circular_deque<scoped_refptr<Query>>;
    QueryQueue pending_queries_;
  
    DISALLOW_COPY_AND_ASSIGN(QueryManager);
  };
  
  }  // namespace gpu
  
  #endif  // GPU_COMMAND_BUFFER_SERVICE_QUERY_MANAGER_H_
  