gl_context_cgl.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 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 "ui/gl/gl_context_cgl.h"
6
7#include <OpenGL/CGLRenderers.h>
8#include <OpenGL/CGLTypes.h>
9#include <vector>
10
11#include "base/debug/trace_event.h"
12#include "base/logging.h"
13#include "ui/gl/gl_bindings.h"
14#include "ui/gl/gl_implementation.h"
15#include "ui/gl/gl_surface_cgl.h"
16#include "ui/gl/gpu_switching_manager.h"
17
18namespace gfx {
19
20GLContextCGL::GLContextCGL(GLShareGroup* share_group)
21  : GLContext(share_group),
22    context_(NULL),
23    gpu_preference_(PreferIntegratedGpu),
24    discrete_pixelformat_(NULL) {
25}
26
27bool GLContextCGL::Initialize(GLSurface* compatible_surface,
28                              GpuPreference gpu_preference) {
29  DCHECK(compatible_surface);
30
31  gpu_preference = ui::GpuSwitchingManager::GetInstance()->AdjustGpuPreference(
32      gpu_preference);
33
34  GLContextCGL* share_context = share_group() ?
35      static_cast<GLContextCGL*>(share_group()->GetContext()) : NULL;
36
37  std::vector<CGLPixelFormatAttribute> attribs;
38  // If the system supports dual gpus then allow offline renderers for every
39  // context, so that they can all be in the same share group.
40  if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus())
41    attribs.push_back(kCGLPFAAllowOfflineRenderers);
42  if (GetGLImplementation() == kGLImplementationAppleGL) {
43    attribs.push_back(kCGLPFARendererID);
44    attribs.push_back((CGLPixelFormatAttribute) kCGLRendererGenericFloatID);
45  }
46  attribs.push_back((CGLPixelFormatAttribute) 0);
47
48  CGLPixelFormatObj format;
49  GLint num_pixel_formats;
50  if (CGLChoosePixelFormat(&attribs.front(),
51                           &format,
52                           &num_pixel_formats) != kCGLNoError) {
53    LOG(ERROR) << "Error choosing pixel format.";
54    return false;
55  }
56  if (!format) {
57    LOG(ERROR) << "format == 0.";
58    return false;
59  }
60  DCHECK_NE(num_pixel_formats, 0);
61
62  // If using the discrete gpu, create a pixel format requiring it before we
63  // create the context.
64  if (!ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus() ||
65      gpu_preference == PreferDiscreteGpu) {
66    std::vector<CGLPixelFormatAttribute> discrete_attribs;
67    discrete_attribs.push_back((CGLPixelFormatAttribute) 0);
68    GLint num_pixel_formats;
69    if (CGLChoosePixelFormat(&discrete_attribs.front(),
70                             &discrete_pixelformat_,
71                             &num_pixel_formats) != kCGLNoError) {
72      LOG(ERROR) << "Error choosing pixel format.";
73      return false;
74    }
75  }
76
77  CGLError res = CGLCreateContext(
78      format,
79      share_context ?
80          static_cast<CGLContextObj>(share_context->GetHandle()) : NULL,
81      reinterpret_cast<CGLContextObj*>(&context_));
82  CGLReleasePixelFormat(format);
83  if (res != kCGLNoError) {
84    LOG(ERROR) << "Error creating context.";
85    Destroy();
86    return false;
87  }
88
89  gpu_preference_ = gpu_preference;
90  return true;
91}
92
93void GLContextCGL::Destroy() {
94  if (discrete_pixelformat_) {
95    CGLReleasePixelFormat(discrete_pixelformat_);
96    discrete_pixelformat_ = NULL;
97  }
98  if (context_) {
99    CGLDestroyContext(static_cast<CGLContextObj>(context_));
100    context_ = NULL;
101  }
102}
103
104bool GLContextCGL::MakeCurrent(GLSurface* surface) {
105  DCHECK(context_);
106  if (IsCurrent(surface))
107    return true;
108
109  TRACE_EVENT0("gpu", "GLContextCGL::MakeCurrent");
110
111  if (CGLSetCurrentContext(
112      static_cast<CGLContextObj>(context_)) != kCGLNoError) {
113    LOG(ERROR) << "Unable to make gl context current.";
114    return false;
115  }
116
117  SetCurrent(this, surface);
118  if (!InitializeExtensionBindings()) {
119    ReleaseCurrent(surface);
120    return false;
121  }
122
123  if (!surface->OnMakeCurrent(this)) {
124    LOG(ERROR) << "Unable to make gl context current.";
125    return false;
126  }
127
128  SetRealGLApi();
129  return true;
130}
131
132void GLContextCGL::ReleaseCurrent(GLSurface* surface) {
133  if (!IsCurrent(surface))
134    return;
135
136  SetCurrent(NULL, NULL);
137  CGLSetCurrentContext(NULL);
138}
139
140bool GLContextCGL::IsCurrent(GLSurface* surface) {
141  bool native_context_is_current = CGLGetCurrentContext() == context_;
142
143  // If our context is current then our notion of which GLContext is
144  // current must be correct. On the other hand, third-party code
145  // using OpenGL might change the current context.
146  DCHECK(!native_context_is_current || (GetCurrent() == this));
147
148  if (!native_context_is_current)
149    return false;
150
151  return true;
152}
153
154void* GLContextCGL::GetHandle() {
155  return context_;
156}
157
158void GLContextCGL::SetSwapInterval(int interval) {
159  DCHECK(IsCurrent(NULL));
160  LOG(WARNING) << "GLContex: GLContextCGL::SetSwapInterval is ignored.";
161}
162
163
164bool GLContextCGL::GetTotalGpuMemory(size_t* bytes) {
165  DCHECK(bytes);
166  *bytes = 0;
167
168  CGLContextObj context = reinterpret_cast<CGLContextObj>(context_);
169  if (!context)
170    return false;
171
172  // Retrieve the current renderer ID
173  GLint current_renderer_id = 0;
174  if (CGLGetParameter(context,
175                      kCGLCPCurrentRendererID,
176                      &current_renderer_id) != kCGLNoError)
177    return false;
178
179  // Iterate through the list of all renderers
180  GLuint display_mask = static_cast<GLuint>(-1);
181  CGLRendererInfoObj renderer_info = NULL;
182  GLint num_renderers = 0;
183  if (CGLQueryRendererInfo(display_mask,
184                           &renderer_info,
185                           &num_renderers) != kCGLNoError)
186    return false;
187
188  ScopedCGLRendererInfoObj scoper(renderer_info);
189
190  for (GLint renderer_index = 0;
191       renderer_index < num_renderers;
192       ++renderer_index) {
193    // Skip this if this renderer is not the current renderer.
194    GLint renderer_id = 0;
195    if (CGLDescribeRenderer(renderer_info,
196                            renderer_index,
197                            kCGLRPRendererID,
198                            &renderer_id) != kCGLNoError)
199        continue;
200    if (renderer_id != current_renderer_id)
201        continue;
202    // Retrieve the video memory for the renderer.
203    GLint video_memory = 0;
204    if (CGLDescribeRenderer(renderer_info,
205                            renderer_index,
206                            kCGLRPVideoMemory,
207                            &video_memory) != kCGLNoError)
208        continue;
209    *bytes = video_memory;
210    return true;
211  }
212
213  return false;
214}
215
216GLContextCGL::~GLContextCGL() {
217  Destroy();
218}
219
220GpuPreference GLContextCGL::GetGpuPreference() {
221  return gpu_preference_;
222}
223
224void ScopedCGLDestroyRendererInfo::operator()(CGLRendererInfoObj x) const {
225  CGLDestroyRendererInfo(x);
226}
227
228}  // namespace gfx
229