Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / ui / gl / gl_context_cgl.cc
blob56a2a642d03d908e71710df7390d78f54ca3d199
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>
9 #include <vector>
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"
18 namespace gfx {
20 bool g_support_renderer_switching;
22 static CGLPixelFormatObj GetPixelFormat() {
23 static CGLPixelFormatObj format;
24 if (format)
25 return 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(),
42 &format,
43 &num_virtual_screens) != kCGLNoError) {
44 LOG(ERROR) << "Error choosing pixel format.";
45 return NULL;
47 if (!format) {
48 LOG(ERROR) << "format == 0.";
49 return NULL;
51 DCHECK_NE(num_virtual_screens, 0);
52 return format;
55 GLContextCGL::GLContextCGL(GLShareGroup* share_group)
56 : GLContext(share_group),
57 context_(NULL),
58 gpu_preference_(PreferIntegratedGpu),
59 discrete_pixelformat_(NULL),
60 screen_(-1),
61 renderer_id_(-1),
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(
70 gpu_preference);
72 GLContextCGL* share_context = share_group() ?
73 static_cast<GLContextCGL*>(share_group()->GetContext()) : NULL;
75 CGLPixelFormatObj format = GetPixelFormat();
76 if (!format)
77 return false;
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.";
90 return false;
92 // The renderer might be switched after this, so ignore the saved ID.
93 share_group()->SetRendererID(-1);
96 CGLError res = CGLCreateContext(
97 format,
98 share_context ?
99 static_cast<CGLContextObj>(share_context->GetHandle()) : NULL,
100 reinterpret_cast<CGLContextObj*>(&context_));
101 if (res != kCGLNoError) {
102 LOG(ERROR) << "Error creating context.";
103 Destroy();
104 return false;
107 gpu_preference_ = gpu_preference;
108 return true;
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;
121 if (context_) {
122 CGLDestroyContext(static_cast<CGLContextObj>(context_));
123 context_ = NULL;
127 bool GLContextCGL::MakeCurrent(GLSurface* surface) {
128 DCHECK(context_);
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();
134 int screen;
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)
147 return false;
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)
153 return false;
155 screen_renderer_id &= kCGLRendererIDMatchingMask;
156 if (screen_renderer_id == renderer_id) {
157 CGLSetVirtualScreen(static_cast<CGLContextObj>(context_), i);
158 screen_ = i;
159 break;
162 renderer_id_ = renderer_id;
166 if (IsCurrent(surface))
167 return true;
169 TRACE_EVENT0("gpu", "GLContextCGL::MakeCurrent");
171 if (CGLSetCurrentContext(
172 static_cast<CGLContextObj>(context_)) != kCGLNoError) {
173 LOG(ERROR) << "Unable to make gl context current.";
174 return false;
177 SetCurrent(this, surface);
178 if (!InitializeExtensionBindings()) {
179 ReleaseCurrent(surface);
180 return false;
183 if (!surface->OnMakeCurrent(this)) {
184 LOG(ERROR) << "Unable to make gl context current.";
185 return false;
188 SetRealGLApi();
189 return true;
192 void GLContextCGL::ReleaseCurrent(GLSurface* surface) {
193 if (!IsCurrent(surface))
194 return;
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)
209 return false;
211 return true;
214 void* GLContextCGL::GetHandle() {
215 return context_;
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) {
225 DCHECK(bytes);
226 *bytes = 0;
228 CGLContextObj context = reinterpret_cast<CGLContextObj>(context_);
229 if (!context)
230 return false;
232 // Retrieve the current renderer ID
233 GLint current_renderer_id = 0;
234 if (CGLGetParameter(context,
235 kCGLCPCurrentRendererID,
236 &current_renderer_id) != kCGLNoError)
237 return false;
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,
244 &renderer_info,
245 &num_renderers) != kCGLNoError)
246 return false;
248 ScopedCGLRendererInfoObj scoper(renderer_info);
250 for (GLint renderer_index = 0;
251 renderer_index < num_renderers;
252 ++renderer_index) {
253 // Skip this if this renderer is not the current renderer.
254 GLint renderer_id = 0;
255 if (CGLDescribeRenderer(renderer_info,
256 renderer_index,
257 kCGLRPRendererID,
258 &renderer_id) != kCGLNoError)
259 continue;
260 if (renderer_id != current_renderer_id)
261 continue;
262 // Retrieve the video memory for the renderer.
263 GLint video_memory = 0;
264 if (CGLDescribeRenderer(renderer_info,
265 renderer_index,
266 kCGLRPVideoMemory,
267 &video_memory) != kCGLNoError)
268 continue;
269 *bytes = video_memory;
270 return true;
273 return false;
276 void GLContextCGL::SetSafeToForceGpuSwitch() {
277 safe_to_force_gpu_switch_ = true;
281 GLContextCGL::~GLContextCGL() {
282 Destroy();
285 GpuPreference GLContextCGL::GetGpuPreference() {
286 return gpu_preference_;
289 void ScopedCGLDestroyRendererInfo::operator()(CGLRendererInfoObj x) const {
290 CGLDestroyRendererInfo(x);
293 } // namespace gfx