• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/memory/scoped_ptr.h"
14 #include "ui/gl/gl_bindings.h"
15 #include "ui/gl/gl_implementation.h"
16 #include "ui/gl/gl_surface.h"
17 #include "ui/gl/gpu_switching_manager.h"
18 
19 namespace gfx {
20 
21 namespace {
22 
23 bool g_support_renderer_switching;
24 
25 struct CGLRendererInfoObjDeleter {
operator ()gfx::__anon7124ae510111::CGLRendererInfoObjDeleter26   void operator()(CGLRendererInfoObj* x) {
27     if (x)
28       CGLDestroyRendererInfo(*x);
29   }
30 };
31 
32 }  // namespace
33 
GetPixelFormat()34 static CGLPixelFormatObj GetPixelFormat() {
35   static CGLPixelFormatObj format;
36   if (format)
37     return format;
38   std::vector<CGLPixelFormatAttribute> attribs;
39   // If the system supports dual gpus then allow offline renderers for every
40   // context, so that they can all be in the same share group.
41   if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus()) {
42     attribs.push_back(kCGLPFAAllowOfflineRenderers);
43     g_support_renderer_switching = true;
44   }
45   if (GetGLImplementation() == kGLImplementationAppleGL) {
46     attribs.push_back(kCGLPFARendererID);
47     attribs.push_back((CGLPixelFormatAttribute) kCGLRendererGenericFloatID);
48     g_support_renderer_switching = false;
49   }
50   attribs.push_back((CGLPixelFormatAttribute) 0);
51 
52   GLint num_virtual_screens;
53   if (CGLChoosePixelFormat(&attribs.front(),
54                            &format,
55                            &num_virtual_screens) != kCGLNoError) {
56     LOG(ERROR) << "Error choosing pixel format.";
57     return NULL;
58   }
59   if (!format) {
60     LOG(ERROR) << "format == 0.";
61     return NULL;
62   }
63   DCHECK_NE(num_virtual_screens, 0);
64   return format;
65 }
66 
GLContextCGL(GLShareGroup * share_group)67 GLContextCGL::GLContextCGL(GLShareGroup* share_group)
68   : GLContextReal(share_group),
69     context_(NULL),
70     gpu_preference_(PreferIntegratedGpu),
71     discrete_pixelformat_(NULL),
72     screen_(-1),
73     renderer_id_(-1),
74     safe_to_force_gpu_switch_(false) {
75 }
76 
Initialize(GLSurface * compatible_surface,GpuPreference gpu_preference)77 bool GLContextCGL::Initialize(GLSurface* compatible_surface,
78                               GpuPreference gpu_preference) {
79   DCHECK(compatible_surface);
80 
81   gpu_preference = ui::GpuSwitchingManager::GetInstance()->AdjustGpuPreference(
82       gpu_preference);
83 
84   GLContextCGL* share_context = share_group() ?
85       static_cast<GLContextCGL*>(share_group()->GetContext()) : NULL;
86 
87   CGLPixelFormatObj format = GetPixelFormat();
88   if (!format)
89     return false;
90 
91   // If using the discrete gpu, create a pixel format requiring it before we
92   // create the context.
93   if (!ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus() ||
94       gpu_preference == PreferDiscreteGpu) {
95     std::vector<CGLPixelFormatAttribute> discrete_attribs;
96     discrete_attribs.push_back((CGLPixelFormatAttribute) 0);
97     GLint num_pixel_formats;
98     if (CGLChoosePixelFormat(&discrete_attribs.front(),
99                              &discrete_pixelformat_,
100                              &num_pixel_formats) != kCGLNoError) {
101       LOG(ERROR) << "Error choosing pixel format.";
102       return false;
103     }
104     // The renderer might be switched after this, so ignore the saved ID.
105     share_group()->SetRendererID(-1);
106   }
107 
108   CGLError res = CGLCreateContext(
109       format,
110       share_context ?
111           static_cast<CGLContextObj>(share_context->GetHandle()) : NULL,
112       reinterpret_cast<CGLContextObj*>(&context_));
113   if (res != kCGLNoError) {
114     LOG(ERROR) << "Error creating context.";
115     Destroy();
116     return false;
117   }
118 
119   gpu_preference_ = gpu_preference;
120   return true;
121 }
122 
Destroy()123 void GLContextCGL::Destroy() {
124   if (discrete_pixelformat_) {
125     // Delay releasing the pixel format for 10 seconds to reduce the number of
126     // unnecessary GPU switches.
127     base::MessageLoop::current()->PostDelayedTask(
128         FROM_HERE,
129         base::Bind(&CGLReleasePixelFormat, discrete_pixelformat_),
130         base::TimeDelta::FromSeconds(10));
131     discrete_pixelformat_ = NULL;
132   }
133   if (context_) {
134     CGLDestroyContext(static_cast<CGLContextObj>(context_));
135     context_ = NULL;
136   }
137 }
138 
MakeCurrent(GLSurface * surface)139 bool GLContextCGL::MakeCurrent(GLSurface* surface) {
140   DCHECK(context_);
141 
142   // The call to CGLSetVirtualScreen can hang on some AMD drivers
143   // http://crbug.com/227228
144   if (safe_to_force_gpu_switch_) {
145     int renderer_id = share_group()->GetRendererID();
146     int screen;
147     CGLGetVirtualScreen(static_cast<CGLContextObj>(context_), &screen);
148 
149     if (g_support_renderer_switching &&
150         !discrete_pixelformat_ && renderer_id != -1 &&
151         (screen != screen_ || renderer_id != renderer_id_)) {
152       // Attempt to find a virtual screen that's using the requested renderer,
153       // and switch the context to use that screen. Don't attempt to switch if
154       // the context requires the discrete GPU.
155       CGLPixelFormatObj format = GetPixelFormat();
156       int virtual_screen_count;
157       if (CGLDescribePixelFormat(format, 0, kCGLPFAVirtualScreenCount,
158                                  &virtual_screen_count) != kCGLNoError)
159         return false;
160 
161       for (int i = 0; i < virtual_screen_count; ++i) {
162         int screen_renderer_id;
163         if (CGLDescribePixelFormat(format, i, kCGLPFARendererID,
164                                    &screen_renderer_id) != kCGLNoError)
165           return false;
166 
167         screen_renderer_id &= kCGLRendererIDMatchingMask;
168         if (screen_renderer_id == renderer_id) {
169           CGLSetVirtualScreen(static_cast<CGLContextObj>(context_), i);
170           screen_ = i;
171           break;
172         }
173       }
174       renderer_id_ = renderer_id;
175     }
176   }
177 
178   if (IsCurrent(surface))
179     return true;
180 
181   ScopedReleaseCurrent release_current;
182   TRACE_EVENT0("gpu", "GLContextCGL::MakeCurrent");
183 
184   if (CGLSetCurrentContext(
185       static_cast<CGLContextObj>(context_)) != kCGLNoError) {
186     LOG(ERROR) << "Unable to make gl context current.";
187     return false;
188   }
189 
190   // Set this as soon as the context is current, since we might call into GL.
191   SetRealGLApi();
192 
193   SetCurrent(surface);
194   if (!InitializeDynamicBindings()) {
195     return false;
196   }
197 
198   if (!surface->OnMakeCurrent(this)) {
199     LOG(ERROR) << "Unable to make gl context current.";
200     return false;
201   }
202 
203   release_current.Cancel();
204   return true;
205 }
206 
ReleaseCurrent(GLSurface * surface)207 void GLContextCGL::ReleaseCurrent(GLSurface* surface) {
208   if (!IsCurrent(surface))
209     return;
210 
211   SetCurrent(NULL);
212   CGLSetCurrentContext(NULL);
213 }
214 
IsCurrent(GLSurface * surface)215 bool GLContextCGL::IsCurrent(GLSurface* surface) {
216   bool native_context_is_current = CGLGetCurrentContext() == context_;
217 
218   // If our context is current then our notion of which GLContext is
219   // current must be correct. On the other hand, third-party code
220   // using OpenGL might change the current context.
221   DCHECK(!native_context_is_current || (GetRealCurrent() == this));
222 
223   if (!native_context_is_current)
224     return false;
225 
226   return true;
227 }
228 
GetHandle()229 void* GLContextCGL::GetHandle() {
230   return context_;
231 }
232 
SetSwapInterval(int interval)233 void GLContextCGL::SetSwapInterval(int interval) {
234   DCHECK(IsCurrent(NULL));
235   LOG(WARNING) << "GLContex: GLContextCGL::SetSwapInterval is ignored.";
236 }
237 
238 
GetTotalGpuMemory(size_t * bytes)239 bool GLContextCGL::GetTotalGpuMemory(size_t* bytes) {
240   DCHECK(bytes);
241   *bytes = 0;
242 
243   CGLContextObj context = reinterpret_cast<CGLContextObj>(context_);
244   if (!context)
245     return false;
246 
247   // Retrieve the current renderer ID
248   GLint current_renderer_id = 0;
249   if (CGLGetParameter(context,
250                       kCGLCPCurrentRendererID,
251                       &current_renderer_id) != kCGLNoError)
252     return false;
253 
254   // Iterate through the list of all renderers
255   GLuint display_mask = static_cast<GLuint>(-1);
256   CGLRendererInfoObj renderer_info = NULL;
257   GLint num_renderers = 0;
258   if (CGLQueryRendererInfo(display_mask,
259                            &renderer_info,
260                            &num_renderers) != kCGLNoError)
261     return false;
262 
263   scoped_ptr<CGLRendererInfoObj,
264       CGLRendererInfoObjDeleter> scoper(&renderer_info);
265 
266   for (GLint renderer_index = 0;
267        renderer_index < num_renderers;
268        ++renderer_index) {
269     // Skip this if this renderer is not the current renderer.
270     GLint renderer_id = 0;
271     if (CGLDescribeRenderer(renderer_info,
272                             renderer_index,
273                             kCGLRPRendererID,
274                             &renderer_id) != kCGLNoError)
275         continue;
276     if (renderer_id != current_renderer_id)
277         continue;
278     // Retrieve the video memory for the renderer.
279     GLint video_memory = 0;
280     if (CGLDescribeRenderer(renderer_info,
281                             renderer_index,
282                             kCGLRPVideoMemory,
283                             &video_memory) != kCGLNoError)
284         continue;
285     *bytes = video_memory;
286     return true;
287   }
288 
289   return false;
290 }
291 
SetSafeToForceGpuSwitch()292 void GLContextCGL::SetSafeToForceGpuSwitch() {
293   safe_to_force_gpu_switch_ = true;
294 }
295 
296 
~GLContextCGL()297 GLContextCGL::~GLContextCGL() {
298   Destroy();
299 }
300 
GetGpuPreference()301 GpuPreference GLContextCGL::GetGpuPreference() {
302   return gpu_preference_;
303 }
304 
305 }  // namespace gfx
306