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.
5 #include "content/browser/renderer_host/compositing_iosurface_context_mac.h"
8 #include <OpenGL/OpenGL.h>
11 #include "base/command_line.h"
12 #include "base/debug/trace_event.h"
13 #include "base/logging.h"
14 #include "content/browser/renderer_host/compositing_iosurface_shader_programs_mac.h"
15 #include "content/browser/gpu/gpu_data_manager_impl.h"
16 #include "ui/base/ui_base_switches.h"
17 #include "ui/gl/gl_switches.h"
18 #include "ui/gl/gpu_switching_manager.h"
22 CoreAnimationStatus GetCoreAnimationStatus() {
23 static CoreAnimationStatus status =
24 CommandLine::ForCurrentProcess()->HasSwitch(
25 switches::kDisableCoreAnimation) ?
26 CORE_ANIMATION_DISABLED : CORE_ANIMATION_ENABLED;
31 scoped_refptr<CompositingIOSurfaceContext>
32 CompositingIOSurfaceContext::Get(int window_number) {
33 TRACE_EVENT0("browser", "CompositingIOSurfaceContext::Get");
35 // Return the context for this window_number, if it exists.
36 WindowMap::iterator found = window_map()->find(window_number);
37 if (found != window_map()->end()) {
38 DCHECK(!found->second->poisoned_);
42 static bool is_vsync_disabled =
43 CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync);
45 base::scoped_nsobject<NSOpenGLContext> nsgl_context;
46 base::ScopedTypeRef<CGLContextObj> cgl_context_strong;
47 CGLContextObj cgl_context = NULL;
48 if (GetCoreAnimationStatus() == CORE_ANIMATION_DISABLED) {
49 std::vector<NSOpenGLPixelFormatAttribute> attributes;
50 attributes.push_back(NSOpenGLPFADoubleBuffer);
51 // We don't need a depth buffer - try setting its size to 0...
52 attributes.push_back(NSOpenGLPFADepthSize); attributes.push_back(0);
53 if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus())
54 attributes.push_back(NSOpenGLPFAAllowOfflineRenderers);
55 attributes.push_back(0);
57 base::scoped_nsobject<NSOpenGLPixelFormat> glPixelFormat(
58 [[NSOpenGLPixelFormat alloc] initWithAttributes:&attributes.front()]);
60 LOG(ERROR) << "NSOpenGLPixelFormat initWithAttributes failed";
64 // Create all contexts in the same share group so that the textures don't
65 // need to be recreated when transitioning contexts.
66 NSOpenGLContext* share_context = nil;
67 if (!window_map()->empty())
68 share_context = window_map()->begin()->second->nsgl_context();
70 [[NSOpenGLContext alloc] initWithFormat:glPixelFormat
71 shareContext:share_context]);
73 LOG(ERROR) << "NSOpenGLContext initWithFormat failed";
77 // Grab the CGL context that the NSGL context is using. Explicitly
78 // retain it, so that it is not double-freed by the scoped type.
79 cgl_context = reinterpret_cast<CGLContextObj>(
80 [nsgl_context CGLContextObj]);
82 LOG(ERROR) << "Failed to retrieve CGLContextObj from NSOpenGLContext";
86 // Force [nsgl_context flushBuffer] to wait for vsync.
87 GLint swapInterval = is_vsync_disabled ? 0 : 1;
88 [nsgl_context setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
90 CGLError error = kCGLNoError;
92 // Create the pixel format object for the context.
93 std::vector<CGLPixelFormatAttribute> attribs;
94 attribs.push_back(kCGLPFADepthSize);
95 attribs.push_back(static_cast<CGLPixelFormatAttribute>(0));
96 if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus()) {
97 attribs.push_back(kCGLPFAAllowOfflineRenderers);
98 attribs.push_back(static_cast<CGLPixelFormatAttribute>(1));
100 attribs.push_back(static_cast<CGLPixelFormatAttribute>(0));
101 GLint number_virtual_screens = 0;
102 base::ScopedTypeRef<CGLPixelFormatObj> pixel_format;
103 error = CGLChoosePixelFormat(&attribs.front(),
104 pixel_format.InitializeInto(),
105 &number_virtual_screens);
106 if (error != kCGLNoError) {
107 LOG(ERROR) << "Failed to create pixel format object.";
111 // Create all contexts in the same share group so that the textures don't
112 // need to be recreated when transitioning contexts.
113 CGLContextObj share_context = NULL;
114 if (!window_map()->empty())
115 share_context = window_map()->begin()->second->cgl_context();
116 error = CGLCreateContext(
117 pixel_format, share_context, cgl_context_strong.InitializeInto());
118 if (error != kCGLNoError) {
119 LOG(ERROR) << "Failed to create context object.";
122 cgl_context = cgl_context_strong;
124 // Note that VSync is ignored because CoreAnimation will automatically
128 // Prepare the shader program cache. Precompile the shader programs
129 // needed to draw the IO Surface for non-offscreen contexts.
130 bool prepared = false;
131 scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache;
133 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context);
134 shader_program_cache.reset(new CompositingIOSurfaceShaderPrograms());
135 if (window_number == kOffscreenContextWindowNumber) {
139 shader_program_cache->UseBlitProgram() &&
140 shader_program_cache->UseSolidWhiteProgram());
145 LOG(ERROR) << "IOSurface failed to compile/link required shader programs.";
149 return new CompositingIOSurfaceContext(
151 nsgl_context.release(),
155 shader_program_cache.Pass());
158 void CompositingIOSurfaceContext::PoisonContextAndSharegroup() {
162 for (WindowMap::iterator it = window_map()->begin();
163 it != window_map()->end();
165 it->second->poisoned_ = true;
167 window_map()->clear();
170 CompositingIOSurfaceContext::CompositingIOSurfaceContext(
172 NSOpenGLContext* nsgl_context,
173 base::ScopedTypeRef<CGLContextObj> cgl_context_strong,
174 CGLContextObj cgl_context,
175 bool is_vsync_disabled,
176 scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache)
177 : window_number_(window_number),
178 nsgl_context_(nsgl_context),
179 cgl_context_strong_(cgl_context_strong),
180 cgl_context_(cgl_context),
181 is_vsync_disabled_(is_vsync_disabled),
182 shader_program_cache_(shader_program_cache.Pass()),
184 DCHECK(window_map()->find(window_number_) == window_map()->end());
185 window_map()->insert(std::make_pair(window_number_, this));
187 GpuDataManager::GetInstance()->AddObserver(this);
190 CompositingIOSurfaceContext::~CompositingIOSurfaceContext() {
191 GpuDataManager::GetInstance()->RemoveObserver(this);
194 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_);
195 shader_program_cache_->Reset();
198 DCHECK(window_map()->find(window_number_) != window_map()->end());
199 DCHECK(window_map()->find(window_number_)->second == this);
200 window_map()->erase(window_number_);
202 WindowMap::const_iterator found = window_map()->find(window_number_);
203 if (found != window_map()->end())
204 DCHECK(found->second != this);
208 NSOpenGLContext* CompositingIOSurfaceContext::nsgl_context() const {
209 // This should not be called from any CoreAnimation paths.
210 CHECK(GetCoreAnimationStatus() == CORE_ANIMATION_DISABLED);
211 return nsgl_context_;
214 void CompositingIOSurfaceContext::OnGpuSwitching() {
215 // Recreate all browser-side GL contexts whenever the GPU switches. If this
216 // is not done, performance will suffer.
217 // http://crbug.com/361493
218 PoisonContextAndSharegroup();
222 CompositingIOSurfaceContext::WindowMap*
223 CompositingIOSurfaceContext::window_map() {
224 return window_map_.Pointer();
228 base::LazyInstance<CompositingIOSurfaceContext::WindowMap>
229 CompositingIOSurfaceContext::window_map_;
231 } // namespace content