// Copyright (c) 2011 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_BROWSER_GPU_GPU_PROCESS_HOST_H_
#define CONTENT_BROWSER_GPU_GPU_PROCESS_HOST_H_
#pragma once

#include <map>
#include <queue>

#include "base/callback_old.h"
#include "base/memory/linked_ptr.h"
#include "base/threading/non_thread_safe.h"
#include "content/browser/browser_child_process_host.h"
#include "content/common/gpu/gpu_info.h"
#include "content/common/gpu/gpu_process_launch_causes.h"
#include "ui/gfx/native_widget_types.h"

struct GPUCreateCommandBufferConfig;

namespace IPC {
class Message;
}

class GpuMainThread;

class GpuProcessHost : public BrowserChildProcessHost,
                       public base::NonThreadSafe {
 public:
  static bool gpu_enabled() { return gpu_enabled_; }

  // Creates a new GpuProcessHost or gets one for a particular
  // renderer process, resulting in the launching of a GPU process if required.
  // Returns null on failure. It is not safe to store the pointer once control
  // has returned to the message loop as it can be destroyed. Instead store the
  // associated GPU host ID. A renderer ID of zero means the browser process.
  // This could return NULL if GPU access is not allowed (blacklisted).
  static GpuProcessHost* GetForRenderer(int renderer_id,
                                        content::CauseForGpuLaunch cause);

  // Helper function to send the given message to the GPU process on the IO
  // thread.  Calls GetForRenderer and if a host is returned, sends it.
  // Can be called from any thread.
  static void SendOnIO(int renderer_id,
                       content::CauseForGpuLaunch cause,
                       IPC::Message* message);

  // Get the GPU process host for the GPU process with the given ID. Returns
  // null if the process no longer exists.
  static GpuProcessHost* FromID(int host_id);
  int host_id() const { return host_id_; }

  virtual bool Send(IPC::Message* msg);

  // ChildProcessHost implementation.
  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
  virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;

  typedef Callback3<const IPC::ChannelHandle&,
                    base::ProcessHandle,
                    const GPUInfo&>::Type
    EstablishChannelCallback;

  // Tells the GPU process to create a new channel for communication with a
  // renderer. Once the GPU process responds asynchronously with the IPC handle
  // and GPUInfo, we call the callback.
  void EstablishGpuChannel(
      int renderer_id, EstablishChannelCallback* callback);

  typedef Callback1<int32>::Type CreateCommandBufferCallback;

  // Tells the GPU process to create a new command buffer that draws into the
  // window associated with the given renderer.
  void CreateViewCommandBuffer(
      gfx::PluginWindowHandle compositing_surface,
      int32 render_view_id,
      int32 renderer_id,
      const GPUCreateCommandBufferConfig& init_params,
      CreateCommandBufferCallback* callback);

 private:
  GpuProcessHost(int host_id);
  virtual ~GpuProcessHost();

  bool Init();

  // Post an IPC message to the UI shim's message handler on the UI thread.
  void RouteOnUIThread(const IPC::Message& message);

  virtual bool CanShutdown();
  virtual void OnProcessLaunched();
  virtual void OnChildDied();
  virtual void OnProcessCrashed(int exit_code);

  // Message handlers.
  void OnChannelEstablished(const IPC::ChannelHandle& channel_handle);
  void OnCommandBufferCreated(const int32 route_id);
  void OnDestroyCommandBuffer(
      gfx::PluginWindowHandle window, int32 renderer_id, int32 render_view_id);
  void OnGraphicsInfoCollected(const GPUInfo& gpu_info);

  bool LaunchGpuProcess();

  void SendOutstandingReplies();
  void EstablishChannelError(
      EstablishChannelCallback* callback,
      const IPC::ChannelHandle& channel_handle,
      base::ProcessHandle renderer_process_for_gpu,
      const GPUInfo& gpu_info);
  void CreateCommandBufferError(CreateCommandBufferCallback* callback,
                                int32 route_id);

  // The serial number of the GpuProcessHost / GpuProcessHostUIShim pair.
  int host_id_;

  // These are the channel requests that we have already sent to
  // the GPU process, but haven't heard back about yet.
  std::queue<linked_ptr<EstablishChannelCallback> > channel_requests_;

  // The pending create command buffer requests we need to reply to.
  std::queue<linked_ptr<CreateCommandBufferCallback> >
      create_command_buffer_requests_;

#if defined(TOOLKIT_USES_GTK) && !defined(TOUCH_UI)
  typedef std::pair<int32 /* renderer_id */,
                    int32 /* render_view_id */> ViewID;

  // Encapsulates surfaces that we lock when creating view command buffers.
  // We release this lock once the command buffer (or associated GPU process)
  // is destroyed. This prevents the browser from destroying the surface
  // while the GPU process is drawing to it.

  // Multimap is used to simulate reference counting, see comment in
  // GpuProcessHostUIShim::CreateViewCommandBuffer.
  class SurfaceRef;
  typedef std::multimap<ViewID, linked_ptr<SurfaceRef> > SurfaceRefMap;
  SurfaceRefMap surface_refs_;
#endif

  // Qeueud messages to send when the process launches.
  std::queue<IPC::Message*> queued_messages_;

  // The handle for the GPU process or null if it is not known to be launched.
  base::ProcessHandle gpu_process_;

  // Whether we are running a GPU thread inside the browser process instead
  // of a separate GPU process.
  bool in_process_;

  scoped_ptr<GpuMainThread> in_process_gpu_thread_;

  // Master switch for enabling/disabling GPU acceleration for the current
  // browser session. It does not change the acceleration settings for
  // existing tabs, just the future ones.
  static bool gpu_enabled_;

  DISALLOW_COPY_AND_ASSIGN(GpuProcessHost);
};

#endif  // CONTENT_BROWSER_GPU_GPU_PROCESS_HOST_H_