1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/renderer_host/compositing_iosurface_context_mac.h"
6
7#include <OpenGL/gl.h>
8#include <OpenGL/OpenGL.h>
9#include <vector>
10
11#include "base/command_line.h"
12#include "base/debug/trace_event.h"
13#include "base/logging.h"
14#include "content/browser/gpu/gpu_data_manager_impl.h"
15#include "ui/base/ui_base_switches.h"
16#include "ui/gl/gl_switches.h"
17#include "ui/gl/gpu_switching_manager.h"
18
19namespace content {
20
21// static
22scoped_refptr<CompositingIOSurfaceContext>
23CompositingIOSurfaceContext::Get(int window_number) {
24  TRACE_EVENT0("browser", "CompositingIOSurfaceContext::Get");
25
26  // Return the context for this window_number, if it exists.
27  WindowMap::iterator found = window_map()->find(window_number);
28  if (found != window_map()->end()) {
29    DCHECK(!found->second->poisoned_);
30    return found->second;
31  }
32
33  base::ScopedTypeRef<CGLContextObj> cgl_context_strong;
34  CGLContextObj cgl_context = NULL;
35  CGLError error = kCGLNoError;
36
37  // Create the pixel format object for the context.
38  std::vector<CGLPixelFormatAttribute> attribs;
39  attribs.push_back(kCGLPFADepthSize);
40  attribs.push_back(static_cast<CGLPixelFormatAttribute>(0));
41  if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus()) {
42    attribs.push_back(kCGLPFAAllowOfflineRenderers);
43    attribs.push_back(static_cast<CGLPixelFormatAttribute>(1));
44  }
45  attribs.push_back(static_cast<CGLPixelFormatAttribute>(0));
46  GLint number_virtual_screens = 0;
47  base::ScopedTypeRef<CGLPixelFormatObj> pixel_format;
48  error = CGLChoosePixelFormat(&attribs.front(),
49                               pixel_format.InitializeInto(),
50                               &number_virtual_screens);
51  if (error != kCGLNoError) {
52    LOG(ERROR) << "Failed to create pixel format object.";
53    return NULL;
54  }
55
56  // Create all contexts in the same share group so that the textures don't
57  // need to be recreated when transitioning contexts.
58  CGLContextObj share_context = NULL;
59  if (!window_map()->empty())
60    share_context = window_map()->begin()->second->cgl_context();
61  error = CGLCreateContext(
62      pixel_format, share_context, cgl_context_strong.InitializeInto());
63  if (error != kCGLNoError) {
64    LOG(ERROR) << "Failed to create context object.";
65    return NULL;
66  }
67  cgl_context = cgl_context_strong;
68
69  // Note that VSync is ignored because CoreAnimation will automatically
70  // rate limit draws.
71
72  return new CompositingIOSurfaceContext(
73      window_number,
74      cgl_context_strong,
75      cgl_context);
76}
77
78void CompositingIOSurfaceContext::PoisonContextAndSharegroup() {
79  if (poisoned_)
80    return;
81
82  for (WindowMap::iterator it = window_map()->begin();
83       it != window_map()->end();
84       ++it) {
85    it->second->poisoned_ = true;
86  }
87  window_map()->clear();
88}
89
90CompositingIOSurfaceContext::CompositingIOSurfaceContext(
91    int window_number,
92    base::ScopedTypeRef<CGLContextObj> cgl_context_strong,
93    CGLContextObj cgl_context)
94    : window_number_(window_number),
95      cgl_context_strong_(cgl_context_strong),
96      cgl_context_(cgl_context),
97      poisoned_(false) {
98  DCHECK(window_map()->find(window_number_) == window_map()->end());
99  window_map()->insert(std::make_pair(window_number_, this));
100
101  GpuDataManager::GetInstance()->AddObserver(this);
102}
103
104CompositingIOSurfaceContext::~CompositingIOSurfaceContext() {
105  GpuDataManager::GetInstance()->RemoveObserver(this);
106
107  if (!poisoned_) {
108    DCHECK(window_map()->find(window_number_) != window_map()->end());
109    DCHECK(window_map()->find(window_number_)->second == this);
110    window_map()->erase(window_number_);
111  } else {
112    WindowMap::const_iterator found = window_map()->find(window_number_);
113    if (found != window_map()->end())
114      DCHECK(found->second != this);
115  }
116}
117
118void CompositingIOSurfaceContext::OnGpuSwitching() {
119  // Recreate all browser-side GL contexts whenever the GPU switches. If this
120  // is not done, performance will suffer.
121  // http://crbug.com/361493
122  PoisonContextAndSharegroup();
123}
124
125// static
126CompositingIOSurfaceContext::WindowMap*
127    CompositingIOSurfaceContext::window_map() {
128  return window_map_.Pointer();
129}
130
131// static
132base::LazyInstance<CompositingIOSurfaceContext::WindowMap>
133    CompositingIOSurfaceContext::window_map_;
134
135}  // namespace content
136