Home | History | Annotate | Download | only in gl
      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