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 
TileKeyTileKey57   TileKey(int ax, int ay) : x(ax), y(ay) {}
58 };
59 
operator ==(const TileKey & k0,const TileKey & k1)60 bool operator==(const TileKey& k0, const TileKey& k1) {
61   return k0.x == k1.x && k0.y == k1.y;
62 }
63 
64 struct TileKeyHasher {
operator ()TileKeyHasher65   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 
GetOrCreateFbo(Window * window,int aWidth,int aHeight)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 
WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)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" {
com_dc_create_window(int width,int height,bool enable_compositor,SyncMode sync_mode)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 
com_dc_destroy_window(Window * window)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 
com_dc_tick(Window *)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 
com_dc_swap_buffers(Window * window)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
com_dc_create_surface(Window * window,uint64_t id,int tile_width,int tile_height,bool is_opaque)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 
com_dc_create_tile(Window * window,uint64_t id,int x,int y)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 
com_dc_destroy_tile(Window * window,uint64_t id,int x,int y)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 
com_dc_destroy_surface(Window * window,uint64_t id)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
com_dc_bind_surface(Window * window,uint64_t surface_id,int tile_x,int tile_y,int * x_offset,int * y_offset,int dirty_x0,int dirty_y0,int dirty_width,int dirty_height)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
com_dc_unbind_surface(Window * window)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 
com_dc_begin_transaction(Window *)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.
com_dc_add_surface(Window * window,uint64_t id,int x,int y,int clip_x,int clip_y,int clip_w,int clip_h)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
com_dc_end_transaction(Window * window)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
com_dc_get_proc_address(const char * name)691 void* com_dc_get_proc_address(const char* name) {
692   return eglGetProcAddress(name);
693 }
694 }
695