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, ¤tFboId);
600 glGetIntegerv(GL_RENDERBUFFER_BINDING, ¤tRboId);
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