tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

lib.cpp (21622B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #define UNICODE
      6 
      7 #include <windows.h>
      8 #include <math.h>
      9 #include <dcomp.h>
     10 #include <d3d11.h>
     11 #include <assert.h>
     12 #include <map>
     13 #include <vector>
     14 #include <dwmapi.h>
     15 #include <unordered_map>
     16 
     17 #define EGL_EGL_PROTOTYPES 1
     18 #define EGL_EGLEXT_PROTOTYPES 1
     19 #define GL_GLEXT_PROTOTYPES 1
     20 #include "EGL/egl.h"
     21 #include "EGL/eglext.h"
     22 #include "EGL/eglext_angle.h"
     23 #include "GL/gl.h"
     24 #include "GLES/gl.h"
     25 #include "GLES/glext.h"
     26 #include "GLES3/gl3.h"
     27 
     28 #define NUM_QUERIES 2
     29 
     30 #define USE_VIRTUAL_SURFACES
     31 #define VIRTUAL_OFFSET 512 * 1024
     32 
     33 enum SyncMode {
     34  None = 0,
     35  Swap = 1,
     36  Commit = 2,
     37  Flush = 3,
     38  Query = 4,
     39 };
     40 
     41 // The OS compositor representation of a picture cache tile.
     42 struct Tile {
     43 #ifndef USE_VIRTUAL_SURFACES
     44  // Represents the underlying DirectComposition surface texture that gets drawn
     45  // into.
     46  IDCompositionSurface* pSurface;
     47  // Represents the node in the visual tree that defines the properties of this
     48  // tile (clip, position etc).
     49  IDCompositionVisual2* pVisual;
     50 #endif
     51 };
     52 
     53 struct TileKey {
     54  int x;
     55  int y;
     56 
     57  TileKey(int ax, int ay) : x(ax), y(ay) {}
     58 };
     59 
     60 bool operator==(const TileKey& k0, const TileKey& k1) {
     61  return k0.x == k1.x && k0.y == k1.y;
     62 }
     63 
     64 struct TileKeyHasher {
     65  size_t operator()(const TileKey& key) const { return key.x ^ key.y; }
     66 };
     67 
     68 struct Surface {
     69  int tile_width;
     70  int tile_height;
     71  bool is_opaque;
     72  std::unordered_map<TileKey, Tile, TileKeyHasher> tiles;
     73  IDCompositionVisual2* pVisual;
     74 #ifdef USE_VIRTUAL_SURFACES
     75  IDCompositionVirtualSurface* pVirtualSurface;
     76 #endif
     77 };
     78 
     79 struct CachedFrameBuffer {
     80  int width;
     81  int height;
     82  GLuint fboId;
     83  GLuint depthRboId;
     84 };
     85 
     86 struct Window {
     87  // Win32 window details
     88  HWND hWnd;
     89  HINSTANCE hInstance;
     90  bool enable_compositor;
     91  RECT client_rect;
     92  SyncMode sync_mode;
     93 
     94  // Main interfaces to D3D11 and DirectComposition
     95  ID3D11Device* pD3D11Device;
     96  IDCompositionDesktopDevice* pDCompDevice;
     97  IDCompositionTarget* pDCompTarget;
     98  IDXGIDevice* pDXGIDevice;
     99  ID3D11Query* pQueries[NUM_QUERIES];
    100  int current_query;
    101 
    102  // ANGLE interfaces that wrap the D3D device
    103  EGLDeviceEXT EGLDevice;
    104  EGLDisplay EGLDisplay;
    105  EGLContext EGLContext;
    106  EGLConfig config;
    107  // Framebuffer surface for debug mode when we are not using DC
    108  EGLSurface fb_surface;
    109 
    110  // The currently bound surface, valid during bind() and unbind()
    111  IDCompositionSurface* pCurrentSurface;
    112  EGLImage mEGLImage;
    113  GLuint mColorRBO;
    114 
    115  // The root of the DC visual tree. Nothing is drawn on this, but
    116  // all child tiles are parented to here.
    117  IDCompositionVisual2* pRoot;
    118  IDCompositionVisualDebug* pVisualDebug;
    119  std::vector<CachedFrameBuffer> mFrameBuffers;
    120 
    121  // Maintain list of layer state between frames to avoid visual tree rebuild.
    122  std::vector<uint64_t> mCurrentLayers;
    123  std::vector<uint64_t> mPrevLayers;
    124 
    125  // Maps WR surface IDs to each OS surface
    126  std::unordered_map<uint64_t, Surface> surfaces;
    127 };
    128 
    129 static const wchar_t* CLASS_NAME = L"WR DirectComposite";
    130 
    131 static GLuint GetOrCreateFbo(Window* window, int aWidth, int aHeight) {
    132  GLuint fboId = 0;
    133 
    134  // Check if we have a cached FBO with matching dimensions
    135  for (auto it = window->mFrameBuffers.begin();
    136       it != window->mFrameBuffers.end(); ++it) {
    137    if (it->width == aWidth && it->height == aHeight) {
    138      fboId = it->fboId;
    139      break;
    140    }
    141  }
    142 
    143  // If not, create a new FBO with attached depth buffer
    144  if (fboId == 0) {
    145    // Create the depth buffer
    146    GLuint depthRboId;
    147    glGenRenderbuffers(1, &depthRboId);
    148    glBindRenderbuffer(GL_RENDERBUFFER, depthRboId);
    149    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, aWidth,
    150                          aHeight);
    151 
    152    // Create the framebuffer and attach the depth buffer to it
    153    glGenFramebuffers(1, &fboId);
    154    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
    155    glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
    156                              GL_RENDERBUFFER, depthRboId);
    157 
    158    // Store this in the cache for future calls.
    159    CachedFrameBuffer frame_buffer_info;
    160    frame_buffer_info.width = aWidth;
    161    frame_buffer_info.height = aHeight;
    162    frame_buffer_info.fboId = fboId;
    163    frame_buffer_info.depthRboId = depthRboId;
    164    window->mFrameBuffers.push_back(frame_buffer_info);
    165  }
    166 
    167  return fboId;
    168 }
    169 
    170 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam,
    171                                LPARAM lParam) {
    172  switch (message) {
    173    case WM_DESTROY:
    174      PostQuitMessage(0);
    175      return 1;
    176  }
    177 
    178  return DefWindowProc(hwnd, message, wParam, lParam);
    179 }
    180 
    181 extern "C" {
    182 Window* com_dc_create_window(int width, int height, bool enable_compositor,
    183                             SyncMode sync_mode) {
    184  // Create a simple Win32 window
    185  Window* window = new Window;
    186  window->hInstance = GetModuleHandle(NULL);
    187  window->enable_compositor = enable_compositor;
    188  window->mEGLImage = EGL_NO_IMAGE;
    189  window->sync_mode = sync_mode;
    190 
    191  WNDCLASSEX wcex = {sizeof(WNDCLASSEX)};
    192  wcex.style = CS_HREDRAW | CS_VREDRAW;
    193  wcex.lpfnWndProc = WndProc;
    194  wcex.cbClsExtra = 0;
    195  wcex.cbWndExtra = 0;
    196  wcex.hInstance = window->hInstance;
    197  wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    198  ;
    199  wcex.lpszMenuName = nullptr;
    200  wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    201  wcex.lpszClassName = CLASS_NAME;
    202  RegisterClassEx(&wcex);
    203 
    204  int dpiX = 0;
    205  int dpiY = 0;
    206  HDC hdc = GetDC(NULL);
    207  if (hdc) {
    208    dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
    209    dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
    210    ReleaseDC(NULL, hdc);
    211  }
    212 
    213  RECT window_rect = {0, 0, width, height};
    214  AdjustWindowRect(&window_rect, WS_OVERLAPPEDWINDOW, FALSE);
    215  UINT window_width = static_cast<UINT>(
    216      ceil(float(window_rect.right - window_rect.left) * dpiX / 96.f));
    217  UINT window_height = static_cast<UINT>(
    218      ceil(float(window_rect.bottom - window_rect.top) * dpiY / 96.f));
    219 
    220  LPCWSTR name;
    221  DWORD style;
    222  if (enable_compositor) {
    223    name = L"example-compositor (DirectComposition)";
    224    style = WS_EX_NOREDIRECTIONBITMAP;
    225  } else {
    226    name = L"example-compositor (Simple)";
    227    style = 0;
    228  }
    229 
    230  window->hWnd =
    231      CreateWindowEx(style, CLASS_NAME, name, WS_OVERLAPPEDWINDOW,
    232                     CW_USEDEFAULT, CW_USEDEFAULT, window_width, window_height,
    233                     NULL, NULL, window->hInstance, NULL);
    234 
    235  ShowWindow(window->hWnd, SW_SHOWNORMAL);
    236  UpdateWindow(window->hWnd);
    237  GetClientRect(window->hWnd, &window->client_rect);
    238 
    239  // Create a D3D11 device
    240  D3D_FEATURE_LEVEL featureLevelSupported;
    241  HRESULT hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, NULL,
    242                                 D3D11_CREATE_DEVICE_BGRA_SUPPORT, NULL, 0,
    243                                 D3D11_SDK_VERSION, &window->pD3D11Device,
    244                                 &featureLevelSupported, nullptr);
    245  assert(SUCCEEDED(hr));
    246 
    247  D3D11_QUERY_DESC query_desc;
    248  memset(&query_desc, 0, sizeof(query_desc));
    249  query_desc.Query = D3D11_QUERY_EVENT;
    250  for (int i = 0; i < NUM_QUERIES; ++i) {
    251    hr = window->pD3D11Device->CreateQuery(&query_desc, &window->pQueries[i]);
    252    assert(SUCCEEDED(hr));
    253  }
    254  window->current_query = 0;
    255 
    256  hr = window->pD3D11Device->QueryInterface(&window->pDXGIDevice);
    257  assert(SUCCEEDED(hr));
    258 
    259  // Create a DirectComposition device
    260  hr = DCompositionCreateDevice2(window->pDXGIDevice,
    261                                 __uuidof(IDCompositionDesktopDevice),
    262                                 (void**)&window->pDCompDevice);
    263  assert(SUCCEEDED(hr));
    264 
    265  // Create a DirectComposition target for a Win32 window handle
    266  hr = window->pDCompDevice->CreateTargetForHwnd(window->hWnd, TRUE,
    267                                                 &window->pDCompTarget);
    268  assert(SUCCEEDED(hr));
    269 
    270  // Create an ANGLE EGL device that wraps D3D11
    271  window->EGLDevice = eglCreateDeviceANGLE(EGL_D3D11_DEVICE_ANGLE,
    272                                           window->pD3D11Device, nullptr);
    273 
    274  EGLint display_attribs[] = {EGL_NONE};
    275 
    276  window->EGLDisplay = eglGetPlatformDisplayEXT(
    277      EGL_PLATFORM_DEVICE_EXT, window->EGLDevice, display_attribs);
    278 
    279  eglInitialize(window->EGLDisplay, nullptr, nullptr);
    280 
    281  EGLint num_configs = 0;
    282  EGLint cfg_attribs[] = {EGL_SURFACE_TYPE,
    283                          EGL_WINDOW_BIT,
    284                          EGL_RENDERABLE_TYPE,
    285                          EGL_OPENGL_ES2_BIT,
    286                          EGL_RED_SIZE,
    287                          8,
    288                          EGL_GREEN_SIZE,
    289                          8,
    290                          EGL_BLUE_SIZE,
    291                          8,
    292                          EGL_ALPHA_SIZE,
    293                          8,
    294                          EGL_DEPTH_SIZE,
    295                          24,
    296                          EGL_NONE};
    297  EGLConfig configs[32];
    298 
    299  eglChooseConfig(window->EGLDisplay, cfg_attribs, configs,
    300                  sizeof(configs) / sizeof(EGLConfig), &num_configs);
    301  assert(num_configs > 0);
    302  window->config = configs[0];
    303 
    304  if (window->enable_compositor) {
    305    window->fb_surface = EGL_NO_SURFACE;
    306  } else {
    307    window->fb_surface = eglCreateWindowSurface(
    308        window->EGLDisplay, window->config, window->hWnd, NULL);
    309    assert(window->fb_surface != EGL_NO_SURFACE);
    310  }
    311 
    312  EGLint ctx_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
    313 
    314  // Create an EGL context that can be used for drawing
    315  window->EGLContext = eglCreateContext(window->EGLDisplay, window->config,
    316                                        EGL_NO_CONTEXT, ctx_attribs);
    317 
    318  // Create the root of the DirectComposition visual tree
    319  hr = window->pDCompDevice->CreateVisual(&window->pRoot);
    320  assert(SUCCEEDED(hr));
    321  hr = window->pDCompTarget->SetRoot(window->pRoot);
    322  assert(SUCCEEDED(hr));
    323 
    324  hr = window->pRoot->QueryInterface(__uuidof(IDCompositionVisualDebug),
    325                                     (void**)&window->pVisualDebug);
    326  assert(SUCCEEDED(hr));
    327 
    328  // Uncomment this to see redraw regions during composite
    329  // window->pVisualDebug->EnableRedrawRegions();
    330 
    331  EGLBoolean ok = eglMakeCurrent(window->EGLDisplay, window->fb_surface,
    332                                 window->fb_surface, window->EGLContext);
    333  assert(ok);
    334 
    335  return window;
    336 }
    337 
    338 void com_dc_destroy_window(Window* window) {
    339  for (auto surface_it = window->surfaces.begin();
    340       surface_it != window->surfaces.end(); ++surface_it) {
    341    Surface& surface = surface_it->second;
    342 
    343 #ifndef USE_VIRTUAL_SURFACES
    344    for (auto tile_it = surface.tiles.begin(); tile_it != surface.tiles.end();
    345         ++tile_it) {
    346      tile_it->second.pSurface->Release();
    347      tile_it->second.pVisual->Release();
    348    }
    349 #endif
    350 
    351    surface.pVisual->Release();
    352  }
    353 
    354  if (window->fb_surface != EGL_NO_SURFACE) {
    355    eglDestroySurface(window->EGLDisplay, window->fb_surface);
    356  }
    357  eglDestroyContext(window->EGLDisplay, window->EGLContext);
    358  eglTerminate(window->EGLDisplay);
    359  eglReleaseDeviceANGLE(window->EGLDevice);
    360 
    361  for (int i = 0; i < NUM_QUERIES; ++i) {
    362    window->pQueries[i]->Release();
    363  }
    364  window->pRoot->Release();
    365  window->pVisualDebug->Release();
    366  window->pD3D11Device->Release();
    367  window->pDXGIDevice->Release();
    368  window->pDCompDevice->Release();
    369  window->pDCompTarget->Release();
    370 
    371  CloseWindow(window->hWnd);
    372  UnregisterClass(CLASS_NAME, window->hInstance);
    373 
    374  delete window;
    375 }
    376 
    377 bool com_dc_tick(Window*) {
    378  // Check and dispatch the windows event loop
    379  MSG msg;
    380  while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
    381    if (msg.message == WM_QUIT) {
    382      return false;
    383    }
    384 
    385    TranslateMessage(&msg);
    386    DispatchMessage(&msg);
    387  }
    388 
    389  return true;
    390 }
    391 
    392 void com_dc_swap_buffers(Window* window) {
    393  // If not using DC mode, then do a normal EGL swap buffers.
    394  if (window->fb_surface != EGL_NO_SURFACE) {
    395    switch (window->sync_mode) {
    396      case SyncMode::None:
    397        eglSwapInterval(window->EGLDisplay, 0);
    398        break;
    399      case SyncMode::Swap:
    400        eglSwapInterval(window->EGLDisplay, 1);
    401        break;
    402      default:
    403        assert(false);  // unexpected vsync mode for simple compositor.
    404        break;
    405    }
    406 
    407    eglSwapBuffers(window->EGLDisplay, window->fb_surface);
    408  } else {
    409    switch (window->sync_mode) {
    410      case SyncMode::None:
    411        break;
    412      case SyncMode::Commit:
    413        window->pDCompDevice->WaitForCommitCompletion();
    414        break;
    415      case SyncMode::Flush:
    416        DwmFlush();
    417        break;
    418      case SyncMode::Query:
    419        // todo!!!!
    420        break;
    421      default:
    422        assert(false);  // unexpected vsync mode for native compositor
    423        break;
    424    }
    425  }
    426 }
    427 
    428 // Create a new DC surface
    429 void com_dc_create_surface(Window* window, uint64_t id, int tile_width,
    430                           int tile_height, bool is_opaque) {
    431  assert(window->surfaces.count(id) == 0);
    432 
    433  Surface surface;
    434  surface.tile_width = tile_width;
    435  surface.tile_height = tile_height;
    436  surface.is_opaque = is_opaque;
    437 
    438  // Create the visual node in the DC tree that stores properties
    439  HRESULT hr = window->pDCompDevice->CreateVisual(&surface.pVisual);
    440  assert(SUCCEEDED(hr));
    441 
    442 #ifdef USE_VIRTUAL_SURFACES
    443  DXGI_ALPHA_MODE alpha_mode = surface.is_opaque
    444                                   ? DXGI_ALPHA_MODE_IGNORE
    445                                   : DXGI_ALPHA_MODE_PREMULTIPLIED;
    446 
    447  hr = window->pDCompDevice->CreateVirtualSurface(
    448      VIRTUAL_OFFSET * 2, VIRTUAL_OFFSET * 2, DXGI_FORMAT_B8G8R8A8_UNORM,
    449      alpha_mode, &surface.pVirtualSurface);
    450  assert(SUCCEEDED(hr));
    451 
    452  // Bind the surface memory to this visual
    453  hr = surface.pVisual->SetContent(surface.pVirtualSurface);
    454  assert(SUCCEEDED(hr));
    455 #endif
    456 
    457  window->surfaces[id] = surface;
    458 }
    459 
    460 void com_dc_create_tile(Window* window, uint64_t id, int x, int y) {
    461  assert(window->surfaces.count(id) == 1);
    462  Surface& surface = window->surfaces[id];
    463 
    464  TileKey key(x, y);
    465  assert(surface.tiles.count(key) == 0);
    466 
    467  Tile tile;
    468 
    469 #ifndef USE_VIRTUAL_SURFACES
    470  // Create the video memory surface.
    471  DXGI_ALPHA_MODE alpha_mode = surface.is_opaque
    472                                   ? DXGI_ALPHA_MODE_IGNORE
    473                                   : DXGI_ALPHA_MODE_PREMULTIPLIED;
    474  HRESULT hr = window->pDCompDevice->CreateSurface(
    475      surface.tile_width, surface.tile_height, DXGI_FORMAT_B8G8R8A8_UNORM,
    476      alpha_mode, &tile.pSurface);
    477  assert(SUCCEEDED(hr));
    478 
    479  // Create the visual node in the DC tree that stores properties
    480  hr = window->pDCompDevice->CreateVisual(&tile.pVisual);
    481  assert(SUCCEEDED(hr));
    482 
    483  // Bind the surface memory to this visual
    484  hr = tile.pVisual->SetContent(tile.pSurface);
    485  assert(SUCCEEDED(hr));
    486 
    487  // Place the visual in local-space of this surface
    488  float offset_x = (float)(x * surface.tile_width);
    489  float offset_y = (float)(y * surface.tile_height);
    490  tile.pVisual->SetOffsetX(offset_x);
    491  tile.pVisual->SetOffsetY(offset_y);
    492 
    493  surface.pVisual->AddVisual(tile.pVisual, FALSE, NULL);
    494 #endif
    495 
    496  surface.tiles[key] = tile;
    497 }
    498 
    499 void com_dc_destroy_tile(Window* window, uint64_t id, int x, int y) {
    500  assert(window->surfaces.count(id) == 1);
    501  Surface& surface = window->surfaces[id];
    502 
    503  TileKey key(x, y);
    504  assert(surface.tiles.count(key) == 1);
    505  Tile& tile = surface.tiles[key];
    506 
    507 #ifndef USE_VIRTUAL_SURFACES
    508  surface.pVisual->RemoveVisual(tile.pVisual);
    509 
    510  tile.pVisual->Release();
    511  tile.pSurface->Release();
    512 #endif
    513 
    514  surface.tiles.erase(key);
    515 }
    516 
    517 void com_dc_destroy_surface(Window* window, uint64_t id) {
    518  assert(window->surfaces.count(id) == 1);
    519  Surface& surface = window->surfaces[id];
    520 
    521  window->pRoot->RemoveVisual(surface.pVisual);
    522 
    523 #ifdef USE_VIRTUAL_SURFACES
    524  surface.pVirtualSurface->Release();
    525 #else
    526  // Release the video memory and visual in the tree
    527  for (auto tile_it = surface.tiles.begin(); tile_it != surface.tiles.end();
    528       ++tile_it) {
    529    tile_it->second.pSurface->Release();
    530    tile_it->second.pVisual->Release();
    531  }
    532 #endif
    533 
    534  surface.pVisual->Release();
    535  window->surfaces.erase(id);
    536 }
    537 
    538 // Bind a DC surface to allow issuing GL commands to it
    539 GLuint com_dc_bind_surface(Window* window, uint64_t surface_id, int tile_x,
    540                           int tile_y, int* x_offset, int* y_offset,
    541                           int dirty_x0, int dirty_y0, int dirty_width,
    542                           int dirty_height) {
    543  assert(window->surfaces.count(surface_id) == 1);
    544  Surface& surface = window->surfaces[surface_id];
    545 
    546  TileKey key(tile_x, tile_y);
    547  assert(surface.tiles.count(key) == 1);
    548  Tile& tile = surface.tiles[key];
    549 
    550  // Inform DC that we want to draw on this surface. DC uses texture
    551  // atlases when the tiles are small. It returns an offset where the
    552  // client code must draw into this surface when this happens.
    553  RECT update_rect;
    554  update_rect.left = dirty_x0;
    555  update_rect.top = dirty_y0;
    556  update_rect.right = dirty_x0 + dirty_width;
    557  update_rect.bottom = dirty_y0 + dirty_height;
    558  POINT offset;
    559  D3D11_TEXTURE2D_DESC desc;
    560  ID3D11Texture2D* pTexture;
    561  HRESULT hr;
    562 
    563  // Store the current surface for unbinding later
    564 #ifdef USE_VIRTUAL_SURFACES
    565  LONG tile_offset_x = VIRTUAL_OFFSET + tile_x * surface.tile_width;
    566  LONG tile_offset_y = VIRTUAL_OFFSET + tile_y * surface.tile_height;
    567 
    568  update_rect.left += tile_offset_x;
    569  update_rect.top += tile_offset_y;
    570  update_rect.right += tile_offset_x;
    571  update_rect.bottom += tile_offset_y;
    572 
    573  hr = surface.pVirtualSurface->BeginDraw(
    574      &update_rect, __uuidof(ID3D11Texture2D), (void**)&pTexture, &offset);
    575  window->pCurrentSurface = surface.pVirtualSurface;
    576 #else
    577  hr = tile.pSurface->BeginDraw(&update_rect, __uuidof(ID3D11Texture2D),
    578                                (void**)&pTexture, &offset);
    579  window->pCurrentSurface = tile.pSurface;
    580 #endif
    581 
    582  // DC includes the origin of the dirty / update rect in the draw offset,
    583  // undo that here since WR expects it to be an absolute offset.
    584  assert(SUCCEEDED(hr));
    585  offset.x -= dirty_x0;
    586  offset.y -= dirty_y0;
    587  pTexture->GetDesc(&desc);
    588  *x_offset = offset.x;
    589  *y_offset = offset.y;
    590 
    591  // Construct an EGLImage wrapper around the D3D texture for ANGLE.
    592  const EGLAttrib attribs[] = {EGL_NONE};
    593  window->mEGLImage = eglCreateImage(
    594      window->EGLDisplay, EGL_NO_CONTEXT, EGL_D3D11_TEXTURE_ANGLE,
    595      static_cast<EGLClientBuffer>(pTexture), attribs);
    596 
    597  // Get the current FBO and RBO id, so we can restore them later
    598  GLint currentFboId, currentRboId;
    599  glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &currentFboId);
    600  glGetIntegerv(GL_RENDERBUFFER_BINDING, &currentRboId);
    601 
    602  // Create a render buffer object that is backed by the EGL image.
    603  glGenRenderbuffers(1, &window->mColorRBO);
    604  glBindRenderbuffer(GL_RENDERBUFFER, window->mColorRBO);
    605  glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, window->mEGLImage);
    606 
    607  // Get or create an FBO for the specified dimensions
    608  GLuint fboId = GetOrCreateFbo(window, desc.Width, desc.Height);
    609 
    610  // Attach the new renderbuffer to the FBO
    611  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
    612  glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
    613                            GL_RENDERBUFFER, window->mColorRBO);
    614 
    615  // Restore previous FBO and RBO bindings
    616  glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFboId);
    617  glBindRenderbuffer(GL_RENDERBUFFER, currentRboId);
    618 
    619  return fboId;
    620 }
    621 
    622 // Unbind a currently bound DC surface
    623 void com_dc_unbind_surface(Window* window) {
    624  HRESULT hr = window->pCurrentSurface->EndDraw();
    625  assert(SUCCEEDED(hr));
    626 
    627  glDeleteRenderbuffers(1, &window->mColorRBO);
    628  window->mColorRBO = 0;
    629 
    630  eglDestroyImage(window->EGLDisplay, window->mEGLImage);
    631  window->mEGLImage = EGL_NO_IMAGE;
    632 }
    633 
    634 void com_dc_begin_transaction(Window*) {}
    635 
    636 // Add a DC surface to the visual tree. Called per-frame to build the
    637 // composition.
    638 void com_dc_add_surface(Window* window, uint64_t id, int x, int y, int clip_x,
    639                        int clip_y, int clip_w, int clip_h) {
    640  Surface surface = window->surfaces[id];
    641  window->mCurrentLayers.push_back(id);
    642 
    643  // Place the visual - this changes frame to frame based on scroll position
    644  // of the slice.
    645  float offset_x = (float)(x + window->client_rect.left);
    646  float offset_y = (float)(y + window->client_rect.top);
    647 #ifdef USE_VIRTUAL_SURFACES
    648  offset_x -= VIRTUAL_OFFSET;
    649  offset_y -= VIRTUAL_OFFSET;
    650 #endif
    651  surface.pVisual->SetOffsetX(offset_x);
    652  surface.pVisual->SetOffsetY(offset_y);
    653 
    654  // Set the clip rect - converting from world space to the pre-offset space
    655  // that DC requires for rectangle clips.
    656  D2D_RECT_F clip_rect;
    657  clip_rect.left = clip_x - offset_x;
    658  clip_rect.top = clip_y - offset_y;
    659  clip_rect.right = clip_rect.left + clip_w;
    660  clip_rect.bottom = clip_rect.top + clip_h;
    661  surface.pVisual->SetClip(clip_rect);
    662 }
    663 
    664 // Finish the composition transaction, telling DC to composite
    665 void com_dc_end_transaction(Window* window) {
    666  bool same = window->mPrevLayers == window->mCurrentLayers;
    667 
    668  if (!same) {
    669    HRESULT hr = window->pRoot->RemoveAllVisuals();
    670    assert(SUCCEEDED(hr));
    671 
    672    for (auto it = window->mCurrentLayers.begin();
    673         it != window->mCurrentLayers.end(); ++it) {
    674      Surface& surface = window->surfaces[*it];
    675 
    676      // Add this visual as the last element in the visual tree (z-order is
    677      // implicit, based on the order tiles are added).
    678      hr = window->pRoot->AddVisual(surface.pVisual, FALSE, NULL);
    679      assert(SUCCEEDED(hr));
    680    }
    681  }
    682 
    683  window->mPrevLayers.swap(window->mCurrentLayers);
    684  window->mCurrentLayers.clear();
    685 
    686  HRESULT hr = window->pDCompDevice->Commit();
    687  assert(SUCCEEDED(hr));
    688 }
    689 
    690 // Get a pointer to an EGL symbol
    691 void* com_dc_get_proc_address(const char* name) {
    692  return eglGetProcAddress(name);
    693 }
    694 }