| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "ui/gl/gl_surface.h" | |
| 6 | |
| 7 #include <dwmapi.h> | |
| 8 | |
| 9 #include "base/command_line.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/memory/scoped_ptr.h" | |
| 12 #include "base/trace_event/trace_event.h" | |
| 13 #include "base/win/windows_version.h" | |
| 14 #include "ui/gfx/frame_time.h" | |
| 15 #include "ui/gfx/native_widget_types.h" | |
| 16 #include "ui/gl/gl_bindings.h" | |
| 17 #include "ui/gl/gl_implementation.h" | |
| 18 #include "ui/gl/gl_surface_egl.h" | |
| 19 #include "ui/gl/gl_surface_osmesa.h" | |
| 20 #include "ui/gl/gl_surface_stub.h" | |
| 21 #include "ui/gl/gl_surface_wgl.h" | |
| 22 | |
| 23 // From ANGLE's egl/eglext.h. | |
| 24 #if !defined(EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE) | |
| 25 #define EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE \ | |
| 26 reinterpret_cast<EGLNativeDisplayType>(-2) | |
| 27 #endif | |
| 28 | |
| 29 namespace gfx { | |
| 30 | |
| 31 // This OSMesa GL surface can use GDI to swap the contents of the buffer to a | |
| 32 // view. | |
| 33 class NativeViewGLSurfaceOSMesa : public GLSurfaceOSMesa { | |
| 34 public: | |
| 35 explicit NativeViewGLSurfaceOSMesa(gfx::AcceleratedWidget window); | |
| 36 virtual ~NativeViewGLSurfaceOSMesa(); | |
| 37 | |
| 38 // Implement subset of GLSurface. | |
| 39 virtual bool Initialize() override; | |
| 40 virtual void Destroy() override; | |
| 41 virtual bool IsOffscreen() override; | |
| 42 virtual bool SwapBuffers() override; | |
| 43 virtual bool SupportsPostSubBuffer() override; | |
| 44 virtual bool PostSubBuffer(int x, int y, int width, int height) override; | |
| 45 | |
| 46 private: | |
| 47 gfx::AcceleratedWidget window_; | |
| 48 HDC device_context_; | |
| 49 | |
| 50 DISALLOW_COPY_AND_ASSIGN(NativeViewGLSurfaceOSMesa); | |
| 51 }; | |
| 52 | |
| 53 class WinVSyncProvider : public VSyncProvider { | |
| 54 public: | |
| 55 explicit WinVSyncProvider(gfx::AcceleratedWidget window) : | |
| 56 window_(window) | |
| 57 { | |
| 58 use_dwm_ = (base::win::GetVersion() >= base::win::VERSION_WIN7); | |
| 59 } | |
| 60 | |
| 61 virtual ~WinVSyncProvider() {} | |
| 62 | |
| 63 virtual void GetVSyncParameters(const UpdateVSyncCallback& callback) { | |
| 64 TRACE_EVENT0("gpu", "WinVSyncProvider::GetVSyncParameters"); | |
| 65 | |
| 66 base::TimeTicks timebase; | |
| 67 base::TimeDelta interval; | |
| 68 bool dwm_active = false; | |
| 69 | |
| 70 // Query the DWM timing info first if available. This will provide the most | |
| 71 // precise values. | |
| 72 if (use_dwm_) { | |
| 73 DWM_TIMING_INFO timing_info; | |
| 74 timing_info.cbSize = sizeof(timing_info); | |
| 75 HRESULT result = DwmGetCompositionTimingInfo(NULL, &timing_info); | |
| 76 if (result == S_OK) { | |
| 77 dwm_active = true; | |
| 78 if (gfx::FrameTime::TimestampsAreHighRes()) { | |
| 79 // qpcRefreshPeriod is very accurate but noisy, and must be used with | |
| 80 // a high resolution timebase to avoid frequently missing Vsync. | |
| 81 timebase = gfx::FrameTime::FromQPCValue( | |
| 82 static_cast<LONGLONG>(timing_info.qpcVBlank)); | |
| 83 interval = base::TimeDelta::FromQPCValue( | |
| 84 static_cast<LONGLONG>(timing_info.qpcRefreshPeriod)); | |
| 85 } else if (timing_info.rateRefresh.uiDenominator > 0 && | |
| 86 timing_info.rateRefresh.uiNumerator > 0) { | |
| 87 // If FrameTime is not high resolution, we do not want to translate | |
| 88 // the QPC value provided by DWM into the low-resolution timebase, | |
| 89 // which would be error prone and jittery. As a fallback, we assume | |
| 90 // the timebase is zero and use rateRefresh, which may be rounded but | |
| 91 // isn't noisy like qpcRefreshPeriod, instead. The fact that we don't | |
| 92 // have a timebase here may lead to brief periods of jank when our | |
| 93 // scheduling becomes offset from the hardware vsync. | |
| 94 | |
| 95 // Swap the numerator/denominator to convert frequency to period. | |
| 96 interval = base::TimeDelta::FromMicroseconds( | |
| 97 timing_info.rateRefresh.uiDenominator * | |
| 98 base::Time::kMicrosecondsPerSecond / | |
| 99 timing_info.rateRefresh.uiNumerator); | |
| 100 } | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 if (!dwm_active) { | |
| 105 // When DWM compositing is active all displays are normalized to the | |
| 106 // refresh rate of the primary display, and won't composite any faster. | |
| 107 // If DWM compositing is disabled, though, we can use the refresh rates | |
| 108 // reported by each display, which will help systems that have mis-matched | |
| 109 // displays that run at different frequencies. | |
| 110 HMONITOR monitor = MonitorFromWindow(window_, MONITOR_DEFAULTTONEAREST); | |
| 111 MONITORINFOEX monitor_info; | |
| 112 monitor_info.cbSize = sizeof(MONITORINFOEX); | |
| 113 BOOL result = GetMonitorInfo(monitor, &monitor_info); | |
| 114 if (result) { | |
| 115 DEVMODE display_info; | |
| 116 display_info.dmSize = sizeof(DEVMODE); | |
| 117 display_info.dmDriverExtra = 0; | |
| 118 result = EnumDisplaySettings(monitor_info.szDevice, | |
| 119 ENUM_CURRENT_SETTINGS, &display_info); | |
| 120 if (result && display_info.dmDisplayFrequency > 1) { | |
| 121 interval = base::TimeDelta::FromMicroseconds( | |
| 122 (1.0 / static_cast<double>(display_info.dmDisplayFrequency)) * | |
| 123 base::Time::kMicrosecondsPerSecond); | |
| 124 } | |
| 125 } | |
| 126 } | |
| 127 | |
| 128 if (interval.ToInternalValue() != 0) { | |
| 129 callback.Run(timebase, interval); | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 private: | |
| 134 DISALLOW_COPY_AND_ASSIGN(WinVSyncProvider); | |
| 135 | |
| 136 gfx::AcceleratedWidget window_; | |
| 137 bool use_dwm_; | |
| 138 }; | |
| 139 | |
| 140 // Helper routine that does one-off initialization like determining the | |
| 141 // pixel format. | |
| 142 bool GLSurface::InitializeOneOffInternal() { | |
| 143 switch (GetGLImplementation()) { | |
| 144 case kGLImplementationDesktopGL: | |
| 145 if (!GLSurfaceWGL::InitializeOneOff()) { | |
| 146 LOG(ERROR) << "GLSurfaceWGL::InitializeOneOff failed."; | |
| 147 return false; | |
| 148 } | |
| 149 break; | |
| 150 case kGLImplementationEGLGLES2: | |
| 151 if (!GLSurfaceEGL::InitializeOneOff()) { | |
| 152 LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed."; | |
| 153 return false; | |
| 154 } | |
| 155 break; | |
| 156 } | |
| 157 return true; | |
| 158 } | |
| 159 | |
| 160 NativeViewGLSurfaceOSMesa::NativeViewGLSurfaceOSMesa( | |
| 161 gfx::AcceleratedWidget window) | |
| 162 : GLSurfaceOSMesa(OSMesaSurfaceFormatRGBA, gfx::Size(1, 1)), | |
| 163 window_(window), | |
| 164 device_context_(NULL) { | |
| 165 DCHECK(window); | |
| 166 } | |
| 167 | |
| 168 NativeViewGLSurfaceOSMesa::~NativeViewGLSurfaceOSMesa() { | |
| 169 Destroy(); | |
| 170 } | |
| 171 | |
| 172 bool NativeViewGLSurfaceOSMesa::Initialize() { | |
| 173 if (!GLSurfaceOSMesa::Initialize()) | |
| 174 return false; | |
| 175 | |
| 176 device_context_ = GetDC(window_); | |
| 177 return true; | |
| 178 } | |
| 179 | |
| 180 void NativeViewGLSurfaceOSMesa::Destroy() { | |
| 181 if (window_ && device_context_) | |
| 182 ReleaseDC(window_, device_context_); | |
| 183 | |
| 184 device_context_ = NULL; | |
| 185 | |
| 186 GLSurfaceOSMesa::Destroy(); | |
| 187 } | |
| 188 | |
| 189 bool NativeViewGLSurfaceOSMesa::IsOffscreen() { | |
| 190 return false; | |
| 191 } | |
| 192 | |
| 193 bool NativeViewGLSurfaceOSMesa::SwapBuffers() { | |
| 194 DCHECK(device_context_); | |
| 195 | |
| 196 gfx::Size size = GetSize(); | |
| 197 | |
| 198 // Note: negating the height below causes GDI to treat the bitmap data as row | |
| 199 // 0 being at the top. | |
| 200 BITMAPV4HEADER info = { sizeof(BITMAPV4HEADER) }; | |
| 201 info.bV4Width = size.width(); | |
| 202 info.bV4Height = -size.height(); | |
| 203 info.bV4Planes = 1; | |
| 204 info.bV4BitCount = 32; | |
| 205 info.bV4V4Compression = BI_BITFIELDS; | |
| 206 info.bV4RedMask = 0x000000FF; | |
| 207 info.bV4GreenMask = 0x0000FF00; | |
| 208 info.bV4BlueMask = 0x00FF0000; | |
| 209 info.bV4AlphaMask = 0xFF000000; | |
| 210 | |
| 211 // Copy the back buffer to the window's device context. Do not check whether | |
| 212 // StretchDIBits succeeds or not. It will fail if the window has been | |
| 213 // destroyed but it is preferable to allow rendering to silently fail if the | |
| 214 // window is destroyed. This is because the primary application of this | |
| 215 // class of GLContext is for testing and we do not want every GL related ui / | |
| 216 // browser test to become flaky if there is a race condition between GL | |
| 217 // context destruction and window destruction. | |
| 218 StretchDIBits(device_context_, | |
| 219 0, 0, size.width(), size.height(), | |
| 220 0, 0, size.width(), size.height(), | |
| 221 GetHandle(), | |
| 222 reinterpret_cast<BITMAPINFO*>(&info), | |
| 223 DIB_RGB_COLORS, | |
| 224 SRCCOPY); | |
| 225 | |
| 226 return true; | |
| 227 } | |
| 228 | |
| 229 bool NativeViewGLSurfaceOSMesa::SupportsPostSubBuffer() { | |
| 230 return true; | |
| 231 } | |
| 232 | |
| 233 bool NativeViewGLSurfaceOSMesa::PostSubBuffer( | |
| 234 int x, int y, int width, int height) { | |
| 235 DCHECK(device_context_); | |
| 236 | |
| 237 gfx::Size size = GetSize(); | |
| 238 | |
| 239 // Note: negating the height below causes GDI to treat the bitmap data as row | |
| 240 // 0 being at the top. | |
| 241 BITMAPV4HEADER info = { sizeof(BITMAPV4HEADER) }; | |
| 242 info.bV4Width = size.width(); | |
| 243 info.bV4Height = -size.height(); | |
| 244 info.bV4Planes = 1; | |
| 245 info.bV4BitCount = 32; | |
| 246 info.bV4V4Compression = BI_BITFIELDS; | |
| 247 info.bV4RedMask = 0x000000FF; | |
| 248 info.bV4GreenMask = 0x0000FF00; | |
| 249 info.bV4BlueMask = 0x00FF0000; | |
| 250 info.bV4AlphaMask = 0xFF000000; | |
| 251 | |
| 252 // Copy the back buffer to the window's device context. Do not check whether | |
| 253 // StretchDIBits succeeds or not. It will fail if the window has been | |
| 254 // destroyed but it is preferable to allow rendering to silently fail if the | |
| 255 // window is destroyed. This is because the primary application of this | |
| 256 // class of GLContext is for testing and we do not want every GL related ui / | |
| 257 // browser test to become flaky if there is a race condition between GL | |
| 258 // context destruction and window destruction. | |
| 259 StretchDIBits(device_context_, | |
| 260 x, size.height() - y - height, width, height, | |
| 261 x, y, width, height, | |
| 262 GetHandle(), | |
| 263 reinterpret_cast<BITMAPINFO*>(&info), | |
| 264 DIB_RGB_COLORS, | |
| 265 SRCCOPY); | |
| 266 | |
| 267 return true; | |
| 268 } | |
| 269 | |
| 270 scoped_refptr<GLSurface> GLSurface::CreateViewGLSurface( | |
| 271 gfx::AcceleratedWidget window) { | |
| 272 TRACE_EVENT0("gpu", "GLSurface::CreateViewGLSurface"); | |
| 273 switch (GetGLImplementation()) { | |
| 274 case kGLImplementationOSMesaGL: { | |
| 275 scoped_refptr<GLSurface> surface( | |
| 276 new NativeViewGLSurfaceOSMesa(window)); | |
| 277 if (!surface->Initialize()) | |
| 278 return NULL; | |
| 279 | |
| 280 return surface; | |
| 281 } | |
| 282 case kGLImplementationEGLGLES2: { | |
| 283 DCHECK(window != gfx::kNullAcceleratedWidget); | |
| 284 scoped_refptr<NativeViewGLSurfaceEGL> surface( | |
| 285 new NativeViewGLSurfaceEGL(window)); | |
| 286 scoped_ptr<VSyncProvider> sync_provider; | |
| 287 sync_provider.reset(new WinVSyncProvider(window)); | |
| 288 if (!surface->Initialize(sync_provider.Pass())) | |
| 289 return NULL; | |
| 290 | |
| 291 return surface; | |
| 292 } | |
| 293 case kGLImplementationDesktopGL: { | |
| 294 scoped_refptr<GLSurface> surface(new NativeViewGLSurfaceWGL( | |
| 295 window)); | |
| 296 if (!surface->Initialize()) | |
| 297 return NULL; | |
| 298 | |
| 299 return surface; | |
| 300 } | |
| 301 case kGLImplementationMockGL: | |
| 302 return new GLSurfaceStub; | |
| 303 default: | |
| 304 NOTREACHED(); | |
| 305 return NULL; | |
| 306 } | |
| 307 } | |
| 308 | |
| 309 scoped_refptr<GLSurface> GLSurface::CreateOffscreenGLSurface( | |
| 310 const gfx::Size& size) { | |
| 311 TRACE_EVENT0("gpu", "GLSurface::CreateOffscreenGLSurface"); | |
| 312 switch (GetGLImplementation()) { | |
| 313 case kGLImplementationOSMesaGL: { | |
| 314 scoped_refptr<GLSurface> surface( | |
| 315 new GLSurfaceOSMesa(OSMesaSurfaceFormatRGBA, size)); | |
| 316 if (!surface->Initialize()) | |
| 317 return NULL; | |
| 318 | |
| 319 return surface; | |
| 320 } | |
| 321 case kGLImplementationEGLGLES2: { | |
| 322 scoped_refptr<GLSurface> surface(new PbufferGLSurfaceEGL(size)); | |
| 323 if (!surface->Initialize()) | |
| 324 return NULL; | |
| 325 | |
| 326 return surface; | |
| 327 } | |
| 328 case kGLImplementationDesktopGL: { | |
| 329 scoped_refptr<GLSurface> surface(new PbufferGLSurfaceWGL(size)); | |
| 330 if (!surface->Initialize()) | |
| 331 return NULL; | |
| 332 | |
| 333 return surface; | |
| 334 } | |
| 335 case kGLImplementationMockGL: | |
| 336 return new GLSurfaceStub; | |
| 337 default: | |
| 338 NOTREACHED(); | |
| 339 return NULL; | |
| 340 } | |
| 341 } | |
| 342 | |
| 343 EGLNativeDisplayType GetPlatformDefaultEGLNativeDisplay() { | |
| 344 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 345 switches::kDisableD3D11) || | |
| 346 base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseWarp)) | |
| 347 return GetDC(NULL); | |
| 348 return EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE; | |
| 349 } | |
| 350 | |
| 351 } // namespace gfx | |
| OLD | NEW |