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/debug/trace_event.h" 11 #include "base/logging.h" 12 #include "base/memory/scoped_ptr.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 #if !defined(EGL_PLATFORM_ANGLE_ANGLE) 29 #define EGL_PLATFORM_ANGLE_ANGLE 0x3201 30 #endif 31 #if !defined(EGL_PLATFORM_ANGLE_TYPE_ANGLE) 32 #define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3202 33 #endif 34 #if !defined(EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE) 35 #define EGL_PLATFORM_ANGLE_TYPE_D3D11_WARP_ANGLE 0x3206 36 #endif 37 38 namespace gfx { 39 40 // This OSMesa GL surface can use GDI to swap the contents of the buffer to a 41 // view. 42 class NativeViewGLSurfaceOSMesa : public GLSurfaceOSMesa { 43 public: 44 explicit NativeViewGLSurfaceOSMesa(gfx::AcceleratedWidget window); 45 virtual ~NativeViewGLSurfaceOSMesa(); 46 47 // Implement subset of GLSurface. 48 virtual bool Initialize() OVERRIDE; 49 virtual void Destroy() OVERRIDE; 50 virtual bool IsOffscreen() OVERRIDE; 51 virtual bool SwapBuffers() OVERRIDE; 52 virtual bool SupportsPostSubBuffer() OVERRIDE; 53 virtual bool PostSubBuffer(int x, int y, int width, int height) OVERRIDE; 54 55 private: 56 gfx::AcceleratedWidget window_; 57 HDC device_context_; 58 59 DISALLOW_COPY_AND_ASSIGN(NativeViewGLSurfaceOSMesa); 60 }; 61 62 class DWMVSyncProvider : public VSyncProvider { 63 public: 64 explicit DWMVSyncProvider() {} 65 66 virtual ~DWMVSyncProvider() {} 67 68 virtual void GetVSyncParameters(const UpdateVSyncCallback& callback) { 69 TRACE_EVENT0("gpu", "DWMVSyncProvider::GetVSyncParameters"); 70 DWM_TIMING_INFO timing_info; 71 timing_info.cbSize = sizeof(timing_info); 72 HRESULT result = DwmGetCompositionTimingInfo(NULL, &timing_info); 73 if (result != S_OK) 74 return; 75 76 base::TimeTicks timebase; 77 // If FrameTime is not high resolution, we do not want to translate the 78 // QPC value provided by DWM into the low-resolution timebase, which 79 // would be error prone and jittery. As a fallback, we assume the timebase 80 // is zero. 81 if (gfx::FrameTime::TimestampsAreHighRes()) { 82 timebase = gfx::FrameTime::FromQPCValue( 83 static_cast<LONGLONG>(timing_info.qpcVBlank)); 84 } 85 86 // Swap the numerator/denominator to convert frequency to period. 87 if (timing_info.rateRefresh.uiDenominator > 0 && 88 timing_info.rateRefresh.uiNumerator > 0) { 89 base::TimeDelta interval = base::TimeDelta::FromMicroseconds( 90 timing_info.rateRefresh.uiDenominator * 91 base::Time::kMicrosecondsPerSecond / 92 timing_info.rateRefresh.uiNumerator); 93 callback.Run(timebase, interval); 94 } 95 } 96 97 private: 98 DISALLOW_COPY_AND_ASSIGN(DWMVSyncProvider); 99 }; 100 101 // Helper routine that does one-off initialization like determining the 102 // pixel format. 103 bool GLSurface::InitializeOneOffInternal() { 104 switch (GetGLImplementation()) { 105 case kGLImplementationDesktopGL: 106 if (!GLSurfaceWGL::InitializeOneOff()) { 107 LOG(ERROR) << "GLSurfaceWGL::InitializeOneOff failed."; 108 return false; 109 } 110 break; 111 case kGLImplementationEGLGLES2: 112 if (!GLSurfaceEGL::InitializeOneOff()) { 113 LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed."; 114 return false; 115 } 116 break; 117 } 118 return true; 119 } 120 121 NativeViewGLSurfaceOSMesa::NativeViewGLSurfaceOSMesa( 122 gfx::AcceleratedWidget window) 123 : GLSurfaceOSMesa(OSMesaSurfaceFormatRGBA, gfx::Size(1, 1)), 124 window_(window), 125 device_context_(NULL) { 126 DCHECK(window); 127 } 128 129 NativeViewGLSurfaceOSMesa::~NativeViewGLSurfaceOSMesa() { 130 Destroy(); 131 } 132 133 bool NativeViewGLSurfaceOSMesa::Initialize() { 134 if (!GLSurfaceOSMesa::Initialize()) 135 return false; 136 137 device_context_ = GetDC(window_); 138 return true; 139 } 140 141 void NativeViewGLSurfaceOSMesa::Destroy() { 142 if (window_ && device_context_) 143 ReleaseDC(window_, device_context_); 144 145 device_context_ = NULL; 146 147 GLSurfaceOSMesa::Destroy(); 148 } 149 150 bool NativeViewGLSurfaceOSMesa::IsOffscreen() { 151 return false; 152 } 153 154 bool NativeViewGLSurfaceOSMesa::SwapBuffers() { 155 DCHECK(device_context_); 156 157 gfx::Size size = GetSize(); 158 159 // Note: negating the height below causes GDI to treat the bitmap data as row 160 // 0 being at the top. 161 BITMAPV4HEADER info = { sizeof(BITMAPV4HEADER) }; 162 info.bV4Width = size.width(); 163 info.bV4Height = -size.height(); 164 info.bV4Planes = 1; 165 info.bV4BitCount = 32; 166 info.bV4V4Compression = BI_BITFIELDS; 167 info.bV4RedMask = 0x000000FF; 168 info.bV4GreenMask = 0x0000FF00; 169 info.bV4BlueMask = 0x00FF0000; 170 info.bV4AlphaMask = 0xFF000000; 171 172 // Copy the back buffer to the window's device context. Do not check whether 173 // StretchDIBits succeeds or not. It will fail if the window has been 174 // destroyed but it is preferable to allow rendering to silently fail if the 175 // window is destroyed. This is because the primary application of this 176 // class of GLContext is for testing and we do not want every GL related ui / 177 // browser test to become flaky if there is a race condition between GL 178 // context destruction and window destruction. 179 StretchDIBits(device_context_, 180 0, 0, size.width(), size.height(), 181 0, 0, size.width(), size.height(), 182 GetHandle(), 183 reinterpret_cast<BITMAPINFO*>(&info), 184 DIB_RGB_COLORS, 185 SRCCOPY); 186 187 return true; 188 } 189 190 bool NativeViewGLSurfaceOSMesa::SupportsPostSubBuffer() { 191 return true; 192 } 193 194 bool NativeViewGLSurfaceOSMesa::PostSubBuffer( 195 int x, int y, int width, int height) { 196 DCHECK(device_context_); 197 198 gfx::Size size = GetSize(); 199 200 // Note: negating the height below causes GDI to treat the bitmap data as row 201 // 0 being at the top. 202 BITMAPV4HEADER info = { sizeof(BITMAPV4HEADER) }; 203 info.bV4Width = size.width(); 204 info.bV4Height = -size.height(); 205 info.bV4Planes = 1; 206 info.bV4BitCount = 32; 207 info.bV4V4Compression = BI_BITFIELDS; 208 info.bV4RedMask = 0x000000FF; 209 info.bV4GreenMask = 0x0000FF00; 210 info.bV4BlueMask = 0x00FF0000; 211 info.bV4AlphaMask = 0xFF000000; 212 213 // Copy the back buffer to the window's device context. Do not check whether 214 // StretchDIBits succeeds or not. It will fail if the window has been 215 // destroyed but it is preferable to allow rendering to silently fail if the 216 // window is destroyed. This is because the primary application of this 217 // class of GLContext is for testing and we do not want every GL related ui / 218 // browser test to become flaky if there is a race condition between GL 219 // context destruction and window destruction. 220 StretchDIBits(device_context_, 221 x, size.height() - y - height, width, height, 222 x, y, width, height, 223 GetHandle(), 224 reinterpret_cast<BITMAPINFO*>(&info), 225 DIB_RGB_COLORS, 226 SRCCOPY); 227 228 return true; 229 } 230 231 scoped_refptr<GLSurface> GLSurface::CreateViewGLSurface( 232 gfx::AcceleratedWidget window) { 233 TRACE_EVENT0("gpu", "GLSurface::CreateViewGLSurface"); 234 switch (GetGLImplementation()) { 235 case kGLImplementationOSMesaGL: { 236 scoped_refptr<GLSurface> surface( 237 new NativeViewGLSurfaceOSMesa(window)); 238 if (!surface->Initialize()) 239 return NULL; 240 241 return surface; 242 } 243 case kGLImplementationEGLGLES2: { 244 DCHECK(window != gfx::kNullAcceleratedWidget); 245 scoped_refptr<NativeViewGLSurfaceEGL> surface( 246 new NativeViewGLSurfaceEGL(window)); 247 scoped_ptr<VSyncProvider> sync_provider; 248 if (base::win::GetVersion() >= base::win::VERSION_VISTA) 249 sync_provider.reset(new DWMVSyncProvider); 250 if (!surface->Initialize(sync_provider.Pass())) 251 return NULL; 252 253 return surface; 254 } 255 case kGLImplementationDesktopGL: { 256 scoped_refptr<GLSurface> surface(new NativeViewGLSurfaceWGL( 257 window)); 258 if (!surface->Initialize()) 259 return NULL; 260 261 return surface; 262 } 263 case kGLImplementationMockGL: 264 return new GLSurfaceStub; 265 default: 266 NOTREACHED(); 267 return NULL; 268 } 269 } 270 271 scoped_refptr<GLSurface> GLSurface::CreateOffscreenGLSurface( 272 const gfx::Size& size) { 273 TRACE_EVENT0("gpu", "GLSurface::CreateOffscreenGLSurface"); 274 switch (GetGLImplementation()) { 275 case kGLImplementationOSMesaGL: { 276 scoped_refptr<GLSurface> surface( 277 new GLSurfaceOSMesa(OSMesaSurfaceFormatRGBA, size)); 278 if (!surface->Initialize()) 279 return NULL; 280 281 return surface; 282 } 283 case kGLImplementationEGLGLES2: { 284 scoped_refptr<GLSurface> surface(new PbufferGLSurfaceEGL(size)); 285 if (!surface->Initialize()) 286 return NULL; 287 288 return surface; 289 } 290 case kGLImplementationDesktopGL: { 291 scoped_refptr<GLSurface> surface(new PbufferGLSurfaceWGL(size)); 292 if (!surface->Initialize()) 293 return NULL; 294 295 return surface; 296 } 297 case kGLImplementationMockGL: 298 return new GLSurfaceStub; 299 default: 300 NOTREACHED(); 301 return NULL; 302 } 303 } 304 305 EGLNativeDisplayType GetPlatformDefaultEGLNativeDisplay() { 306 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableD3D11) || 307 CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseWarp)) 308 return GetDC(NULL); 309 return EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE; 310 } 311 312 } // namespace gfx 313