OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/renderer_host/compositing_iosurface_context_mac.h" | 5 #include "content/browser/renderer_host/compositing_iosurface_context_mac.h" |
6 | 6 |
7 #include <OpenGL/gl.h> | 7 #include <OpenGL/gl.h> |
8 #include <OpenGL/OpenGL.h> | 8 #include <OpenGL/OpenGL.h> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
12 #include "base/debug/trace_event.h" | 12 #include "base/debug/trace_event.h" |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "content/browser/renderer_host/compositing_iosurface_shader_programs_ma
c.h" | 14 #include "content/browser/renderer_host/compositing_iosurface_shader_programs_ma
c.h" |
15 #include "content/browser/gpu/gpu_data_manager_impl.h" | 15 #include "content/browser/gpu/gpu_data_manager_impl.h" |
16 #include "ui/base/ui_base_switches.h" | 16 #include "ui/base/ui_base_switches.h" |
17 #include "ui/gl/gl_switches.h" | 17 #include "ui/gl/gl_switches.h" |
18 #include "ui/gl/gpu_switching_manager.h" | 18 #include "ui/gl/gpu_switching_manager.h" |
19 | 19 |
20 namespace content { | 20 namespace content { |
21 | 21 |
22 CoreAnimationStatus GetCoreAnimationStatus() { | |
23 static CoreAnimationStatus status = | |
24 CommandLine::ForCurrentProcess()->HasSwitch( | |
25 switches::kDisableCoreAnimation) ? | |
26 CORE_ANIMATION_DISABLED : CORE_ANIMATION_ENABLED; | |
27 return status; | |
28 } | |
29 | |
30 // static | 22 // static |
31 scoped_refptr<CompositingIOSurfaceContext> | 23 scoped_refptr<CompositingIOSurfaceContext> |
32 CompositingIOSurfaceContext::Get(int window_number) { | 24 CompositingIOSurfaceContext::Get(int window_number) { |
33 TRACE_EVENT0("browser", "CompositingIOSurfaceContext::Get"); | 25 TRACE_EVENT0("browser", "CompositingIOSurfaceContext::Get"); |
34 | 26 |
35 // Return the context for this window_number, if it exists. | 27 // Return the context for this window_number, if it exists. |
36 WindowMap::iterator found = window_map()->find(window_number); | 28 WindowMap::iterator found = window_map()->find(window_number); |
37 if (found != window_map()->end()) { | 29 if (found != window_map()->end()) { |
38 DCHECK(!found->second->poisoned_); | 30 DCHECK(!found->second->poisoned_); |
39 return found->second; | 31 return found->second; |
40 } | 32 } |
41 | 33 |
42 static bool is_vsync_disabled = | 34 static bool is_vsync_disabled = |
43 CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync); | 35 CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync); |
44 | 36 |
45 base::scoped_nsobject<NSOpenGLContext> nsgl_context; | |
46 base::ScopedTypeRef<CGLContextObj> cgl_context_strong; | 37 base::ScopedTypeRef<CGLContextObj> cgl_context_strong; |
47 CGLContextObj cgl_context = NULL; | 38 CGLContextObj cgl_context = NULL; |
48 if (GetCoreAnimationStatus() == CORE_ANIMATION_DISABLED) { | 39 CGLError error = kCGLNoError; |
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); | |
56 | 40 |
57 base::scoped_nsobject<NSOpenGLPixelFormat> glPixelFormat( | 41 // Create the pixel format object for the context. |
58 [[NSOpenGLPixelFormat alloc] initWithAttributes:&attributes.front()]); | 42 std::vector<CGLPixelFormatAttribute> attribs; |
59 if (!glPixelFormat) { | 43 attribs.push_back(kCGLPFADepthSize); |
60 LOG(ERROR) << "NSOpenGLPixelFormat initWithAttributes failed"; | 44 attribs.push_back(static_cast<CGLPixelFormatAttribute>(0)); |
61 return NULL; | 45 if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus()) { |
62 } | 46 attribs.push_back(kCGLPFAAllowOfflineRenderers); |
| 47 attribs.push_back(static_cast<CGLPixelFormatAttribute>(1)); |
| 48 } |
| 49 attribs.push_back(static_cast<CGLPixelFormatAttribute>(0)); |
| 50 GLint number_virtual_screens = 0; |
| 51 base::ScopedTypeRef<CGLPixelFormatObj> pixel_format; |
| 52 error = CGLChoosePixelFormat(&attribs.front(), |
| 53 pixel_format.InitializeInto(), |
| 54 &number_virtual_screens); |
| 55 if (error != kCGLNoError) { |
| 56 LOG(ERROR) << "Failed to create pixel format object."; |
| 57 return NULL; |
| 58 } |
63 | 59 |
64 // Create all contexts in the same share group so that the textures don't | 60 // Create all contexts in the same share group so that the textures don't |
65 // need to be recreated when transitioning contexts. | 61 // need to be recreated when transitioning contexts. |
66 NSOpenGLContext* share_context = nil; | 62 CGLContextObj share_context = NULL; |
67 if (!window_map()->empty()) | 63 if (!window_map()->empty()) |
68 share_context = window_map()->begin()->second->nsgl_context(); | 64 share_context = window_map()->begin()->second->cgl_context(); |
69 nsgl_context.reset( | 65 error = CGLCreateContext( |
70 [[NSOpenGLContext alloc] initWithFormat:glPixelFormat | 66 pixel_format, share_context, cgl_context_strong.InitializeInto()); |
71 shareContext:share_context]); | 67 if (error != kCGLNoError) { |
72 if (!nsgl_context) { | 68 LOG(ERROR) << "Failed to create context object."; |
73 LOG(ERROR) << "NSOpenGLContext initWithFormat failed"; | 69 return NULL; |
74 return NULL; | 70 } |
75 } | 71 cgl_context = cgl_context_strong; |
76 | 72 |
77 // Grab the CGL context that the NSGL context is using. Explicitly | 73 // Note that VSync is ignored because CoreAnimation will automatically |
78 // retain it, so that it is not double-freed by the scoped type. | 74 // rate limit draws. |
79 cgl_context = reinterpret_cast<CGLContextObj>( | |
80 [nsgl_context CGLContextObj]); | |
81 if (!cgl_context) { | |
82 LOG(ERROR) << "Failed to retrieve CGLContextObj from NSOpenGLContext"; | |
83 return NULL; | |
84 } | |
85 | |
86 // Force [nsgl_context flushBuffer] to wait for vsync. | |
87 GLint swapInterval = is_vsync_disabled ? 0 : 1; | |
88 [nsgl_context setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval]; | |
89 } else { | |
90 CGLError error = kCGLNoError; | |
91 | |
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)); | |
99 } | |
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."; | |
108 return NULL; | |
109 } | |
110 | |
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."; | |
120 return NULL; | |
121 } | |
122 cgl_context = cgl_context_strong; | |
123 | |
124 // Note that VSync is ignored because CoreAnimation will automatically | |
125 // rate limit draws. | |
126 } | |
127 | 75 |
128 // Prepare the shader program cache. Precompile the shader programs | 76 // Prepare the shader program cache. Precompile the shader programs |
129 // needed to draw the IO Surface for non-offscreen contexts. | 77 // needed to draw the IO Surface for non-offscreen contexts. |
130 bool prepared = false; | 78 bool prepared = false; |
131 scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache; | 79 scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache; |
132 { | 80 { |
133 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context); | 81 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context); |
134 shader_program_cache.reset(new CompositingIOSurfaceShaderPrograms()); | 82 shader_program_cache.reset(new CompositingIOSurfaceShaderPrograms()); |
135 if (window_number == kOffscreenContextWindowNumber) { | 83 if (window_number == kOffscreenContextWindowNumber) { |
136 prepared = true; | 84 prepared = true; |
137 } else { | 85 } else { |
138 prepared = ( | 86 prepared = ( |
139 shader_program_cache->UseBlitProgram() && | 87 shader_program_cache->UseBlitProgram() && |
140 shader_program_cache->UseSolidWhiteProgram()); | 88 shader_program_cache->UseSolidWhiteProgram()); |
141 } | 89 } |
142 glUseProgram(0u); | 90 glUseProgram(0u); |
143 } | 91 } |
144 if (!prepared) { | 92 if (!prepared) { |
145 LOG(ERROR) << "IOSurface failed to compile/link required shader programs."; | 93 LOG(ERROR) << "IOSurface failed to compile/link required shader programs."; |
146 return NULL; | 94 return NULL; |
147 } | 95 } |
148 | 96 |
149 return new CompositingIOSurfaceContext( | 97 return new CompositingIOSurfaceContext( |
150 window_number, | 98 window_number, |
151 nsgl_context.release(), | |
152 cgl_context_strong, | 99 cgl_context_strong, |
153 cgl_context, | 100 cgl_context, |
154 is_vsync_disabled, | 101 is_vsync_disabled, |
155 shader_program_cache.Pass()); | 102 shader_program_cache.Pass()); |
156 } | 103 } |
157 | 104 |
158 void CompositingIOSurfaceContext::PoisonContextAndSharegroup() { | 105 void CompositingIOSurfaceContext::PoisonContextAndSharegroup() { |
159 if (poisoned_) | 106 if (poisoned_) |
160 return; | 107 return; |
161 | 108 |
162 for (WindowMap::iterator it = window_map()->begin(); | 109 for (WindowMap::iterator it = window_map()->begin(); |
163 it != window_map()->end(); | 110 it != window_map()->end(); |
164 ++it) { | 111 ++it) { |
165 it->second->poisoned_ = true; | 112 it->second->poisoned_ = true; |
166 } | 113 } |
167 window_map()->clear(); | 114 window_map()->clear(); |
168 } | 115 } |
169 | 116 |
170 CompositingIOSurfaceContext::CompositingIOSurfaceContext( | 117 CompositingIOSurfaceContext::CompositingIOSurfaceContext( |
171 int window_number, | 118 int window_number, |
172 NSOpenGLContext* nsgl_context, | |
173 base::ScopedTypeRef<CGLContextObj> cgl_context_strong, | 119 base::ScopedTypeRef<CGLContextObj> cgl_context_strong, |
174 CGLContextObj cgl_context, | 120 CGLContextObj cgl_context, |
175 bool is_vsync_disabled, | 121 bool is_vsync_disabled, |
176 scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache) | 122 scoped_ptr<CompositingIOSurfaceShaderPrograms> shader_program_cache) |
177 : window_number_(window_number), | 123 : window_number_(window_number), |
178 nsgl_context_(nsgl_context), | |
179 cgl_context_strong_(cgl_context_strong), | 124 cgl_context_strong_(cgl_context_strong), |
180 cgl_context_(cgl_context), | 125 cgl_context_(cgl_context), |
181 is_vsync_disabled_(is_vsync_disabled), | 126 is_vsync_disabled_(is_vsync_disabled), |
182 shader_program_cache_(shader_program_cache.Pass()), | 127 shader_program_cache_(shader_program_cache.Pass()), |
183 poisoned_(false) { | 128 poisoned_(false) { |
184 DCHECK(window_map()->find(window_number_) == window_map()->end()); | 129 DCHECK(window_map()->find(window_number_) == window_map()->end()); |
185 window_map()->insert(std::make_pair(window_number_, this)); | 130 window_map()->insert(std::make_pair(window_number_, this)); |
186 | 131 |
187 GpuDataManager::GetInstance()->AddObserver(this); | 132 GpuDataManager::GetInstance()->AddObserver(this); |
188 } | 133 } |
189 | 134 |
190 CompositingIOSurfaceContext::~CompositingIOSurfaceContext() { | 135 CompositingIOSurfaceContext::~CompositingIOSurfaceContext() { |
191 GpuDataManager::GetInstance()->RemoveObserver(this); | 136 GpuDataManager::GetInstance()->RemoveObserver(this); |
192 | 137 |
193 { | 138 { |
194 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_); | 139 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_); |
195 shader_program_cache_->Reset(); | 140 shader_program_cache_->Reset(); |
196 } | 141 } |
197 if (!poisoned_) { | 142 if (!poisoned_) { |
198 DCHECK(window_map()->find(window_number_) != window_map()->end()); | 143 DCHECK(window_map()->find(window_number_) != window_map()->end()); |
199 DCHECK(window_map()->find(window_number_)->second == this); | 144 DCHECK(window_map()->find(window_number_)->second == this); |
200 window_map()->erase(window_number_); | 145 window_map()->erase(window_number_); |
201 } else { | 146 } else { |
202 WindowMap::const_iterator found = window_map()->find(window_number_); | 147 WindowMap::const_iterator found = window_map()->find(window_number_); |
203 if (found != window_map()->end()) | 148 if (found != window_map()->end()) |
204 DCHECK(found->second != this); | 149 DCHECK(found->second != this); |
205 } | 150 } |
206 } | 151 } |
207 | 152 |
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_; | |
212 } | |
213 | |
214 void CompositingIOSurfaceContext::OnGpuSwitching() { | 153 void CompositingIOSurfaceContext::OnGpuSwitching() { |
215 // Recreate all browser-side GL contexts whenever the GPU switches. If this | 154 // Recreate all browser-side GL contexts whenever the GPU switches. If this |
216 // is not done, performance will suffer. | 155 // is not done, performance will suffer. |
217 // http://crbug.com/361493 | 156 // http://crbug.com/361493 |
218 PoisonContextAndSharegroup(); | 157 PoisonContextAndSharegroup(); |
219 } | 158 } |
220 | 159 |
221 // static | 160 // static |
222 CompositingIOSurfaceContext::WindowMap* | 161 CompositingIOSurfaceContext::WindowMap* |
223 CompositingIOSurfaceContext::window_map() { | 162 CompositingIOSurfaceContext::window_map() { |
224 return window_map_.Pointer(); | 163 return window_map_.Pointer(); |
225 } | 164 } |
226 | 165 |
227 // static | 166 // static |
228 base::LazyInstance<CompositingIOSurfaceContext::WindowMap> | 167 base::LazyInstance<CompositingIOSurfaceContext::WindowMap> |
229 CompositingIOSurfaceContext::window_map_; | 168 CompositingIOSurfaceContext::window_map_; |
230 | 169 |
231 } // namespace content | 170 } // namespace content |
OLD | NEW |