| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "ui/accelerated_widget_mac/io_surface_ns_gl_surface.h" | 5 #include "ui/accelerated_widget_mac/io_surface_ns_gl_surface.h" |
| 6 | 6 |
| 7 #include <OpenGL/CGLRenderers.h> |
| 7 #include <OpenGL/GL.h> | 8 #include <OpenGL/GL.h> |
| 8 | 9 |
| 9 #include "base/callback_helpers.h" | 10 #include "base/callback_helpers.h" |
| 11 #include "base/command_line.h" |
| 10 #include "base/mac/bind_objc_block.h" | 12 #include "base/mac/bind_objc_block.h" |
| 13 #include "base/mac/mac_util.h" |
| 11 #include "base/mac/sdk_forward_declarations.h" | 14 #include "base/mac/sdk_forward_declarations.h" |
| 12 #include "base/message_loop/message_loop.h" | 15 #include "base/message_loop/message_loop.h" |
| 13 #include "base/trace_event/trace_event.h" | 16 #include "base/trace_event/trace_event.h" |
| 14 #include "ui/base/cocoa/animation_utils.h" | 17 #include "ui/base/cocoa/animation_utils.h" |
| 18 #include "ui/base/ui_base_switches.h" |
| 15 #include "ui/gfx/geometry/dip_util.h" | 19 #include "ui/gfx/geometry/dip_util.h" |
| 16 #include "ui/gfx/geometry/rect.h" | 20 #include "ui/gfx/geometry/rect.h" |
| 17 #include "ui/gl/gpu_switching_manager.h" | 21 #include "ui/gl/gpu_switching_manager.h" |
| 18 | 22 |
| 19 namespace ui { | 23 namespace ui { |
| 20 | 24 |
| 21 IOSurfaceNSGLSurface* IOSurfaceNSGLSurface::Create( | 25 IOSurfaceNSGLSurface* IOSurfaceNSGLSurface::Create( |
| 22 IOSurfaceNSGLSurfaceClient* client, NSView* view) { | 26 IOSurfaceNSGLSurfaceClient* client, |
| 23 scoped_refptr<IOSurfaceTexture> iosurface = IOSurfaceTexture::Create(false); | 27 NSView* view, |
| 28 bool needs_gl_finish_workaround) { |
| 29 scoped_refptr<IOSurfaceTexture> iosurface = |
| 30 IOSurfaceTexture::Create(needs_gl_finish_workaround, true); |
| 24 if (!iosurface) | 31 if (!iosurface) |
| 25 return NULL; | 32 return NULL; |
| 26 | 33 |
| 27 std::vector<NSOpenGLPixelFormatAttribute> attribs; | 34 std::vector<NSOpenGLPixelFormatAttribute> attribs; |
| 28 attribs.push_back(NSOpenGLPFAColorSize); | 35 attribs.push_back(NSOpenGLPFAColorSize); |
| 29 attribs.push_back(24); | 36 attribs.push_back(24); |
| 30 attribs.push_back(NSOpenGLPFAAlphaSize); | 37 attribs.push_back(NSOpenGLPFAAlphaSize); |
| 31 attribs.push_back(8); | 38 attribs.push_back(8); |
| 32 attribs.push_back(NSOpenGLPFAAccelerated); | 39 attribs.push_back(NSOpenGLPFAAccelerated); |
| 33 if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus()) | 40 if (ui::GpuSwitchingManager::GetInstance()->SupportsDualGpus()) |
| 34 attribs.push_back(NSOpenGLPFAAllowOfflineRenderers); | 41 attribs.push_back(NSOpenGLPFAAllowOfflineRenderers); |
| 35 attribs.push_back(0); | 42 attribs.push_back(0); |
| 36 base::scoped_nsobject<NSOpenGLPixelFormat> pixel_format( | 43 base::scoped_nsobject<NSOpenGLPixelFormat> pixel_format( |
| 37 [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs.front()]); | 44 [[NSOpenGLPixelFormat alloc] initWithAttributes:&attribs.front()]); |
| 38 if (!pixel_format) { | 45 if (!pixel_format) { |
| 39 LOG(ERROR) << "Failed to create pixel format object."; | 46 LOG(ERROR) << "Failed to create pixel format object."; |
| 40 return NULL; | 47 return NULL; |
| 41 } | 48 } |
| 42 | 49 |
| 43 base::scoped_nsobject<NSOpenGLContext> ns_gl_context( | 50 base::scoped_nsobject<NSOpenGLContext> ns_gl_context( |
| 44 [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:nil]); | 51 [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:nil]); |
| 45 if (!ns_gl_context) { | 52 if (!ns_gl_context) { |
| 46 LOG(ERROR) << "Failed to create context object."; | 53 LOG(ERROR) << "Failed to create context object."; |
| 47 return NULL; | 54 return NULL; |
| 48 } | 55 } |
| 49 | 56 |
| 50 return new IOSurfaceNSGLSurface(client, view, ns_gl_context, iosurface); | 57 return new IOSurfaceNSGLSurface( |
| 58 client, view, needs_gl_finish_workaround, ns_gl_context, iosurface); |
| 51 } | 59 } |
| 52 | 60 |
| 53 IOSurfaceNSGLSurface::IOSurfaceNSGLSurface( | 61 IOSurfaceNSGLSurface::IOSurfaceNSGLSurface( |
| 54 IOSurfaceNSGLSurfaceClient* client, | 62 IOSurfaceNSGLSurfaceClient* client, |
| 55 NSView* view, | 63 NSView* view, |
| 64 bool needs_gl_finish_workaround, |
| 56 base::scoped_nsobject<NSOpenGLContext> ns_gl_context, | 65 base::scoped_nsobject<NSOpenGLContext> ns_gl_context, |
| 57 scoped_refptr<ui::IOSurfaceTexture> iosurface) | 66 scoped_refptr<ui::IOSurfaceTexture> iosurface) |
| 58 : client_(client), view_(view), iosurface_(iosurface), | 67 : client_(client), view_(view), iosurface_(iosurface), |
| 59 ns_gl_context_(ns_gl_context), contents_scale_factor_(1), | 68 ns_gl_context_(ns_gl_context), contents_scale_factor_(1), |
| 60 pending_draw_exists_(false) { | 69 pending_draw_exists_(false), needs_to_be_recreated_(false) { |
| 61 [[view_ layer] setContentsGravity:kCAGravityTopLeft]; | 70 [[view_ layer] setContentsGravity:kCAGravityTopLeft]; |
| 71 ui::GpuSwitchingManager::GetInstance()->AddObserver(this); |
| 62 } | 72 } |
| 63 | 73 |
| 64 IOSurfaceNSGLSurface::~IOSurfaceNSGLSurface() { | 74 IOSurfaceNSGLSurface::~IOSurfaceNSGLSurface() { |
| 75 ui::GpuSwitchingManager::GetInstance()->RemoveObserver(this); |
| 65 DoPendingDrawIfNeeded(); | 76 DoPendingDrawIfNeeded(); |
| 66 [ns_gl_context_ makeCurrentContext]; | 77 [ns_gl_context_ makeCurrentContext]; |
| 67 iosurface_ = NULL; | 78 iosurface_ = NULL; |
| 68 [NSOpenGLContext clearCurrentContext]; | 79 [NSOpenGLContext clearCurrentContext]; |
| 69 [ns_gl_context_ clearDrawable]; | 80 [ns_gl_context_ clearDrawable]; |
| 70 | 81 |
| 71 ScopedCAActionDisabler disabler; | 82 ScopedCAActionDisabler disabler; |
| 72 [[view_ layer] setContents:nil]; | 83 [[view_ layer] setContents:nil]; |
| 73 } | 84 } |
| 74 | 85 |
| 75 bool IOSurfaceNSGLSurface::GotFrame(IOSurfaceID io_surface_id, | 86 bool IOSurfaceNSGLSurface::GotFrame(IOSurfaceID io_surface_id, |
| 76 gfx::Size frame_pixel_size, | 87 gfx::Size frame_pixel_size, |
| 77 float frame_scale_factor, | 88 float frame_scale_factor, |
| 78 gfx::Rect pixel_damage_rect) { | 89 gfx::Rect pixel_damage_rect) { |
| 90 TRACE_EVENT0("ui", "IOSurfaceNSGLSurface::GotFrame"); |
| 79 pending_draw_exists_ = true; | 91 pending_draw_exists_ = true; |
| 80 pending_draw_damage_rect_.Union(pixel_damage_rect); | 92 pending_draw_damage_rect_.Union(pixel_damage_rect); |
| 81 | 93 |
| 82 // If the OpenGL framebuffer does not match the frame in scale factor or | 94 // If the OpenGL framebuffer does not match the frame in scale factor or |
| 83 // pixel size, then re-latch them. Note that they will latch to the layer's | 95 // pixel size, then re-latch them. Note that they will latch to the layer's |
| 84 // bounds, which will not necessarily match the frame's pixel size. | 96 // bounds, which will not necessarily match the frame's pixel size. |
| 85 if (!iosurface_->IsUpToDate(io_surface_id, frame_pixel_size) || | 97 if (!iosurface_->IsUpToDate(io_surface_id, frame_pixel_size) || |
| 86 [ns_gl_context_ view] != view_ || | 98 [ns_gl_context_ view] != view_ || |
| 87 frame_pixel_size != contents_pixel_size_ || | 99 frame_pixel_size != contents_pixel_size_ || |
| 88 frame_scale_factor != contents_scale_factor_) { | 100 frame_scale_factor != contents_scale_factor_) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 102 glClear(GL_COLOR_BUFFER_BIT); | 114 glClear(GL_COLOR_BUFFER_BIT); |
| 103 | 115 |
| 104 // Retrieve the backbuffer's size from the GL viewport state. | 116 // Retrieve the backbuffer's size from the GL viewport state. |
| 105 GLint viewport[4]; | 117 GLint viewport[4]; |
| 106 glGetIntegerv(GL_VIEWPORT, viewport); | 118 glGetIntegerv(GL_VIEWPORT, viewport); |
| 107 contents_pixel_size_ = gfx::Size(viewport[2], viewport[3]); | 119 contents_pixel_size_ = gfx::Size(viewport[2], viewport[3]); |
| 108 contents_scale_factor_ = frame_scale_factor; | 120 contents_scale_factor_ = frame_scale_factor; |
| 109 | 121 |
| 110 bool result = iosurface_->SetIOSurface(io_surface_id, frame_pixel_size); | 122 bool result = iosurface_->SetIOSurface(io_surface_id, frame_pixel_size); |
| 111 [NSOpenGLContext clearCurrentContext]; | 123 [NSOpenGLContext clearCurrentContext]; |
| 112 if (!result) | 124 if (!result) { |
| 125 LOG(ERROR) << "Failed to set IOSurface, poisoning NSOpenGLContext."; |
| 126 needs_to_be_recreated_ = true; |
| 113 return false; | 127 return false; |
| 128 } |
| 114 | 129 |
| 115 pending_draw_damage_rect_ = gfx::Rect(frame_pixel_size); | 130 pending_draw_damage_rect_ = gfx::Rect(frame_pixel_size); |
| 116 } | 131 } |
| 117 | 132 |
| 118 DoPendingDrawIfNeeded(); | 133 DoPendingDrawIfNeeded(); |
| 119 return true; | 134 return true; |
| 120 } | 135 } |
| 121 | 136 |
| 122 void IOSurfaceNSGLSurface::DoPendingDrawIfNeeded() { | 137 void IOSurfaceNSGLSurface::DoPendingDrawIfNeeded() { |
| 123 if (!pending_draw_exists_) | 138 if (!pending_draw_exists_) |
| 124 return; | 139 return; |
| 125 | 140 |
| 126 { | 141 { |
| 127 TRACE_EVENT0("ui", "IOSurfaceNSGLSurface::Draw"); | 142 TRACE_EVENT0("ui", "IOSurfaceNSGLSurface::Draw"); |
| 128 [ns_gl_context_ makeCurrentContext]; | 143 [ns_gl_context_ makeCurrentContext]; |
| 129 ignore_result(iosurface_->DrawIOSurfaceWithDamageRect( | 144 bool result = iosurface_->DrawIOSurfaceWithDamageRect( |
| 130 pending_draw_damage_rect_)); | 145 pending_draw_damage_rect_); |
| 146 if (!result) { |
| 147 LOG(ERROR) << "Failed to draw IOSurface, poisoning NSOpenGLContext."; |
| 148 needs_to_be_recreated_ = true; |
| 149 } |
| 131 glFlush(); | 150 glFlush(); |
| 132 [NSOpenGLContext clearCurrentContext]; | 151 [NSOpenGLContext clearCurrentContext]; |
| 133 } | 152 } |
| 134 | 153 |
| 135 pending_draw_exists_ = false; | 154 pending_draw_exists_ = false; |
| 136 pending_draw_damage_rect_ = gfx::Rect(); | 155 pending_draw_damage_rect_ = gfx::Rect(); |
| 137 | 156 |
| 138 client_->IOSurfaceNSGLSurfaceDidDrawFrame(); | 157 client_->IOSurfaceNSGLSurfaceDidDrawFrame(); |
| 139 } | 158 } |
| 140 | 159 |
| 141 }; | 160 int IOSurfaceNSGLSurface::GetRendererID() { |
| 161 GLint current_renderer_id = -1; |
| 162 CGLContextObj cgl_context = static_cast<CGLContextObj>( |
| 163 [ns_gl_context_ CGLContextObj]); |
| 164 if (CGLGetParameter(cgl_context, |
| 165 kCGLCPCurrentRendererID, |
| 166 ¤t_renderer_id) == kCGLNoError) { |
| 167 return current_renderer_id & kCGLRendererIDMatchingMask; |
| 168 } |
| 169 return -1; |
| 170 } |
| 171 |
| 172 void IOSurfaceNSGLSurface::OnGpuSwitched() { |
| 173 needs_to_be_recreated_ = true; |
| 174 } |
| 175 |
| 176 // static |
| 177 bool IOSurfaceNSGLSurface::CanUseNSGLSurfaceForView(NSView* view) { |
| 178 // This must be explicitly enabled at the command line. |
| 179 static bool use_ns_gl_surfaces = |
| 180 base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 181 switches::kEnableNSGLSurfaces); |
| 182 if (!use_ns_gl_surfaces) |
| 183 return false; |
| 184 |
| 185 // Do not attempt this before 10.9. The power savings are not worth the |
| 186 // stability risk and testing burden. |
| 187 if (!base::mac::IsOSMavericksOrLater()) |
| 188 return false; |
| 189 |
| 190 // If the NSView being attached to the NSOpenGLContext is not on the main |
| 191 // monitor, then, due to an OS X bug, the IOSurface will be thrashed, |
| 192 // resulting in hangs of 50 msec or more. |
| 193 CGDirectDisplayID main_display = CGMainDisplayID(); |
| 194 NSScreen* screen = [[view window] screen]; |
| 195 NSDictionary* screen_description = [screen deviceDescription]; |
| 196 NSNumber* screen_number = [screen_description objectForKey:@"NSScreenNumber"]; |
| 197 CGDirectDisplayID display_id = [screen_number unsignedIntValue]; |
| 198 if (display_id != main_display) |
| 199 return false; |
| 200 |
| 201 return true; |
| 202 } |
| 203 |
| 204 } // namespace ui |
| OLD | NEW |