diff --git a/gfx/webrender_bindings/DCLayerTree.cpp b/gfx/webrender_bindings/DCLayerTree.cpp index ac2e566837cb..18573595129b 100644 --- a/gfx/webrender_bindings/DCLayerTree.cpp +++ b/gfx/webrender_bindings/DCLayerTree.cpp @@ -50,11 +50,15 @@ DCLayerTree::DCLayerTree(gl::GLContext* aGL, EGLConfig 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); @@ -210,23 +214,34 @@ void DCLayerTree::CompositorEndFrame() { 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 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 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 surface = mCurrentSurface.ref(); + surface->EndDraw(); - layer->EndDraw(); - mCurrentId = Nothing(); + mCurrentSurface = Nothing(); } void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId, @@ -274,6 +289,13 @@ void DCLayerTree::AddSurface(wr::NativeSurfaceId aId, 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); @@ -336,7 +358,10 @@ GLuint DCLayerTree::GetOrCreateFbo(int aWidth, int aHeight) { 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() {} @@ -349,6 +374,20 @@ bool DCSurface::Initialize() { 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; } @@ -362,17 +401,53 @@ void DCSurface::CreateTile(int aX, int 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); @@ -380,18 +455,17 @@ DCLayer* DCSurface::GetLayer(int aX, int aY) const { 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)); @@ -415,10 +489,12 @@ bool DCLayer::Initialize(int aX, int aY, wr::DeviceIntSize aSize, // 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 DCLayer::CreateCompositionSurface( wr::DeviceIntSize aSize, bool aIsOpaque) { HRESULT hr; @@ -437,45 +513,38 @@ RefPtr DCLayer::CreateCompositionSurface( } 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 aCompositionSurface, + wr::DeviceIntPoint aSurfaceOffset) { + MOZ_ASSERT(aCompositionSurface.get()); HRESULT hr; - const auto gl = mDCLayerTree->GetGLContext(); + const auto gl = GetGLContext(); RefPtr backBuf; POINT offset; - LayoutDeviceIntRect dirtyRect(aDirtyRect.origin.x, aDirtyRect.origin.y, - aDirtyRect.size.width, 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(); + 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* 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); @@ -500,7 +569,7 @@ GLuint DCLayer::CreateEGLSurfaceForCompositionSurface( 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); @@ -518,8 +587,8 @@ GLuint DCLayer::CreateEGLSurfaceForCompositionSurface( return fboId; } -void DCLayer::DestroyEGLSurface() { - const auto gl = mDCLayerTree->GetGLContext(); +void DCLayerTree::DestroyEGLSurface() { + const auto gl = GetGLContext(); if (mColorRBO) { gl->fDeleteRenderbuffers(1, &mColorRBO); @@ -534,15 +603,5 @@ void DCLayer::DestroyEGLSurface() { } } -void DCLayer::EndDraw() { - MOZ_ASSERT(mCompositionSurface.get()); - if (!mCompositionSurface) { - return; - } - - mCompositionSurface->EndDraw(); - DestroyEGLSurface(); -} - } // namespace wr } // namespace mozilla diff --git a/gfx/webrender_bindings/DCLayerTree.h b/gfx/webrender_bindings/DCLayerTree.h index a88cd9ba2fa7..901bb760be61 100644 --- a/gfx/webrender_bindings/DCLayerTree.h +++ b/gfx/webrender_bindings/DCLayerTree.h @@ -23,6 +23,7 @@ struct IDCompositionSurface; struct IDCompositionTarget; struct IDCompositionVisual2; struct IDXGISwapChain1; +struct IDCompositionVirtualSurface; namespace mozilla { @@ -32,6 +33,15 @@ 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; @@ -81,6 +91,11 @@ class DCLayerTree { bool Initialize(HWND aHwnd); bool MaybeUpdateDebugCounter(); bool MaybeUpdateDebugVisualRedrawRegions(); + void DestroyEGLSurface(); + GLuint CreateEGLSurfaceForCompositionSurface( + wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset, + RefPtr aCompositionSurface, + wr::DeviceIntPoint aSurfaceOffset); RefPtr mGL; EGLConfig mEGLConfig; @@ -95,7 +110,15 @@ class DCLayerTree { bool mDebugCounter; bool mDebugVisualRedrawRegions; - Maybe mCurrentId; + Maybe> 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 { @@ -153,6 +176,16 @@ class DCSurface { int32_t mY; }; +#ifdef USE_VIRTUAL_SURFACES + wr::DeviceIntSize GetTileSize() const { return mTileSize; } + + IDCompositionVirtualSurface* GetCompositionSurface() const { + return mVirtualSurface; + } + + void UpdateAllocatedRect(); +#endif + protected: DCLayerTree* mDCLayerTree; @@ -170,7 +203,12 @@ class DCSurface { wr::DeviceIntSize mTileSize; bool mIsOpaque; + bool mAllocatedRectDirty; std::unordered_map, TileKeyHashFn> mDCLayers; + +#ifdef USE_VIRTUAL_SURFACES + RefPtr mVirtualSurface; +#endif }; /** @@ -182,10 +220,8 @@ class DCLayer { 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; } @@ -194,23 +230,12 @@ class DCLayer { protected: RefPtr CreateCompositionSurface(wr::DeviceIntSize aSize, bool aIsOpaque); - void DestroyEGLSurface(); - - DCLayerTree* mDCLayerTree; RefPtr mCompositionSurface; - - // 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; - - LayoutDeviceIntSize mBufferSize; - RefPtr mVisual; +#endif + + DCLayerTree* mDCLayerTree; }; static inline bool operator==(const DCSurface::TileKey& a0, diff --git a/gfx/wr/example-compositor/compositor-windows/src/lib.cpp b/gfx/wr/example-compositor/compositor-windows/src/lib.cpp index 28ae7a2d0dc1..600d25c36936 100644 --- a/gfx/wr/example-compositor/compositor-windows/src/lib.cpp +++ b/gfx/wr/example-compositor/compositor-windows/src/lib.cpp @@ -27,6 +27,9 @@ #define NUM_QUERIES 2 +#define USE_VIRTUAL_SURFACES +#define VIRTUAL_OFFSET 512 * 1024 + enum SyncMode { None = 0, Swap = 1, @@ -37,10 +40,12 @@ enum SyncMode { // 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 { @@ -66,6 +71,9 @@ struct Surface { bool is_opaque; std::unordered_map tiles; IDCompositionVisual2 *pVisual; +#ifdef USE_VIRTUAL_SURFACES + IDCompositionVirtualSurface *pVirtualSurface; +#endif }; struct CachedFrameBuffer { @@ -216,7 +224,8 @@ extern "C" { name = L"example-compositor (Simple)"; } - window->hWnd = CreateWindow( + window->hWnd = CreateWindowEx( + WS_EX_NOREDIRECTIONBITMAP, CLASS_NAME, name, WS_OVERLAPPEDWINDOW, @@ -379,10 +388,12 @@ extern "C" { 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(); } @@ -480,6 +491,23 @@ extern "C" { 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; } @@ -497,6 +525,7 @@ extern "C" { 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( @@ -527,6 +556,7 @@ extern "C" { FALSE, NULL ); +#endif surface.tiles[key] = tile; } @@ -544,10 +574,12 @@ extern "C" { 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); } @@ -561,11 +593,15 @@ extern "C" { 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); @@ -591,9 +627,6 @@ extern "C" { 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. @@ -605,17 +638,40 @@ extern "C" { 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; @@ -690,6 +746,10 @@ extern "C" { // 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);