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