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, ¤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 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 }