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.
  
  #include "content/common/gpu/gpu_channel_manager.h"
  
  #include "base/bind.h"
  #include "base/command_line.h"
  #include "content/common/child_thread.h"
  #include "content/common/gpu/gpu_channel.h"
  #include "content/common/gpu/gpu_memory_manager.h"
  #include "content/common/gpu/gpu_messages.h"
  #include "content/common/gpu/sync_point_manager.h"
  #include "gpu/command_buffer/service/feature_info.h"
  #include "gpu/command_buffer/service/gpu_switches.h"
  #include "gpu/command_buffer/service/mailbox_manager.h"
  #include "gpu/command_buffer/service/memory_program_cache.h"
  #include "ui/gl/gl_bindings.h"
  #include "ui/gl/gl_share_group.h"
  
  namespace content {
  
  GpuChannelManager::ImageOperation::ImageOperation(
      int32 sync_point, base::Closure callback)
      : sync_point(sync_point),
        callback(callback) {
  }
  
  GpuChannelManager::ImageOperation::~ImageOperation() {
  }
  
  GpuChannelManager::GpuChannelManager(ChildThread* gpu_child_thread,
                                       GpuWatchdog* watchdog,
                                       base::MessageLoopProxy* io_message_loop,
                                       base::WaitableEvent* shutdown_event)
      : weak_factory_(this),
        io_message_loop_(io_message_loop),
        shutdown_event_(shutdown_event),
        gpu_child_thread_(gpu_child_thread),
        gpu_memory_manager_(
            this, GpuMemoryManager::kDefaultMaxSurfacesWithFrontbufferSoftLimit),
        watchdog_(watchdog),
        sync_point_manager_(new SyncPointManager),
        program_cache_(NULL) {
    DCHECK(gpu_child_thread);
    DCHECK(io_message_loop);
    DCHECK(shutdown_event);
  }
  
  GpuChannelManager::~GpuChannelManager() {
    gpu_channels_.clear();
    if (default_offscreen_surface_) {
      default_offscreen_surface_->Destroy();
      default_offscreen_surface_ = NULL;
    }
    DCHECK(image_operations_.empty());
  }
  
  gpu::gles2::ProgramCache* GpuChannelManager::program_cache() {
    if (!program_cache_.get() &&
        (gfx::g_driver_gl.ext.b_ARB_get_program_binary ||
         gfx::g_driver_gl.ext.b_OES_get_program_binary) &&
        !CommandLine::ForCurrentProcess()->HasSwitch(
            switches::kDisableGpuProgramCache)) {
      program_cache_.reset(new gpu::gles2::MemoryProgramCache());
    }
    return program_cache_.get();
  }
  
  void GpuChannelManager::RemoveChannel(int client_id) {
    Send(new GpuHostMsg_DestroyChannel(client_id));
    gpu_channels_.erase(client_id);
  }
  
  int GpuChannelManager::GenerateRouteID() {
    static int last_id = 0;
    return ++last_id;
  }
  
  void GpuChannelManager::AddRoute(int32 routing_id, IPC::Listener* listener) {
    gpu_child_thread_->AddRoute(routing_id, listener);
  }
  
  void GpuChannelManager::RemoveRoute(int32 routing_id) {
    gpu_child_thread_->RemoveRoute(routing_id);
  }
  
  GpuChannel* GpuChannelManager::LookupChannel(int32 client_id) {
    GpuChannelMap::const_iterator iter = gpu_channels_.find(client_id);
    if (iter == gpu_channels_.end())
      return NULL;
    else
      return iter->second;
  }
  
  bool GpuChannelManager::OnMessageReceived(const IPC::Message& msg) {
    bool msg_is_ok = true;
    bool handled = true;
    IPC_BEGIN_MESSAGE_MAP_EX(GpuChannelManager, msg, msg_is_ok)
      IPC_MESSAGE_HANDLER(GpuMsg_EstablishChannel, OnEstablishChannel)
      IPC_MESSAGE_HANDLER(GpuMsg_CloseChannel, OnCloseChannel)
      IPC_MESSAGE_HANDLER(GpuMsg_CreateViewCommandBuffer,
                          OnCreateViewCommandBuffer)
      IPC_MESSAGE_HANDLER(GpuMsg_CreateImage, OnCreateImage)
      IPC_MESSAGE_HANDLER(GpuMsg_DeleteImage, OnDeleteImage)
      IPC_MESSAGE_HANDLER(GpuMsg_LoadedShader, OnLoadedShader)
      IPC_MESSAGE_UNHANDLED(handled = false)
    IPC_END_MESSAGE_MAP_EX()
    return handled;
  }
  
  bool GpuChannelManager::Send(IPC::Message* msg) {
    return gpu_child_thread_->Send(msg);
  }
  
  void GpuChannelManager::OnEstablishChannel(int client_id, bool share_context) {
    IPC::ChannelHandle channel_handle;
  
    gfx::GLShareGroup* share_group = NULL;
    gpu::gles2::MailboxManager* mailbox_manager = NULL;
    if (share_context) {
      if (!share_group_) {
        share_group_ = new gfx::GLShareGroup;
        DCHECK(!mailbox_manager_);
        mailbox_manager_ = new gpu::gles2::MailboxManager;
      }
      share_group = share_group_;
      mailbox_manager = mailbox_manager_;
    }
  
    scoped_refptr<GpuChannel> channel = new GpuChannel(this,
                                                       watchdog_,
                                                       share_group,
                                                       mailbox_manager,
                                                       client_id,
                                                       false);
    if (channel->Init(io_message_loop_, shutdown_event_)) {
      gpu_channels_[client_id] = channel;
      channel_handle.name = channel->GetChannelName();
  
  #if defined(OS_POSIX)
      // On POSIX, pass the renderer-side FD. Also mark it as auto-close so
      // that it gets closed after it has been sent.
      int renderer_fd = channel->TakeRendererFileDescriptor();
      DCHECK_NE(-1, renderer_fd);
      channel_handle.socket = base::FileDescriptor(renderer_fd, true);
  #endif
    }
  
    Send(new GpuHostMsg_ChannelEstablished(channel_handle));
  }
  
  void GpuChannelManager::OnCloseChannel(
      const IPC::ChannelHandle& channel_handle) {
    for (GpuChannelMap::iterator iter = gpu_channels_.begin();
         iter != gpu_channels_.end(); ++iter) {
      if (iter->second->GetChannelName() == channel_handle.name) {
        gpu_channels_.erase(iter);
        return;
      }
    }
  }
  
  void GpuChannelManager::OnCreateViewCommandBuffer(
      const gfx::GLSurfaceHandle& window,
      int32 surface_id,
      int32 client_id,
      const GPUCreateCommandBufferConfig& init_params) {
    DCHECK(surface_id);
    int32 route_id = MSG_ROUTING_NONE;
  
    GpuChannelMap::const_iterator iter = gpu_channels_.find(client_id);
    if (iter != gpu_channels_.end()) {
      iter->second->CreateViewCommandBuffer(
          window, surface_id, init_params, &route_id);
    }
  
    Send(new GpuHostMsg_CommandBufferCreated(route_id));
  }
  
  void GpuChannelManager::CreateImage(
      gfx::PluginWindowHandle window, int32 client_id, int32 image_id) {
    gfx::Size size;
  
    GpuChannelMap::const_iterator iter = gpu_channels_.find(client_id);
    if (iter != gpu_channels_.end()) {
      iter->second->CreateImage(window, image_id, &size);
    }
  
    Send(new GpuHostMsg_ImageCreated(size));
  }
  
  void GpuChannelManager::OnCreateImage(
      gfx::PluginWindowHandle window, int32 client_id, int32 image_id) {
    DCHECK(image_id);
  
    if (image_operations_.empty()) {
      CreateImage(window, client_id, image_id);
    } else {
      image_operations_.push_back(
          new ImageOperation(0, base::Bind(&GpuChannelManager::CreateImage,
                                           base::Unretained(this),
                                           window,
                                           client_id,
                                           image_id)));
    }
  }
  
  void GpuChannelManager::DeleteImage(int32 client_id, int32 image_id) {
    GpuChannelMap::const_iterator iter = gpu_channels_.find(client_id);
    if (iter != gpu_channels_.end()) {
      iter->second->DeleteImage(image_id);
    }
  }
  
  void GpuChannelManager::OnDeleteImage(
      int32 client_id, int32 image_id, int32 sync_point) {
    DCHECK(image_id);
  
    if (!sync_point && image_operations_.empty()) {
      DeleteImage(client_id, image_id);
    } else {
      image_operations_.push_back(
          new ImageOperation(sync_point,
                             base::Bind(&GpuChannelManager::DeleteImage,
                                        base::Unretained(this),
                                        client_id,
                                        image_id)));
      if (sync_point) {
        sync_point_manager()->AddSyncPointCallback(
            sync_point,
            base::Bind(&GpuChannelManager::OnDeleteImageSyncPointRetired,
                       base::Unretained(this),
                       image_operations_.back()));
      }
    }
  }
  
  void GpuChannelManager::OnDeleteImageSyncPointRetired(
      ImageOperation* image_operation) {
    // Mark operation as no longer having a pending sync point.
    image_operation->sync_point = 0;
  
    // De-queue operations until we reach a pending sync point.
    while (!image_operations_.empty()) {
      // Check if operation has a pending sync point.
      if (image_operations_.front()->sync_point)
        return;
  
      image_operations_.front()->callback.Run();
      delete image_operations_.front();
      image_operations_.pop_front();
    }
  }
  
  void GpuChannelManager::OnLoadedShader(std::string program_proto) {
    if (program_cache())
      program_cache()->LoadProgram(program_proto);
  }
  
  bool GpuChannelManager::HandleMessagesScheduled() {
    for (GpuChannelMap::iterator iter = gpu_channels_.begin();
         iter != gpu_channels_.end(); ++iter) {
      if (iter->second->handle_messages_scheduled())
        return true;
    }
    return false;
  }
  
  uint64 GpuChannelManager::MessagesProcessed() {
    uint64 messages_processed = 0;
  
    for (GpuChannelMap::iterator iter = gpu_channels_.begin();
         iter != gpu_channels_.end(); ++iter) {
      messages_processed += iter->second->messages_processed();
    }
    return messages_processed;
  }
  
  void GpuChannelManager::LoseAllContexts() {
    MessageLoop::current()->PostTask(
        FROM_HERE,
        base::Bind(&GpuChannelManager::OnLoseAllContexts,
                   weak_factory_.GetWeakPtr()));
  }
  
  void GpuChannelManager::OnLoseAllContexts() {
    gpu_channels_.clear();
  }
  
  gfx::GLSurface* GpuChannelManager::GetDefaultOffscreenSurface() {
    if (!default_offscreen_surface_) {
      default_offscreen_surface_ = gfx::GLSurface::CreateOffscreenGLSurface(
          false, gfx::Size(1, 1));
    }
    return default_offscreen_surface_.get();
  }
  
  }  // namespace content
  