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