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 38namespace gfx { 39 40// This OSMesa GL surface can use GDI to swap the contents of the buffer to a 41// view. 42class 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 62class 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. 103bool 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 121NativeViewGLSurfaceOSMesa::NativeViewGLSurfaceOSMesa( 122 gfx::AcceleratedWidget window) 123 : GLSurfaceOSMesa(OSMesaSurfaceFormatRGBA, gfx::Size(1, 1)), 124 window_(window), 125 device_context_(NULL) { 126 DCHECK(window); 127} 128 129NativeViewGLSurfaceOSMesa::~NativeViewGLSurfaceOSMesa() { 130 Destroy(); 131} 132 133bool NativeViewGLSurfaceOSMesa::Initialize() { 134 if (!GLSurfaceOSMesa::Initialize()) 135 return false; 136 137 device_context_ = GetDC(window_); 138 return true; 139} 140 141void NativeViewGLSurfaceOSMesa::Destroy() { 142 if (window_ && device_context_) 143 ReleaseDC(window_, device_context_); 144 145 device_context_ = NULL; 146 147 GLSurfaceOSMesa::Destroy(); 148} 149 150bool NativeViewGLSurfaceOSMesa::IsOffscreen() { 151 return false; 152} 153 154bool 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 190bool NativeViewGLSurfaceOSMesa::SupportsPostSubBuffer() { 191 return true; 192} 193 194bool 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 231scoped_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 271scoped_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 305EGLNativeDisplayType 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