| author | Glenn Watson <git@intuitionlibrary.com> |
| Tue, 17 Dec 2019 21:44:03 +0000 | |
| changeset 507464 | c81cf0c409bcaea6a6e6299cb2f421e4528e023b |
| parent 507463 | 35af0b925215124a619bc054aeeb8d01e99b4e63 |
| child 507465 | 666ed2796ff392bb355d71f9f139d9ea5f0aadf7 |
| push id | 36928 |
| push user | opoprus@mozilla.com |
| push date | Wed, 18 Dec 2019 09:16:14 +0000 |
| treeherder | mozilla-central@e928d6001344 [default view] [failures only] |
| perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
| reviewers | mstange, sotaro |
| bugs | 1604383 |
| milestone | 73.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 @@ -99,16 +99,22 @@ bool DCLayerTree::Initialize(HWND aHwnd) // By default, a visual inherits the interpolation mode of the parent visual. // If no visuals set the interpolation mode, the default for the entire visual // tree is nearest neighbor interpolation. mRootVisual->SetBitmapInterpolationMode( DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR); return true; } +DCSurface* DCLayerTree::GetSurface(wr::NativeSurfaceId aId) const { + auto surface_it = mDCSurfaces.find(aId); + MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end()); + return surface_it->second.get(); +} + void DCLayerTree::SetDefaultSwapChain(IDXGISwapChain1* aSwapChain) { mRootVisual->AddVisual(mDefaultSwapChainVisual, TRUE, nullptr); mDefaultSwapChainVisual->SetContent(aSwapChain); // Default SwapChain's visual does not need linear interporation. mDefaultSwapChainVisual->SetBitmapInterpolationMode( DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR); mCompositionDevice->Commit(); } @@ -171,122 +177,124 @@ bool DCLayerTree::MaybeUpdateDebugVisual mDebugVisualRedrawRegions = debugVisualRedrawRegions; return true; } void DCLayerTree::CompositorBeginFrame() {} void DCLayerTree::CompositorEndFrame() { + // Check if the visual tree of surfaces is the same as last frame. bool same = mPrevLayers == mCurrentLayers; if (!same) { + // If not, we need to rebuild the visual tree. Note that addition or + // removal of tiles no longer needs to rebuild the main visual tree + // here, since they are added as children of the surface visual. mRootVisual->RemoveAllVisuals(); + // Add surfaces in z-order they were added to the scene. for (auto it = mCurrentLayers.begin(); it != mCurrentLayers.end(); ++it) { - auto layer_it = mDCLayers.find(*it); - MOZ_ASSERT(layer_it != mDCLayers.end()); - const auto layer = layer_it->second.get(); - const auto visual = layer->GetVisual(); + auto surface_it = mDCSurfaces.find(*it); + MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end()); + const auto surface = surface_it->second.get(); + const auto visual = surface->GetVisual(); mRootVisual->AddVisual(visual, FALSE, nullptr); } } mPrevLayers.swap(mCurrentLayers); mCurrentLayers.clear(); mCompositionDevice->Commit(); } -void DCLayerTree::Bind(wr::NativeSurfaceId aId, wr::DeviceIntPoint* aOffset, +void DCLayerTree::Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId, wr::DeviceIntRect aDirtyRect) { - auto it = mDCLayers.find(wr::AsUint64(aId)); - MOZ_ASSERT(it != mDCLayers.end()); - if (it == mDCLayers.end()) { - gfxCriticalNote << "Failed to get DCLayer for bind: " << wr::AsUint64(aId); - return; - } - - const auto layer = it->second.get(); + auto surface = GetSurface(aId.surface_id); + auto layer = surface->GetLayer(aId.x, aId.y); *aFboId = layer->CreateEGLSurfaceForCompositionSurface(aDirtyRect, aOffset); - mCurrentId = Some(aId); } void DCLayerTree::Unbind() { if (mCurrentId.isNothing()) { return; } - auto it = mDCLayers.find(wr::AsUint64(mCurrentId.ref())); - MOZ_RELEASE_ASSERT(it != mDCLayers.end()); - - const auto layer = it->second.get(); + const auto id = mCurrentId.ref(); + auto surface = GetSurface(id.surface_id); + auto layer = surface->GetLayer(id.x, id.y); layer->EndDraw(); mCurrentId = Nothing(); } void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId, - wr::DeviceIntSize aSize, bool aIsOpaque) { - auto it = mDCLayers.find(wr::AsUint64(aId)); - MOZ_RELEASE_ASSERT(it == mDCLayers.end()); - MOZ_ASSERT(it == mDCLayers.end()); - if (it != mDCLayers.end()) { - // DCLayer already exists. + wr::DeviceIntSize aTileSize) { + auto it = mDCSurfaces.find(aId); + MOZ_RELEASE_ASSERT(it == mDCSurfaces.end()); + if (it != mDCSurfaces.end()) { + // DCSurface already exists. + return; + } + + auto surface = MakeUnique<DCSurface>(aTileSize, this); + if (!surface->Initialize()) { + gfxCriticalNote << "Failed to initialize DCSurface: " << wr::AsUint64(aId); return; } - auto layer = MakeUnique<DCLayer>(this); - if (!layer->Initialize(aSize, aIsOpaque)) { - gfxCriticalNote << "Failed to initialize DCLayer: " << wr::AsUint64(aId); - return; - } - - mDCLayers[wr::AsUint64(aId)] = std::move(layer); + mDCSurfaces[aId] = std::move(surface); } void DCLayerTree::DestroySurface(NativeSurfaceId aId) { - auto it = mDCLayers.find(wr::AsUint64(aId)); - MOZ_ASSERT(it != mDCLayers.end()); - if (it == mDCLayers.end()) { - return; - } - mDCLayers.erase(it); + auto surface_it = mDCSurfaces.find(aId); + MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end()); + auto surface = surface_it->second.get(); + + mRootVisual->RemoveVisual(surface->GetVisual()); + mDCSurfaces.erase(surface_it); +} + +void DCLayerTree::CreateTile(wr::NativeSurfaceId aId, int aX, int aY, + bool aIsOpaque) { + auto surface = GetSurface(aId); + surface->CreateTile(aX, aY, aIsOpaque); +} + +void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId, int aX, int aY) { + auto surface = GetSurface(aId); + surface->DestroyTile(aX, aY); } void DCLayerTree::AddSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aPosition, wr::DeviceIntRect aClipRect) { - auto it = mDCLayers.find(wr::AsUint64(aId)); - MOZ_ASSERT(it != mDCLayers.end()); - if (it == mDCLayers.end()) { - return; - } + auto it = mDCSurfaces.find(aId); + MOZ_RELEASE_ASSERT(it != mDCSurfaces.end()); const auto layer = it->second.get(); const auto visual = layer->GetVisual(); // Place the visual - this changes frame to frame based on scroll position // of the slice. - int offset_x = aPosition.x; - int offset_y = aPosition.y; - visual->SetOffsetX(offset_x); - visual->SetOffsetY(offset_y); + 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; - clip_rect.left = aClipRect.origin.x - offset_x; - clip_rect.top = aClipRect.origin.y - offset_y; + clip_rect.left = aClipRect.origin.x - aPosition.x; + clip_rect.top = aClipRect.origin.y - aPosition.y; clip_rect.right = clip_rect.left + aClipRect.size.width; clip_rect.bottom = clip_rect.top + aClipRect.size.height; visual->SetClip(clip_rect); - mCurrentLayers.push_back(wr::AsUint64(aId)); + mCurrentLayers.push_back(aId); } GLuint DCLayerTree::GetOrCreateFbo(int aWidth, int aHeight) { const auto gl = GetGLContext(); GLuint fboId = 0; // Check if we have a cached FBO with matching dimensions for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) { @@ -322,22 +330,68 @@ GLuint DCLayerTree::GetOrCreateFbo(int a frame_buffer_info.fboId = fboId; frame_buffer_info.depthRboId = depthRboId; mFrameBuffers.push_back(frame_buffer_info); } return fboId; } +DCSurface::DCSurface(wr::DeviceIntSize aTileSize, DCLayerTree* aDCLayerTree) + : mDCLayerTree(aDCLayerTree), mTileSize(aTileSize) {} + +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; + } + + return true; +} + +void DCSurface::CreateTile(int aX, int aY, bool aIsOpaque) { + TileKey key(aX, aY); + MOZ_RELEASE_ASSERT(mDCLayers.find(key) == mDCLayers.end()); + + auto layer = MakeUnique<DCLayer>(mDCLayerTree); + if (!layer->Initialize(aX, aY, mTileSize, aIsOpaque)) { + gfxCriticalNote << "Failed to initialize DCLayer: " << aX << aY; + return; + } + + mVisual->AddVisual(layer->GetVisual(), FALSE, NULL); + mDCLayers[key] = std::move(layer); +} + +void DCSurface::DestroyTile(int aX, int aY) { + TileKey key(aX, aY); + auto layer = GetLayer(aX, aY); + mVisual->RemoveVisual(layer->GetVisual()); + mDCLayers.erase(key); +} + +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() { DestroyEGLSurface(); } -bool DCLayer::Initialize(wr::DeviceIntSize aSize, bool aIsOpaque) { +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); HRESULT hr; const auto dCompDevice = mDCLayerTree->GetCompositionDevice(); hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual)); @@ -352,16 +406,21 @@ bool DCLayer::Initialize(wr::DeviceIntSi } hr = mVisual->SetContent(mCompositionSurface); if (FAILED(hr)) { 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); + return true; } RefPtr<IDCompositionSurface> DCLayer::CreateCompositionSurface( wr::DeviceIntSize aSize, bool aIsOpaque) { HRESULT hr; const auto dCompDevice = mDCLayerTree->GetCompositionDevice(); const auto alphaMode =
--- a/gfx/webrender_bindings/DCLayerTree.h +++ b/gfx/webrender_bindings/DCLayerTree.h @@ -28,16 +28,17 @@ namespace mozilla { namespace gl { class GLContext; } namespace wr { class DCLayer; +class DCSurface; /** * DCLayerTree manages direct composition layers. * It does not manage gecko's layers::Layer. */ class DCLayerTree { public: static UniquePtr<DCLayerTree> Create(gl::GLContext* aGL, EGLConfig aEGLConfig, @@ -49,31 +50,34 @@ class DCLayerTree { void SetDefaultSwapChain(IDXGISwapChain1* aSwapChain); void MaybeUpdateDebug(); void WaitForCommitCompletion(); // Interface for wr::Compositor void CompositorBeginFrame(); void CompositorEndFrame(); - void Bind(wr::NativeSurfaceId aId, wr::DeviceIntPoint* aOffset, - uint32_t* aFboId, wr::DeviceIntRect aDirtyRect); + void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId, + wr::DeviceIntRect aDirtyRect); void Unbind(); - void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntSize aSize, - bool aIsOpaque); + void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntSize aTileSize); void DestroySurface(NativeSurfaceId aId); + void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY, + bool aIsOpaque); + void DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY); void AddSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aPosition, wr::DeviceIntRect aClipRect); gl::GLContext* GetGLContext() const { return mGL; } EGLConfig GetEGLConfig() const { return mEGLConfig; } ID3D11Device* GetDevice() const { return mDevice; } IDCompositionDevice2* GetCompositionDevice() const { return mCompositionDevice; } + DCSurface* GetSurface(wr::NativeSurfaceId aId) const; // 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(); @@ -86,60 +90,115 @@ class DCLayerTree { RefPtr<IDCompositionDevice2> mCompositionDevice; RefPtr<IDCompositionTarget> mCompositionTarget; RefPtr<IDCompositionVisual2> mRootVisual; RefPtr<IDCompositionVisual2> mDefaultSwapChainVisual; bool mDebugCounter; bool mDebugVisualRedrawRegions; - Maybe<wr::NativeSurfaceId> mCurrentId; + Maybe<wr::NativeTileId> mCurrentId; - std::unordered_map<uint64_t, UniquePtr<DCLayer>> mDCLayers; + struct SurfaceIdHashFn { + std::size_t operator()(const wr::NativeSurfaceId& aId) const { + return HashGeneric(wr::AsUint64(aId)); + } + }; + + std::unordered_map<wr::NativeSurfaceId, UniquePtr<DCSurface>, SurfaceIdHashFn> + mDCSurfaces; // A list of layer IDs as they are added to the visual tree this frame. - std::vector<uint64_t> mCurrentLayers; + std::vector<wr::NativeSurfaceId> mCurrentLayers; // The previous frame's list of layer IDs in visual order. - std::vector<uint64_t> mPrevLayers; + std::vector<wr::NativeSurfaceId> mPrevLayers; // Information about a cached FBO that is retained between frames. struct CachedFrameBuffer { int width; int height; GLuint fboId; GLuint depthRboId; }; // A cache of FBOs, containing a depth buffer allocated to a specific size. // TODO(gw): Might be faster as a hashmap? The length is typically much less // than 10. std::vector<CachedFrameBuffer> mFrameBuffers; }; +/** + Represents a single picture cache slice. Each surface contains some + number of tiles. An implementation may choose to allocate individual + tiles to render in to (as the current impl does), or allocate a large + single virtual surface to draw into (e.g. the DirectComposition virtual + surface API in future). + */ +class DCSurface { + public: + explicit DCSurface(wr::DeviceIntSize aTileSize, DCLayerTree* aDCLayerTree); + ~DCSurface(); + + bool Initialize(); + void CreateTile(int32_t aX, int32_t aY, bool aIsOpaque); + void DestroyTile(int32_t aX, int32_t aY); + + IDCompositionVisual2* GetVisual() const { return mVisual; } + DCLayer* GetLayer(int32_t aX, int32_t aY) const; + + struct TileKey { + TileKey(int32_t aX, int32_t aY) : mX(aX), mY(aY) {} + + int32_t mX; + int32_t mY; + }; + + protected: + DCLayerTree* mDCLayerTree; + + struct TileKeyHashFn { + std::size_t operator()(const TileKey& aId) const { + return HashGeneric(aId.mX, aId.mY); + } + }; + + // 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; + std::unordered_map<TileKey, UniquePtr<DCLayer>, TileKeyHashFn> mDCLayers; +}; + +/** + 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(wr::DeviceIntSize aSize, bool aIsOpaque); + bool Initialize(int aX, int aY, wr::DeviceIntSize aSize, bool aIsOpaque); GLuint CreateEGLSurfaceForCompositionSurface(wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset); void EndDraw(); IDCompositionSurface* GetCompositionSurface() const { return mCompositionSurface; } IDCompositionVisual2* GetVisual() const { return mVisual; } protected: RefPtr<IDCompositionSurface> CreateCompositionSurface(wr::DeviceIntSize aSize, bool aIsOpaque); void DestroyEGLSurface(); - protected: DCLayerTree* mDCLayerTree; RefPtr<IDCompositionSurface> mCompositionSurface; // The EGL image that is bound to the D3D texture provided by // DirectComposition. EGLImage mEGLImage; @@ -147,12 +206,17 @@ class DCLayer { // an FBO. GLuint mColorRBO; LayoutDeviceIntSize mBufferSize; RefPtr<IDCompositionVisual2> mVisual; }; +static inline bool operator==(const DCSurface::TileKey& a0, + const DCSurface::TileKey& a1) { + return a0.mX == a1.mX && a0.mY == a1.mY; +} + } // namespace wr } // namespace mozilla #endif
--- a/gfx/webrender_bindings/RenderCompositor.cpp +++ b/gfx/webrender_bindings/RenderCompositor.cpp @@ -30,27 +30,39 @@ void wr_compositor_add_surface(void* aCo compositor->AddSurface(aId, aPosition, aClipRect); } void wr_compositor_begin_frame(void* aCompositor) { RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor); compositor->CompositorBeginFrame(); } -void wr_compositor_bind(void* aCompositor, wr::NativeSurfaceId aId, +void wr_compositor_bind(void* aCompositor, wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId, wr::DeviceIntRect aDirtyRect) { RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor); compositor->Bind(aId, aOffset, aFboId, aDirtyRect); } void wr_compositor_create_surface(void* aCompositor, wr::NativeSurfaceId aId, - wr::DeviceIntSize aSize, bool aIsOpaque) { + wr::DeviceIntSize aTileSize) { + RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor); + compositor->CreateSurface(aId, aTileSize); +} + +void wr_compositor_create_tile(void* aCompositor, wr::NativeSurfaceId aId, + int32_t aX, int32_t aY, bool aIsOpaque) { RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor); - compositor->CreateSurface(aId, aSize, aIsOpaque); + compositor->CreateTile(aId, aX, aY, aIsOpaque); +} + +void wr_compositor_destroy_tile(void* aCompositor, wr::NativeSurfaceId aId, + int32_t aX, int32_t aY) { + RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor); + compositor->DestroyTile(aId, aX, aY); } void wr_compositor_destroy_surface(void* aCompositor, NativeSurfaceId aId) { RenderCompositor* compositor = static_cast<RenderCompositor*>(aCompositor); compositor->DestroySurface(aId); } void wr_compositor_end_frame(void* aCompositor) {
--- a/gfx/webrender_bindings/RenderCompositor.h +++ b/gfx/webrender_bindings/RenderCompositor.h @@ -80,22 +80,25 @@ class RenderCompositor { virtual bool IsContextLost(); virtual bool ShouldUseNativeCompositor() { return false; } virtual uint32_t GetMaxUpdateRects() { return 0; } // Interface for wr::Compositor virtual void CompositorBeginFrame() {} virtual void CompositorEndFrame() {} - virtual void Bind(wr::NativeSurfaceId aId, wr::DeviceIntPoint* aOffset, + virtual void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId, wr::DeviceIntRect aDirtyRect) {} virtual void Unbind() {} - virtual void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntSize aSize, - bool aIsOpaque) {} + virtual void CreateSurface(wr::NativeSurfaceId aId, + wr::DeviceIntSize aTileSize) {} virtual void DestroySurface(NativeSurfaceId aId) {} + virtual void CreateTile(wr::NativeSurfaceId, int32_t aX, int32_t aY, + bool aIsOpaque) {} + virtual void DestroyTile(wr::NativeSurfaceId, int32_t aX, int32_t aY) {} virtual void AddSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aPosition, wr::DeviceIntRect aClipRect) {} // Interface for partial present virtual bool UsePartialPresent() { return false; } virtual bool RequestFullRender() { return false; } virtual uint32_t GetMaxPartialPresentRects() { return 0; }
--- a/gfx/webrender_bindings/RenderCompositorANGLE.cpp +++ b/gfx/webrender_bindings/RenderCompositorANGLE.cpp @@ -745,34 +745,43 @@ uint32_t RenderCompositorANGLE::GetMaxUp void RenderCompositorANGLE::CompositorBeginFrame() { mDCLayerTree->CompositorBeginFrame(); } void RenderCompositorANGLE::CompositorEndFrame() { mDCLayerTree->CompositorEndFrame(); } -void RenderCompositorANGLE::Bind(wr::NativeSurfaceId aId, +void RenderCompositorANGLE::Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId, wr::DeviceIntRect aDirtyRect) { mDCLayerTree->Bind(aId, aOffset, aFboId, aDirtyRect); } void RenderCompositorANGLE::Unbind() { mDCLayerTree->Unbind(); } void RenderCompositorANGLE::CreateSurface(wr::NativeSurfaceId aId, - wr::DeviceIntSize aSize, - bool aIsOpaque) { - mDCLayerTree->CreateSurface(aId, aSize, aIsOpaque); + wr::DeviceIntSize aTileSize) { + mDCLayerTree->CreateSurface(aId, aTileSize); } void RenderCompositorANGLE::DestroySurface(NativeSurfaceId aId) { mDCLayerTree->DestroySurface(aId); } +void RenderCompositorANGLE::CreateTile(wr::NativeSurfaceId aId, int aX, int aY, + bool aIsOpaque) { + mDCLayerTree->CreateTile(aId, aX, aY, aIsOpaque); +} + +void RenderCompositorANGLE::DestroyTile(wr::NativeSurfaceId aId, int aX, + int aY) { + mDCLayerTree->DestroyTile(aId, aX, aY); +} + void RenderCompositorANGLE::AddSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aPosition, wr::DeviceIntRect aClipRect) { mDCLayerTree->AddSurface(aId, aPosition, aClipRect); } void RenderCompositorANGLE::InitializeUsePartialPresent() { if (UseCompositor() || !mSwapChain1 ||
--- a/gfx/webrender_bindings/RenderCompositorANGLE.h +++ b/gfx/webrender_bindings/RenderCompositorANGLE.h @@ -65,22 +65,25 @@ class RenderCompositorANGLE : public Ren bool SurfaceOriginIsTopLeft() override { return true; } bool ShouldUseNativeCompositor() override; uint32_t GetMaxUpdateRects() override; // Interface for wr::Compositor void CompositorBeginFrame() override; void CompositorEndFrame() override; - void Bind(wr::NativeSurfaceId aId, wr::DeviceIntPoint* aOffset, - uint32_t* aFboId, wr::DeviceIntRect aDirtyRect) override; + void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId, + wr::DeviceIntRect aDirtyRect) override; void Unbind() override; - void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntSize aSize, - bool aIsOpaque) override; + void CreateSurface(wr::NativeSurfaceId aId, + wr::DeviceIntSize aTileSize) override; void DestroySurface(NativeSurfaceId aId) override; + void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY, + bool aIsOpaque) override; + void DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) override; void AddSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aPosition, wr::DeviceIntRect aClipRect) override; // Interface for partial present bool UsePartialPresent() override; bool RequestFullRender() override; uint32_t GetMaxPartialPresentRects() override;
--- a/gfx/webrender_bindings/RenderCompositorOGL.cpp +++ b/gfx/webrender_bindings/RenderCompositorOGL.cpp @@ -171,43 +171,51 @@ void RenderCompositorOGL::CompositorBegi mBeginFrameTimeStamp = TimeStamp::NowUnfuzzed(); } void RenderCompositorOGL::CompositorEndFrame() { #ifdef MOZ_GECKO_PROFILER if (profiler_thread_is_being_profiled()) { auto bufferSize = GetBufferSize(); uint64_t windowPixelCount = uint64_t(bufferSize.width) * bufferSize.height; + int nativeLayerCount = 0; + for (const auto& it : mSurfaces) { + nativeLayerCount += int(it.second.mNativeLayers.size()); + } profiler_add_text_marker( "WR OS Compositor frame", nsPrintfCString("%d%% painting, %d%% overdraw, %d used " "layers (%d%% memory) + %d unused layers (%d%% memory)", int(mDrawnPixelCount * 100 / windowPixelCount), int(mAddedClippedPixelCount * 100 / windowPixelCount), int(mAddedLayers.Length()), int(mAddedPixelCount * 100 / windowPixelCount), - int(mNativeLayers.size() - mAddedLayers.Length()), + int(nativeLayerCount - mAddedLayers.Length()), int((mTotalPixelCount - mAddedPixelCount) * 100 / windowPixelCount)), JS::ProfilingCategoryPair::GRAPHICS, mBeginFrameTimeStamp, TimeStamp::NowUnfuzzed()); } #endif mDrawnPixelCount = 0; mNativeLayerRoot->SetLayers(mAddedLayers); } -void RenderCompositorOGL::Bind(wr::NativeSurfaceId aId, +void RenderCompositorOGL::Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId, wr::DeviceIntRect aDirtyRect) { MOZ_RELEASE_ASSERT(!mCurrentlyBoundNativeLayer); - auto layerCursor = mNativeLayers.find(wr::AsUint64(aId)); - MOZ_RELEASE_ASSERT(layerCursor != mNativeLayers.end()); + auto surfaceCursor = mSurfaces.find(aId.surface_id); + MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end()); + Surface& surface = surfaceCursor->second; + + auto layerCursor = surface.mNativeLayers.find(TileKey(aId.x, aId.y)); + MOZ_RELEASE_ASSERT(layerCursor != surface.mNativeLayers.end()); RefPtr<layers::NativeLayer> layer = layerCursor->second; gfx::IntRect dirtyRect(aDirtyRect.origin.x, aDirtyRect.origin.y, aDirtyRect.size.width, aDirtyRect.size.height); Maybe<GLuint> fbo = layer->NextSurfaceAsFramebuffer(dirtyRect, true); MOZ_RELEASE_ASSERT(fbo); // TODO: make fallible mCurrentlyBoundNativeLayer = layer; @@ -222,52 +230,75 @@ void RenderCompositorOGL::Unbind() { MOZ_RELEASE_ASSERT(mCurrentlyBoundNativeLayer); mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0); mCurrentlyBoundNativeLayer->NotifySurfaceReady(); mCurrentlyBoundNativeLayer = nullptr; } void RenderCompositorOGL::CreateSurface(wr::NativeSurfaceId aId, - wr::DeviceIntSize aSize, - bool aIsOpaque) { + wr::DeviceIntSize aTileSize) { + Surface surface(aTileSize); + mSurfaces.insert({aId, surface}); +} + +void RenderCompositorOGL::DestroySurface(NativeSurfaceId aId) { + mSurfaces.erase(aId); +} + +void RenderCompositorOGL::CreateTile(wr::NativeSurfaceId aId, int aX, int aY, + bool aIsOpaque) { + auto surfaceCursor = mSurfaces.find(aId); + MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end()); + Surface& surface = surfaceCursor->second; + RefPtr<layers::NativeLayer> layer = mNativeLayerRoot->CreateLayer( - IntSize(aSize.width, aSize.height), aIsOpaque); + IntSize(surface.mTileSize.width, surface.mTileSize.height), aIsOpaque); layer->SetGLContext(mGL); - mNativeLayers.insert({wr::AsUint64(aId), layer}); + surface.mNativeLayers.insert({TileKey(aX, aY), layer}); mTotalPixelCount += gfx::IntRect({}, layer->GetSize()).Area(); } -void RenderCompositorOGL::DestroySurface(NativeSurfaceId aId) { - auto layerCursor = mNativeLayers.find(wr::AsUint64(aId)); - MOZ_RELEASE_ASSERT(layerCursor != mNativeLayers.end()); +void RenderCompositorOGL::DestroyTile(wr::NativeSurfaceId aId, int aX, int aY) { + auto surfaceCursor = mSurfaces.find(aId); + MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end()); + Surface& surface = surfaceCursor->second; + + auto layerCursor = surface.mNativeLayers.find(TileKey(aX, aY)); + MOZ_RELEASE_ASSERT(layerCursor != surface.mNativeLayers.end()); RefPtr<layers::NativeLayer> layer = std::move(layerCursor->second); - mNativeLayers.erase(layerCursor); + surface.mNativeLayers.erase(layerCursor); mTotalPixelCount -= gfx::IntRect({}, layer->GetSize()).Area(); // If the layer is currently present in mNativeLayerRoot, it will be destroyed // once CompositorEndFrame() replaces mNativeLayerRoot's layers and drops that // reference. } void RenderCompositorOGL::AddSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aPosition, wr::DeviceIntRect aClipRect) { MOZ_RELEASE_ASSERT(!mCurrentlyBoundNativeLayer); - auto layerCursor = mNativeLayers.find(wr::AsUint64(aId)); - MOZ_RELEASE_ASSERT(layerCursor != mNativeLayers.end()); - RefPtr<layers::NativeLayer> layer = layerCursor->second; + auto surfaceCursor = mSurfaces.find(aId); + MOZ_RELEASE_ASSERT(surfaceCursor != mSurfaces.end()); + const Surface& surface = surfaceCursor->second; - gfx::IntSize layerSize = layer->GetSize(); - gfx::IntRect layerRect(aPosition.x, aPosition.y, layerSize.width, - layerSize.height); - gfx::IntRect clipRect(aClipRect.origin.x, aClipRect.origin.y, - aClipRect.size.width, aClipRect.size.height); - layer->SetPosition(layerRect.TopLeft()); - layer->SetClipRect(Some(clipRect)); - mAddedLayers.AppendElement(layer); + for (auto it = surface.mNativeLayers.begin(); + it != surface.mNativeLayers.end(); ++it) { + RefPtr<layers::NativeLayer> layer = it->second; + gfx::IntSize layerSize = layer->GetSize(); + gfx::IntRect layerRect( + aPosition.x + surface.mTileSize.width * it->first.mX, + aPosition.y + surface.mTileSize.height * it->first.mY, layerSize.width, + layerSize.height); + gfx::IntRect clipRect(aClipRect.origin.x, aClipRect.origin.y, + aClipRect.size.width, aClipRect.size.height); + layer->SetPosition(layerRect.TopLeft()); + layer->SetClipRect(Some(clipRect)); + mAddedLayers.AppendElement(layer); - mAddedPixelCount += layerRect.Area(); - mAddedClippedPixelCount += clipRect.Intersect(layerRect).Area(); + mAddedPixelCount += layerRect.Area(); + mAddedClippedPixelCount += clipRect.Intersect(layerRect).Area(); + } } } // namespace wr } // namespace mozilla
--- a/gfx/webrender_bindings/RenderCompositorOGL.h +++ b/gfx/webrender_bindings/RenderCompositorOGL.h @@ -41,46 +41,82 @@ class RenderCompositorOGL : public Rende LayoutDeviceIntSize GetBufferSize() override; bool ShouldUseNativeCompositor() override; uint32_t GetMaxUpdateRects() override; // Interface for wr::Compositor void CompositorBeginFrame() override; void CompositorEndFrame() override; - void Bind(wr::NativeSurfaceId aId, wr::DeviceIntPoint* aOffset, - uint32_t* aFboId, wr::DeviceIntRect aDirtyRect) override; + void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId, + wr::DeviceIntRect aDirtyRect) override; void Unbind() override; - void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntSize aSize, - bool aIsOpaque) override; + void CreateSurface(wr::NativeSurfaceId aId, + wr::DeviceIntSize aTileSize) override; void DestroySurface(NativeSurfaceId aId) override; + void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY, + bool aIsOpaque) override; + void DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) override; void AddSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aPosition, wr::DeviceIntRect aClipRect) override; + struct TileKey { + TileKey(int32_t aX, int32_t aY) : mX(aX), mY(aY) {} + + int32_t mX; + int32_t mY; + }; + protected: void InsertFrameDoneSync(); RefPtr<gl::GLContext> mGL; // Can be null. RefPtr<layers::NativeLayerRoot> mNativeLayerRoot; RefPtr<layers::NativeLayer> mNativeLayerForEntireWindow; + struct TileKeyHashFn { + std::size_t operator()(const TileKey& aId) const { + return HashGeneric(aId.mX, aId.mY); + } + }; + + class Surface { + public: + explicit Surface(wr::DeviceIntSize aTileSize) : mTileSize(aTileSize) {} + + wr::DeviceIntSize mTileSize; + std::unordered_map<TileKey, RefPtr<layers::NativeLayer>, TileKeyHashFn> + mNativeLayers; + }; + + struct SurfaceIdHashFn { + std::size_t operator()(const wr::NativeSurfaceId& aId) const { + return HashGeneric(wr::AsUint64(aId)); + } + }; + // Used in native compositor mode: RefPtr<layers::NativeLayer> mCurrentlyBoundNativeLayer; nsTArray<RefPtr<layers::NativeLayer>> mAddedLayers; uint64_t mTotalPixelCount = 0; uint64_t mAddedPixelCount = 0; uint64_t mAddedClippedPixelCount = 0; uint64_t mDrawnPixelCount = 0; gfx::IntRect mVisibleBounds; - std::unordered_map<uint64_t, RefPtr<layers::NativeLayer>> mNativeLayers; + std::unordered_map<wr::NativeSurfaceId, Surface, SurfaceIdHashFn> mSurfaces; TimeStamp mBeginFrameTimeStamp; // Used to apply back-pressure in WaitForGPU(). GLsync mPreviousFrameDoneSync; GLsync mThisFrameDoneSync; }; +static inline bool operator==(const RenderCompositorOGL::TileKey& a0, + const RenderCompositorOGL::TileKey& a1) { + return a0.mX == a1.mX && a0.mY == a1.mY; +} + } // namespace wr } // namespace mozilla #endif
--- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -28,17 +28,17 @@ use std::os::raw::{c_int}; use std::time::Duration; use gleam::gl; use webrender::{ api::*, api::units::*, ApiRecordingReceiver, AsyncPropertySampler, AsyncScreenshotHandle, BinaryRecorder, Compositor, DebugFlags, Device, NativeSurfaceId, PipelineInfo, ProfilerHooks, RecordedFrameHandle, Renderer, RendererOptions, RendererStats, SceneBuilderHooks, ShaderPrecacheFlags, Shaders, ThreadListener, UploadMethod, VertexUsageHint, - WrShaders, set_profiler_hooks, CompositorConfig, NativeSurfaceInfo + WrShaders, set_profiler_hooks, CompositorConfig, NativeSurfaceInfo, NativeTileId }; use thread_profiler::register_thread_with_profiler; use moz2d_renderer::Moz2dBlobImageHandler; use program_cache::{WrProgramCache, remove_disk_cache}; use rayon; use num_cpus; use euclid::SideOffsets2D; use nsstring::nsAString; @@ -1231,26 +1231,38 @@ fn wr_device_new(gl_context: *mut c_void false, ) } extern "C" { fn wr_compositor_create_surface( compositor: *mut c_void, id: NativeSurfaceId, - size: DeviceIntSize, - is_opaque: bool, + tile_size: DeviceIntSize, ); fn wr_compositor_destroy_surface( compositor: *mut c_void, id: NativeSurfaceId, ); + fn wr_compositor_create_tile( + compositor: *mut c_void, + id: NativeSurfaceId, + x: i32, + y: i32, + is_opaque: bool, + ); + fn wr_compositor_destroy_tile( + compositor: *mut c_void, + id: NativeSurfaceId, + x: i32, + y: i32, + ); fn wr_compositor_bind( compositor: *mut c_void, - id: NativeSurfaceId, + id: NativeTileId, offset: &mut DeviceIntPoint, fbo_id: &mut u32, dirty_rect: DeviceIntRect, ); fn wr_compositor_unbind(compositor: *mut c_void); fn wr_compositor_begin_frame(compositor: *mut c_void); fn wr_compositor_add_surface( compositor: *mut c_void, @@ -1262,44 +1274,72 @@ extern "C" { } pub struct WrCompositor(*mut c_void); impl Compositor for WrCompositor { fn create_surface( &mut self, id: NativeSurfaceId, - size: DeviceIntSize, - is_opaque: bool, + tile_size: DeviceIntSize, ) { unsafe { wr_compositor_create_surface( self.0, id, - size, - is_opaque, + tile_size, ); } } fn destroy_surface( &mut self, id: NativeSurfaceId, ) { unsafe { wr_compositor_destroy_surface( self.0, id, ); } } + fn create_tile( + &mut self, + id: NativeTileId, + is_opaque: bool, + ) { + unsafe { + wr_compositor_create_tile( + self.0, + id.surface_id, + id.x, + id.y, + is_opaque, + ); + } + } + + fn destroy_tile( + &mut self, + id: NativeTileId, + ) { + unsafe { + wr_compositor_destroy_tile( + self.0, + id.surface_id, + id.x, + id.y, + ); + } + } + fn bind( &mut self, - id: NativeSurfaceId, + id: NativeTileId, dirty_rect: DeviceIntRect, ) -> NativeSurfaceInfo { let mut surface_info = NativeSurfaceInfo { origin: DeviceIntPoint::zero(), fbo_id: 0, }; unsafe {
--- a/gfx/wr/example-compositor/compositor-windows/src/lib.cpp +++ b/gfx/wr/example-compositor/compositor-windows/src/lib.cpp @@ -7,16 +7,17 @@ #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" @@ -37,16 +38,40 @@ enum SyncMode { // The OS compositor representation of a picture cache tile. struct Tile { // 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; }; +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; + std::unordered_map<TileKey, Tile, TileKeyHasher> tiles; + IDCompositionVisual2 *pVisual; +}; + struct CachedFrameBuffer { int width; int height; GLuint fboId; GLuint depthRboId; }; struct Window { @@ -77,23 +102,24 @@ struct Window { 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; - // Maps the WR surface IDs to the DC representation of each tile. - std::map<uint64_t, Tile> tiles; 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 @@ -344,19 +370,25 @@ extern "C" { window->EGLContext ); assert(ok); return window; } void com_dc_destroy_window(Window *window) { - for (auto it=window->tiles.begin() ; it != window->tiles.end() ; ++it) { - it->second.pSurface->Release(); - it->second.pVisual->Release(); + for (auto surface_it=window->surfaces.begin() ; surface_it != window->surfaces.end() ; ++surface_it) { + Surface &surface = surface_it->second; + + for (auto tile_it=surface.tiles.begin() ; tile_it != surface.tiles.end() ; ++tile_it) { + tile_it->second.pSurface->Release(); + tile_it->second.pVisual->Release(); + } + + 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); @@ -427,74 +459,140 @@ extern "C" { } } } // Create a new DC surface void com_dc_create_surface( Window *window, uint64_t id, - int width, - int height, + int tile_width, + int tile_height + ) { + assert(window->surfaces.count(id) == 0); + + Surface surface; + surface.tile_width = tile_width; + surface.tile_height = tile_height; + + // Create the visual node in the DC tree that stores properties + HRESULT hr = window->pDCompDevice->CreateVisual(&surface.pVisual); + assert(SUCCEEDED(hr)); + + window->surfaces[id] = surface; + } + + void com_dc_create_tile( + Window *window, + uint64_t id, + int x, + int y, bool is_opaque ) { - assert(window->tiles.count(id) == 0); + assert(window->surfaces.count(id) == 1); + Surface &surface = window->surfaces[id]; + + TileKey key(x, y); + assert(surface.tiles.count(key) == 0); Tile tile; + // Create the video memory surface. DXGI_ALPHA_MODE alpha_mode = is_opaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; - - // Create the video memory surface. HRESULT hr = window->pDCompDevice->CreateSurface( - width, - height, + 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)); - window->tiles[id] = tile; + // 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 + ); + + 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]; + + surface.pVisual->RemoveVisual(tile.pVisual); + + tile.pVisual->Release(); + tile.pSurface->Release(); + + surface.tiles.erase(key); } void com_dc_destroy_surface( Window *window, uint64_t id ) { - assert(window->tiles.count(id) == 1); + assert(window->surfaces.count(id) == 1); + Surface &surface = window->surfaces[id]; + + window->pRoot->RemoveVisual(surface.pVisual); // Release the video memory and visual in the tree - Tile &tile = window->tiles[id]; - tile.pVisual->Release(); - tile.pSurface->Release(); + for (auto tile_it=surface.tiles.begin() ; tile_it != surface.tiles.end() ; ++tile_it) { + tile_it->second.pSurface->Release(); + tile_it->second.pVisual->Release(); + } - window->tiles.erase(id); + 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 id, + 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->tiles.count(id) == 1); - Tile &tile = window->tiles[id]; + 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; @@ -578,52 +676,51 @@ extern "C" { uint64_t id, int x, int y, int clip_x, int clip_y, int clip_w, int clip_h ) { - Tile &tile = window->tiles[id]; - + 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); - tile.pVisual->SetOffsetX(offset_x); - tile.pVisual->SetOffsetY(offset_y); + 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; - tile.pVisual->SetClip(clip_rect); + 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) { - Tile &tile = window->tiles[*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). - HRESULT hr = window->pRoot->AddVisual( - tile.pVisual, + hr = window->pRoot->AddVisual( + surface.pVisual, FALSE, NULL ); assert(SUCCEEDED(hr)); } } window->mPrevLayers.swap(window->mCurrentLayers);
--- a/gfx/wr/example-compositor/compositor-windows/src/lib.rs +++ b/gfx/wr/example-compositor/compositor-windows/src/lib.rs @@ -31,29 +31,45 @@ extern { 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, - width: i32, - height: i32, + tile_width: i32, + tile_height: i32, + ); + + fn com_dc_create_tile( + window: *mut Window, + id: u64, + x: i32, + y: i32, is_opaque: bool, ); + 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, - id: u64, + 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); @@ -101,27 +117,59 @@ pub fn get_proc_address(name: *const c_c unsafe { com_dc_get_proc_address(name) } } pub fn create_surface( window: *mut Window, id: u64, - width: i32, - height: i32, - is_opaque: bool, + tile_width: i32, + tile_height: i32, ) { unsafe { com_dc_create_surface( window, id, - width, - height, - is_opaque + tile_width, + tile_height, + ) + } +} + +pub fn create_tile( + window: *mut Window, + id: u64, + x: i32, + y: i32, + is_opaque: bool, +) { + unsafe { + com_dc_create_tile( + window, + id, + x, + y, + is_opaque, + ) + } +} + +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, ) { @@ -130,29 +178,33 @@ pub fn destroy_surface( window, id, ) } } pub fn bind_surface( window: *mut Window, - id: u64, + 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, - id, + surface_id, + tile_x, + tile_y, &mut x_offset, &mut y_offset, dirty_x0, dirty_y0, dirty_width, dirty_height, );
--- a/gfx/wr/example-compositor/compositor/src/main.rs +++ b/gfx/wr/example-compositor/compositor/src/main.rs @@ -37,43 +37,69 @@ impl DirectCompositeInterface { } } } impl webrender::Compositor for DirectCompositeInterface { fn create_surface( &mut self, id: webrender::NativeSurfaceId, - size: DeviceIntSize, - is_opaque: bool, + tile_size: DeviceIntSize, ) { compositor::create_surface( self.window, id.0, - size.width, - size.height, - is_opaque, + tile_size.width, + tile_size.height, ); } fn destroy_surface( &mut self, id: webrender::NativeSurfaceId, ) { compositor::destroy_surface(self.window, id.0); } + fn create_tile( + &mut self, + id: webrender::NativeTileId, + is_opaque: bool, + ) { + compositor::create_tile( + self.window, + id.surface_id.0, + id.x, + id.y, + is_opaque, + ); + } + + 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::NativeSurfaceId, + id: webrender::NativeTileId, dirty_rect: DeviceIntRect, ) -> webrender::NativeSurfaceInfo { let (fbo_id, x, y) = compositor::bind_surface( self.window, - id.0, + 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),
--- a/gfx/wr/webrender/src/batch.rs +++ b/gfx/wr/webrender/src/batch.rs @@ -2,25 +2,25 @@ * 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 api::{AlphaType, ClipMode, ExternalImageType, ImageRendering}; use api::{YuvColorSpace, YuvFormat, ColorDepth, ColorRange, PremultipliedColorF}; use api::units::*; use crate::clip::{ClipDataStore, ClipNodeFlags, ClipNodeRange, ClipItemKind, ClipStore}; use crate::clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex, CoordinateSystemId}; -use crate::composite::{CompositeState, CompositeTile, CompositeTileSurface}; +use crate::composite::{CompositeState}; use crate::glyph_rasterizer::GlyphFormat; use crate::gpu_cache::{GpuBlockData, GpuCache, GpuCacheHandle, GpuCacheAddress}; use crate::gpu_types::{BrushFlags, BrushInstance, PrimitiveHeaders, ZBufferId, ZBufferIdGenerator}; use crate::gpu_types::{ClipMaskInstance, SplitCompositeInstance, BrushShaderKind}; use crate::gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance}; use crate::gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette}; use crate::internal_types::{FastHashMap, SavedTargetIndex, Swizzle, TextureSource, Filter}; -use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, TileSurface}; +use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive}; use crate::prim_store::{DeferredResolve, EdgeAaSegmentMask, PrimitiveInstanceKind, PrimitiveVisibilityIndex, PrimitiveVisibilityMask}; use crate::prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex}; use crate::prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, VECS_PER_SEGMENT, SpaceMapper}; use crate::prim_store::image::ImageSource; use crate::render_target::RenderTargetContext; use crate::render_task_graph::{RenderTaskId, RenderTaskGraph}; use crate::render_task::RenderTaskAddress; use crate::renderer::{BlendMode, ImageBufferKind, ShaderColorMode}; @@ -1217,53 +1217,24 @@ impl BatchBuilder { return; } }; let world_clip_rect = map_local_to_world .map(&local_tile_clip_rect) .expect("bug: unable to map clip rect"); let device_clip_rect = (world_clip_rect * ctx.global_device_pixel_scale).round(); let z_id = composite_state.z_generator.next(); - for key in &tile_cache.tiles_to_draw { - let tile = &tile_cache.tiles[key]; - if !tile.is_visible { - // This can occur when a tile is found to be occluded during frame building. - continue; - } - let device_rect = (tile.world_rect * ctx.global_device_pixel_scale).round(); - let dirty_rect = (tile.world_dirty_rect * ctx.global_device_pixel_scale).round(); - let surface = tile.surface.as_ref().expect("no tile surface set!"); - let (surface, is_opaque) = match surface { - TileSurface::Color { color } => { - (CompositeTileSurface::Color { color: *color }, true) - } - TileSurface::Clear => { - (CompositeTileSurface::Clear, false) - } - TileSurface::Texture { descriptor, .. } => { - let surface = descriptor.resolve(ctx.resource_cache); - ( - CompositeTileSurface::Texture { surface }, - tile.is_opaque || tile_cache.is_opaque(), - ) - } - }; - - let tile = CompositeTile { - surface, - rect: device_rect, - dirty_rect, - clip_rect: device_clip_rect, - z_id, - tile_id: tile.id, - }; - - composite_state.push_tile(tile, is_opaque); - } + composite_state.push_surface( + tile_cache, + device_clip_rect, + z_id, + ctx.global_device_pixel_scale, + ctx.resource_cache, + ); } PictureCompositeMode::Filter(ref filter) => { assert!(filter.is_visible()); match filter { Filter::Blur(..) => { let kind = BatchKind::Brush( BrushBatchKind::Image(ImageBufferKind::Texture2DArray) );
--- a/gfx/wr/webrender/src/composite.rs +++ b/gfx/wr/webrender/src/composite.rs @@ -1,41 +1,50 @@ /* 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 api::ColorF; -use api::units::{DeviceRect, DeviceIntSize, DeviceIntRect, DeviceIntPoint, WorldRect, DevicePixelScale}; +use api::units::{DeviceRect, DeviceIntSize, DeviceIntRect, DeviceIntPoint, WorldRect, DevicePixelScale, DevicePoint}; use crate::gpu_types::{ZBufferId, ZBufferIdGenerator}; -use crate::picture::{ResolvedSurfaceTexture, TileId}; +use crate::picture::{ResolvedSurfaceTexture, TileId, TileCacheInstance, TileSurface}; +use crate::resource_cache::ResourceCache; use std::{ops, u64}; /* Types and definitions related to compositing picture cache tiles and/or OS compositor integration. */ /// Describes details of an operation to apply to a native surface #[derive(Debug, Clone)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum NativeSurfaceOperationDetails { CreateSurface { - size: DeviceIntSize, + id: NativeSurfaceId, + tile_size: DeviceIntSize, + }, + DestroySurface { + id: NativeSurfaceId, + }, + CreateTile { + id: NativeTileId, is_opaque: bool, }, - DestroySurface, + DestroyTile { + id: NativeTileId, + } } /// Describes an operation to apply to a native surface #[derive(Debug, Clone)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct NativeSurfaceOperation { - pub id: NativeSurfaceId, pub details: NativeSurfaceOperationDetails, } /// Describes the source surface information for a tile to be composited. This /// is the analog of the TileSurface type, with target surface information /// resolved such that it can be used by the renderer. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -126,49 +135,51 @@ impl Default for CompositorKind { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] struct Occluder { slice: usize, device_rect: DeviceIntRect, } /// Describes the properties that identify a tile composition uniquely. -#[derive(PartialEq)] -pub struct CompositeTileDescriptor { - pub rect: DeviceRect, +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(PartialEq, Clone)] +pub struct CompositeSurfaceDescriptor { + pub slice: usize, + pub surface_id: Option<NativeSurfaceId>, + pub offset: DevicePoint, pub clip_rect: DeviceRect, - pub tile_id: TileId, } -/// Describes which tiles and properties were used to composite a frame. This +/// Describes surface properties used to composite a frame. This /// is used to compare compositions between frames. -#[derive(PartialEq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(PartialEq, Clone)] pub struct CompositeDescriptor { - tiles: Vec<CompositeTileDescriptor>, + pub surfaces: Vec<CompositeSurfaceDescriptor>, } impl CompositeDescriptor { /// Construct an empty descriptor. pub fn empty() -> Self { CompositeDescriptor { - tiles: Vec::new(), + surfaces: Vec::new(), } } } /// The list of tiles to be drawn this frame #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct CompositeState { // TODO(gw): Consider splitting up CompositeState into separate struct types depending // on the selected compositing mode. Many of the fields in this state struct // are only applicable to either Native or Draw compositing mode. - /// List of tiles to be drawn by the native compositor. These are added in draw order - /// and not separated by kind (opacity is a property of the native surface). - pub native_tiles: Vec<CompositeTile>, /// List of opaque tiles to be drawn by the Draw compositor. pub opaque_tiles: Vec<CompositeTile>, /// List of alpha tiles to be drawn by the Draw compositor. pub alpha_tiles: Vec<CompositeTile>, /// List of clear tiles to be drawn by the Draw compositor. pub clear_tiles: Vec<CompositeTile>, /// Used to generate z-id values for tiles in the Draw compositor mode. pub z_generator: ZBufferIdGenerator, @@ -182,16 +193,18 @@ pub struct CompositeState { /// The kind of compositor for picture cache tiles (e.g. drawn by WR, or OS compositor) pub compositor_kind: CompositorKind, /// Picture caching may be disabled dynamically, based on debug flags, pinch zoom etc. pub picture_caching_is_enabled: bool, /// The overall device pixel scale, used for tile occlusion conversions. global_device_pixel_scale: DevicePixelScale, /// List of registered occluders occluders: Vec<Occluder>, + /// Description of the surfaces and properties that are being composited. + pub descriptor: CompositeDescriptor, } impl CompositeState { /// Construct a new state for compositing picture tiles. This is created /// during each frame construction and passed to the renderer. pub fn new( compositor_kind: CompositorKind, mut picture_caching_is_enabled: bool, @@ -202,49 +215,26 @@ impl CompositeState { if let CompositorKind::Native { .. } = compositor_kind { if !picture_caching_is_enabled { warn!("Picture caching cannot be disabled in native compositor config"); } picture_caching_is_enabled = true; } CompositeState { - native_tiles: Vec::new(), opaque_tiles: Vec::new(), alpha_tiles: Vec::new(), clear_tiles: Vec::new(), z_generator: ZBufferIdGenerator::new(0), dirty_rects_are_valid: true, compositor_kind, picture_caching_is_enabled, global_device_pixel_scale, occluders: Vec::new(), - } - } - - /// Construct a descriptor of this composition state. Used for comparing - /// composition states between frames. - pub fn create_descriptor(&self) -> CompositeDescriptor { - let mut tiles = Vec::new(); - - let native_iter = self.native_tiles.iter(); - let opaque_iter = self.opaque_tiles.iter(); - let clear_iter = self.clear_tiles.iter(); - let alpha_iter = self.alpha_tiles.iter(); - - for tile in native_iter.chain(opaque_iter).chain(clear_iter).chain(alpha_iter) { - tiles.push(CompositeTileDescriptor { - rect: tile.rect, - clip_rect: tile.clip_rect, - tile_id: tile.tile_id, - }); - } - - CompositeDescriptor { - tiles, + descriptor: CompositeDescriptor::empty(), } } /// Register an occluder during picture cache updates that can be /// used during frame building to occlude tiles. pub fn register_occluder( &mut self, slice: usize, @@ -284,71 +274,140 @@ impl CompositeState { // Calculate the non-overlapping area of the valid occluders. let cover_area = area_of_occluders(&self.occluders, slice, &device_rect); debug_assert!(cover_area <= ref_area); // Check if the tile area is completely covered ref_area == cover_area } + /// Add a picture cache to be composited + pub fn push_surface( + &mut self, + tile_cache: &TileCacheInstance, + device_clip_rect: DeviceRect, + z_id: ZBufferId, + global_device_pixel_scale: DevicePixelScale, + resource_cache: &ResourceCache, + ) { + let mut visible_tile_count = 0; + + for key in &tile_cache.tiles_to_draw { + let tile = &tile_cache.tiles[key]; + if !tile.is_visible { + // This can occur when a tile is found to be occluded during frame building. + continue; + } + + visible_tile_count += 1; + + let device_rect = (tile.world_rect * global_device_pixel_scale).round(); + let dirty_rect = (tile.world_dirty_rect * global_device_pixel_scale).round(); + let surface = tile.surface.as_ref().expect("no tile surface set!"); + + let (surface, is_opaque) = match surface { + TileSurface::Color { color } => { + (CompositeTileSurface::Color { color: *color }, true) + } + TileSurface::Clear => { + (CompositeTileSurface::Clear, false) + } + TileSurface::Texture { descriptor, .. } => { + let surface = descriptor.resolve(resource_cache, tile_cache.current_tile_size); + ( + CompositeTileSurface::Texture { surface }, + tile.is_opaque || tile_cache.is_opaque(), + ) + } + }; + + let tile = CompositeTile { + surface, + rect: device_rect, + dirty_rect, + clip_rect: device_clip_rect, + z_id, + tile_id: tile.id, + }; + + self.push_tile(tile, is_opaque); + } + + if visible_tile_count > 0 { + self.descriptor.surfaces.push( + CompositeSurfaceDescriptor { + slice: tile_cache.slice, + surface_id: tile_cache.native_surface_id, + offset: tile_cache.device_position, + clip_rect: device_clip_rect, + } + ); + } + } + /// Add a tile to the appropriate array, depending on tile properties and compositor mode. - pub fn push_tile( + fn push_tile( &mut self, tile: CompositeTile, is_opaque: bool, ) { - match (self.compositor_kind, &tile.surface) { - (CompositorKind::Draw { .. }, CompositeTileSurface::Color { .. }) => { + match tile.surface { + CompositeTileSurface::Color { .. } => { // Color tiles are, by definition, opaque. We might support non-opaque color // tiles if we ever find pages that have a lot of these. self.opaque_tiles.push(tile); } - (CompositorKind::Draw { .. }, CompositeTileSurface::Clear) => { + CompositeTileSurface::Clear => { // Clear tiles have a special bucket self.clear_tiles.push(tile); } - (CompositorKind::Draw { .. }, CompositeTileSurface::Texture { .. }) => { + CompositeTileSurface::Texture { .. } => { // Texture surfaces get bucketed by opaque/alpha, for z-rejection // on the Draw compositor mode. if is_opaque { self.opaque_tiles.push(tile); } else { self.alpha_tiles.push(tile); } } - (CompositorKind::Native { .. }, CompositeTileSurface::Color { .. }) => { - // Native compositor doesn't (yet) support color surfaces. - unreachable!(); - } - (CompositorKind::Native { .. }, CompositeTileSurface::Clear) => { - // Native compositor doesn't support color surfaces. - unreachable!(); - } - (CompositorKind::Native { .. }, CompositeTileSurface::Texture { .. }) => { - // Native tiles are supplied to the OS compositor in draw order, - // since there is no z-buffer involved (opacity is supplied as part - // of the surface properties). - self.native_tiles.push(tile); - } } } } /// An arbitrary identifier for a native (OS compositor) surface #[repr(C)] #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct NativeSurfaceId(pub u64); impl NativeSurfaceId { /// A special id for the native surface that is used for debug / profiler overlays. pub const DEBUG_OVERLAY: NativeSurfaceId = NativeSurfaceId(u64::MAX); } +#[repr(C)] +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct NativeTileId { + pub surface_id: NativeSurfaceId, + pub x: i32, + pub y: i32, +} + +impl NativeTileId { + /// A special id for the native surface that is used for debug / profiler overlays. + pub const DEBUG_OVERLAY: NativeTileId = NativeTileId { + surface_id: NativeSurfaceId::DEBUG_OVERLAY, + x: 0, + y: 0, + }; +} + /// Information about a bound surface that the native compositor /// returns to WR. #[repr(C)] #[derive(Copy, Clone)] pub struct NativeSurfaceInfo { /// An offset into the surface that WR should draw. Some compositing /// implementations (notably, DirectComposition) use texture atlases /// when the surface sizes are small. In this case, an offset can @@ -365,49 +424,60 @@ pub struct NativeSurfaceInfo { pub fbo_id: u32, } /// Defines an interface to a native (OS level) compositor. If supplied /// by the client application, then picture cache slices will be /// composited by the OS compositor, rather than drawn via WR batches. pub trait Compositor { /// Create a new OS compositor surface with the given properties. - /// The surface is allocated but not placed in the visual tree. fn create_surface( &mut self, id: NativeSurfaceId, - size: DeviceIntSize, - is_opaque: bool, + tile_size: DeviceIntSize, ); /// Destroy the surface with the specified id. WR may call this /// at any time the surface is no longer required (including during /// renderer deinit). It's the responsibility of the embedder /// to ensure that the surface is only freed once the GPU is /// no longer using the surface (if this isn't already handled /// by the operating system). fn destroy_surface( &mut self, id: NativeSurfaceId, ); + /// Create a new OS compositor tile with the given properties. + fn create_tile( + &mut self, + id: NativeTileId, + is_opaque: bool, + ); + + /// Destroy an existing compositor tile. + fn destroy_tile( + &mut self, + id: NativeTileId, + ); + /// Bind this surface such that WR can issue OpenGL commands /// that will target the surface. Returns an (x, y) offset /// where WR should draw into the surface. This can be set /// to (0, 0) if the OS doesn't use texture atlases. The dirty /// rect is a local surface rect that specifies which part /// of the surface needs to be updated. If max_update_rects /// in CompositeConfig is 0, this will always be the size /// of the entire surface. The returned offset is only /// relevant to compositors that store surfaces in a texture /// atlas (that is, WR expects that the dirty rect doesn't /// affect the coordinates of the returned origin). fn bind( &mut self, - id: NativeSurfaceId, + id: NativeTileId, dirty_rect: DeviceIntRect, ) -> NativeSurfaceInfo; /// Unbind the surface. This is called by WR when it has /// finished issuing OpenGL commands on the current surface. fn unbind( &mut self, );
--- a/gfx/wr/webrender/src/frame_builder.rs +++ b/gfx/wr/webrender/src/frame_builder.rs @@ -354,21 +354,20 @@ impl FrameBuilder { // allows the first frame of a new display list to reuse any existing tiles // and surfaces that match. Once the `update_visibility` call above is // complete, any tiles that are left remaining in the `retained_tiles` // map are not needed and will be dropped. For simple compositing mode, // this is fine, since texture cache handles are garbage collected at // the end of each frame. However, if we're in native compositor mode, // we need to manually clean up any native compositor surfaces that were // allocated by these tiles. - for (_, cache_state) in visibility_state.retained_tiles.caches.drain() { - visibility_state.composite_state.destroy_native_surfaces( - cache_state.tiles.values(), - visibility_state.resource_cache, - ); + for (_, mut cache_state) in visibility_state.retained_tiles.caches.drain() { + if let Some(native_surface_id) = cache_state.native_surface_id.take() { + visibility_state.resource_cache.destroy_compositor_surface(native_surface_id); + } } } let mut frame_state = FrameBuildingState { render_tasks, profile_counters, clip_store: &mut scene.clip_store, resource_cache,
--- a/gfx/wr/webrender/src/lib.rs +++ b/gfx/wr/webrender/src/lib.rs @@ -196,17 +196,17 @@ extern crate png; #[cfg(test)] extern crate rand; #[macro_use] pub extern crate api; extern crate webrender_build; #[doc(hidden)] -pub use crate::composite::{CompositorConfig, Compositor, NativeSurfaceId, NativeSurfaceInfo}; +pub use crate::composite::{CompositorConfig, Compositor, NativeSurfaceId, NativeTileId, NativeSurfaceInfo}; pub use crate::device::{build_shader_strings, UploadMethod, VertexUsageHint, get_gl_target}; pub use crate::device::{ProgramBinary, ProgramCache, ProgramCacheObserver, FormatDesc}; pub use crate::device::Device; pub use crate::frame_builder::ChasePrimitive; pub use crate::prim_store::PrimitiveDebugId; pub use crate::profiler::{ProfilerHooks, set_profiler_hooks}; pub use crate::renderer::{ AsyncPropertySampler, CpuProfile, DebugFlags, RendererKind, GpuProfile, GraphicsApi,
--- a/gfx/wr/webrender/src/picture.rs +++ b/gfx/wr/webrender/src/picture.rs @@ -73,17 +73,17 @@ use api::{MixBlendMode, PipelineId, Prem use api::{PropertyBinding, PropertyBindingId, FilterPrimitive, FontRenderMode}; use api::{DebugFlags, RasterSpace, ImageKey, ColorF, PrimitiveFlags}; use api::units::*; use crate::box_shadow::{BLUR_SAMPLE_SCALE}; use crate::clip::{ClipStore, ClipChainInstance, ClipDataHandle, ClipChainId}; use crate::clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, CoordinateSpaceMapping, SpatialNodeIndex, VisibleFace }; -use crate::composite::{CompositorKind, CompositeState, NativeSurfaceId}; +use crate::composite::{CompositorKind, CompositeState, NativeSurfaceId, NativeTileId}; use crate::debug_colors; use euclid::{vec3, Point2D, Scale, Size2D, Vector2D, Rect}; use euclid::approxeq::ApproxEq; use crate::filterdata::SFilterData; use crate::frame_builder::{FrameVisibilityContext, FrameVisibilityState}; use crate::intern::ItemUid; use crate::internal_types::{FastHashMap, FastHashSet, PlaneSplitter, Filter, PlaneSplitAnchor, TextureSource}; use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext}; @@ -197,16 +197,18 @@ pub struct PictureCacheState { /// State of opacity bindings from previous frame opacity_bindings: FastHashMap<PropertyBindingId, OpacityBindingInfo>, /// The current transform of the picture cache root spatial node root_transform: TransformKey, /// The current tile size in device pixels current_tile_size: DeviceIntSize, /// Various allocations we want to avoid re-doing. allocations: PictureCacheRecycledAllocations, + /// Currently allocated native compositor surface for this picture cache. + pub native_surface_id: Option<NativeSurfaceId>, } pub struct PictureCacheRecycledAllocations { old_tiles: FastHashMap<TileOffset, Tile>, old_opacity_bindings: FastHashMap<PropertyBindingId, OpacityBindingInfo>, compare_cache: FastHashMap<PrimitiveComparisonKey, PrimitiveCompareResult>, } @@ -441,64 +443,63 @@ pub struct TileId(pub usize); pub enum SurfaceTextureDescriptor { /// When using the WR compositor, the tile is drawn into an entry /// in the WR texture cache. TextureCache { handle: TextureCacheHandle }, /// When using an OS compositor, the tile is drawn into a native /// surface identified by arbitrary id. - NativeSurface { - /// The arbitrary id of this surface. - id: Option<NativeSurfaceId>, - /// Size in device pixels of the native surface. - size: DeviceIntSize, + Native { + /// The arbitrary id of this tile. + id: Option<NativeTileId>, }, } /// This is the same as a `SurfaceTextureDescriptor` but has been resolved /// into a texture cache handle (if appropriate) that can be used by the /// batching and compositing code in the renderer. #[derive(Clone, Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum ResolvedSurfaceTexture { TextureCache { /// The texture ID to draw to. texture: TextureSource, /// Slice index in the texture array to draw to. layer: i32, }, - NativeSurface { - /// The arbitrary id of this surface. - id: NativeSurfaceId, - /// Size in device pixels of the native surface. + Native { + /// The arbitrary id of this tile. + id: NativeTileId, + /// The size of the tile in device pixels. size: DeviceIntSize, } } impl SurfaceTextureDescriptor { /// Create a resolved surface texture for this descriptor pub fn resolve( &self, resource_cache: &ResourceCache, + size: DeviceIntSize, ) -> ResolvedSurfaceTexture { match self { SurfaceTextureDescriptor::TextureCache { handle } => { let cache_item = resource_cache.texture_cache.get(handle); ResolvedSurfaceTexture::TextureCache { texture: cache_item.texture_id, layer: cache_item.texture_layer, } } - SurfaceTextureDescriptor::NativeSurface { id, size } => { - ResolvedSurfaceTexture::NativeSurface { + SurfaceTextureDescriptor::Native { id } => { + ResolvedSurfaceTexture::Native { id: id.expect("bug: native surface not allocated"), - size: *size, + size, } } } } } /// The backing surface for this tile. #[derive(Debug)] @@ -968,25 +969,25 @@ impl Tile { Some(TileSurface::Texture { mut descriptor, visibility_mask }) => { // If opacity changed, and this is a native OS compositor surface, // it needs to be recreated. // TODO(gw): This is a limitation of the DirectComposite APIs. It might // make sense on other platforms to be able to change this as // a property on a surface, if we ever see pages where this // is changing frequently. if opacity_changed { - if let SurfaceTextureDescriptor::NativeSurface { ref mut id, .. } = descriptor { + if let SurfaceTextureDescriptor::Native { ref mut id, .. } = descriptor { // Reset the dirty rect and tile validity in this case, to // force the new tile to be completely redrawn. self.invalidate(None, InvalidationReason::SurfaceOpacityChanged); // If this tile has a currently allocated native surface, destroy it. It // will be re-allocated next time it's determined to be visible. if let Some(id) = id.take() { - state.resource_cache.destroy_compositor_surface(id); + state.resource_cache.destroy_compositor_tile(id); } } } // Reuse the existing descriptor and vis mask TileSurface::Texture { descriptor, visibility_mask, @@ -1004,19 +1005,18 @@ impl Tile { SurfaceTextureDescriptor::TextureCache { handle: TextureCacheHandle::invalid(), } } CompositorKind::Native { .. } => { // Create a native surface surface descriptor, but don't allocate // a surface yet. The surface is allocated *after* occlusion // culling occurs, so that only visible tiles allocate GPU memory. - SurfaceTextureDescriptor::NativeSurface { + SurfaceTextureDescriptor::Native { id: None, - size: ctx.current_tile_size, } } }; TileSurface::Texture { descriptor, visibility_mask: PrimitiveVisibilityMask::empty(), } @@ -1506,16 +1506,23 @@ pub struct TileCacheInstance { /// we don't want to constantly invalidate and reallocate different tile size /// configuration each frame. frames_until_size_eval: usize, /// The current fractional offset of the cached picture fract_offset: PictureVector2D, /// keep around the hash map used as compare_cache to avoid reallocating it each /// frame. compare_cache: FastHashMap<PrimitiveComparisonKey, PrimitiveCompareResult>, + /// The allocated compositor surface for this picture cache. May be None if + /// not using native compositor, or if the surface was destroyed and needs + /// to be reallocated next time this surface contains valid tiles. + pub native_surface_id: Option<NativeSurfaceId>, + /// The current device position of this cache. Used to set the compositor + /// offset of the surface when building the visual tree. + pub device_position: DevicePoint, } impl TileCacheInstance { pub fn new( slice: usize, spatial_node_index: SpatialNodeIndex, background_color: Option<ColorF>, shared_clips: Vec<ClipDataHandle>, @@ -1553,16 +1560,18 @@ impl TileCacheInstance { subpixel_mode: SubpixelMode::Allow, root_transform: TransformKey::Local, shared_clips, shared_clip_chain, current_tile_size: DeviceIntSize::zero(), frames_until_size_eval: 0, fract_offset: PictureVector2D::zero(), compare_cache: FastHashMap::default(), + native_surface_id: None, + device_position: DevicePoint::zero(), } } /// Returns true if this tile cache is considered opaque. pub fn is_opaque(&self) -> bool { // If known opaque due to background clear color and being the first slice. // The background_color will only be Some(..) if this is the first slice. match self.background_color { @@ -1673,16 +1682,17 @@ impl TileCacheInstance { // If there are pending retained state, retrieve it. if let Some(prev_state) = frame_state.retained_tiles.caches.remove(&self.slice) { self.tiles.extend(prev_state.tiles); self.root_transform = prev_state.root_transform; self.spatial_nodes = prev_state.spatial_nodes; self.opacity_bindings = prev_state.opacity_bindings; self.current_tile_size = prev_state.current_tile_size; + self.native_surface_id = prev_state.native_surface_id; fn recycle_map<K: std::cmp::Eq + std::hash::Hash, V>( ideal_len: usize, dest: &mut FastHashMap<K, V>, src: FastHashMap<K, V>, ) { if dest.capacity() < src.capacity() { if src.capacity() < 3 * ideal_len { @@ -1730,20 +1740,19 @@ impl TileCacheInstance { desired_tile_size = TILE_SIZE_DEFAULT; } // If the desired tile size has changed, then invalidate and drop any // existing tiles. if desired_tile_size != self.current_tile_size { // Destroy any native surfaces on the tiles that will be dropped due // to resizing. - frame_state.composite_state.destroy_native_surfaces( - self.tiles.values(), - frame_state.resource_cache, - ); + if let Some(native_surface_id) = self.native_surface_id.take() { + frame_state.resource_cache.destroy_compositor_surface(native_surface_id); + } self.tiles.clear(); self.current_tile_size = desired_tile_size; } // Reset counter until next evaluating the desired tile size. This is an // arbitrary value. self.frames_until_size_eval = 120; } @@ -1757,16 +1766,17 @@ impl TileCacheInstance { let world_origin = pic_to_world_mapper .map(&PictureRect::new(PicturePoint::zero(), PictureSize::new(1.0, 1.0))) .expect("bug: unable to map origin to world space") .origin; // Get the desired integer device coordinate let device_origin = world_origin * frame_context.global_device_pixel_scale; let desired_device_origin = device_origin.round(); + self.device_position = desired_device_origin; // Unmap from device space to world space rect let ref_world_rect = WorldRect::new( desired_device_origin / frame_context.global_device_pixel_scale, WorldSize::new(1.0, 1.0), ); // Unmap from world space to picture space @@ -1906,17 +1916,17 @@ impl TileCacheInstance { self.tiles.insert(key, tile); } } // Any old tiles that remain after the loop above are going to be dropped. For // simple composite mode, the texture cache handle will expire and be collected // by the texture cache. For native compositor mode, we need to explicitly // invoke a callback to the client to destroy that surface. - frame_state.composite_state.destroy_native_surfaces( + frame_state.composite_state.destroy_native_tiles( self.old_tiles.values(), frame_state.resource_cache, ); world_culling_rect } /// Update the dependencies for each tile for a given primitive instance. @@ -3145,26 +3155,27 @@ impl PicturePrimitive { /// Destroy an existing picture. This is called just before /// a frame builder is replaced with a newly built scene. It /// gives a picture a chance to retain any cached tiles that /// may be useful during the next scene build. pub fn destroy( &mut self, retained_tiles: &mut RetainedTiles, ) { - if let Some(tile_cache) = self.tile_cache.take() { + if let Some(mut tile_cache) = self.tile_cache.take() { if !tile_cache.tiles.is_empty() { retained_tiles.caches.insert( tile_cache.slice, PictureCacheState { tiles: tile_cache.tiles, spatial_nodes: tile_cache.spatial_nodes, opacity_bindings: tile_cache.opacity_bindings, root_transform: tile_cache.root_transform, current_tile_size: tile_cache.current_tile_size, + native_surface_id: tile_cache.native_surface_id.take(), allocations: PictureCacheRecycledAllocations { old_tiles: tile_cache.old_tiles, old_opacity_bindings: tile_cache.old_opacity_bindings, compare_cache: tile_cache.compare_cache, }, }, ); } @@ -3600,19 +3611,19 @@ impl PicturePrimitive { // code below. if frame_state.composite_state.is_tile_occluded(tile_cache.slice, tile_draw_rect) { // If this tile has an allocated native surface, free it, since it's completely // occluded. We will need to re-allocate this surface if it becomes visible, // but that's likely to be rare (e.g. when there is no content display list // for a frame or two during a tab switch). let surface = tile.surface.as_mut().expect("no tile surface set!"); - if let TileSurface::Texture { descriptor: SurfaceTextureDescriptor::NativeSurface { id, .. }, .. } = surface { + if let TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { id, .. }, .. } = surface { if let Some(id) = id.take() { - frame_state.resource_cache.destroy_compositor_surface(id); + frame_state.resource_cache.destroy_compositor_tile(id); } } tile.is_visible = false; continue; } if frame_context.debug_flags.contains(DebugFlags::PICTURE_CACHING_DBG) { @@ -3656,17 +3667,17 @@ impl PicturePrimitive { // TODO(gw): Consider switching to manual eviction policy? frame_state.resource_cache.texture_cache.request(handle, frame_state.gpu_cache); } else { // If the texture was evicted on a previous frame, we need to assume // that the entire tile rect is dirty. tile.invalidate(None, InvalidationReason::NoTexture); } } - SurfaceTextureDescriptor::NativeSurface { id, .. } => { + SurfaceTextureDescriptor::Native { id, .. } => { if id.is_none() { // There is no current surface allocation, so ensure the entire tile is invalidated tile.invalidate(None, InvalidationReason::NoSurface); } } } } @@ -3684,22 +3695,42 @@ impl PicturePrimitive { if !frame_state.resource_cache.texture_cache.is_allocated(handle) { frame_state.resource_cache.texture_cache.update_picture_cache( tile_cache.current_tile_size, handle, frame_state.gpu_cache, ); } } - SurfaceTextureDescriptor::NativeSurface { id, size } => { + SurfaceTextureDescriptor::Native { id } => { if id.is_none() { - *id = Some(frame_state.resource_cache.create_compositor_surface( - *size, + // Allocate a native surface id if we're in native compositing mode, + // and we don't have a surface yet (due to first frame, or destruction + // due to tile size changing etc). + if tile_cache.native_surface_id.is_none() { + let surface_id = frame_state + .resource_cache + .create_compositor_surface(tile_cache.current_tile_size); + + tile_cache.native_surface_id = Some(surface_id); + } + + // Create the tile identifier and allocate it. + let tile_id = NativeTileId { + surface_id: tile_cache.native_surface_id.unwrap(), + x: key.x, + y: key.y, + }; + + frame_state.resource_cache.create_compositor_tile( + tile_id, tile.is_opaque, - )); + ); + + *id = Some(tile_id); } } } *visibility_mask = PrimitiveVisibilityMask::empty(); let dirty_region_index = tile_cache.dirty_region.dirty_rects.len(); // If we run out of dirty regions, then force the last dirty region to @@ -3734,17 +3765,20 @@ impl PicturePrimitive { -tile.world_rect.origin.to_vector() ); // The world rect is guaranteed to be device pixel aligned, by the tile // sizing code in tile::pre_update. However, there might be some // small floating point accuracy issues (these were observed on ARM // CPUs). Round the rect here before casting to integer device pixels // to ensure the scissor rect is correct. let scissor_rect = (scissor_rect * device_pixel_scale).round(); - let surface = descriptor.resolve(frame_state.resource_cache); + let surface = descriptor.resolve( + frame_state.resource_cache, + tile_cache.current_tile_size, + ); let task = RenderTask::new_picture( RenderTaskLocation::PictureCache { size: tile_cache.current_tile_size, surface, }, tile_cache.current_tile_size.to_f32(), pic_index, @@ -5189,31 +5223,31 @@ impl TileNode { } } } } } impl CompositeState { // A helper function to destroy all native surfaces for a given list of tiles - pub fn destroy_native_surfaces<'a, I: Iterator<Item = &'a Tile>>( + pub fn destroy_native_tiles<'a, I: Iterator<Item = &'a Tile>>( &mut self, tiles_iter: I, resource_cache: &mut ResourceCache, ) { // Any old tiles that remain after the loop above are going to be dropped. For // simple composite mode, the texture cache handle will expire and be collected // by the texture cache. For native compositor mode, we need to explicitly // invoke a callback to the client to destroy that surface. if let CompositorKind::Native { .. } = self.compositor_kind { for tile in tiles_iter { // Only destroy native surfaces that have been allocated. It's // possible for display port tiles to be created that never // come on screen, and thus never get a native surface allocated. - if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::NativeSurface { id, .. }, .. }) = tile.surface { + if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { id, .. }, .. }) = tile.surface { if let Some(id) = id { - resource_cache.destroy_compositor_surface(id); + resource_cache.destroy_compositor_tile(id); } } } } } }
--- a/gfx/wr/webrender/src/render_backend.rs +++ b/gfx/wr/webrender/src/render_backend.rs @@ -1543,17 +1543,18 @@ impl RenderBackend { let pending_update = self.resource_cache.pending_updates(); (pending_update, rendered_document) }; // Build a small struct that represents the state of the tiles to be composited. let composite_descriptor = rendered_document .frame .composite_state - .create_descriptor(); + .descriptor + .clone(); // If there are no texture cache updates to apply, and if the produced // frame is a no-op, and the compositor state is equal, then we can skip // compositing this frame completely. if pending_update.is_nop() && rendered_document.frame.is_nop() && composite_descriptor == doc.prev_composite_descriptor { doc.rendered_frame_is_valid = true;
--- a/gfx/wr/webrender/src/render_task.rs +++ b/gfx/wr/webrender/src/render_task.rs @@ -1353,17 +1353,17 @@ impl RenderTask { (DeviceIntRect::zero(), RenderTargetIndex(0)) } RenderTaskLocation::TextureCache {layer, rect, .. } => { (rect, RenderTargetIndex(layer as usize)) } RenderTaskLocation::PictureCache { ref surface, size, .. } => { let layer = match surface { ResolvedSurfaceTexture::TextureCache { layer, .. } => *layer, - ResolvedSurfaceTexture::NativeSurface { .. } => 0, + ResolvedSurfaceTexture::Native { .. } => 0, }; ( DeviceIntRect::new( DeviceIntPoint::zero(), size, ), RenderTargetIndex(layer as usize),
--- a/gfx/wr/webrender/src/renderer.rs +++ b/gfx/wr/webrender/src/renderer.rs @@ -44,17 +44,17 @@ use api::{RenderApiSender, RenderNotifie use api::ExternalImage; use api::channel; use api::units::*; pub use api::DebugFlags; use api::channel::{MsgSender, PayloadReceiverHelperMethods}; use crate::batch::{AlphaBatchContainer, BatchKind, BatchFeatures, BatchTextures, BrushBatchKind, ClipBatchList}; #[cfg(any(feature = "capture", feature = "replay"))] use crate::capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage}; -use crate::composite::{CompositeState, CompositeTileSurface, CompositeTile, CompositorKind, Compositor}; +use crate::composite::{CompositeState, CompositeTileSurface, CompositeTile, CompositorKind, Compositor, NativeTileId}; use crate::composite::{CompositorConfig, NativeSurfaceOperationDetails, NativeSurfaceId, NativeSurfaceOperation}; use crate::debug_colors; use crate::debug_render::{DebugItem, DebugRenderer}; use crate::device::{DepthFunction, Device, GpuFrameId, Program, UploadMethod, Texture, PBO}; use crate::device::{DrawTarget, ExternalTexture, FBOId, ReadTarget, TextureSlot}; use crate::device::{ShaderError, TextureFilter, TextureFlags, VertexUsageHint, VAO, VBO, CustomVAO}; use crate::device::ProgramCache; @@ -2963,33 +2963,36 @@ impl Renderer { } } // Allocate a new surface, if we need it and there isn't one. if self.debug_overlay_state.is_enabled && self.debug_overlay_state.current_size.is_none() { compositor.create_surface( NativeSurfaceId::DEBUG_OVERLAY, framebuffer_size, + ); + compositor.create_tile( + NativeTileId::DEBUG_OVERLAY, false, ); self.debug_overlay_state.current_size = Some(framebuffer_size); } } } /// Bind a draw target for the debug / profiler overlays, if required. fn bind_debug_overlay(&mut self) { // Debug overlay setup are only required in native compositing mode if self.debug_overlay_state.is_enabled { if let CompositorConfig::Native { ref mut compositor, .. } = self.compositor_config { let surface_size = self.debug_overlay_state.current_size.unwrap(); // Bind the native surface let surface_info = compositor.bind( - NativeSurfaceId::DEBUG_OVERLAY, + NativeTileId::DEBUG_OVERLAY, DeviceIntRect::new( DeviceIntPoint::zero(), surface_size, ), ); // Bind the native surface to current FBO target let draw_target = DrawTarget::NativeSurface { @@ -4114,17 +4117,17 @@ impl Renderer { (TextureSource::Dummy, 0.0, color) } CompositeTileSurface::Clear => { (TextureSource::Dummy, 0.0, ColorF::BLACK) } CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::TextureCache { texture, layer } } => { (texture, layer as f32, ColorF::WHITE) } - CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::NativeSurface { .. } } => { + CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::Native { .. } } => { unreachable!("bug: found native surface in simple composite path"); } }; let textures = BatchTextures::color(texture); // Determine a clip rect to apply to this tile, depending on what // the partial present mode is. let partial_clip_rect = match partial_present_mode { @@ -5062,33 +5065,31 @@ impl Renderer { debug_assert!(self.texture_resolver.prev_pass_color.is_none()); } fn update_native_surfaces(&mut self) { match self.compositor_config { CompositorConfig::Native { ref mut compositor, .. } => { for op in self.pending_native_surface_updates.drain(..) { match op.details { - NativeSurfaceOperationDetails::CreateSurface { size, is_opaque } => { - let _inserted = self.allocated_native_surfaces.insert(op.id); + NativeSurfaceOperationDetails::CreateSurface { id, tile_size } => { + let _inserted = self.allocated_native_surfaces.insert(id); debug_assert!(_inserted, "bug: creating existing surface"); - - compositor.create_surface( - op.id, - size, - is_opaque, - ); + compositor.create_surface(id, tile_size); } - NativeSurfaceOperationDetails::DestroySurface => { - let _existed = self.allocated_native_surfaces.remove(&op.id); + NativeSurfaceOperationDetails::DestroySurface { id } => { + let _existed = self.allocated_native_surfaces.remove(&id); debug_assert!(_existed, "bug: removing unknown surface"); - - compositor.destroy_surface( - op.id, - ); + compositor.destroy_surface(id); + } + NativeSurfaceOperationDetails::CreateTile { id, is_opaque } => { + compositor.create_tile(id, is_opaque); + } + NativeSurfaceOperationDetails::DestroyTile { id } => { + compositor.destroy_tile(id); } } } } CompositorConfig::Draw { .. } => { // Ensure nothing is added in simple composite mode, since otherwise // memory will leak as this doesn't get drained debug_assert!(self.pending_native_surface_updates.is_empty()); @@ -5253,17 +5254,17 @@ impl Renderer { .expect("bug"); DrawTarget::from_texture( texture, layer as usize, true, ) } - ResolvedSurfaceTexture::NativeSurface { id, size, .. } => { + ResolvedSurfaceTexture::Native { id, size } => { let surface_info = match self.compositor_config { CompositorConfig::Native { ref mut compositor, .. } => { compositor.bind(id, picture_target.dirty_rect) } CompositorConfig::Draw { .. } => { unreachable!(); } }; @@ -5290,17 +5291,17 @@ impl Renderer { draw_target, frame.content_origin, &projection, &frame.render_tasks, &mut results.stats, ); // Native OS surfaces must be unbound at the end of drawing to them - if let ResolvedSurfaceTexture::NativeSurface { .. } = picture_target.surface { + if let ResolvedSurfaceTexture::Native { .. } = picture_target.surface { match self.compositor_config { CompositorConfig::Native { ref mut compositor, .. } => { compositor.unbind(); } CompositorConfig::Draw { .. } => { unreachable!(); } } @@ -6769,27 +6770,20 @@ fn should_skip_batch(kind: &BatchKind, f impl CompositeState { /// Use the client provided native compositor interface to add all picture /// cache tiles to the OS compositor fn composite_native( &self, compositor: &mut dyn Compositor, ) { - // For each tile, update the properties with the native OS compositor, - // such as position and clip rect. z-order of the tiles are implicit based - // on the order they are added in this loop. - for tile in &self.native_tiles { - // Extract the native surface id. We should only ever encounter native surfaces here! - let id = match tile.surface { - CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::NativeSurface { id, .. }, .. } => id, - _ => unreachable!(), - }; - - // Add the tile to the OS compositor. + // Add each surface to the visual tree. z-order is implicit based on + // order added. Offset and clip rect apply to all tiles within this + // surface. + for surface in &self.descriptor.surfaces { compositor.add_surface( - id, - tile.rect.origin.to_i32(), - tile.clip_rect.to_i32(), + surface.surface_id.expect("bug: no native surface allocated"), + surface.offset.to_i32(), + surface.clip_rect.to_i32(), ); } } }
--- a/gfx/wr/webrender/src/resource_cache.rs +++ b/gfx/wr/webrender/src/resource_cache.rs @@ -12,17 +12,17 @@ use api::{ImageData, ImageDescriptor, Im use api::{BlobImageData, BlobImageKey, MemoryReport, VoidPtrToSizeFn}; use api::units::*; #[cfg(feature = "capture")] use crate::capture::ExternalCaptureImage; #[cfg(feature = "replay")] use crate::capture::PlainExternalImage; #[cfg(any(feature = "replay", feature = "png"))] use crate::capture::CaptureConfig; -use crate::composite::{NativeSurfaceId, NativeSurfaceOperation, NativeSurfaceOperationDetails}; +use crate::composite::{NativeSurfaceId, NativeSurfaceOperation, NativeTileId, NativeSurfaceOperationDetails}; use crate::device::TextureFilter; use euclid::{point2, size2}; use crate::glyph_cache::GlyphCache; use crate::glyph_cache::GlyphCacheEntry; use crate::glyph_rasterizer::{GLYPH_FLASHING, BaseFontInstance, FontInstance, GlyphFormat, GlyphKey, GlyphRasterizer}; use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle}; use crate::gpu_types::UvRectKind; use crate::image::{compute_tile_size, compute_tile_rect, compute_tile_range, for_each_tile_in_range}; @@ -1770,47 +1770,76 @@ impl ResourceCache { UvRectKind::Rect, eviction, ); } } } /// Queue up allocation of a new OS native compositor surface with the - /// specified id and dimensions. + /// specified tile size. pub fn create_compositor_surface( &mut self, - size: DeviceIntSize, - is_opaque: bool, + tile_size: DeviceIntSize, ) -> NativeSurfaceId { let id = NativeSurfaceId(NEXT_NATIVE_SURFACE_ID.fetch_add(1, Ordering::Relaxed)); self.pending_native_surface_updates.push( NativeSurfaceOperation { - id, details: NativeSurfaceOperationDetails::CreateSurface { - size, - is_opaque, - } + id, + tile_size, + }, } ); id } /// Queue up destruction of an existing native OS surface. This is used when - /// a picture cache tile is dropped or resized. + /// a picture cache surface is dropped or resized. pub fn destroy_compositor_surface( &mut self, id: NativeSurfaceId, ) { self.pending_native_surface_updates.push( NativeSurfaceOperation { - id, - details: NativeSurfaceOperationDetails::DestroySurface, + details: NativeSurfaceOperationDetails::DestroySurface { + id, + } + } + ); + } + + /// Queue construction of a native compositor tile on a given surface. + pub fn create_compositor_tile( + &mut self, + id: NativeTileId, + is_opaque: bool, + ) { + self.pending_native_surface_updates.push( + NativeSurfaceOperation { + details: NativeSurfaceOperationDetails::CreateTile { + id, + is_opaque, + }, + } + ); + } + + /// Queue destruction of a native compositor tile. + pub fn destroy_compositor_tile( + &mut self, + id: NativeTileId, + ) { + self.pending_native_surface_updates.push( + NativeSurfaceOperation { + details: NativeSurfaceOperationDetails::DestroyTile { + id, + }, } ); } pub fn end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) { debug_assert_eq!(self.state, State::QueryResources); self.state = State::Idle; self.texture_cache.end_frame(texture_cache_profile);