mirror of
https://github.com/servo/servo.git
synced 2025-08-02 12:10:29 +01:00
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:
parent
c19eb800de
commit
49277f5c3f
1215 changed files with 185677 additions and 34 deletions
9
third_party/webrender/example-compositor/compositor-windows/Cargo.toml
vendored
Normal file
9
third_party/webrender/example-compositor/compositor-windows/Cargo.toml
vendored
Normal 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"
|
25
third_party/webrender/example-compositor/compositor-windows/build.rs
vendored
Normal file
25
third_party/webrender/example-compositor/compositor-windows/build.rs
vendored
Normal 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");
|
||||
}
|
802
third_party/webrender/example-compositor/compositor-windows/src/lib.cpp
vendored
Normal file
802
third_party/webrender/example-compositor/compositor-windows/src/lib.cpp
vendored
Normal 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, ¤tFboId);
|
||||
glGetIntegerv(GL_RENDERBUFFER_BINDING, ¤tRboId);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
261
third_party/webrender/example-compositor/compositor-windows/src/lib.rs
vendored
Normal file
261
third_party/webrender/example-compositor/compositor-windows/src/lib.rs
vendored
Normal 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);
|
||||
}
|
||||
}
|
13
third_party/webrender/example-compositor/compositor/Cargo.toml
vendored
Normal file
13
third_party/webrender/example-compositor/compositor/Cargo.toml
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "compositor"
|
||||
version = "0.1.0"
|
||||
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
|
||||
[dependencies]
|
||||
webrender = { path = "../../webrender" }
|
||||
gleam = "0.12.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
compositor-windows = { path = "../compositor-windows" }
|
484
third_party/webrender/example-compositor/compositor/src/main.rs
vendored
Normal file
484
third_party/webrender/example-compositor/compositor/src/main.rs
vendored
Normal file
|
@ -0,0 +1,484 @@
|
|||
/* 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/. */
|
||||
|
||||
/*
|
||||
|
||||
An example of how to implement the Compositor trait that
|
||||
allows picture caching surfaces to be composited by the operating
|
||||
system.
|
||||
|
||||
The current example supports DirectComposite on Windows only.
|
||||
|
||||
*/
|
||||
|
||||
use euclid::Angle;
|
||||
use gleam::gl;
|
||||
use std::ffi::CString;
|
||||
use std::sync::mpsc;
|
||||
use webrender::api::*;
|
||||
use webrender::api::units::*;
|
||||
#[cfg(target_os = "windows")]
|
||||
use compositor_windows as compositor;
|
||||
use std::{env, f32, process};
|
||||
|
||||
// A very hacky integration with DirectComposite. It proxies calls from the compositor
|
||||
// interface to a simple C99 library which does the DirectComposition / D3D11 / ANGLE
|
||||
// interfacing. This is a very unsafe impl due to the way the window pointer is passed
|
||||
// around!
|
||||
struct DirectCompositeInterface {
|
||||
window: *mut compositor::Window,
|
||||
}
|
||||
|
||||
impl DirectCompositeInterface {
|
||||
fn new(window: *mut compositor::Window) -> Self {
|
||||
DirectCompositeInterface {
|
||||
window,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl webrender::Compositor for DirectCompositeInterface {
|
||||
fn create_surface(
|
||||
&mut self,
|
||||
id: webrender::NativeSurfaceId,
|
||||
tile_size: DeviceIntSize,
|
||||
is_opaque: bool,
|
||||
) {
|
||||
compositor::create_surface(
|
||||
self.window,
|
||||
id.0,
|
||||
tile_size.width,
|
||||
tile_size.height,
|
||||
is_opaque,
|
||||
);
|
||||
}
|
||||
|
||||
fn destroy_surface(
|
||||
&mut self,
|
||||
id: webrender::NativeSurfaceId,
|
||||
) {
|
||||
compositor::destroy_surface(self.window, id.0);
|
||||
}
|
||||
|
||||
fn create_tile(
|
||||
&mut self,
|
||||
id: webrender::NativeTileId,
|
||||
) {
|
||||
compositor::create_tile(
|
||||
self.window,
|
||||
id.surface_id.0,
|
||||
id.x,
|
||||
id.y,
|
||||
);
|
||||
}
|
||||
|
||||
fn destroy_tile(
|
||||
&mut self,
|
||||
id: webrender::NativeTileId,
|
||||
) {
|
||||
compositor::destroy_tile(
|
||||
self.window,
|
||||
id.surface_id.0,
|
||||
id.x,
|
||||
id.y,
|
||||
);
|
||||
}
|
||||
|
||||
fn bind(
|
||||
&mut self,
|
||||
id: webrender::NativeTileId,
|
||||
dirty_rect: DeviceIntRect,
|
||||
) -> webrender::NativeSurfaceInfo {
|
||||
let (fbo_id, x, y) = compositor::bind_surface(
|
||||
self.window,
|
||||
id.surface_id.0,
|
||||
id.x,
|
||||
id.y,
|
||||
dirty_rect.origin.x,
|
||||
dirty_rect.origin.y,
|
||||
dirty_rect.size.width,
|
||||
dirty_rect.size.height,
|
||||
);
|
||||
|
||||
webrender::NativeSurfaceInfo {
|
||||
origin: DeviceIntPoint::new(x, y),
|
||||
fbo_id,
|
||||
}
|
||||
}
|
||||
|
||||
fn unbind(&mut self) {
|
||||
compositor::unbind_surface(self.window);
|
||||
}
|
||||
|
||||
fn begin_frame(&mut self) {
|
||||
compositor::begin_transaction(self.window);
|
||||
}
|
||||
|
||||
fn add_surface(
|
||||
&mut self,
|
||||
id: webrender::NativeSurfaceId,
|
||||
position: DeviceIntPoint,
|
||||
clip_rect: DeviceIntRect,
|
||||
) {
|
||||
compositor::add_surface(
|
||||
self.window,
|
||||
id.0,
|
||||
position.x,
|
||||
position.y,
|
||||
clip_rect.origin.x,
|
||||
clip_rect.origin.y,
|
||||
clip_rect.size.width,
|
||||
clip_rect.size.height,
|
||||
);
|
||||
}
|
||||
|
||||
fn end_frame(&mut self) {
|
||||
compositor::end_transaction(self.window);
|
||||
}
|
||||
}
|
||||
|
||||
// Simplisitic implementation of the WR notifier interface to know when a frame
|
||||
// has been prepared and can be rendered.
|
||||
struct Notifier {
|
||||
tx: mpsc::Sender<()>,
|
||||
}
|
||||
|
||||
impl Notifier {
|
||||
fn new(tx: mpsc::Sender<()>) -> Self {
|
||||
Notifier {
|
||||
tx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderNotifier for Notifier {
|
||||
fn clone(&self) -> Box<dyn RenderNotifier> {
|
||||
Box::new(Notifier {
|
||||
tx: self.tx.clone()
|
||||
})
|
||||
}
|
||||
|
||||
fn wake_up(&self) {
|
||||
}
|
||||
|
||||
fn new_frame_ready(&self,
|
||||
_: DocumentId,
|
||||
_scrolled: bool,
|
||||
_composite_needed: bool,
|
||||
_render_time: Option<u64>) {
|
||||
self.tx.send(()).ok();
|
||||
}
|
||||
}
|
||||
|
||||
fn push_rotated_rect(
|
||||
builder: &mut DisplayListBuilder,
|
||||
rect: LayoutRect,
|
||||
color: ColorF,
|
||||
spatial_id: SpatialId,
|
||||
root_pipeline_id: PipelineId,
|
||||
angle: f32,
|
||||
time: f32,
|
||||
) {
|
||||
let color = color.scale_rgb(time);
|
||||
let rotation = LayoutTransform::create_rotation(
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
Angle::radians(2.0 * std::f32::consts::PI * angle),
|
||||
);
|
||||
let transform_origin = LayoutVector3D::new(
|
||||
rect.origin.x + rect.size.width * 0.5,
|
||||
rect.origin.y + rect.size.height * 0.5,
|
||||
0.0,
|
||||
);
|
||||
let transform = rotation
|
||||
.pre_translate(-transform_origin)
|
||||
.post_translate(transform_origin);
|
||||
let spatial_id = builder.push_reference_frame(
|
||||
LayoutPoint::zero(),
|
||||
spatial_id,
|
||||
TransformStyle::Flat,
|
||||
PropertyBinding::Value(transform),
|
||||
ReferenceFrameKind::Transform,
|
||||
);
|
||||
builder.push_rect(
|
||||
&CommonItemProperties::new(
|
||||
rect,
|
||||
SpaceAndClipInfo {
|
||||
spatial_id,
|
||||
clip_id: ClipId::root(root_pipeline_id),
|
||||
},
|
||||
),
|
||||
rect,
|
||||
color,
|
||||
);
|
||||
}
|
||||
|
||||
fn build_display_list(
|
||||
builder: &mut DisplayListBuilder,
|
||||
scroll_id: ExternalScrollId,
|
||||
root_pipeline_id: PipelineId,
|
||||
layout_size: LayoutSize,
|
||||
time: f32,
|
||||
invalidations: Invalidations,
|
||||
) {
|
||||
let size_factor = match invalidations {
|
||||
Invalidations::Small => 0.1,
|
||||
Invalidations::Large | Invalidations::Scrolling => 1.0,
|
||||
};
|
||||
|
||||
let fixed_space_info = SpaceAndClipInfo {
|
||||
spatial_id: SpatialId::root_scroll_node(root_pipeline_id),
|
||||
clip_id: ClipId::root(root_pipeline_id),
|
||||
};
|
||||
|
||||
let scroll_space_info = builder.define_scroll_frame(
|
||||
&fixed_space_info,
|
||||
Some(scroll_id),
|
||||
LayoutRect::new(LayoutPoint::zero(), layout_size),
|
||||
LayoutRect::new(LayoutPoint::zero(), layout_size),
|
||||
ScrollSensitivity::Script,
|
||||
LayoutVector2D::zero(),
|
||||
);
|
||||
|
||||
builder.push_rect(
|
||||
&CommonItemProperties::new(
|
||||
LayoutRect::new(LayoutPoint::zero(), layout_size).inflate(-10.0, -10.0),
|
||||
fixed_space_info,
|
||||
),
|
||||
LayoutRect::new(LayoutPoint::zero(), layout_size).inflate(-10.0, -10.0),
|
||||
ColorF::new(0.8, 0.8, 0.8, 1.0),
|
||||
);
|
||||
|
||||
push_rotated_rect(
|
||||
builder,
|
||||
LayoutRect::new(
|
||||
LayoutPoint::new(100.0, 100.0),
|
||||
LayoutSize::new(size_factor * 400.0, size_factor * 400.0),
|
||||
),
|
||||
ColorF::new(1.0, 0.0, 0.0, 1.0),
|
||||
scroll_space_info.spatial_id,
|
||||
root_pipeline_id,
|
||||
time,
|
||||
time,
|
||||
);
|
||||
|
||||
push_rotated_rect(
|
||||
builder,
|
||||
LayoutRect::new(
|
||||
LayoutPoint::new(800.0, 100.0),
|
||||
LayoutSize::new(size_factor * 100.0, size_factor * 600.0),
|
||||
),
|
||||
ColorF::new(0.0, 1.0, 0.0, 1.0),
|
||||
fixed_space_info.spatial_id,
|
||||
root_pipeline_id,
|
||||
0.2,
|
||||
time,
|
||||
);
|
||||
|
||||
push_rotated_rect(
|
||||
builder,
|
||||
LayoutRect::new(
|
||||
LayoutPoint::new(700.0, 200.0),
|
||||
LayoutSize::new(size_factor * 300.0, size_factor * 300.0),
|
||||
),
|
||||
ColorF::new(0.0, 0.0, 1.0, 1.0),
|
||||
scroll_space_info.spatial_id,
|
||||
root_pipeline_id,
|
||||
0.1,
|
||||
time,
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum Invalidations {
|
||||
Large,
|
||||
Small,
|
||||
Scrolling,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum Sync {
|
||||
None = 0,
|
||||
Swap = 1,
|
||||
Commit = 2,
|
||||
Flush = 3,
|
||||
Query = 4,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
if args.len() != 6 {
|
||||
println!("USAGE: compositor [native|none] [small|large|scroll] [none|swap|commit|flush|query] width height");
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
let enable_compositor = match args[1].parse::<String>().unwrap().as_str() {
|
||||
"native" => true,
|
||||
"none" => false,
|
||||
_ => panic!("invalid compositor [native, none]"),
|
||||
};
|
||||
|
||||
let inv_mode = match args[2].parse::<String>().unwrap().as_str() {
|
||||
"small" => Invalidations::Small,
|
||||
"large" => Invalidations::Large,
|
||||
"scroll" => Invalidations::Scrolling,
|
||||
_ => panic!("invalid invalidations [small, large, scroll]"),
|
||||
};
|
||||
|
||||
let sync_mode = match args[3].parse::<String>().unwrap().as_str() {
|
||||
"none" => Sync::None,
|
||||
"swap" => Sync::Swap,
|
||||
"commit" => Sync::Commit,
|
||||
"flush" => Sync::Flush,
|
||||
"query" => Sync::Query,
|
||||
_ => panic!("invalid sync mode [none, swap, commit, flush, query]"),
|
||||
};
|
||||
|
||||
let width = args[4].parse().unwrap();
|
||||
let height = args[5].parse().unwrap();
|
||||
let device_size = DeviceIntSize::new(width, height);
|
||||
|
||||
// Load GL, construct WR and the native compositor interface.
|
||||
let window = compositor::create_window(
|
||||
device_size.width,
|
||||
device_size.height,
|
||||
enable_compositor,
|
||||
sync_mode as i32,
|
||||
);
|
||||
let debug_flags = DebugFlags::empty();
|
||||
let compositor_config = if enable_compositor {
|
||||
webrender::CompositorConfig::Native {
|
||||
max_update_rects: 1,
|
||||
compositor: Box::new(DirectCompositeInterface::new(window)),
|
||||
}
|
||||
} else {
|
||||
webrender::CompositorConfig::Draw {
|
||||
max_partial_present_rects: 0,
|
||||
}
|
||||
};
|
||||
let opts = webrender::RendererOptions {
|
||||
clear_color: Some(ColorF::new(1.0, 1.0, 1.0, 1.0)),
|
||||
debug_flags,
|
||||
enable_picture_caching: true,
|
||||
compositor_config,
|
||||
..webrender::RendererOptions::default()
|
||||
};
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let notifier = Box::new(Notifier::new(tx));
|
||||
let gl = unsafe {
|
||||
gl::GlesFns::load_with(
|
||||
|symbol| {
|
||||
let symbol = CString::new(symbol).unwrap();
|
||||
let ptr = compositor::get_proc_address(symbol.as_ptr());
|
||||
ptr
|
||||
}
|
||||
)
|
||||
};
|
||||
let (mut renderer, sender) = webrender::Renderer::new(
|
||||
gl.clone(),
|
||||
notifier,
|
||||
opts,
|
||||
None,
|
||||
device_size,
|
||||
).unwrap();
|
||||
let api = sender.create_api();
|
||||
let document_id = api.add_document(device_size, 0);
|
||||
let device_pixel_ratio = 1.0;
|
||||
let mut current_epoch = Epoch(0);
|
||||
let root_pipeline_id = PipelineId(0, 0);
|
||||
let layout_size = device_size.to_f32() / euclid::Scale::new(device_pixel_ratio);
|
||||
let mut time = 0.0;
|
||||
let scroll_id = ExternalScrollId(3, root_pipeline_id);
|
||||
|
||||
// Kick off first transaction which will mean we get a notify below to build the DL and render.
|
||||
let mut txn = Transaction::new();
|
||||
txn.set_root_pipeline(root_pipeline_id);
|
||||
|
||||
if let Invalidations::Scrolling = inv_mode {
|
||||
let mut root_builder = DisplayListBuilder::new(root_pipeline_id, layout_size);
|
||||
|
||||
build_display_list(
|
||||
&mut root_builder,
|
||||
scroll_id,
|
||||
root_pipeline_id,
|
||||
layout_size,
|
||||
1.0,
|
||||
inv_mode,
|
||||
);
|
||||
|
||||
txn.set_display_list(
|
||||
current_epoch,
|
||||
None,
|
||||
layout_size,
|
||||
root_builder.finalize(),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
txn.generate_frame();
|
||||
api.send_transaction(document_id, txn);
|
||||
|
||||
// Tick the compositor (in this sample, we don't block on UI events)
|
||||
while compositor::tick(window) {
|
||||
// If there is a new frame ready to draw
|
||||
if let Ok(..) = rx.try_recv() {
|
||||
// Update and render. This will invoke the native compositor interface implemented above
|
||||
// as required.
|
||||
renderer.update();
|
||||
renderer.render(device_size).unwrap();
|
||||
let _ = renderer.flush_pipeline_info();
|
||||
|
||||
// Construct a simple display list that can be drawn and composited by DC.
|
||||
let mut txn = Transaction::new();
|
||||
|
||||
match inv_mode {
|
||||
Invalidations::Small | Invalidations::Large => {
|
||||
let mut root_builder = DisplayListBuilder::new(root_pipeline_id, layout_size);
|
||||
|
||||
build_display_list(
|
||||
&mut root_builder,
|
||||
scroll_id,
|
||||
root_pipeline_id,
|
||||
layout_size,
|
||||
time,
|
||||
inv_mode,
|
||||
);
|
||||
|
||||
txn.set_display_list(
|
||||
current_epoch,
|
||||
None,
|
||||
layout_size,
|
||||
root_builder.finalize(),
|
||||
true,
|
||||
);
|
||||
}
|
||||
Invalidations::Scrolling => {
|
||||
let d = 0.5 - 0.5 * (2.0 * f32::consts::PI * 5.0 * time).cos();
|
||||
txn.scroll_node_with_id(
|
||||
LayoutPoint::new(0.0, (d * 100.0).round()),
|
||||
scroll_id,
|
||||
ScrollClamping::NoClamping,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
txn.generate_frame();
|
||||
api.send_transaction(document_id, txn);
|
||||
current_epoch.0 += 1;
|
||||
time += 0.001;
|
||||
if time > 1.0 {
|
||||
time = 0.0;
|
||||
}
|
||||
|
||||
// This does nothing when native compositor is enabled
|
||||
compositor::swap_buffers(window);
|
||||
}
|
||||
}
|
||||
|
||||
renderer.deinit();
|
||||
compositor::destroy_window(window);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue