Vendor the current version of WebRender

This is a step toward upgrading WebRender, which will be upgraded and
patched in the `third_party` directory. This change vendors the current
private branch of WebRender that we use and adds a `patches` directory
which tracks the changes on top of the upstream WebRender commit
described by third_party/webrender/patches/head.
This commit is contained in:
Martin Robinson 2023-07-03 17:43:57 +02:00
parent c19eb800de
commit 49277f5c3f
No known key found for this signature in database
GPG key ID: D56AA4FA55EFE6F8
1215 changed files with 185677 additions and 34 deletions

View file

@ -0,0 +1,9 @@
[package]
name = "compositor-windows"
version = "0.1.0"
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
edition = "2018"
license = "MPL-2.0"
[build-dependencies]
cc = "1.0"

View file

@ -0,0 +1,25 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
fn main() {
// HACK - This build script relies on Gecko having been built, so that the ANGLE libraries
// have already been compiled. It also assumes they are being built with an in-tree
// x86_64 object directory.
cc::Build::new()
.file("src/lib.cpp")
.include("../../../angle/checkout/include")
.compile("windows");
// Set up linker paths for ANGLE that is built by Gecko
println!("cargo:rustc-link-search=../../obj-x86_64-pc-mingw32/gfx/angle/targets/libEGL");
println!("cargo:rustc-link-search=../../obj-x86_64-pc-mingw32/gfx/angle/targets/libGLESv2");
// Link to libEGL and libGLESv2 (ANGLE) and D3D11 + DirectComposition
println!("cargo:rustc-link-lib=libEGL");
println!("cargo:rustc-link-lib=libGLESv2");
println!("cargo:rustc-link-lib=dcomp");
println!("cargo:rustc-link-lib=d3d11");
println!("cargo:rustc-link-lib=dwmapi");
}

View file

@ -0,0 +1,802 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define UNICODE
#include <windows.h>
#include <math.h>
#include <dcomp.h>
#include <d3d11.h>
#include <assert.h>
#include <map>
#include <vector>
#include <dwmapi.h>
#include <unordered_map>
#define EGL_EGL_PROTOTYPES 1
#define EGL_EGLEXT_PROTOTYPES 1
#define GL_GLEXT_PROTOTYPES 1
#include "EGL/egl.h"
#include "EGL/eglext.h"
#include "EGL/eglext_angle.h"
#include "GL/gl.h"
#include "GLES/gl.h"
#include "GLES/glext.h"
#include "GLES3/gl3.h"
#define NUM_QUERIES 2
#define USE_VIRTUAL_SURFACES
#define VIRTUAL_OFFSET 512 * 1024
enum SyncMode {
None = 0,
Swap = 1,
Commit = 2,
Flush = 3,
Query = 4,
};
// The OS compositor representation of a picture cache tile.
struct Tile {
#ifndef USE_VIRTUAL_SURFACES
// Represents the underlying DirectComposition surface texture that gets drawn into.
IDCompositionSurface *pSurface;
// Represents the node in the visual tree that defines the properties of this tile (clip, position etc).
IDCompositionVisual2 *pVisual;
#endif
};
struct TileKey {
int x;
int y;
TileKey(int ax, int ay) : x(ax), y(ay) {}
};
bool operator==(const TileKey &k0, const TileKey &k1) {
return k0.x == k1.x && k0.y == k1.y;
}
struct TileKeyHasher {
size_t operator()(const TileKey &key) const {
return key.x ^ key.y;
}
};
struct Surface {
int tile_width;
int tile_height;
bool is_opaque;
std::unordered_map<TileKey, Tile, TileKeyHasher> tiles;
IDCompositionVisual2 *pVisual;
#ifdef USE_VIRTUAL_SURFACES
IDCompositionVirtualSurface *pVirtualSurface;
#endif
};
struct CachedFrameBuffer {
int width;
int height;
GLuint fboId;
GLuint depthRboId;
};
struct Window {
// Win32 window details
HWND hWnd;
HINSTANCE hInstance;
bool enable_compositor;
RECT client_rect;
SyncMode sync_mode;
// Main interfaces to D3D11 and DirectComposition
ID3D11Device *pD3D11Device;
IDCompositionDesktopDevice *pDCompDevice;
IDCompositionTarget *pDCompTarget;
IDXGIDevice *pDXGIDevice;
ID3D11Query *pQueries[NUM_QUERIES];
int current_query;
// ANGLE interfaces that wrap the D3D device
EGLDeviceEXT EGLDevice;
EGLDisplay EGLDisplay;
EGLContext EGLContext;
EGLConfig config;
// Framebuffer surface for debug mode when we are not using DC
EGLSurface fb_surface;
// The currently bound surface, valid during bind() and unbind()
IDCompositionSurface *pCurrentSurface;
EGLImage mEGLImage;
GLuint mColorRBO;
// The root of the DC visual tree. Nothing is drawn on this, but
// all child tiles are parented to here.
IDCompositionVisual2 *pRoot;
IDCompositionVisualDebug *pVisualDebug;
std::vector<CachedFrameBuffer> mFrameBuffers;
// Maintain list of layer state between frames to avoid visual tree rebuild.
std::vector<uint64_t> mCurrentLayers;
std::vector<uint64_t> mPrevLayers;
// Maps WR surface IDs to each OS surface
std::unordered_map<uint64_t, Surface> surfaces;
};
static const wchar_t *CLASS_NAME = L"WR DirectComposite";
static GLuint GetOrCreateFbo(Window *window, int aWidth, int aHeight) {
GLuint fboId = 0;
// Check if we have a cached FBO with matching dimensions
for (auto it = window->mFrameBuffers.begin(); it != window->mFrameBuffers.end(); ++it) {
if (it->width == aWidth && it->height == aHeight) {
fboId = it->fboId;
break;
}
}
// If not, create a new FBO with attached depth buffer
if (fboId == 0) {
// Create the depth buffer
GLuint depthRboId;
glGenRenderbuffers(1, &depthRboId);
glBindRenderbuffer(GL_RENDERBUFFER, depthRboId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24,
aWidth, aHeight);
// Create the framebuffer and attach the depth buffer to it
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, depthRboId);
// Store this in the cache for future calls.
CachedFrameBuffer frame_buffer_info;
frame_buffer_info.width = aWidth;
frame_buffer_info.height = aHeight;
frame_buffer_info.fboId = fboId;
frame_buffer_info.depthRboId = depthRboId;
window->mFrameBuffers.push_back(frame_buffer_info);
}
return fboId;
}
static LRESULT CALLBACK WndProc(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
) {
switch (message) {
case WM_DESTROY:
PostQuitMessage(0);
return 1;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
extern "C" {
Window *com_dc_create_window(int width, int height, bool enable_compositor, SyncMode sync_mode) {
// Create a simple Win32 window
Window *window = new Window;
window->hInstance = GetModuleHandle(NULL);
window->enable_compositor = enable_compositor;
window->mEGLImage = EGL_NO_IMAGE;
window->sync_mode = sync_mode;
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = window->hInstance;
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);;
wcex.lpszMenuName = nullptr;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.lpszClassName = CLASS_NAME;
RegisterClassEx(&wcex);
int dpiX = 0;
int dpiY = 0;
HDC hdc = GetDC(NULL);
if (hdc) {
dpiX = GetDeviceCaps(hdc, LOGPIXELSX);
dpiY = GetDeviceCaps(hdc, LOGPIXELSY);
ReleaseDC(NULL, hdc);
}
RECT window_rect = { 0, 0, width, height };
AdjustWindowRect(&window_rect, WS_OVERLAPPEDWINDOW, FALSE);
UINT window_width = static_cast<UINT>(ceil(float(window_rect.right - window_rect.left) * dpiX / 96.f));
UINT window_height = static_cast<UINT>(ceil(float(window_rect.bottom - window_rect.top) * dpiY / 96.f));
LPCWSTR name;
DWORD style;
if (enable_compositor) {
name = L"example-compositor (DirectComposition)";
style = WS_EX_NOREDIRECTIONBITMAP;
} else {
name = L"example-compositor (Simple)";
style = 0;
}
window->hWnd = CreateWindowEx(
style,
CLASS_NAME,
name,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
window_width,
window_height,
NULL,
NULL,
window->hInstance,
NULL
);
ShowWindow(window->hWnd, SW_SHOWNORMAL);
UpdateWindow(window->hWnd);
GetClientRect(window->hWnd, &window->client_rect);
// Create a D3D11 device
D3D_FEATURE_LEVEL featureLevelSupported;
HRESULT hr = D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
NULL,
0,
D3D11_SDK_VERSION,
&window->pD3D11Device,
&featureLevelSupported,
nullptr
);
assert(SUCCEEDED(hr));
D3D11_QUERY_DESC query_desc;
memset(&query_desc, 0, sizeof(query_desc));
query_desc.Query = D3D11_QUERY_EVENT;
for (int i=0 ; i < NUM_QUERIES ; ++i) {
hr = window->pD3D11Device->CreateQuery(&query_desc, &window->pQueries[i]);
assert(SUCCEEDED(hr));
}
window->current_query = 0;
hr = window->pD3D11Device->QueryInterface(&window->pDXGIDevice);
assert(SUCCEEDED(hr));
// Create a DirectComposition device
hr = DCompositionCreateDevice2(
window->pDXGIDevice,
__uuidof(IDCompositionDesktopDevice),
(void **) &window->pDCompDevice
);
assert(SUCCEEDED(hr));
// Create a DirectComposition target for a Win32 window handle
hr = window->pDCompDevice->CreateTargetForHwnd(
window->hWnd,
TRUE,
&window->pDCompTarget
);
assert(SUCCEEDED(hr));
// Create an ANGLE EGL device that wraps D3D11
window->EGLDevice = eglCreateDeviceANGLE(
EGL_D3D11_DEVICE_ANGLE,
window->pD3D11Device,
nullptr
);
EGLint display_attribs[] = {
EGL_NONE
};
window->EGLDisplay = eglGetPlatformDisplayEXT(
EGL_PLATFORM_DEVICE_EXT,
window->EGLDevice,
display_attribs
);
eglInitialize(
window->EGLDisplay,
nullptr,
nullptr
);
EGLint num_configs = 0;
EGLint cfg_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 24,
EGL_NONE
};
EGLConfig configs[32];
eglChooseConfig(
window->EGLDisplay,
cfg_attribs,
configs,
sizeof(configs) / sizeof(EGLConfig),
&num_configs
);
assert(num_configs > 0);
window->config = configs[0];
if (window->enable_compositor) {
window->fb_surface = EGL_NO_SURFACE;
} else {
window->fb_surface = eglCreateWindowSurface(
window->EGLDisplay,
window->config,
window->hWnd,
NULL
);
assert(window->fb_surface != EGL_NO_SURFACE);
}
EGLint ctx_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE
};
// Create an EGL context that can be used for drawing
window->EGLContext = eglCreateContext(
window->EGLDisplay,
window->config,
EGL_NO_CONTEXT,
ctx_attribs
);
// Create the root of the DirectComposition visual tree
hr = window->pDCompDevice->CreateVisual(&window->pRoot);
assert(SUCCEEDED(hr));
hr = window->pDCompTarget->SetRoot(window->pRoot);
assert(SUCCEEDED(hr));
hr = window->pRoot->QueryInterface(
__uuidof(IDCompositionVisualDebug),
(void **) &window->pVisualDebug
);
assert(SUCCEEDED(hr));
// Uncomment this to see redraw regions during composite
// window->pVisualDebug->EnableRedrawRegions();
EGLBoolean ok = eglMakeCurrent(
window->EGLDisplay,
window->fb_surface,
window->fb_surface,
window->EGLContext
);
assert(ok);
return window;
}
void com_dc_destroy_window(Window *window) {
for (auto surface_it=window->surfaces.begin() ; surface_it != window->surfaces.end() ; ++surface_it) {
Surface &surface = surface_it->second;
#ifndef USE_VIRTUAL_SURFACES
for (auto tile_it=surface.tiles.begin() ; tile_it != surface.tiles.end() ; ++tile_it) {
tile_it->second.pSurface->Release();
tile_it->second.pVisual->Release();
}
#endif
surface.pVisual->Release();
}
if (window->fb_surface != EGL_NO_SURFACE) {
eglDestroySurface(window->EGLDisplay, window->fb_surface);
}
eglDestroyContext(window->EGLDisplay, window->EGLContext);
eglTerminate(window->EGLDisplay);
eglReleaseDeviceANGLE(window->EGLDevice);
for (int i=0 ; i < NUM_QUERIES ; ++i) {
window->pQueries[i]->Release();
}
window->pRoot->Release();
window->pVisualDebug->Release();
window->pD3D11Device->Release();
window->pDXGIDevice->Release();
window->pDCompDevice->Release();
window->pDCompTarget->Release();
CloseWindow(window->hWnd);
UnregisterClass(CLASS_NAME, window->hInstance);
delete window;
}
bool com_dc_tick(Window *) {
// Check and dispatch the windows event loop
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
return false;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return true;
}
void com_dc_swap_buffers(Window *window) {
// If not using DC mode, then do a normal EGL swap buffers.
if (window->fb_surface != EGL_NO_SURFACE) {
switch (window->sync_mode) {
case SyncMode::None:
eglSwapInterval(window->EGLDisplay, 0);
break;
case SyncMode::Swap:
eglSwapInterval(window->EGLDisplay, 1);
break;
default:
assert(false); // unexpected vsync mode for simple compositor.
break;
}
eglSwapBuffers(window->EGLDisplay, window->fb_surface);
} else {
switch (window->sync_mode) {
case SyncMode::None:
break;
case SyncMode::Commit:
window->pDCompDevice->WaitForCommitCompletion();
break;
case SyncMode::Flush:
DwmFlush();
break;
case SyncMode::Query:
// todo!!!!
break;
default:
assert(false); // unexpected vsync mode for native compositor
break;
}
}
}
// Create a new DC surface
void com_dc_create_surface(
Window *window,
uint64_t id,
int tile_width,
int tile_height,
bool is_opaque
) {
assert(window->surfaces.count(id) == 0);
Surface surface;
surface.tile_width = tile_width;
surface.tile_height = tile_height;
surface.is_opaque = is_opaque;
// Create the visual node in the DC tree that stores properties
HRESULT hr = window->pDCompDevice->CreateVisual(&surface.pVisual);
assert(SUCCEEDED(hr));
#ifdef USE_VIRTUAL_SURFACES
DXGI_ALPHA_MODE alpha_mode = surface.is_opaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
hr = window->pDCompDevice->CreateVirtualSurface(
VIRTUAL_OFFSET * 2,
VIRTUAL_OFFSET * 2,
DXGI_FORMAT_B8G8R8A8_UNORM,
alpha_mode,
&surface.pVirtualSurface
);
assert(SUCCEEDED(hr));
// Bind the surface memory to this visual
hr = surface.pVisual->SetContent(surface.pVirtualSurface);
assert(SUCCEEDED(hr));
#endif
window->surfaces[id] = surface;
}
void com_dc_create_tile(
Window *window,
uint64_t id,
int x,
int y
) {
assert(window->surfaces.count(id) == 1);
Surface &surface = window->surfaces[id];
TileKey key(x, y);
assert(surface.tiles.count(key) == 0);
Tile tile;
#ifndef USE_VIRTUAL_SURFACES
// Create the video memory surface.
DXGI_ALPHA_MODE alpha_mode = surface.is_opaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
HRESULT hr = window->pDCompDevice->CreateSurface(
surface.tile_width,
surface.tile_height,
DXGI_FORMAT_B8G8R8A8_UNORM,
alpha_mode,
&tile.pSurface
);
assert(SUCCEEDED(hr));
// Create the visual node in the DC tree that stores properties
hr = window->pDCompDevice->CreateVisual(&tile.pVisual);
assert(SUCCEEDED(hr));
// Bind the surface memory to this visual
hr = tile.pVisual->SetContent(tile.pSurface);
assert(SUCCEEDED(hr));
// Place the visual in local-space of this surface
float offset_x = (float) (x * surface.tile_width);
float offset_y = (float) (y * surface.tile_height);
tile.pVisual->SetOffsetX(offset_x);
tile.pVisual->SetOffsetY(offset_y);
surface.pVisual->AddVisual(
tile.pVisual,
FALSE,
NULL
);
#endif
surface.tiles[key] = tile;
}
void com_dc_destroy_tile(
Window *window,
uint64_t id,
int x,
int y
) {
assert(window->surfaces.count(id) == 1);
Surface &surface = window->surfaces[id];
TileKey key(x, y);
assert(surface.tiles.count(key) == 1);
Tile &tile = surface.tiles[key];
#ifndef USE_VIRTUAL_SURFACES
surface.pVisual->RemoveVisual(tile.pVisual);
tile.pVisual->Release();
tile.pSurface->Release();
#endif
surface.tiles.erase(key);
}
void com_dc_destroy_surface(
Window *window,
uint64_t id
) {
assert(window->surfaces.count(id) == 1);
Surface &surface = window->surfaces[id];
window->pRoot->RemoveVisual(surface.pVisual);
#ifdef USE_VIRTUAL_SURFACES
surface.pVirtualSurface->Release();
#else
// Release the video memory and visual in the tree
for (auto tile_it=surface.tiles.begin() ; tile_it != surface.tiles.end() ; ++tile_it) {
tile_it->second.pSurface->Release();
tile_it->second.pVisual->Release();
}
#endif
surface.pVisual->Release();
window->surfaces.erase(id);
}
// Bind a DC surface to allow issuing GL commands to it
GLuint 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
) {
assert(window->surfaces.count(surface_id) == 1);
Surface &surface = window->surfaces[surface_id];
TileKey key(tile_x, tile_y);
assert(surface.tiles.count(key) == 1);
Tile &tile = surface.tiles[key];
// Inform DC that we want to draw on this surface. DC uses texture
// atlases when the tiles are small. It returns an offset where the
// client code must draw into this surface when this happens.
RECT update_rect;
update_rect.left = dirty_x0;
update_rect.top = dirty_y0;
update_rect.right = dirty_x0 + dirty_width;
update_rect.bottom = dirty_y0 + dirty_height;
POINT offset;
D3D11_TEXTURE2D_DESC desc;
ID3D11Texture2D *pTexture;
HRESULT hr;
// Store the current surface for unbinding later
#ifdef USE_VIRTUAL_SURFACES
LONG tile_offset_x = VIRTUAL_OFFSET + tile_x * surface.tile_width;
LONG tile_offset_y = VIRTUAL_OFFSET + tile_y * surface.tile_height;
update_rect.left += tile_offset_x;
update_rect.top += tile_offset_y;
update_rect.right += tile_offset_x;
update_rect.bottom += tile_offset_y;
hr = surface.pVirtualSurface->BeginDraw(
&update_rect,
__uuidof(ID3D11Texture2D),
(void **) &pTexture,
&offset
);
window->pCurrentSurface = surface.pVirtualSurface;
#else
hr = tile.pSurface->BeginDraw(
&update_rect,
__uuidof(ID3D11Texture2D),
(void **) &pTexture,
&offset
);
window->pCurrentSurface = tile.pSurface;
#endif
// DC includes the origin of the dirty / update rect in the draw offset,
// undo that here since WR expects it to be an absolute offset.
assert(SUCCEEDED(hr));
offset.x -= dirty_x0;
offset.y -= dirty_y0;
pTexture->GetDesc(&desc);
*x_offset = offset.x;
*y_offset = offset.y;
// Construct an EGLImage wrapper around the D3D texture for ANGLE.
const EGLAttrib attribs[] = { EGL_NONE };
window->mEGLImage = eglCreateImage(
window->EGLDisplay,
EGL_NO_CONTEXT,
EGL_D3D11_TEXTURE_ANGLE,
static_cast<EGLClientBuffer>(pTexture),
attribs
);
// Get the current FBO and RBO id, so we can restore them later
GLint currentFboId, currentRboId;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &currentFboId);
glGetIntegerv(GL_RENDERBUFFER_BINDING, &currentRboId);
// Create a render buffer object that is backed by the EGL image.
glGenRenderbuffers(1, &window->mColorRBO);
glBindRenderbuffer(GL_RENDERBUFFER, window->mColorRBO);
glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, window->mEGLImage);
// Get or create an FBO for the specified dimensions
GLuint fboId = GetOrCreateFbo(window, desc.Width, desc.Height);
// Attach the new renderbuffer to the FBO
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER,
window->mColorRBO);
// Restore previous FBO and RBO bindings
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFboId);
glBindRenderbuffer(GL_RENDERBUFFER, currentRboId);
return fboId;
}
// Unbind a currently bound DC surface
void com_dc_unbind_surface(Window *window) {
HRESULT hr = window->pCurrentSurface->EndDraw();
assert(SUCCEEDED(hr));
glDeleteRenderbuffers(1, &window->mColorRBO);
window->mColorRBO = 0;
eglDestroyImage(window->EGLDisplay, window->mEGLImage);
window->mEGLImage = EGL_NO_IMAGE;
}
void com_dc_begin_transaction(Window *) {
}
// Add a DC surface to the visual tree. Called per-frame to build the composition.
void 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
) {
Surface surface = window->surfaces[id];
window->mCurrentLayers.push_back(id);
// Place the visual - this changes frame to frame based on scroll position
// of the slice.
float offset_x = (float) (x + window->client_rect.left);
float offset_y = (float) (y + window->client_rect.top);
#ifdef USE_VIRTUAL_SURFACES
offset_x -= VIRTUAL_OFFSET;
offset_y -= VIRTUAL_OFFSET;
#endif
surface.pVisual->SetOffsetX(offset_x);
surface.pVisual->SetOffsetY(offset_y);
// Set the clip rect - converting from world space to the pre-offset space
// that DC requires for rectangle clips.
D2D_RECT_F clip_rect;
clip_rect.left = clip_x - offset_x;
clip_rect.top = clip_y - offset_y;
clip_rect.right = clip_rect.left + clip_w;
clip_rect.bottom = clip_rect.top + clip_h;
surface.pVisual->SetClip(clip_rect);
}
// Finish the composition transaction, telling DC to composite
void com_dc_end_transaction(Window *window) {
bool same = window->mPrevLayers == window->mCurrentLayers;
if (!same) {
HRESULT hr = window->pRoot->RemoveAllVisuals();
assert(SUCCEEDED(hr));
for (auto it = window->mCurrentLayers.begin(); it != window->mCurrentLayers.end(); ++it) {
Surface &surface = window->surfaces[*it];
// Add this visual as the last element in the visual tree (z-order is implicit,
// based on the order tiles are added).
hr = window->pRoot->AddVisual(
surface.pVisual,
FALSE,
NULL
);
assert(SUCCEEDED(hr));
}
}
window->mPrevLayers.swap(window->mCurrentLayers);
window->mCurrentLayers.clear();
HRESULT hr = window->pDCompDevice->Commit();
assert(SUCCEEDED(hr));
}
// Get a pointer to an EGL symbol
void *com_dc_get_proc_address(const char *name) {
return eglGetProcAddress(name);
}
}

View file

@ -0,0 +1,261 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::os::raw::{c_void, c_char};
/*
This is a very simple (and unsafe!) rust wrapper for the DirectComposite / D3D11 / ANGLE
implementation in lib.cpp.
It just proxies the calls from the Compositor impl to the C99 code. This is very
hacky and not suitable for production!
*/
// Opaque wrapper for the Window type in lib.cpp
#[repr(C)]
pub struct Window {
_unused: [u8; 0]
}
// C99 functions that do the compositor work
extern {
fn com_dc_create_window(
width: i32,
height: i32,
enable_compositor: bool,
sync_mode: i32,
) -> *mut Window;
fn com_dc_destroy_window(window: *mut Window);
fn com_dc_tick(window: *mut Window) -> bool;
fn com_dc_get_proc_address(name: *const c_char) -> *const c_void;
fn com_dc_swap_buffers(window: *mut Window);
fn com_dc_create_surface(
window: *mut Window,
id: u64,
tile_width: i32,
tile_height: i32,
is_opaque: bool,
);
fn com_dc_create_tile(
window: *mut Window,
id: u64,
x: i32,
y: i32,
);
fn com_dc_destroy_tile(
window: *mut Window,
id: u64,
x: i32,
y: i32,
);
fn com_dc_destroy_surface(
window: *mut Window,
id: u64,
);
fn com_dc_bind_surface(
window: *mut Window,
surface_id: u64,
tile_x: i32,
tile_y: i32,
x_offset: &mut i32,
y_offset: &mut i32,
dirty_x0: i32,
dirty_y0: i32,
dirty_width: i32,
dirty_height: i32,
) -> u32;
fn com_dc_unbind_surface(window: *mut Window);
fn com_dc_begin_transaction(window: *mut Window);
fn com_dc_add_surface(
window: *mut Window,
id: u64,
x: i32,
y: i32,
clip_x: i32,
clip_y: i32,
clip_w: i32,
clip_h: i32,
);
fn com_dc_end_transaction(window: *mut Window);
}
pub fn create_window(
width: i32,
height: i32,
enable_compositor: bool,
sync_mode: i32,
) -> *mut Window {
unsafe {
com_dc_create_window(width, height, enable_compositor, sync_mode)
}
}
pub fn destroy_window(window: *mut Window) {
unsafe {
com_dc_destroy_window(window);
}
}
pub fn tick(window: *mut Window) -> bool {
unsafe {
com_dc_tick(window)
}
}
pub fn get_proc_address(name: *const c_char) -> *const c_void {
unsafe {
com_dc_get_proc_address(name)
}
}
pub fn create_surface(
window: *mut Window,
id: u64,
tile_width: i32,
tile_height: i32,
is_opaque: bool,
) {
unsafe {
com_dc_create_surface(
window,
id,
tile_width,
tile_height,
is_opaque,
)
}
}
pub fn create_tile(
window: *mut Window,
id: u64,
x: i32,
y: i32,
) {
unsafe {
com_dc_create_tile(
window,
id,
x,
y,
)
}
}
pub fn destroy_tile(
window: *mut Window,
id: u64,
x: i32,
y: i32,
) {
unsafe {
com_dc_destroy_tile(
window,
id,
x,
y,
)
}
}
pub fn destroy_surface(
window: *mut Window,
id: u64,
) {
unsafe {
com_dc_destroy_surface(
window,
id,
)
}
}
pub fn bind_surface(
window: *mut Window,
surface_id: u64,
tile_x: i32,
tile_y: i32,
dirty_x0: i32,
dirty_y0: i32,
dirty_width: i32,
dirty_height: i32,
) -> (u32, i32, i32) {
unsafe {
let mut x_offset = 0;
let mut y_offset = 0;
let fbo_id = com_dc_bind_surface(
window,
surface_id,
tile_x,
tile_y,
&mut x_offset,
&mut y_offset,
dirty_x0,
dirty_y0,
dirty_width,
dirty_height,
);
(fbo_id, x_offset, y_offset)
}
}
pub fn add_surface(
window: *mut Window,
id: u64,
x: i32,
y: i32,
clip_x: i32,
clip_y: i32,
clip_w: i32,
clip_h: i32,
) {
unsafe {
com_dc_add_surface(
window,
id,
x,
y,
clip_x,
clip_y,
clip_w,
clip_h,
)
}
}
pub fn begin_transaction(window: *mut Window) {
unsafe {
com_dc_begin_transaction(window)
}
}
pub fn unbind_surface(window: *mut Window) {
unsafe {
com_dc_unbind_surface(window)
}
}
pub fn end_transaction(window: *mut Window) {
unsafe {
com_dc_end_transaction(window)
}
}
pub fn swap_buffers(window: *mut Window) {
unsafe {
com_dc_swap_buffers(window);
}
}