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.
5 #include "ui/gl/gl_surface.h"
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/trace_event/trace_event.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"
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)
31 // This OSMesa GL surface can use GDI to swap the contents of the buffer to a
33 class NativeViewGLSurfaceOSMesa
: public GLSurfaceOSMesa
{
35 explicit NativeViewGLSurfaceOSMesa(gfx::AcceleratedWidget window
);
36 ~NativeViewGLSurfaceOSMesa() override
;
38 // Implement subset of GLSurface.
39 bool Initialize() override
;
40 void Destroy() override
;
41 bool IsOffscreen() override
;
42 bool SwapBuffers() override
;
43 bool SupportsPostSubBuffer() override
;
44 bool PostSubBuffer(int x
, int y
, int width
, int height
) override
;
47 gfx::AcceleratedWidget window_
;
50 DISALLOW_COPY_AND_ASSIGN(NativeViewGLSurfaceOSMesa
);
53 class WinVSyncProvider
: public VSyncProvider
{
55 explicit WinVSyncProvider(gfx::AcceleratedWidget window
) :
58 use_dwm_
= (base::win::GetVersion() >= base::win::VERSION_WIN7
);
61 ~WinVSyncProvider() override
{}
63 void GetVSyncParameters(const UpdateVSyncCallback
& callback
) override
{
64 TRACE_EVENT0("gpu", "WinVSyncProvider::GetVSyncParameters");
66 base::TimeTicks timebase
;
67 base::TimeDelta interval
;
68 bool dwm_active
= false;
70 // Query the DWM timing info first if available. This will provide the most
73 DWM_TIMING_INFO timing_info
;
74 timing_info
.cbSize
= sizeof(timing_info
);
75 HRESULT result
= DwmGetCompositionTimingInfo(NULL
, &timing_info
);
78 if (gfx::FrameTime::TimestampsAreHighRes()) {
79 // qpcRefreshPeriod is very accurate but noisy, and must be used with
80 // a high resolution timebase to avoid frequently missing Vsync.
81 timebase
= gfx::FrameTime::FromQPCValue(
82 static_cast<LONGLONG
>(timing_info
.qpcVBlank
));
83 interval
= base::TimeDelta::FromQPCValue(
84 static_cast<LONGLONG
>(timing_info
.qpcRefreshPeriod
));
85 } else if (timing_info
.rateRefresh
.uiDenominator
> 0 &&
86 timing_info
.rateRefresh
.uiNumerator
> 0) {
87 // If FrameTime is not high resolution, we do not want to translate
88 // the QPC value provided by DWM into the low-resolution timebase,
89 // which would be error prone and jittery. As a fallback, we assume
90 // the timebase is zero and use rateRefresh, which may be rounded but
91 // isn't noisy like qpcRefreshPeriod, instead. The fact that we don't
92 // have a timebase here may lead to brief periods of jank when our
93 // scheduling becomes offset from the hardware vsync.
95 // Swap the numerator/denominator to convert frequency to period.
96 interval
= base::TimeDelta::FromMicroseconds(
97 timing_info
.rateRefresh
.uiDenominator
*
98 base::Time::kMicrosecondsPerSecond
/
99 timing_info
.rateRefresh
.uiNumerator
);
105 // When DWM compositing is active all displays are normalized to the
106 // refresh rate of the primary display, and won't composite any faster.
107 // If DWM compositing is disabled, though, we can use the refresh rates
108 // reported by each display, which will help systems that have mis-matched
109 // displays that run at different frequencies.
110 HMONITOR monitor
= MonitorFromWindow(window_
, MONITOR_DEFAULTTONEAREST
);
111 MONITORINFOEX monitor_info
;
112 monitor_info
.cbSize
= sizeof(MONITORINFOEX
);
113 BOOL result
= GetMonitorInfo(monitor
, &monitor_info
);
115 DEVMODE display_info
;
116 display_info
.dmSize
= sizeof(DEVMODE
);
117 display_info
.dmDriverExtra
= 0;
118 result
= EnumDisplaySettings(monitor_info
.szDevice
,
119 ENUM_CURRENT_SETTINGS
, &display_info
);
120 if (result
&& display_info
.dmDisplayFrequency
> 1) {
121 interval
= base::TimeDelta::FromMicroseconds(
122 (1.0 / static_cast<double>(display_info
.dmDisplayFrequency
)) *
123 base::Time::kMicrosecondsPerSecond
);
128 if (interval
.ToInternalValue() != 0) {
129 callback
.Run(timebase
, interval
);
134 DISALLOW_COPY_AND_ASSIGN(WinVSyncProvider
);
136 gfx::AcceleratedWidget window_
;
140 // Helper routine that does one-off initialization like determining the
142 bool GLSurface::InitializeOneOffInternal() {
143 switch (GetGLImplementation()) {
144 case kGLImplementationDesktopGL
:
145 if (!GLSurfaceWGL::InitializeOneOff()) {
146 LOG(ERROR
) << "GLSurfaceWGL::InitializeOneOff failed.";
150 case kGLImplementationEGLGLES2
:
151 if (!GLSurfaceEGL::InitializeOneOff()) {
152 LOG(ERROR
) << "GLSurfaceEGL::InitializeOneOff failed.";
160 NativeViewGLSurfaceOSMesa::NativeViewGLSurfaceOSMesa(
161 gfx::AcceleratedWidget window
)
162 : GLSurfaceOSMesa(OSMesaSurfaceFormatRGBA
, gfx::Size(1, 1)),
164 device_context_(NULL
) {
168 NativeViewGLSurfaceOSMesa::~NativeViewGLSurfaceOSMesa() {
172 bool NativeViewGLSurfaceOSMesa::Initialize() {
173 if (!GLSurfaceOSMesa::Initialize())
176 device_context_
= GetDC(window_
);
180 void NativeViewGLSurfaceOSMesa::Destroy() {
181 if (window_
&& device_context_
)
182 ReleaseDC(window_
, device_context_
);
184 device_context_
= NULL
;
186 GLSurfaceOSMesa::Destroy();
189 bool NativeViewGLSurfaceOSMesa::IsOffscreen() {
193 bool NativeViewGLSurfaceOSMesa::SwapBuffers() {
194 DCHECK(device_context_
);
196 gfx::Size size
= GetSize();
198 // Note: negating the height below causes GDI to treat the bitmap data as row
199 // 0 being at the top.
200 BITMAPV4HEADER info
= { sizeof(BITMAPV4HEADER
) };
201 info
.bV4Width
= size
.width();
202 info
.bV4Height
= -size
.height();
204 info
.bV4BitCount
= 32;
205 info
.bV4V4Compression
= BI_BITFIELDS
;
206 info
.bV4RedMask
= 0x000000FF;
207 info
.bV4GreenMask
= 0x0000FF00;
208 info
.bV4BlueMask
= 0x00FF0000;
209 info
.bV4AlphaMask
= 0xFF000000;
211 // Copy the back buffer to the window's device context. Do not check whether
212 // StretchDIBits succeeds or not. It will fail if the window has been
213 // destroyed but it is preferable to allow rendering to silently fail if the
214 // window is destroyed. This is because the primary application of this
215 // class of GLContext is for testing and we do not want every GL related ui /
216 // browser test to become flaky if there is a race condition between GL
217 // context destruction and window destruction.
218 StretchDIBits(device_context_
,
219 0, 0, size
.width(), size
.height(),
220 0, 0, size
.width(), size
.height(),
222 reinterpret_cast<BITMAPINFO
*>(&info
),
229 bool NativeViewGLSurfaceOSMesa::SupportsPostSubBuffer() {
233 bool NativeViewGLSurfaceOSMesa::PostSubBuffer(
234 int x
, int y
, int width
, int height
) {
235 DCHECK(device_context_
);
237 gfx::Size size
= GetSize();
239 // Note: negating the height below causes GDI to treat the bitmap data as row
240 // 0 being at the top.
241 BITMAPV4HEADER info
= { sizeof(BITMAPV4HEADER
) };
242 info
.bV4Width
= size
.width();
243 info
.bV4Height
= -size
.height();
245 info
.bV4BitCount
= 32;
246 info
.bV4V4Compression
= BI_BITFIELDS
;
247 info
.bV4RedMask
= 0x000000FF;
248 info
.bV4GreenMask
= 0x0000FF00;
249 info
.bV4BlueMask
= 0x00FF0000;
250 info
.bV4AlphaMask
= 0xFF000000;
252 // Copy the back buffer to the window's device context. Do not check whether
253 // StretchDIBits succeeds or not. It will fail if the window has been
254 // destroyed but it is preferable to allow rendering to silently fail if the
255 // window is destroyed. This is because the primary application of this
256 // class of GLContext is for testing and we do not want every GL related ui /
257 // browser test to become flaky if there is a race condition between GL
258 // context destruction and window destruction.
259 StretchDIBits(device_context_
,
260 x
, size
.height() - y
- height
, width
, height
,
263 reinterpret_cast<BITMAPINFO
*>(&info
),
270 scoped_refptr
<GLSurface
> GLSurface::CreateViewGLSurface(
271 gfx::AcceleratedWidget window
) {
272 TRACE_EVENT0("gpu", "GLSurface::CreateViewGLSurface");
273 switch (GetGLImplementation()) {
274 case kGLImplementationOSMesaGL
: {
275 scoped_refptr
<GLSurface
> surface(
276 new NativeViewGLSurfaceOSMesa(window
));
277 if (!surface
->Initialize())
282 case kGLImplementationEGLGLES2
: {
283 DCHECK(window
!= gfx::kNullAcceleratedWidget
);
284 scoped_refptr
<NativeViewGLSurfaceEGL
> surface(
285 new NativeViewGLSurfaceEGL(window
));
286 scoped_ptr
<VSyncProvider
> sync_provider
;
287 sync_provider
.reset(new WinVSyncProvider(window
));
288 if (!surface
->Initialize(sync_provider
.Pass()))
293 case kGLImplementationDesktopGL
: {
294 scoped_refptr
<GLSurface
> surface(new NativeViewGLSurfaceWGL(
296 if (!surface
->Initialize())
301 case kGLImplementationMockGL
:
302 return new GLSurfaceStub
;
309 scoped_refptr
<GLSurface
> GLSurface::CreateOffscreenGLSurface(
310 const gfx::Size
& size
) {
311 TRACE_EVENT0("gpu", "GLSurface::CreateOffscreenGLSurface");
312 switch (GetGLImplementation()) {
313 case kGLImplementationOSMesaGL
: {
314 scoped_refptr
<GLSurface
> surface(
315 new GLSurfaceOSMesa(OSMesaSurfaceFormatRGBA
, size
));
316 if (!surface
->Initialize())
321 case kGLImplementationEGLGLES2
: {
322 scoped_refptr
<GLSurface
> surface(new PbufferGLSurfaceEGL(size
));
323 if (!surface
->Initialize())
328 case kGLImplementationDesktopGL
: {
329 scoped_refptr
<GLSurface
> surface(new PbufferGLSurfaceWGL(size
));
330 if (!surface
->Initialize())
335 case kGLImplementationMockGL
:
336 return new GLSurfaceStub
;
343 EGLNativeDisplayType
GetPlatformDefaultEGLNativeDisplay() {
344 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
345 switches::kDisableD3D11
) ||
346 base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseWarp
))
348 return EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE
;