Revert 268405 "Make sure that ScratchBuffer::Allocate() always r..."
[chromium-blink-merge.git] / content / browser / renderer_host / compositing_iosurface_context_mac.mm
blobfecab509325fd0c1f8d6cd3cb840afdf488ae973
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"
7 #include <OpenGL/gl.h>
8 #include <OpenGL/OpenGL.h>
9 #include <vector>
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"
20 namespace content {
22 CoreAnimationStatus GetCoreAnimationStatus() {
23   static CoreAnimationStatus status =
24       CommandLine::ForCurrentProcess()->HasSwitch(
25           switches::kDisableCoreAnimation) ?
26               CORE_ANIMATION_DISABLED : CORE_ANIMATION_ENABLED;
27   return status;
30 // static
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_);
39     return found->second;
40   }
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()]);
59     if (!glPixelFormat) {
60       LOG(ERROR) << "NSOpenGLPixelFormat initWithAttributes failed";
61       return NULL;
62     }
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();
69     nsgl_context.reset(
70         [[NSOpenGLContext alloc] initWithFormat:glPixelFormat
71                                    shareContext:share_context]);
72     if (!nsgl_context) {
73       LOG(ERROR) << "NSOpenGLContext initWithFormat failed";
74       return NULL;
75     }
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]);
81     if (!cgl_context) {
82       LOG(ERROR) << "Failed to retrieve CGLContextObj from NSOpenGLContext";
83       return NULL;
84     }
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;
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     }
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;
124     // Note that VSync is ignored because CoreAnimation will automatically
125     // rate limit draws.
126   }
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;
132   {
133     gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context);
134     shader_program_cache.reset(new CompositingIOSurfaceShaderPrograms());
135     if (window_number == kOffscreenContextWindowNumber) {
136       prepared = true;
137     } else {
138       prepared = (
139           shader_program_cache->UseBlitProgram() &&
140           shader_program_cache->UseSolidWhiteProgram());
141     }
142     glUseProgram(0u);
143   }
144   if (!prepared) {
145     LOG(ERROR) << "IOSurface failed to compile/link required shader programs.";
146     return NULL;
147   }
149   return new CompositingIOSurfaceContext(
150       window_number,
151       nsgl_context.release(),
152       cgl_context_strong,
153       cgl_context,
154       is_vsync_disabled,
155       shader_program_cache.Pass());
158 void CompositingIOSurfaceContext::PoisonContextAndSharegroup() {
159   if (poisoned_)
160     return;
162   for (WindowMap::iterator it = window_map()->begin();
163        it != window_map()->end();
164        ++it) {
165     it->second->poisoned_ = true;
166   }
167   window_map()->clear();
170 CompositingIOSurfaceContext::CompositingIOSurfaceContext(
171     int window_number,
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()),
183       poisoned_(false) {
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);
193   {
194     gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_);
195     shader_program_cache_->Reset();
196   }
197   if (!poisoned_) {
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_);
201   } else {
202     WindowMap::const_iterator found = window_map()->find(window_number_);
203     if (found != window_map()->end())
204       DCHECK(found->second != this);
205   }
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();
221 // static
222 CompositingIOSurfaceContext::WindowMap*
223     CompositingIOSurfaceContext::window_map() {
224   return window_map_.Pointer();
227 // static
228 base::LazyInstance<CompositingIOSurfaceContext::WindowMap>
229     CompositingIOSurfaceContext::window_map_;
231 }  // namespace content