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