// 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 CONTENT_COMMON_GPU_GPU_COMMAND_BUFFER_STUB_H_
#define CONTENT_COMMON_GPU_GPU_COMMAND_BUFFER_STUB_H_
#pragma once

#include <deque>
#include <string>
#include <vector>

#include "base/id_map.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "content/common/content_export.h"
#include "content/common/gpu/gpu_memory_allocation.h"
#include "content/common/gpu/gpu_memory_allocation.h"
#include "content/common/gpu/media/gpu_video_decode_accelerator.h"
#include "gpu/command_buffer/common/constants.h"
#include "gpu/command_buffer/service/command_buffer_service.h"
#include "gpu/command_buffer/service/context_group.h"
#include "gpu/command_buffer/service/gpu_scheduler.h"
#include "ipc/ipc_channel.h"
#include "ipc/ipc_message.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/size.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/gpu_preference.h"
#include "ui/surface/transport_dib.h"

#if defined(OS_MACOSX)
#include "ui/surface/accelerated_surface_mac.h"
#endif

class GpuChannel;
struct GpuMemoryAllocation;
class GpuWatchdog;

namespace gpu {
namespace gles2 {
class MailboxManager;
}
}

// This Base class is used to expose methods of GpuCommandBufferStub used for
// testability.
class CONTENT_EXPORT GpuCommandBufferStubBase {
 public:
  struct CONTENT_EXPORT SurfaceState {
    int32 surface_id;
    bool visible;
    base::TimeTicks last_used_time;

    SurfaceState(int32 surface_id,
                 bool visible,
                 base::TimeTicks last_used_time);
  };

 public:
  virtual ~GpuCommandBufferStubBase() {}

  // Will not have surface state if this is an offscreen commandbuffer.
  virtual bool client_has_memory_allocation_changed_callback() const = 0;
  virtual bool has_surface_state() const = 0;
  virtual const SurfaceState& surface_state() const = 0;

  virtual gfx::Size GetSurfaceSize() const = 0;

  virtual bool IsInSameContextShareGroup(
      const GpuCommandBufferStubBase& other) const = 0;

  virtual void SetMemoryAllocation(
      const GpuMemoryAllocation& allocation) = 0;
};

class GpuCommandBufferStub
    : public GpuCommandBufferStubBase,
      public IPC::Channel::Listener,
      public IPC::Message::Sender,
      public base::SupportsWeakPtr<GpuCommandBufferStub> {
 public:
  class DestructionObserver {
   public:
    ~DestructionObserver() {}

    // Called in Destroy(), before the context/surface are released.
    virtual void OnWillDestroyStub(GpuCommandBufferStub* stub) = 0;
  };

  GpuCommandBufferStub(
      GpuChannel* channel,
      GpuCommandBufferStub* share_group,
      const gfx::GLSurfaceHandle& handle,
      gpu::gles2::MailboxManager* mailbox_manager,
      const gfx::Size& size,
      const gpu::gles2::DisallowedFeatures& disallowed_features,
      const std::string& allowed_extensions,
      const std::vector<int32>& attribs,
      gfx::GpuPreference gpu_preference,
      int32 route_id,
      int32 surface_id,
      GpuWatchdog* watchdog,
      bool software);

  virtual ~GpuCommandBufferStub();

  // IPC::Channel::Listener implementation:
  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;

  // IPC::Message::Sender implementation:
  virtual bool Send(IPC::Message* msg) OVERRIDE;

  // GpuCommandBufferStubBase implementation:
  virtual bool client_has_memory_allocation_changed_callback() const OVERRIDE;
  virtual bool has_surface_state() const OVERRIDE;
  virtual const GpuCommandBufferStubBase::SurfaceState& surface_state() const
      OVERRIDE;

  // Returns surface size.
  virtual gfx::Size GetSurfaceSize() const OVERRIDE;

  // Returns true iff |other| is in the same context share group as this stub.
  virtual bool IsInSameContextShareGroup(
      const GpuCommandBufferStubBase& other) const OVERRIDE;

  // Sets buffer usage depending on Memory Allocation
  virtual void SetMemoryAllocation(
      const GpuMemoryAllocation& allocation) OVERRIDE;

  // Whether this command buffer can currently handle IPC messages.
  bool IsScheduled();

  // Whether this command buffer needs to be polled again in the future.
  bool HasMoreWork();

  // Poll the command buffer to execute work.
  void PollWork();

  // Whether there are commands in the buffer that haven't been processed.
  bool HasUnprocessedCommands();

  // Delay an echo message until the command buffer has been rescheduled.
  void DelayEcho(IPC::Message*);

  gpu::gles2::GLES2Decoder* decoder() const { return decoder_.get(); }
  gpu::GpuScheduler* scheduler() const { return scheduler_.get(); }
  GpuChannel* channel() const { return channel_; }

  // Identifies the target surface.
  int32 surface_id() const {
    return (surface_state_.get()) ? surface_state_->surface_id : 0;
  }

  // Identifies the various GpuCommandBufferStubs in the GPU process belonging
  // to the same renderer process.
  int32 route_id() const { return route_id_; }

  gfx::GpuPreference gpu_preference() { return gpu_preference_; }

  // Sends a message to the console.
  void SendConsoleMessage(int32 id, const std::string& message);

  gfx::GLSurface* surface() const { return surface_; }

  void AddDestructionObserver(DestructionObserver* observer);
  void RemoveDestructionObserver(DestructionObserver* observer);

  // Associates a sync point to this stub. When the stub is destroyed, it will
  // retire all sync points that haven't been previously retired.
  void AddSyncPoint(uint32 sync_point);

  void SetPreemptByCounter(scoped_refptr<gpu::RefCountedCounter> counter);

 private:
  bool MakeCurrent();
  void Destroy();

  // Cleans up and sends reply if OnInitialize failed.
  void OnInitializeFailed(IPC::Message* reply_message);

  // Message handlers:
  void OnInitialize(IPC::Message* reply_message);
  void OnSetGetBuffer(int32 shm_id, IPC::Message* reply_message);
  void OnSetSharedStateBuffer(int32 shm_id, IPC::Message* reply_message);
  void OnSetParent(int32 parent_route_id,
                   uint32 parent_texture_id,
                   IPC::Message* reply_message);
  void OnGetState(IPC::Message* reply_message);
  void OnGetStateFast(IPC::Message* reply_message);
  void OnAsyncFlush(int32 put_offset, uint32 flush_count);
  void OnEcho(const IPC::Message& message);
  void OnRescheduled();
  void OnCreateTransferBuffer(int32 size,
                              int32 id_request,
                              IPC::Message* reply_message);
  void OnRegisterTransferBuffer(base::SharedMemoryHandle transfer_buffer,
                                size_t size,
                                int32 id_request,
                                IPC::Message* reply_message);
  void OnDestroyTransferBuffer(int32 id, IPC::Message* reply_message);
  void OnGetTransferBuffer(int32 id, IPC::Message* reply_message);

  void OnCreateVideoDecoder(
      media::VideoCodecProfile profile,
      IPC::Message* reply_message);
  void OnDestroyVideoDecoder(int32 decoder_route_id);

  void OnSetSurfaceVisible(bool visible);

  void OnDiscardBackbuffer();
  void OnEnsureBackbuffer();

  void OnRetireSyncPoint(uint32 sync_point);
  void OnWaitSyncPoint(uint32 sync_point);
  void OnSyncPointRetired();
  void OnSignalSyncPoint(uint32 sync_point, uint32 id);
  void OnSignalSyncPointAck(uint32 id);

  void OnSetClientHasMemoryAllocationChangedCallback(bool);

  void OnReschedule();

  void OnCommandProcessed();
  void OnParseError();

  void ReportState();

  // The lifetime of objects of this class is managed by a GpuChannel. The
  // GpuChannels destroy all the GpuCommandBufferStubs that they own when they
  // are destroyed. So a raw pointer is safe.
  GpuChannel* channel_;

  // The group of contexts that share namespaces with this context.
  scoped_refptr<gpu::gles2::ContextGroup> context_group_;

  gfx::GLSurfaceHandle handle_;
  gfx::Size initial_size_;
  gpu::gles2::DisallowedFeatures disallowed_features_;
  std::string allowed_extensions_;
  std::vector<int32> requested_attribs_;
  gfx::GpuPreference gpu_preference_;
  int32 route_id_;
  bool software_;
  bool client_has_memory_allocation_changed_callback_;
  uint32 last_flush_count_;
  scoped_ptr<GpuCommandBufferStubBase::SurfaceState> surface_state_;

  scoped_ptr<gpu::CommandBufferService> command_buffer_;
  scoped_ptr<gpu::gles2::GLES2Decoder> decoder_;
  scoped_ptr<gpu::GpuScheduler> scheduler_;
  scoped_refptr<gfx::GLContext> context_;
  scoped_refptr<gfx::GLSurface> surface_;

  // SetParent may be called before Initialize, in which case we need to keep
  // around the parent stub, so that Initialize can set the parent correctly.
  base::WeakPtr<GpuCommandBufferStub> parent_stub_for_initialization_;
  uint32 parent_texture_for_initialization_;

  GpuWatchdog* watchdog_;

  std::deque<IPC::Message*> delayed_echos_;

  // Zero or more video decoders owned by this stub, keyed by their
  // decoder_route_id.
  IDMap<GpuVideoDecodeAccelerator, IDMapOwnPointer> video_decoders_;

  ObserverList<DestructionObserver> destruction_observers_;

  // A queue of sync points associated with this stub.
  std::deque<uint32> sync_points_;
  int sync_point_wait_count_;

  scoped_refptr<gpu::RefCountedCounter> preempt_by_counter_;

  DISALLOW_COPY_AND_ASSIGN(GpuCommandBufferStub);
};

#endif  // CONTENT_COMMON_GPU_GPU_COMMAND_BUFFER_STUB_H_