| author | Glenn Watson <git@intuitionlibrary.com> |
| Tue, 07 Jan 2020 20:42:58 +0000 | |
| changeset 509215 | 1d212c2ddb76f5d54fd884e8e08001fc8475e13c |
| parent 509214 | 6cad172ad4bcf77bcace164816a8955802a6770d |
| child 509216 | 9f0cfd2aaa21590530f222b71ce648a6bf80707e |
| push id | 36993 |
| push user | dluca@mozilla.com |
| push date | Wed, 08 Jan 2020 09:41:58 +0000 |
| treeherder | mozilla-central@12fb5e522dd3 [default view] [failures only] |
| perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
| reviewers | sotaro |
| bugs | 1607352 |
| milestone | 74.0a1 |
| first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
| last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/gfx/webrender_bindings/DCLayerTree.cpp +++ b/gfx/webrender_bindings/DCLayerTree.cpp @@ -45,21 +45,25 @@ UniquePtr<DCLayerTree> DCLayerTree::Crea DCLayerTree::DCLayerTree(gl::GLContext* aGL, EGLConfig aEGLConfig, ID3D11Device* aDevice, IDCompositionDevice2* aCompositionDevice) : mGL(aGL), mEGLConfig(aEGLConfig), mDevice(aDevice), mCompositionDevice(aCompositionDevice), mDebugCounter(false), - mDebugVisualRedrawRegions(false) {} + mDebugVisualRedrawRegions(false), + mEGLImage(EGL_NO_IMAGE), + mColorRBO(0) {} DCLayerTree::~DCLayerTree() { const auto gl = GetGLContext(); + DestroyEGLSurface(); + // Delete any cached FBO objects for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) { gl->fDeleteRenderbuffers(1, &it->depthRboId); gl->fDeleteFramebuffers(1, &it->fboId); } } bool DCLayerTree::Initialize(HWND aHwnd) { @@ -205,33 +209,44 @@ void DCLayerTree::CompositorEndFrame() { mCurrentLayers.clear(); mCompositionDevice->Commit(); } void DCLayerTree::Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId, wr::DeviceIntRect aDirtyRect) { auto surface = GetSurface(aId.surface_id); - auto layer = surface->GetLayer(aId.x, aId.y); + wr::DeviceIntPoint targetOffset{0, 0}; - *aFboId = layer->CreateEGLSurfaceForCompositionSurface(aDirtyRect, aOffset); - mCurrentId = Some(aId); +#ifdef USE_VIRTUAL_SURFACES + wr::DeviceIntSize tileSize = surface->GetTileSize(); + RefPtr<IDCompositionSurface> compositionSurface = + surface->GetCompositionSurface(); + targetOffset.x = VIRTUAL_OFFSET + tileSize.width * aId.x; + targetOffset.y = VIRTUAL_OFFSET + tileSize.height * aId.y; +#else + auto layer = surface->GetLayer(aId.x, aId.y); + RefPtr<IDCompositionSurface> compositionSurface = + layer->GetCompositionSurface(); +#endif + + *aFboId = CreateEGLSurfaceForCompositionSurface( + aDirtyRect, aOffset, compositionSurface, targetOffset); + mCurrentSurface = Some(compositionSurface); } void DCLayerTree::Unbind() { - if (mCurrentId.isNothing()) { + if (mCurrentSurface.isNothing()) { return; } - const auto id = mCurrentId.ref(); - auto surface = GetSurface(id.surface_id); - auto layer = surface->GetLayer(id.x, id.y); + RefPtr<IDCompositionSurface> surface = mCurrentSurface.ref(); + surface->EndDraw(); - layer->EndDraw(); - mCurrentId = Nothing(); + mCurrentSurface = Nothing(); } void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntSize aTileSize, bool aIsOpaque) { auto it = mDCSurfaces.find(aId); MOZ_RELEASE_ASSERT(it == mDCSurfaces.end()); if (it != mDCSurfaces.end()) { // DCSurface already exists. @@ -269,16 +284,23 @@ void DCLayerTree::DestroyTile(wr::Native void DCLayerTree::AddSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aPosition, wr::DeviceIntRect aClipRect) { auto it = mDCSurfaces.find(aId); MOZ_RELEASE_ASSERT(it != mDCSurfaces.end()); const auto layer = it->second.get(); const auto visual = layer->GetVisual(); +#ifdef USE_VIRTUAL_SURFACES + layer->UpdateAllocatedRect(); + + aPosition.x -= VIRTUAL_OFFSET; + aPosition.y -= VIRTUAL_OFFSET; +#endif + // Place the visual - this changes frame to frame based on scroll position // of the slice. visual->SetOffsetX(aPosition.x); visual->SetOffsetY(aPosition.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; @@ -331,72 +353,124 @@ GLuint DCLayerTree::GetOrCreateFbo(int a mFrameBuffers.push_back(frame_buffer_info); } return fboId; } DCSurface::DCSurface(wr::DeviceIntSize aTileSize, bool aIsOpaque, DCLayerTree* aDCLayerTree) - : mDCLayerTree(aDCLayerTree), mTileSize(aTileSize), mIsOpaque(aIsOpaque) {} + : mDCLayerTree(aDCLayerTree), + mTileSize(aTileSize), + mIsOpaque(aIsOpaque), + mAllocatedRectDirty(true) {} DCSurface::~DCSurface() {} bool DCSurface::Initialize() { HRESULT hr; const auto dCompDevice = mDCLayerTree->GetCompositionDevice(); hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual)); if (FAILED(hr)) { gfxCriticalNote << "Failed to create DCompositionVisual: " << gfx::hexa(hr); return false; } +#ifdef USE_VIRTUAL_SURFACES + DXGI_ALPHA_MODE alpha_mode = + mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; + + hr = dCompDevice->CreateVirtualSurface(VIRTUAL_OFFSET * 2, VIRTUAL_OFFSET * 2, + DXGI_FORMAT_B8G8R8A8_UNORM, alpha_mode, + getter_AddRefs(mVirtualSurface)); + MOZ_ASSERT(SUCCEEDED(hr)); + + // Bind the surface memory to this visual + hr = mVisual->SetContent(mVirtualSurface); + MOZ_ASSERT(SUCCEEDED(hr)); +#endif + return true; } void DCSurface::CreateTile(int aX, int aY) { TileKey key(aX, aY); MOZ_RELEASE_ASSERT(mDCLayers.find(key) == mDCLayers.end()); auto layer = MakeUnique<DCLayer>(mDCLayerTree); if (!layer->Initialize(aX, aY, mTileSize, mIsOpaque)) { gfxCriticalNote << "Failed to initialize DCLayer: " << aX << aY; return; } +#ifdef USE_VIRTUAL_SURFACES + mAllocatedRectDirty = true; +#else mVisual->AddVisual(layer->GetVisual(), FALSE, NULL); +#endif + mDCLayers[key] = std::move(layer); } void DCSurface::DestroyTile(int aX, int aY) { TileKey key(aX, aY); +#ifdef USE_VIRTUAL_SURFACES + mAllocatedRectDirty = true; +#else auto layer = GetLayer(aX, aY); mVisual->RemoveVisual(layer->GetVisual()); +#endif mDCLayers.erase(key); } +#ifdef USE_VIRTUAL_SURFACES +void DCSurface::UpdateAllocatedRect() { + if (mAllocatedRectDirty) { + RECT rect = {1000000, 1000000, -1000000, -1000000}; + + for (auto it = mDCLayers.begin(); it != mDCLayers.end(); ++it) { + int x = it->first.mX; + int y = it->first.mY; + + rect.left = std::min((int)rect.left, x * mTileSize.width); + rect.right = std::max((int)rect.right, (x + 1) * mTileSize.width); + + rect.top = std::min((int)rect.top, y * mTileSize.height); + rect.bottom = std::max((int)rect.bottom, (y + 1) * mTileSize.height); + } + + rect.left += VIRTUAL_OFFSET; + rect.top += VIRTUAL_OFFSET; + rect.bottom += VIRTUAL_OFFSET; + rect.right += VIRTUAL_OFFSET; + + mVirtualSurface->Trim(&rect, 1); + mAllocatedRectDirty = false; + } +} +#endif + DCLayer* DCSurface::GetLayer(int aX, int aY) const { TileKey key(aX, aY); auto layer_it = mDCLayers.find(key); MOZ_RELEASE_ASSERT(layer_it != mDCLayers.end()); return layer_it->second.get(); } -DCLayer::DCLayer(DCLayerTree* aDCLayerTree) - : mDCLayerTree(aDCLayerTree), mEGLImage(EGL_NO_IMAGE), mColorRBO(0) {} +DCLayer::DCLayer(DCLayerTree* aDCLayerTree) : mDCLayerTree(aDCLayerTree) {} -DCLayer::~DCLayer() { DestroyEGLSurface(); } +DCLayer::~DCLayer() {} bool DCLayer::Initialize(int aX, int aY, wr::DeviceIntSize aSize, bool aIsOpaque) { if (aSize.width <= 0 || aSize.height <= 0) { return false; } - mBufferSize = LayoutDeviceIntSize(aSize.width, aSize.height); +#ifndef USE_VIRTUAL_SURFACES HRESULT hr; const auto dCompDevice = mDCLayerTree->GetCompositionDevice(); hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual)); if (FAILED(hr)) { gfxCriticalNote << "Failed to create DCompositionVisual: " << gfx::hexa(hr); return false; } @@ -410,20 +484,22 @@ bool DCLayer::Initialize(int aX, int aY, gfxCriticalNote << "SetContent failed: " << gfx::hexa(hr); return false; } // Position this tile at a local space offset within the parent visual // Scroll offsets get applied to the parent visual only. mVisual->SetOffsetX(aX * aSize.width); mVisual->SetOffsetY(aY * aSize.height); +#endif return true; } +#ifndef USE_VIRTUAL_SURFACES RefPtr<IDCompositionSurface> DCLayer::CreateCompositionSurface( wr::DeviceIntSize aSize, bool aIsOpaque) { HRESULT hr; const auto dCompDevice = mDCLayerTree->GetCompositionDevice(); const auto alphaMode = aIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; RefPtr<IDCompositionSurface> compositionSurface; @@ -432,55 +508,48 @@ RefPtr<IDCompositionSurface> DCLayer::Cr getter_AddRefs(compositionSurface)); if (FAILED(hr)) { gfxCriticalNote << "Failed to create DCompositionSurface: " << gfx::hexa(hr); return nullptr; } return compositionSurface; } +#endif -GLuint DCLayer::CreateEGLSurfaceForCompositionSurface( - wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset) { - MOZ_ASSERT(mCompositionSurface.get()); +GLuint DCLayerTree::CreateEGLSurfaceForCompositionSurface( + wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset, + RefPtr<IDCompositionSurface> aCompositionSurface, + wr::DeviceIntPoint aSurfaceOffset) { + MOZ_ASSERT(aCompositionSurface.get()); HRESULT hr; - const auto gl = mDCLayerTree->GetGLContext(); + const auto gl = GetGLContext(); RefPtr<ID3D11Texture2D> backBuf; POINT offset; - LayoutDeviceIntRect dirtyRect(aDirtyRect.origin.x, aDirtyRect.origin.y, - aDirtyRect.size.width, aDirtyRect.size.height); + RECT update_rect; + update_rect.left = aSurfaceOffset.x + aDirtyRect.origin.x; + update_rect.top = aSurfaceOffset.y + aDirtyRect.origin.y; + update_rect.right = update_rect.left + aDirtyRect.size.width; + update_rect.bottom = update_rect.top + aDirtyRect.size.height; - RECT update_rect; - update_rect.left = dirtyRect.X(); - update_rect.top = dirtyRect.Y(); - update_rect.right = dirtyRect.XMost(); - update_rect.bottom = dirtyRect.YMost(); - - RECT* rect = &update_rect; - if (StaticPrefs::gfx_webrender_compositor_max_update_rects_AtStartup() <= 0) { - // Update entire surface - rect = nullptr; - } - - hr = mCompositionSurface->BeginDraw(rect, __uuidof(ID3D11Texture2D), + hr = aCompositionSurface->BeginDraw(&update_rect, __uuidof(ID3D11Texture2D), (void**)getter_AddRefs(backBuf), &offset); if (FAILED(hr)) { gfxCriticalNote << "DCompositionSurface::BeginDraw failed: " - << gfx::hexa(hr) << "dirtyRect: " << dirtyRect; + << gfx::hexa(hr); return false; } // 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. - offset.x -= dirtyRect.X(); - offset.y -= dirtyRect.Y(); + offset.x -= aDirtyRect.origin.x; + offset.y -= aDirtyRect.origin.y; - // Texture size could be diffrent from mBufferSize. D3D11_TEXTURE2D_DESC desc; backBuf->GetDesc(&desc); const auto& gle = gl::GLContextEGL::Cast(gl); const auto& egl = gle->mEgl; const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get()); @@ -495,17 +564,17 @@ GLuint DCLayer::CreateEGLSurfaceForCompo gl->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, ¤tRboId); // Create a render buffer object that is backed by the EGL image. gl->fGenRenderbuffers(1, &mColorRBO); gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mColorRBO); gl->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER, mEGLImage); // Get or create an FBO for the specified dimensions - GLuint fboId = mDCLayerTree->GetOrCreateFbo(desc.Width, desc.Height); + GLuint fboId = GetOrCreateFbo(desc.Width, desc.Height); // Attach the new renderbuffer to the FBO gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId); gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, LOCAL_GL_RENDERBUFFER, mColorRBO); // Restore previous FBO and RBO bindings @@ -513,36 +582,26 @@ GLuint DCLayer::CreateEGLSurfaceForCompo gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, currentRboId); aOffset->x = offset.x; aOffset->y = offset.y; return fboId; } -void DCLayer::DestroyEGLSurface() { - const auto gl = mDCLayerTree->GetGLContext(); +void DCLayerTree::DestroyEGLSurface() { + const auto gl = GetGLContext(); if (mColorRBO) { gl->fDeleteRenderbuffers(1, &mColorRBO); mColorRBO = 0; } if (mEGLImage) { const auto& gle = gl::GLContextEGL::Cast(gl); const auto& egl = gle->mEgl; egl->fDestroyImage(egl->Display(), mEGLImage); mEGLImage = EGL_NO_IMAGE; } } -void DCLayer::EndDraw() { - MOZ_ASSERT(mCompositionSurface.get()); - if (!mCompositionSurface) { - return; - } - - mCompositionSurface->EndDraw(); - DestroyEGLSurface(); -} - } // namespace wr } // namespace mozilla
--- a/gfx/webrender_bindings/DCLayerTree.h +++ b/gfx/webrender_bindings/DCLayerTree.h @@ -18,25 +18,35 @@ struct ID3D11Device; struct ID3D11DeviceContext; struct IDCompositionDevice2; struct IDCompositionSurface; struct IDCompositionTarget; struct IDCompositionVisual2; struct IDXGISwapChain1; +struct IDCompositionVirtualSurface; namespace mozilla { namespace gl { class GLContext; } namespace wr { +#define USE_VIRTUAL_SURFACES + +// DirectComposition virtual surfaces are zero based, but WR picture cache +// bounds can potentially have a negative origin. Shift all the picture cache +// coordinates by a large fixed amount, such that we don't need to re-create +// the surface if the picture cache origin becomes negative due to adding more +// tiles to the above / left. +#define VIRTUAL_OFFSET 512 * 1024 + class DCLayer; class DCSurface; /** * DCLayerTree manages direct composition layers. * It does not manage gecko's layers::Layer. */ class DCLayerTree { @@ -76,31 +86,44 @@ class DCLayerTree { // Get or create an FBO with depth buffer suitable for specified dimensions GLuint GetOrCreateFbo(int aWidth, int aHeight); protected: bool Initialize(HWND aHwnd); bool MaybeUpdateDebugCounter(); bool MaybeUpdateDebugVisualRedrawRegions(); + void DestroyEGLSurface(); + GLuint CreateEGLSurfaceForCompositionSurface( + wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset, + RefPtr<IDCompositionSurface> aCompositionSurface, + wr::DeviceIntPoint aSurfaceOffset); RefPtr<gl::GLContext> mGL; EGLConfig mEGLConfig; RefPtr<ID3D11Device> mDevice; RefPtr<IDCompositionDevice2> mCompositionDevice; RefPtr<IDCompositionTarget> mCompositionTarget; RefPtr<IDCompositionVisual2> mRootVisual; RefPtr<IDCompositionVisual2> mDefaultSwapChainVisual; bool mDebugCounter; bool mDebugVisualRedrawRegions; - Maybe<wr::NativeTileId> mCurrentId; + Maybe<RefPtr<IDCompositionSurface>> mCurrentSurface; + + // The EGL image that is bound to the D3D texture provided by + // DirectComposition. + EGLImage mEGLImage; + + // The GL render buffer ID that maps the EGLImage to an RBO for attaching to + // an FBO. + GLuint mColorRBO; struct SurfaceIdHashFn { std::size_t operator()(const wr::NativeSurfaceId& aId) const { return HashGeneric(wr::AsUint64(aId)); } }; std::unordered_map<wr::NativeSurfaceId, UniquePtr<DCSurface>, SurfaceIdHashFn> @@ -148,16 +171,26 @@ class DCSurface { struct TileKey { TileKey(int32_t aX, int32_t aY) : mX(aX), mY(aY) {} int32_t mX; int32_t mY; }; +#ifdef USE_VIRTUAL_SURFACES + wr::DeviceIntSize GetTileSize() const { return mTileSize; } + + IDCompositionVirtualSurface* GetCompositionSurface() const { + return mVirtualSurface; + } + + void UpdateAllocatedRect(); +#endif + protected: DCLayerTree* mDCLayerTree; struct TileKeyHashFn { std::size_t operator()(const TileKey& aId) const { return HashGeneric(aId.mX, aId.mY); } }; @@ -165,57 +198,49 @@ class DCSurface { // The visual for this surface. No content is attached to here, but tiles // that belong to this surface are added as children. In this way, we can // set the clip and scroll offset once, on this visual, to affect all // children. RefPtr<IDCompositionVisual2> mVisual; wr::DeviceIntSize mTileSize; bool mIsOpaque; + bool mAllocatedRectDirty; std::unordered_map<TileKey, UniquePtr<DCLayer>, TileKeyHashFn> mDCLayers; + +#ifdef USE_VIRTUAL_SURFACES + RefPtr<IDCompositionVirtualSurface> mVirtualSurface; +#endif }; /** Represents a tile within a surface. TODO(gw): We should probably rename this to DCTile as a follow up. */ class DCLayer { public: explicit DCLayer(DCLayerTree* aDCLayerTree); ~DCLayer(); bool Initialize(int aX, int aY, wr::DeviceIntSize aSize, bool aIsOpaque); - GLuint CreateEGLSurfaceForCompositionSurface(wr::DeviceIntRect aDirtyRect, - wr::DeviceIntPoint* aOffset); - void EndDraw(); +#ifndef USE_VIRTUAL_SURFACES IDCompositionSurface* GetCompositionSurface() const { return mCompositionSurface; } IDCompositionVisual2* GetVisual() const { return mVisual; } protected: RefPtr<IDCompositionSurface> CreateCompositionSurface(wr::DeviceIntSize aSize, bool aIsOpaque); - void DestroyEGLSurface(); - - DCLayerTree* mDCLayerTree; RefPtr<IDCompositionSurface> mCompositionSurface; - - // The EGL image that is bound to the D3D texture provided by - // DirectComposition. - EGLImage mEGLImage; + RefPtr<IDCompositionVisual2> mVisual; +#endif - // The GL render buffer ID that maps the EGLImage to an RBO for attaching to - // an FBO. - GLuint mColorRBO; - - LayoutDeviceIntSize mBufferSize; - - RefPtr<IDCompositionVisual2> mVisual; + DCLayerTree* mDCLayerTree; }; static inline bool operator==(const DCSurface::TileKey& a0, const DCSurface::TileKey& a1) { return a0.mX == a1.mX && a0.mY == a1.mY; } } // namespace wr
--- a/gfx/wr/example-compositor/compositor-windows/src/lib.cpp +++ b/gfx/wr/example-compositor/compositor-windows/src/lib.cpp @@ -22,30 +22,35 @@ #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) {} }; @@ -61,16 +66,19 @@ struct TileKeyHasher { }; 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; }; @@ -211,17 +219,18 @@ extern "C" { LPCWSTR name; if (enable_compositor) { name = L"example-compositor (DirectComposition)"; } else { name = L"example-compositor (Simple)"; } - window->hWnd = CreateWindow( + window->hWnd = CreateWindowEx( + WS_EX_NOREDIRECTIONBITMAP, CLASS_NAME, name, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, window_width, window_height, NULL, @@ -374,20 +383,22 @@ extern "C" { 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); @@ -475,16 +486,33 @@ extern "C" { 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 @@ -492,16 +520,17 @@ extern "C" { 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 @@ -522,16 +551,17 @@ extern "C" { 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, @@ -539,38 +569,44 @@ extern "C" { ) { 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, @@ -586,41 +622,61 @@ extern "C" { ) { 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]; - // Store the current surface for unbinding later - window->pCurrentSurface = tile.pSurface; - // 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 = tile.pSurface->BeginDraw( + 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; - assert(SUCCEEDED(hr)); 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, @@ -685,16 +741,20 @@ extern "C" { ) { 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;