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.
5 #include "ui/gl/gl_context_cgl.h"
7 #include <OpenGL/CGLRenderers.h>
8 #include <OpenGL/CGLTypes.h>
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"
20 bool g_support_renderer_switching
;
22 static CGLPixelFormatObj
GetPixelFormat() {
23 static CGLPixelFormatObj format
;
26 std::vector
<CGLPixelFormatAttribute
> attribs
;
27 // If the system supports dual gpus then allow offline renderers for every
28 // context, so that they can all be in the same share group.
29 if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus()) {
30 attribs
.push_back(kCGLPFAAllowOfflineRenderers
);
31 g_support_renderer_switching
= true;
33 if (GetGLImplementation() == kGLImplementationAppleGL
) {
34 attribs
.push_back(kCGLPFARendererID
);
35 attribs
.push_back((CGLPixelFormatAttribute
) kCGLRendererGenericFloatID
);
36 g_support_renderer_switching
= false;
38 attribs
.push_back((CGLPixelFormatAttribute
) 0);
40 GLint num_virtual_screens
;
41 if (CGLChoosePixelFormat(&attribs
.front(),
43 &num_virtual_screens
) != kCGLNoError
) {
44 LOG(ERROR
) << "Error choosing pixel format.";
48 LOG(ERROR
) << "format == 0.";
51 DCHECK_NE(num_virtual_screens
, 0);
55 GLContextCGL::GLContextCGL(GLShareGroup
* share_group
)
56 : GLContext(share_group
),
58 gpu_preference_(PreferIntegratedGpu
),
59 discrete_pixelformat_(NULL
),
62 safe_to_force_gpu_switch_(false) {
65 bool GLContextCGL::Initialize(GLSurface
* compatible_surface
,
66 GpuPreference gpu_preference
) {
67 DCHECK(compatible_surface
);
69 gpu_preference
= ui::GpuSwitchingManager::GetInstance()->AdjustGpuPreference(
72 GLContextCGL
* share_context
= share_group() ?
73 static_cast<GLContextCGL
*>(share_group()->GetContext()) : NULL
;
75 CGLPixelFormatObj format
= GetPixelFormat();
79 // If using the discrete gpu, create a pixel format requiring it before we
80 // create the context.
81 if (!ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus() ||
82 gpu_preference
== PreferDiscreteGpu
) {
83 std::vector
<CGLPixelFormatAttribute
> discrete_attribs
;
84 discrete_attribs
.push_back((CGLPixelFormatAttribute
) 0);
85 GLint num_pixel_formats
;
86 if (CGLChoosePixelFormat(&discrete_attribs
.front(),
87 &discrete_pixelformat_
,
88 &num_pixel_formats
) != kCGLNoError
) {
89 LOG(ERROR
) << "Error choosing pixel format.";
92 // The renderer might be switched after this, so ignore the saved ID.
93 share_group()->SetRendererID(-1);
96 CGLError res
= CGLCreateContext(
99 static_cast<CGLContextObj
>(share_context
->GetHandle()) : NULL
,
100 reinterpret_cast<CGLContextObj
*>(&context_
));
101 if (res
!= kCGLNoError
) {
102 LOG(ERROR
) << "Error creating context.";
107 gpu_preference_
= gpu_preference
;
111 void GLContextCGL::Destroy() {
112 if (discrete_pixelformat_
) {
113 // Delay releasing the pixel format for 10 seconds to reduce the number of
114 // unnecessary GPU switches.
115 MessageLoop::current()->PostDelayedTask(FROM_HERE
,
116 base::Bind(&CGLReleasePixelFormat
,
117 discrete_pixelformat_
),
118 base::TimeDelta::FromSeconds(10));
119 discrete_pixelformat_
= NULL
;
122 CGLDestroyContext(static_cast<CGLContextObj
>(context_
));
127 bool GLContextCGL::MakeCurrent(GLSurface
* surface
) {
130 // The call to CGLSetVirtualScreen can hang on some AMD drivers
131 // http://crbug.com/227228
132 if (safe_to_force_gpu_switch_
) {
133 int renderer_id
= share_group()->GetRendererID();
135 CGLGetVirtualScreen(static_cast<CGLContextObj
>(context_
), &screen
);
137 if (g_support_renderer_switching
&&
138 !discrete_pixelformat_
&& renderer_id
!= -1 &&
139 (screen
!= screen_
|| renderer_id
!= renderer_id_
)) {
140 // Attempt to find a virtual screen that's using the requested renderer,
141 // and switch the context to use that screen. Don't attempt to switch if
142 // the context requires the discrete GPU.
143 CGLPixelFormatObj format
= GetPixelFormat();
144 int virtual_screen_count
;
145 if (CGLDescribePixelFormat(format
, 0, kCGLPFAVirtualScreenCount
,
146 &virtual_screen_count
) != kCGLNoError
)
149 for (int i
= 0; i
< virtual_screen_count
; ++i
) {
150 int screen_renderer_id
;
151 if (CGLDescribePixelFormat(format
, i
, kCGLPFARendererID
,
152 &screen_renderer_id
) != kCGLNoError
)
155 screen_renderer_id
&= kCGLRendererIDMatchingMask
;
156 if (screen_renderer_id
== renderer_id
) {
157 CGLSetVirtualScreen(static_cast<CGLContextObj
>(context_
), i
);
162 renderer_id_
= renderer_id
;
166 if (IsCurrent(surface
))
169 TRACE_EVENT0("gpu", "GLContextCGL::MakeCurrent");
171 if (CGLSetCurrentContext(
172 static_cast<CGLContextObj
>(context_
)) != kCGLNoError
) {
173 LOG(ERROR
) << "Unable to make gl context current.";
177 SetCurrent(this, surface
);
178 if (!InitializeExtensionBindings()) {
179 ReleaseCurrent(surface
);
183 if (!surface
->OnMakeCurrent(this)) {
184 LOG(ERROR
) << "Unable to make gl context current.";
192 void GLContextCGL::ReleaseCurrent(GLSurface
* surface
) {
193 if (!IsCurrent(surface
))
196 SetCurrent(NULL
, NULL
);
197 CGLSetCurrentContext(NULL
);
200 bool GLContextCGL::IsCurrent(GLSurface
* surface
) {
201 bool native_context_is_current
= CGLGetCurrentContext() == context_
;
203 // If our context is current then our notion of which GLContext is
204 // current must be correct. On the other hand, third-party code
205 // using OpenGL might change the current context.
206 DCHECK(!native_context_is_current
|| (GetCurrent() == this));
208 if (!native_context_is_current
)
214 void* GLContextCGL::GetHandle() {
218 void GLContextCGL::SetSwapInterval(int interval
) {
219 DCHECK(IsCurrent(NULL
));
220 LOG(WARNING
) << "GLContex: GLContextCGL::SetSwapInterval is ignored.";
224 bool GLContextCGL::GetTotalGpuMemory(size_t* bytes
) {
228 CGLContextObj context
= reinterpret_cast<CGLContextObj
>(context_
);
232 // Retrieve the current renderer ID
233 GLint current_renderer_id
= 0;
234 if (CGLGetParameter(context
,
235 kCGLCPCurrentRendererID
,
236 ¤t_renderer_id
) != kCGLNoError
)
239 // Iterate through the list of all renderers
240 GLuint display_mask
= static_cast<GLuint
>(-1);
241 CGLRendererInfoObj renderer_info
= NULL
;
242 GLint num_renderers
= 0;
243 if (CGLQueryRendererInfo(display_mask
,
245 &num_renderers
) != kCGLNoError
)
248 ScopedCGLRendererInfoObj
scoper(renderer_info
);
250 for (GLint renderer_index
= 0;
251 renderer_index
< num_renderers
;
253 // Skip this if this renderer is not the current renderer.
254 GLint renderer_id
= 0;
255 if (CGLDescribeRenderer(renderer_info
,
258 &renderer_id
) != kCGLNoError
)
260 if (renderer_id
!= current_renderer_id
)
262 // Retrieve the video memory for the renderer.
263 GLint video_memory
= 0;
264 if (CGLDescribeRenderer(renderer_info
,
267 &video_memory
) != kCGLNoError
)
269 *bytes
= video_memory
;
276 void GLContextCGL::SetSafeToForceGpuSwitch() {
277 safe_to_force_gpu_switch_
= true;
281 GLContextCGL::~GLContextCGL() {
285 GpuPreference
GLContextCGL::GetGpuPreference() {
286 return gpu_preference_
;
289 void ScopedCGLDestroyRendererInfo::operator()(CGLRendererInfoObj x
) const {
290 CGLDestroyRendererInfo(x
);