summaryrefslogtreecommitdiffstats
path: root/gfx/layers
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/layers/CanvasDrawEventRecorder.cpp21
-rw-r--r--gfx/layers/CanvasDrawEventRecorder.h5
-rw-r--r--gfx/layers/FrameMetrics.h4
-rw-r--r--gfx/layers/LayersTypes.h8
-rw-r--r--gfx/layers/NativeLayerCA.h6
-rw-r--r--gfx/layers/NativeLayerCA.mm93
-rw-r--r--gfx/layers/RemoteTextureMap.cpp5
-rw-r--r--gfx/layers/apz/public/APZPublicUtils.h5
-rw-r--r--gfx/layers/apz/public/GeckoContentControllerTypes.h4
-rw-r--r--gfx/layers/apz/src/APZCTreeManager.cpp23
-rw-r--r--gfx/layers/apz/src/APZCTreeManager.h80
-rw-r--r--gfx/layers/apz/src/APZSampler.cpp13
-rw-r--r--gfx/layers/apz/src/APZUtils.h8
-rw-r--r--gfx/layers/apz/src/AsyncPanZoomController.cpp41
-rw-r--r--gfx/layers/apz/src/AsyncPanZoomController.h13
-rw-r--r--gfx/layers/apz/src/HitTestingTreeNode.h14
-rw-r--r--gfx/layers/apz/src/InputBlockState.cpp12
-rw-r--r--gfx/layers/apz/src/InputBlockState.h14
-rw-r--r--gfx/layers/apz/src/SampledAPZCState.cpp2
-rw-r--r--gfx/layers/apz/test/mochitest/helper_bug1669625.html3
-rw-r--r--gfx/layers/apz/test/mochitest/helper_bug1806400-2.html58
-rw-r--r--gfx/layers/apz/test/mochitest/helper_bug1806400-3.html49
-rw-r--r--gfx/layers/apz/test/mochitest/helper_bug1806400-4.html49
-rw-r--r--gfx/layers/apz/test/mochitest/helper_bug1806400.html54
-rw-r--r--gfx/layers/apz/test/mochitest/helper_hittest_iframe_perspective-4.html86
-rw-r--r--gfx/layers/apz/test/mochitest/helper_hittest_pointerevents_svg.html8
-rw-r--r--gfx/layers/apz/test/mochitest/helper_scrollframe_activation_on_load.html5
-rw-r--r--gfx/layers/apz/test/mochitest/helper_touch_synthesized_mouseevents.html9
-rw-r--r--gfx/layers/apz/test/mochitest/test_group_hittest-3.html1
-rw-r--r--gfx/layers/apz/test/mochitest/test_group_touchevents-5.html18
-rw-r--r--gfx/layers/apz/test/mochitest/test_layerization.html5
-rw-r--r--gfx/layers/apz/test/mochitest/test_scroll_inactive_bug1190112.html3
-rw-r--r--gfx/layers/apz/util/APZEventState.cpp23
-rw-r--r--gfx/layers/apz/util/APZEventState.h6
-rw-r--r--gfx/layers/apz/util/ActiveElementManager.cpp119
-rw-r--r--gfx/layers/apz/util/ActiveElementManager.h47
-rw-r--r--gfx/layers/client/TextureClient.cpp7
-rw-r--r--gfx/layers/client/TextureClient.h20
-rw-r--r--gfx/layers/client/TextureClientPool.cpp307
-rw-r--r--gfx/layers/client/TextureClientPool.h175
-rw-r--r--gfx/layers/client/TextureRecorded.cpp48
-rw-r--r--gfx/layers/client/TextureRecorded.h2
-rw-r--r--gfx/layers/ipc/CanvasChild.cpp27
-rw-r--r--gfx/layers/ipc/CanvasChild.h9
-rw-r--r--gfx/layers/ipc/CanvasTranslator.cpp66
-rw-r--r--gfx/layers/ipc/CanvasTranslator.h6
-rw-r--r--gfx/layers/ipc/CompositorBridgeChild.cpp14
-rw-r--r--gfx/layers/ipc/CompositorBridgeChild.h3
-rw-r--r--gfx/layers/ipc/CompositorBridgeParent.cpp20
-rw-r--r--gfx/layers/ipc/LayersMessageUtils.h31
-rw-r--r--gfx/layers/ipc/SharedSurfacesMemoryReport.h25
-rw-r--r--gfx/layers/moz.build2
-rw-r--r--gfx/layers/wr/StackingContextHelper.cpp9
-rw-r--r--gfx/layers/wr/StackingContextHelper.h30
-rw-r--r--gfx/layers/wr/WebRenderBridgeParent.cpp16
-rw-r--r--gfx/layers/wr/WebRenderBridgeParent.h2
-rw-r--r--gfx/layers/wr/WebRenderCommandBuilder.cpp29
-rw-r--r--gfx/layers/wr/WebRenderCommandBuilder.h3
-rw-r--r--gfx/layers/wr/WebRenderLayerManager.cpp25
-rw-r--r--gfx/layers/wr/WebRenderLayerManager.h3
60 files changed, 914 insertions, 879 deletions
diff --git a/gfx/layers/CanvasDrawEventRecorder.cpp b/gfx/layers/CanvasDrawEventRecorder.cpp
index af143bf5cb..6140fc4ba7 100644
--- a/gfx/layers/CanvasDrawEventRecorder.cpp
+++ b/gfx/layers/CanvasDrawEventRecorder.cpp
@@ -127,6 +127,7 @@ int64_t CanvasDrawEventRecorder::CreateCheckpoint() {
int64_t checkpoint = mHeader->eventCount;
RecordEvent(RecordedCheckpoint());
ClearProcessedExternalSurfaces();
+ ClearProcessedExternalImages();
return checkpoint;
}
@@ -276,6 +277,7 @@ void CanvasDrawEventRecorder::DropFreeBuffers() {
}
ClearProcessedExternalSurfaces();
+ ClearProcessedExternalImages();
}
void CanvasDrawEventRecorder::IncrementEventCount() {
@@ -444,6 +446,16 @@ void CanvasDrawEventRecorder::StoreSourceSurfaceRecording(
DrawEventRecorderPrivate::StoreSourceSurfaceRecording(aSurface, aReason);
}
+void CanvasDrawEventRecorder::StoreImageRecording(
+ const RefPtr<Image>& aImageOfSurfaceDescriptor, const char* aReasony) {
+ NS_ASSERT_OWNINGTHREAD(CanvasDrawEventRecorder);
+
+ StoreExternalImageRecording(aImageOfSurfaceDescriptor);
+ mExternalImages.back().mEventCount = mHeader->eventCount;
+
+ ClearProcessedExternalImages();
+}
+
void CanvasDrawEventRecorder::ClearProcessedExternalSurfaces() {
while (!mExternalSurfaces.empty()) {
if (mExternalSurfaces.front().mEventCount > mHeader->processedCount) {
@@ -453,5 +465,14 @@ void CanvasDrawEventRecorder::ClearProcessedExternalSurfaces() {
}
}
+void CanvasDrawEventRecorder::ClearProcessedExternalImages() {
+ while (!mExternalImages.empty()) {
+ if (mExternalImages.front().mEventCount > mHeader->processedCount) {
+ break;
+ }
+ mExternalImages.pop_front();
+ }
+}
+
} // namespace layers
} // namespace mozilla
diff --git a/gfx/layers/CanvasDrawEventRecorder.h b/gfx/layers/CanvasDrawEventRecorder.h
index c9eacf27ac..aa95eec3e6 100644
--- a/gfx/layers/CanvasDrawEventRecorder.h
+++ b/gfx/layers/CanvasDrawEventRecorder.h
@@ -111,6 +111,9 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate,
void StoreSourceSurfaceRecording(gfx::SourceSurface* aSurface,
const char* aReason) final;
+ void StoreImageRecording(const RefPtr<Image>& aImageOfSurfaceDescriptor,
+ const char* aReasony) final;
+
gfx::RecorderType GetRecorderType() const override {
return gfx::RecorderType::CANVAS;
}
@@ -134,6 +137,8 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate,
void ClearProcessedExternalSurfaces();
+ void ClearProcessedExternalImages();
+
protected:
gfx::ContiguousBuffer& GetContiguousBuffer(size_t aSize) final;
diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h
index 5d13d36703..c79a088e53 100644
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -700,7 +700,7 @@ MOZ_DEFINE_ENUM_CLASS_WITH_BASE(
std::ostream& operator<<(std::ostream& aStream,
const OverscrollBehavior& aBehavior);
-struct OverscrollBehaviorInfo {
+struct OverscrollBehaviorInfo final {
OverscrollBehaviorInfo();
// Construct from StyleOverscrollBehavior values.
@@ -711,6 +711,8 @@ struct OverscrollBehaviorInfo {
friend std::ostream& operator<<(std::ostream& aStream,
const OverscrollBehaviorInfo& aInfo);
+ auto MutTiedFields() { return std::tie(mBehaviorX, mBehaviorY); }
+
OverscrollBehavior mBehaviorX;
OverscrollBehavior mBehaviorY;
};
diff --git a/gfx/layers/LayersTypes.h b/gfx/layers/LayersTypes.h
index 340ea76fa5..c02851d81f 100644
--- a/gfx/layers/LayersTypes.h
+++ b/gfx/layers/LayersTypes.h
@@ -46,9 +46,11 @@ class TextureHost;
#undef NONE
#undef OPAQUE
-struct LayersId {
+struct LayersId final {
uint64_t mId = 0;
+ auto MutTiedFields() { return std::tie(mId); }
+
bool IsValid() const { return mId != 0; }
// Allow explicit cast to a uint64_t for now
@@ -75,9 +77,11 @@ struct LayersId {
};
template <typename T>
-struct BaseTransactionId {
+struct BaseTransactionId final {
uint64_t mId = 0;
+ auto MutTiedFields() { return std::tie(mId); }
+
bool IsValid() const { return mId != 0; }
[[nodiscard]] BaseTransactionId<T> Next() const {
diff --git a/gfx/layers/NativeLayerCA.h b/gfx/layers/NativeLayerCA.h
index b41ac36c23..93b6b3a6de 100644
--- a/gfx/layers/NativeLayerCA.h
+++ b/gfx/layers/NativeLayerCA.h
@@ -143,7 +143,7 @@ class NativeLayerRootCA : public NativeLayerRoot {
void SetWindowIsFullscreen(bool aFullscreen);
- VideoLowPowerType CheckVideoLowPower();
+ VideoLowPowerType CheckVideoLowPower(const MutexAutoLock& aProofOfLock);
protected:
explicit NativeLayerRootCA(CALayer* aLayer);
@@ -335,8 +335,7 @@ class NativeLayerCA : public NativeLayer {
Maybe<SurfaceWithInvalidRegion> GetUnusedSurfaceAndCleanUp(
const MutexAutoLock& aProofOfLock);
- bool IsVideo();
- bool IsVideoAndLocked(const MutexAutoLock& aProofOfLock);
+ bool IsVideo(const MutexAutoLock& aProofOfLock);
bool ShouldSpecializeVideo(const MutexAutoLock& aProofOfLock);
bool HasExtent() const { return mHasExtent; }
void SetHasExtent(bool aHasExtent) { mHasExtent = aHasExtent; }
@@ -484,6 +483,7 @@ class NativeLayerCA : public NativeLayer {
bool mSpecializeVideo = false;
bool mHasExtent = false;
bool mIsDRM = false;
+ bool mIsTextureHostVideo = false;
#ifdef NIGHTLY_BUILD
// Track the consistency of our caller's API usage. Layers that are drawn
diff --git a/gfx/layers/NativeLayerCA.mm b/gfx/layers/NativeLayerCA.mm
index 42a889184e..a983e3a45e 100644
--- a/gfx/layers/NativeLayerCA.mm
+++ b/gfx/layers/NativeLayerCA.mm
@@ -330,37 +330,38 @@ bool NativeLayerRootCA::CommitToScreen() {
mWindowIsFullscreen);
mCommitPending = false;
- }
- if (StaticPrefs::gfx_webrender_debug_dump_native_layer_tree_to_file()) {
- static uint32_t sFrameID = 0;
- uint32_t frameID = sFrameID++;
-
- NSString* dirPath =
- [NSString stringWithFormat:@"%@/Desktop/nativelayerdumps-%d",
- NSHomeDirectory(), getpid()];
- if ([NSFileManager.defaultManager createDirectoryAtPath:dirPath
- withIntermediateDirectories:YES
- attributes:nil
- error:nullptr]) {
- NSString* filename =
- [NSString stringWithFormat:@"frame-%d.html", frameID];
- NSString* filePath = [dirPath stringByAppendingPathComponent:filename];
- DumpLayerTreeToFile([filePath UTF8String]);
- } else {
- NSLog(@"Failed to create directory %@", dirPath);
+ if (StaticPrefs::gfx_webrender_debug_dump_native_layer_tree_to_file()) {
+ static uint32_t sFrameID = 0;
+ uint32_t frameID = sFrameID++;
+
+ NSString* dirPath =
+ [NSString stringWithFormat:@"%@/Desktop/nativelayerdumps-%d",
+ NSHomeDirectory(), getpid()];
+ if ([NSFileManager.defaultManager createDirectoryAtPath:dirPath
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:nullptr]) {
+ NSString* filename =
+ [NSString stringWithFormat:@"frame-%d.html", frameID];
+ NSString* filePath = [dirPath stringByAppendingPathComponent:filename];
+ DumpLayerTreeToFile([filePath UTF8String]);
+ } else {
+ NSLog(@"Failed to create directory %@", dirPath);
+ }
}
- }
- // Decide if we are going to emit telemetry about video low power on this
- // commit.
- static const int32_t TELEMETRY_COMMIT_PERIOD =
- StaticPrefs::gfx_core_animation_low_power_telemetry_frames_AtStartup();
- mTelemetryCommitCount = (mTelemetryCommitCount + 1) % TELEMETRY_COMMIT_PERIOD;
- if (mTelemetryCommitCount == 0) {
- // Figure out if we are hitting video low power mode.
- VideoLowPowerType videoLowPower = CheckVideoLowPower();
- EmitTelemetryForVideoLowPower(videoLowPower);
+ // Decide if we are going to emit telemetry about video low power on this
+ // commit.
+ static const int32_t TELEMETRY_COMMIT_PERIOD =
+ StaticPrefs::gfx_core_animation_low_power_telemetry_frames_AtStartup();
+ mTelemetryCommitCount =
+ (mTelemetryCommitCount + 1) % TELEMETRY_COMMIT_PERIOD;
+ if (mTelemetryCommitCount == 0) {
+ // Figure out if we are hitting video low power mode.
+ VideoLowPowerType videoLowPower = CheckVideoLowPower(lock);
+ EmitTelemetryForVideoLowPower(videoLowPower);
+ }
}
return true;
@@ -579,7 +580,8 @@ void NativeLayerRootCA::SetWindowIsFullscreen(bool aFullscreen) {
return components[componentCount - 1] >= 1.0f;
}
-VideoLowPowerType NativeLayerRootCA::CheckVideoLowPower() {
+VideoLowPowerType NativeLayerRootCA::CheckVideoLowPower(
+ const MutexAutoLock& aProofOfLock) {
// This deteremines whether the current layer contents qualify for the
// macOS Core Animation video low power mode. Those requirements are
// summarized at
@@ -609,7 +611,7 @@ VideoLowPowerType NativeLayerRootCA::CheckVideoLowPower() {
secondCALayer = topCALayer;
topCALayer = topLayer->UnderlyingCALayer(WhichRepresentation::ONSCREEN);
- topLayerIsVideo = topLayer->IsVideo();
+ topLayerIsVideo = topLayer->IsVideo(aProofOfLock);
if (topLayerIsVideo) {
++videoLayerCount;
}
@@ -835,9 +837,8 @@ NativeLayerCA::NativeLayerCA(bool aIsOpaque)
mIsOpaque(aIsOpaque) {
#ifdef NIGHTLY_BUILD
if (StaticPrefs::gfx_core_animation_specialize_video_log()) {
- NSLog(@"VIDEO_LOG: NativeLayerCA: %p is being created to host video, which "
- @"will force a video "
- @"layer rebuild.",
+ NSLog(@"VIDEO_LOG: NativeLayerCA: %p is being created to host an external "
+ @"image, which may force a video layer rebuild.",
this);
}
#endif
@@ -864,7 +865,7 @@ NativeLayerCA::~NativeLayerCA() {
if (mHasEverAttachExternalImage &&
StaticPrefs::gfx_core_animation_specialize_video_log()) {
NSLog(@"VIDEO_LOG: ~NativeLayerCA: %p is being destroyed after hosting "
- @"video.",
+ @"an external image.",
this);
}
#endif
@@ -902,6 +903,9 @@ void NativeLayerCA::AttachExternalImage(wr::RenderTextureHost* aExternalImage) {
return;
}
+ // Determine if TextureHost is a video surface.
+ mIsTextureHostVideo = gfx::Info(mTextureHost->GetFormat())->isYuv;
+
gfx::IntSize oldSize = mSize;
mSize = texture->GetSize(0);
bool changedSizeAndDisplayRect = (mSize != oldSize);
@@ -933,18 +937,15 @@ void NativeLayerCA::AttachExternalImage(wr::RenderTextureHost* aExternalImage) {
});
}
-bool NativeLayerCA::IsVideo() {
- // Anything with a texture host is considered a video source.
- return mTextureHost;
-}
-
-bool NativeLayerCA::IsVideoAndLocked(const MutexAutoLock& aProofOfLock) {
- // Anything with a texture host is considered a video source.
- return mTextureHost;
+bool NativeLayerCA::IsVideo(const MutexAutoLock& aProofOfLock) {
+ // If we have a texture host, we've checked to see if it's providing video.
+ // And if we don't have a texture host, it isn't video, so we just check
+ // the value we've computed.
+ return mIsTextureHostVideo;
}
bool NativeLayerCA::ShouldSpecializeVideo(const MutexAutoLock& aProofOfLock) {
- if (!IsVideoAndLocked(aProofOfLock)) {
+ if (!IsVideo(aProofOfLock)) {
// Only videos are eligible.
return false;
}
@@ -1410,6 +1411,8 @@ void NativeLayerCA::NotifySurfaceReady() {
mInProgressSurface,
"NotifySurfaceReady called without preceding call to NextSurface");
+ mIsTextureHostVideo = false;
+
if (mInProgressLockedIOSurface) {
mInProgressLockedIOSurface->Unlock(false);
mInProgressLockedIOSurface = nullptr;
@@ -1465,7 +1468,7 @@ void NativeLayerCA::ForAllRepresentations(F aFn) {
NativeLayerCA::UpdateType NativeLayerCA::HasUpdate(
WhichRepresentation aRepresentation) {
MutexAutoLock lock(mMutex);
- return GetRepresentation(aRepresentation).HasUpdate(IsVideoAndLocked(lock));
+ return GetRepresentation(aRepresentation).HasUpdate(IsVideo(lock));
}
/* static */
@@ -1510,7 +1513,7 @@ bool NativeLayerCA::ApplyChanges(WhichRepresentation aRepresentation,
.ApplyChanges(aUpdate, mSize, mIsOpaque, mPosition, mTransform,
mDisplayRect, mClipRect, mBackingScale, mSurfaceIsFlipped,
mSamplingFilter, mSpecializeVideo, surface, mColor, mIsDRM,
- IsVideo());
+ IsVideo(lock));
}
CALayer* NativeLayerCA::UnderlyingCALayer(WhichRepresentation aRepresentation) {
diff --git a/gfx/layers/RemoteTextureMap.cpp b/gfx/layers/RemoteTextureMap.cpp
index 3fe3b13deb..95db676651 100644
--- a/gfx/layers/RemoteTextureMap.cpp
+++ b/gfx/layers/RemoteTextureMap.cpp
@@ -19,6 +19,7 @@
#include "mozilla/layers/RemoteTextureHostWrapper.h"
#include "mozilla/layers/TextureClientSharedSurface.h"
#include "mozilla/layers/WebRenderTextureHost.h"
+#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_webgl.h"
#include "mozilla/webgpu/ExternalTexture.h"
#include "mozilla/webrender/RenderThread.h"
@@ -334,7 +335,9 @@ bool RemoteTextureMap::RecycleTexture(
// Recycle texture data
recycled.mTextureData = std::move(aHolder.mTextureData);
}
- aRecycleBin->mRecycledTextures.push_back(std::move(recycled));
+ if (!StaticPrefs::gfx_remote_texture_recycle_disabled()) {
+ aRecycleBin->mRecycledTextures.push_back(std::move(recycled));
+ }
return true;
}
diff --git a/gfx/layers/apz/public/APZPublicUtils.h b/gfx/layers/apz/public/APZPublicUtils.h
index 6433008b4c..47a07c8fd6 100644
--- a/gfx/layers/apz/public/APZPublicUtils.h
+++ b/gfx/layers/apz/public/APZPublicUtils.h
@@ -55,9 +55,8 @@ const ScreenMargin CalculatePendingDisplayPort(
* between 1 and 8 inclusive. The multiplier is chosen based on the provided
* base size, such that multiplier is larger when the base size is larger.
* The exact details are somewhat arbitrary and tuned by hand.
- * This function is intended to only be used with WebRender, because that is
- * the codepath that wants to use a larger displayport alignment, because
- * moving the displayport is relatively expensive with WebRender.
+ * We use a large displayport alignment because moving the displayport is
+ * relatively expensive with WebRender.
*/
gfx::IntSize GetDisplayportAlignmentMultiplier(const ScreenSize& aBaseSize);
diff --git a/gfx/layers/apz/public/GeckoContentControllerTypes.h b/gfx/layers/apz/public/GeckoContentControllerTypes.h
index 8ab478eab5..8616455137 100644
--- a/gfx/layers/apz/public/GeckoContentControllerTypes.h
+++ b/gfx/layers/apz/public/GeckoContentControllerTypes.h
@@ -24,7 +24,7 @@ MOZ_DEFINE_ENUM_CLASS(GeckoContentController_APZStateChange, (
eTransformEnd,
/**
* APZ started a touch.
- * |aArg| is 1 if touch can be a pan, 0 otherwise.
+ * |aArg| is 1 if touch can be a pan or zoom, 0 otherwise.
*/
eStartTouch,
/**
@@ -33,7 +33,7 @@ MOZ_DEFINE_ENUM_CLASS(GeckoContentController_APZStateChange, (
eStartPanning,
/**
* APZ finished processing a touch.
- * |aArg| is 1 if touch was a click, 0 otherwise.
+ * |aArg| is a `apz::SingleTapState` defined in APZUtils.h.
*/
eEndTouch
));
diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp
index ef3cde3596..bc0b6a8dc6 100644
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -145,8 +145,7 @@ struct APZCTreeManager::TreeBuildingState {
// root node of the layers (sub-)tree, which may not be same as the RCD node
// for the subtree, and so we need this mechanism to ensure it gets propagated
// to the RCD's APZC instance. Once it is set on the APZC instance, the value
- // is cleared back to Nothing(). Note that this is only used in the WebRender
- // codepath.
+ // is cleared back to Nothing().
Maybe<uint64_t> mZoomAnimationId;
// See corresponding members of APZCTreeManager. These are the same thing, but
@@ -538,12 +537,9 @@ APZCTreeManager::UpdateHitTestingTree(const WebRenderScrollDataWrapper& aRoot,
AsyncPanZoomController* apzc = node->GetApzc();
aLayerMetrics.SetApzc(apzc);
- // GetScrollbarAnimationId is only set when webrender is enabled,
- // which limits the extra thumb mapping work to the webrender-enabled
- // case where it is needed.
- // Note also that when webrender is enabled, a "valid" animation id
- // is always nonzero, so we don't need to worry about handling the
- // case where WR is enabled and the animation id is zero.
+ // Note that a "valid" animation id is always nonzero, so we don't
+ // need to worry about handling the case where the animation id is
+ // zero.
if (node->GetScrollbarAnimationId()) {
if (node->IsScrollThumbNode()) {
state.mScrollThumbs.push_back(node);
@@ -555,11 +551,9 @@ APZCTreeManager::UpdateHitTestingTree(const WebRenderScrollDataWrapper& aRoot,
}
}
- // GetFixedPositionAnimationId is only set when webrender is enabled.
if (node->GetFixedPositionAnimationId().isSome()) {
state.mFixedPositionInfo.emplace_back(node);
}
- // GetStickyPositionAnimationId is only set when webrender is enabled.
if (node->GetStickyPositionAnimationId().isSome()) {
state.mStickyPositionInfo.emplace_back(node);
}
@@ -2290,8 +2284,7 @@ void APZCTreeManager::SetupScrollbarDrag(
// Under some conditions, we can confirm the drag block right away.
// Otherwise, we have to wait for a main-thread confirmation.
- if (StaticPrefs::apz_drag_initial_enabled() &&
- // check that the scrollbar's target scroll frame is layerized
+ if (/* check that the scrollbar's target scroll frame is layerized */
aScrollThumbNode->GetScrollTargetId() == aApzc->GetGuid().mScrollId &&
!aApzc->IsScrollInfoLayer()) {
uint64_t dragBlockId = dragBlock->GetBlockId();
@@ -3538,12 +3531,6 @@ already_AddRefed<AsyncPanZoomController> APZCTreeManager::CommonAncestor(
}
bool APZCTreeManager::IsFixedToRootContent(
- const HitTestingTreeNode* aNode) const {
- MutexAutoLock lock(mMapLock);
- return IsFixedToRootContent(FixedPositionInfo(aNode), lock);
-}
-
-bool APZCTreeManager::IsFixedToRootContent(
const FixedPositionInfo& aFixedInfo,
const MutexAutoLock& aProofOfMapLock) const {
ScrollableLayerGuid::ViewID fixedTarget = aFixedInfo.mFixedPosTarget;
diff --git a/gfx/layers/apz/src/APZCTreeManager.h b/gfx/layers/apz/src/APZCTreeManager.h
index 71d35fd5a6..939e7572a4 100644
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -199,12 +199,10 @@ class APZCTreeManager : public IAPZCTreeManager, public APZInputBridge {
LayersId aOriginatingLayersId, uint32_t aPaintSequenceNumber);
/**
- * Called when webrender is enabled, from the sampler thread. This function
- * populates the provided transaction with any async scroll offsets needed.
- * It also advances APZ animations to the specified sample time, and requests
- * another composite if there are still active animations.
- * In effect it is the webrender equivalent of (part of) the code in
- * AsyncCompositionManager.
+ * Called from the sampler thread. This function populates the provided
+ * transaction with any async scroll offsets needed. It also advances APZ
+ * animations to the specified sample time, and requests another composite if
+ * there are still active animations.
*/
void SampleForWebRender(const Maybe<VsyncId>& aVsyncId,
wr::TransactionWrapper& aTxn,
@@ -509,7 +507,7 @@ class APZCTreeManager : public IAPZCTreeManager, public APZInputBridge {
void AssertOnUpdaterThread();
// Returns a pointer to the WebRenderAPI this APZCTreeManager is for.
- // This might be null (for example, if WebRender is not enabled).
+ // This might be null (for example, during GTests).
already_AddRefed<wr::WebRenderAPI> GetWebRenderAPI() const;
protected:
@@ -679,12 +677,8 @@ class APZCTreeManager : public IAPZCTreeManager, public APZInputBridge {
struct FixedPositionInfo;
struct StickyPositionInfo;
- // Returns true if |aNode| is a fixed layer that is fixed to the root content
- // APZC.
- // The map lock is required within these functions; if the map lock is already
- // being held by the caller, the second overload should be used. If the map
- // lock is not being held at the call site, the first overload should be used.
- bool IsFixedToRootContent(const HitTestingTreeNode* aNode) const;
+ // Returns true if |aFixedInfo| represents a layer that is fixed to the root
+ // content APZC.
bool IsFixedToRootContent(const FixedPositionInfo& aFixedInfo,
const MutexAutoLock& aProofOfMapLock) const;
@@ -919,15 +913,14 @@ class APZCTreeManager : public IAPZCTreeManager, public APZInputBridge {
}
};
/**
- * If this APZCTreeManager is being used with WebRender, this vector gets
- * populated during a layers update. It holds a package of information needed
- * to compute and set the async transforms on scroll thumbs. This information
- * is extracted from the HitTestingTreeNodes for the WebRender case because
- * accessing the HitTestingTreeNodes requires holding the tree lock which
- * we cannot do on the WR sampler thread. mScrollThumbInfo, however, can
+ * This vector gets populated during a layers update. It holds a package of
+ * information needed to compute and set the async transforms on scroll
+ * thumbs. This information is extracted from the HitTestingTreeNodes because
+ * accessing the HitTestingTreeNodes requires holding the tree lock which we
+ * cannot do on the WebRender sampler thread. mScrollThumbInfo, however, can
* be accessed while just holding the mMapLock which is safe to do on the
- * sampler thread.
- * mMapLock must be acquired while accessing or modifying mScrollThumbInfo.
+ * sampler thread. mMapLock must be acquired while accessing or modifying
+ * mScrollThumbInfo.
*/
std::vector<ScrollThumbInfo> mScrollThumbInfo;
@@ -945,12 +938,11 @@ class APZCTreeManager : public IAPZCTreeManager, public APZInputBridge {
mScrollDirection(aScrollDirection) {}
};
/**
- * If this APZCTreeManager is being used with WebRender, this vector gets
- * populated during a layers update. It holds a package of information needed
- * to compute and set the async transforms on root scrollbars. This
- * information is extracted from the HitTestingTreeNodes for the WebRender
- * case because accessing the HitTestingTreeNodes requires holding the tree
- * lock which we cannot do on the WR sampler thread. mRootScrollbarInfo,
+ * This vector gets populated during a layers update. It holds a package of
+ * information needed to compute and set the async transforms on root
+ * scrollbars. This information is extracted from the HitTestingTreeNodes
+ * because accessing the HitTestingTreeNodes requires holding the tree lock
+ * which we cannot do on the WebRender sampler thread. mRootScrollbarInfo,
* however, can be accessed while just holding the mMapLock which is safe to
* do on the sampler thread.
* mMapLock must be acquired while accessing or modifying mRootScrollbarInfo.
@@ -970,15 +962,14 @@ class APZCTreeManager : public IAPZCTreeManager, public APZInputBridge {
explicit FixedPositionInfo(const HitTestingTreeNode* aNode);
};
/**
- * If this APZCTreeManager is being used with WebRender, this vector gets
- * populated during a layers update. It holds a package of information needed
- * to compute and set the async transforms on fixed position content. This
- * information is extracted from the HitTestingTreeNodes for the WebRender
- * case because accessing the HitTestingTreeNodes requires holding the tree
- * lock which we cannot do on the WR sampler thread. mFixedPositionInfo,
- * however, can be accessed while just holding the mMapLock which is safe to
- * do on the sampler thread. mMapLock must be acquired while accessing or
- * modifying mFixedPositionInfo.
+ * This vector gets populated during a layers update. It holds a package of
+ * information needed to compute and set the async transforms on fixed
+ * position content. This information is extracted from the
+ * HitTestingTreeNodes because accessing the HitTestingTreeNodes requires
+ * holding the tree lock which we cannot do on the WebRender sampler thread.
+ * mFixedPositionInfo, however, can be accessed while just holding the
+ * mMapLock which is safe to do on the sampler thread. mMapLock must be
+ * acquired while accessing or modifying mFixedPositionInfo.
*/
std::vector<FixedPositionInfo> mFixedPositionInfo;
@@ -997,15 +988,14 @@ class APZCTreeManager : public IAPZCTreeManager, public APZInputBridge {
explicit StickyPositionInfo(const HitTestingTreeNode* aNode);
};
/**
- * If this APZCTreeManager is being used with WebRender, this vector gets
- * populated during a layers update. It holds a package of information needed
- * to compute and set the async transforms on sticky position content. This
- * information is extracted from the HitTestingTreeNodes for the WebRender
- * case because accessing the HitTestingTreeNodes requires holding the tree
- * lock which we cannot do on the WR sampler thread. mStickyPositionInfo,
- * however, can be accessed while just holding the mMapLock which is safe to
- * do on the sampler thread. mMapLock must be acquired while accessing or
- * modifying mStickyPositionInfo.
+ * This vector gets populated during a layers update. It holds a package of
+ * information needed to compute and set the async transforms on sticky
+ * position content. This information is extracted from the
+ * HitTestingTreeNodes because accessing the HitTestingTreeNodes requires
+ * holding the tree lock which we cannot do on the WebRender sampler thread.
+ * mStickyPositionInfo, however, can be accessed while just holding the
+ * mMapLock which is safe to do on the sampler thread. mMapLock must be
+ * acquired while accessing or modifying mStickyPositionInfo.
*/
std::vector<StickyPositionInfo> mStickyPositionInfo;
diff --git a/gfx/layers/apz/src/APZSampler.cpp b/gfx/layers/apz/src/APZSampler.cpp
index d0e251cec4..e14535da0d 100644
--- a/gfx/layers/apz/src/APZSampler.cpp
+++ b/gfx/layers/apz/src/APZSampler.cpp
@@ -125,19 +125,16 @@ AsyncTransform APZSampler::GetCurrentAsyncTransform(
ParentLayerRect APZSampler::GetCompositionBounds(
const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
const MutexAutoLock& aProofOfMapLock) const {
- // This function can get called on the compositor in case of non WebRender
- // get called on the sampler thread in case of WebRender.
AssertOnSamplerThread();
RefPtr<AsyncPanZoomController> apzc =
mApz->GetTargetAPZC(aLayersId, aScrollId, aProofOfMapLock);
if (!apzc) {
- // On WebRender it's possible that this function can get called even after
- // the target APZC has been already destroyed because destroying the
- // animation which triggers this function call is basically processed later
- // than the APZC one, i.e. queue mCompositorAnimationsToDelete in
- // WebRenderBridgeParent and then remove them in
- // WebRenderBridgeParent::RemoveEpochDataPriorTo.
+ // It's possible that this function can get called even after the target
+ // APZC has been already destroyed because destroying the animation which
+ // triggers this function call is basically processed later than the APZC
+ // one, i.e. queue mCompositorAnimationsToDelete in WebRenderBridgeParent
+ // and then remove them in WebRenderBridgeParent::RemoveEpochDataPriorTo.
return ParentLayerRect();
}
diff --git a/gfx/layers/apz/src/APZUtils.h b/gfx/layers/apz/src/APZUtils.h
index 6614b6eeae..f36a01f65d 100644
--- a/gfx/layers/apz/src/APZUtils.h
+++ b/gfx/layers/apz/src/APZUtils.h
@@ -233,6 +233,14 @@ bool AboutToCheckerboard(const FrameMetrics& aPaintedMetrics,
*/
SideBits GetOverscrollSideBits(const ParentLayerPoint& aOverscrollAmount);
+// Represents tri-state when a touch-end event received.
+enum class SingleTapState : uint8_t {
+ NotClick, // The touch-block doesn't trigger a click event
+ WasClick, // The touch-block did trigger a click event
+ NotYetDetermined, // It's not yet determined whether the touch-block trigger
+ // a click event or not since double-tapping might happen
+};
+
} // namespace apz
} // namespace layers
diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp
index edbd2ecffa..a070340421 100644
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -242,11 +242,6 @@ typedef PlatformSpecificStateBase
* Setting this pref to true will cause APZ to handle mouse-dragging of
* scrollbar thumbs.
*
- * \li\b apz.drag.initial.enabled
- * Setting this pref to true will cause APZ to try to handle mouse-dragging
- * of scrollbar thumbs without an initial round-trip to content to start it
- * if possible. Only has an effect if apz.drag.enabled is also true.
- *
* \li\b apz.drag.touch.enabled
* Setting this pref to true will cause APZ to handle touch-dragging of
* scrollbar thumbs. Only has an effect if apz.drag.enabled is also true.
@@ -1310,10 +1305,13 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(
if (RefPtr<GeckoContentController> controller =
GetGeckoContentController()) {
MOZ_ASSERT(GetCurrentTouchBlock());
- controller->NotifyAPZStateChange(
- GetGuid(), APZStateChange::eStartTouch,
+ const bool canBePanOrZoom =
GetCurrentTouchBlock()->GetOverscrollHandoffChain()->CanBePanned(
- this),
+ this) ||
+ (ZoomConstraintsAllowDoubleTapZoom() &&
+ GetCurrentTouchBlock()->TouchActionAllowsDoubleTapZoom());
+ controller->NotifyAPZStateChange(
+ GetGuid(), APZStateChange::eStartTouch, canBePanOrZoom,
Some(GetCurrentTouchBlock()->GetBlockId()));
}
mLastTouch.mTimeStamp = mTouchStartTime = aEvent.mTimeStamp;
@@ -3112,7 +3110,7 @@ nsEventStatus AsyncPanZoomController::GenerateSingleTap(
// touch block caused a `click` event or not, thus for long-tap events,
// it's not necessary.
if (aType != TapType::eLongTapUp) {
- touch->SetSingleTapOccurred();
+ touch->SetSingleTapState(apz::SingleTapState::WasClick);
}
}
// Because this may be being running as part of
@@ -3143,7 +3141,7 @@ void AsyncPanZoomController::OnTouchEndOrCancel() {
MOZ_ASSERT(GetCurrentTouchBlock());
controller->NotifyAPZStateChange(
GetGuid(), APZStateChange::eEndTouch,
- GetCurrentTouchBlock()->SingleTapOccurred(),
+ static_cast<int>(GetCurrentTouchBlock()->SingleTapState()),
Some(GetCurrentTouchBlock()->GetBlockId()));
}
}
@@ -3160,6 +3158,21 @@ nsEventStatus AsyncPanZoomController::OnSingleTapUp(
return GenerateSingleTap(TapType::eSingleTap, aEvent.mPoint,
aEvent.modifiers);
}
+
+ // Ignore the event if it does not have valid local coordinates.
+ // GenerateSingleTap will not send a tap in this case.
+ if (!ConvertToGecko(aEvent.mPoint)) {
+ return nsEventStatus_eIgnore;
+ }
+
+ // Here we need to wait for the call to OnSingleTapConfirmed, we need to tell
+ // it to ActiveElementManager so that we can do element activation once
+ // ActiveElementManager got a single tap event later.
+ if (TouchBlockState* touch = GetCurrentTouchBlock()) {
+ if (!touch->IsDuringFastFling()) {
+ touch->SetSingleTapState(apz::SingleTapState::NotYetDetermined);
+ }
+ }
return nsEventStatus_eIgnore;
}
@@ -5304,10 +5317,10 @@ void AsyncPanZoomController::UpdateCheckerboardEvent(
const MutexAutoLock& aProofOfLock, uint32_t aMagnitude) {
if (mCheckerboardEvent && mCheckerboardEvent->RecordFrameInfo(aMagnitude)) {
// This checkerboard event is done. Report some metrics to telemetry.
- mozilla::glean::gfx_checkerboard::severity.AccumulateSamples(
- {mCheckerboardEvent->GetSeverity()});
- mozilla::glean::gfx_checkerboard::peak_pixel_count.AccumulateSamples(
- {mCheckerboardEvent->GetPeak()});
+ mozilla::glean::gfx_checkerboard::severity.AccumulateSingleSample(
+ mCheckerboardEvent->GetSeverity());
+ mozilla::glean::gfx_checkerboard::peak_pixel_count.AccumulateSingleSample(
+ mCheckerboardEvent->GetPeak());
mozilla::glean::gfx_checkerboard::duration.AccumulateRawDuration(
mCheckerboardEvent->GetDuration());
diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h
index d0c4537a66..5db831d1bf 100644
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -1133,13 +1133,12 @@ class AsyncPanZoomController {
UniquePtr<OverscrollEffectBase> mOverscrollEffect;
- // Zoom animation id, used for zooming in WebRender. This should only be
- // set on the APZC instance for the root content document (i.e. the one we
- // support zooming on), and is only used if WebRender is enabled. The
- // animation id itself refers to the transform animation id that was set on
- // the stacking context in the WR display list. By changing the transform
- // associated with this id, we can adjust the scaling that WebRender applies,
- // thereby controlling the zoom.
+ // Zoom animation id, used for zooming. This should only be set on the APZC
+ // instance for the root content document (i.e. the one we support zooming
+ // on). The animation id itself refers to the transform animation id that was
+ // set on the stacking context in the WR display list. By changing the
+ // transform associated with this id, we can adjust the scaling that WebRender
+ // applies, thereby controlling the zoom.
Maybe<uint64_t> mZoomAnimationId;
// Position on screen where user first put their finger down.
diff --git a/gfx/layers/apz/src/HitTestingTreeNode.h b/gfx/layers/apz/src/HitTestingTreeNode.h
index a4958b1af5..b76f317825 100644
--- a/gfx/layers/apz/src/HitTestingTreeNode.h
+++ b/gfx/layers/apz/src/HitTestingTreeNode.h
@@ -182,16 +182,16 @@ class HitTestingTreeNode {
LayersId mLayersId;
- // This is only set if WebRender is enabled, and only for HTTNs
- // where IsScrollThumbNode() returns true. It holds the animation id that we
- // use to move the thumb node to reflect async scrolling.
+ // This is only set for HTTNs where IsScrollThumbNode() returns true. It holds
+ // the animation id that we use to move the thumb node to reflect async
+ // scrolling.
Maybe<uint64_t> mScrollbarAnimationId;
// This is set for scrollbar Container and Thumb layers.
ScrollbarData mScrollbarData;
- // This is only set if WebRender is enabled. It holds the animation id that
- // we use to adjust fixed position content for the toolbar.
+ // This holds the animation id that we use to adjust fixed position content
+ // for the toolbar.
Maybe<uint64_t> mFixedPositionAnimationId;
ScrollableLayerGuid::ViewID mFixedPosTarget;
@@ -200,8 +200,8 @@ class HitTestingTreeNode {
ScrollableLayerGuid::ViewID mStickyPosTarget;
LayerRectAbsolute mStickyScrollRangeOuter;
LayerRectAbsolute mStickyScrollRangeInner;
- // This is only set if WebRender is enabled. It holds the animation id that
- // we use to adjust sticky position content for the toolbar.
+ // This holds the animation id that we use to adjust sticky position content
+ // for the toolbar.
Maybe<uint64_t> mStickyPositionAnimationId;
LayerIntRect mVisibleRect;
diff --git a/gfx/layers/apz/src/InputBlockState.cpp b/gfx/layers/apz/src/InputBlockState.cpp
index 367fbf9e90..785df56c10 100644
--- a/gfx/layers/apz/src/InputBlockState.cpp
+++ b/gfx/layers/apz/src/InputBlockState.cpp
@@ -16,6 +16,7 @@
#include "mozilla/StaticPrefs_test.h"
#include "mozilla/Telemetry.h" // for Telemetry
#include "mozilla/ToString.h"
+#include "mozilla/layers/APZEventState.h"
#include "mozilla/layers/IAPZCTreeManager.h" // for AllowedTouchBehavior
#include "OverscrollHandoffState.h"
#include "QueuedInput.h"
@@ -636,12 +637,12 @@ TouchBlockState::TouchBlockState(
: CancelableBlockState(aTargetApzc, aFlags),
mAllowedTouchBehaviorSet(false),
mDuringFastFling(false),
- mSingleTapOccurred(false),
mInSlop(false),
mForLongTap(false),
mLongTapWasProcessed(false),
mIsWaitingLongTapResult(false),
mNeedsWaitTouchMove(false),
+ mSingleTapState(apz::SingleTapState::NotClick),
mTouchCounter(aCounter),
mStartTime(GetTargetApzc()->GetFrameTime().Time()) {
mOriginalTargetConfirmedState = mTargetConfirmed;
@@ -700,13 +701,12 @@ void TouchBlockState::SetDuringFastFling() {
bool TouchBlockState::IsDuringFastFling() const { return mDuringFastFling; }
-void TouchBlockState::SetSingleTapOccurred() {
- TBS_LOG("%p setting single-tap-occurred flag\n", this);
- mSingleTapOccurred = true;
+void TouchBlockState::SetSingleTapState(apz::SingleTapState aState) {
+ TBS_LOG("%p setting single-tap-state: %d\n", this,
+ static_cast<uint8_t>(aState));
+ mSingleTapState = aState;
}
-bool TouchBlockState::SingleTapOccurred() const { return mSingleTapOccurred; }
-
bool TouchBlockState::MustStayActive() {
// If this touch block is for long-tap, it doesn't need to be active after the
// block was processed, it will be taken over by the original touch block
diff --git a/gfx/layers/apz/src/InputBlockState.h b/gfx/layers/apz/src/InputBlockState.h
index f20a4a5901..d65b1cb57b 100644
--- a/gfx/layers/apz/src/InputBlockState.h
+++ b/gfx/layers/apz/src/InputBlockState.h
@@ -454,14 +454,12 @@ class TouchBlockState : public CancelableBlockState {
*/
bool IsDuringFastFling() const;
/**
- * Set the single-tap-occurred flag that indicates that this touch block
- * triggered a single tap event.
+ * Set the single-tap state flag that indicates that this touch block
+ * triggered (1) a click, (2) not a click, or (3) not yet sure it will trigger
+ * a click or not.
*/
- void SetSingleTapOccurred();
- /**
- * @return true iff the single-tap-occurred flag is set on this block.
- */
- bool SingleTapOccurred() const;
+ void SetSingleTapState(apz::SingleTapState aState);
+ apz::SingleTapState SingleTapState() const { return mSingleTapState; }
/**
* @return false iff touch-action is enabled and the allowed touch behaviors
@@ -537,7 +535,6 @@ class TouchBlockState : public CancelableBlockState {
nsTArray<TouchBehaviorFlags> mAllowedTouchBehaviors;
bool mAllowedTouchBehaviorSet;
bool mDuringFastFling;
- bool mSingleTapOccurred;
bool mInSlop;
// A long tap involves two touch blocks: the original touch
// block containing the `touchstart`, and a second one
@@ -557,6 +554,7 @@ class TouchBlockState : public CancelableBlockState {
// content response for a touch move event. It will be set just before
// triggering a long-press event.
bool mNeedsWaitTouchMove;
+ apz::SingleTapState mSingleTapState;
ScreenIntPoint mSlopOrigin;
// A reference to the InputQueue's touch counter
TouchCounter& mTouchCounter;
diff --git a/gfx/layers/apz/src/SampledAPZCState.cpp b/gfx/layers/apz/src/SampledAPZCState.cpp
index 712a46a3b1..8cdd905aba 100644
--- a/gfx/layers/apz/src/SampledAPZCState.cpp
+++ b/gfx/layers/apz/src/SampledAPZCState.cpp
@@ -86,6 +86,8 @@ void SampledAPZCState::RemoveFractionalAsyncDelta() {
// a snapshot of APZ state (decoupling it from APZ assumptions) and provides
// it as an input to the compositor (so all compositor state should be
// internally consistent based on this input).
+ // TODO(bug 1889267): Now that we use WebRender everywhere, can this hack be
+ // removed?
if (mLayoutViewport.TopLeft() == mVisualScrollOffset) {
return;
}
diff --git a/gfx/layers/apz/test/mochitest/helper_bug1669625.html b/gfx/layers/apz/test/mochitest/helper_bug1669625.html
index 95d2a4bc2c..4a91c7f0b6 100644
--- a/gfx/layers/apz/test/mochitest/helper_bug1669625.html
+++ b/gfx/layers/apz/test/mochitest/helper_bug1669625.html
@@ -11,8 +11,7 @@
<script type="application/javascript">
async function test() {
- if (SpecialPowers.getBoolPref("apz.force_disable_desktop_zooming_scrollbars") ||
- getPlatform() == "android") {
+ if (getPlatform() == "android") {
return;
}
diff --git a/gfx/layers/apz/test/mochitest/helper_bug1806400-2.html b/gfx/layers/apz/test/mochitest/helper_bug1806400-2.html
new file mode 100644
index 0000000000..8d2eeca58d
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_bug1806400-2.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+<meta name="viewport" content="width=device-width; initial-scale=0.4">
+<title>Tests that double-tap-to-zoom never activates elements inside a scrollable container</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="apz_test_utils.js"></script>
+<script src="apz_test_native_event_utils.js"></script>
+<style>
+#scrollable {
+ height: 50vh;
+ width: 50vw;
+ background: yellow;
+ overflow: scroll;
+}
+
+#scrollabletarget {
+ height: 200vh;
+ width: 200vh;
+ background: green;
+}
+
+#scrollabletarget:active {
+ background: red;
+}
+
+</style>
+<div id="scrollable">
+ <div id="scrollabletarget">
+ </div>
+</div>
+<script>
+async function test() {
+ ok(!scrollabletarget.matches(":active"), "should not be active initially");
+
+ let rAFID = requestAnimationFrame(function ensureInactive() {
+ let isActive = scrollabletarget.matches(":active");
+ ok(!isActive, "Element activation should never happen!");
+ if (!isActive) {
+ rAFID = requestAnimationFrame(ensureInactive);
+ }
+ });
+
+ await doubleTapOn(scrollabletarget, 50, 50, false /* useTouchpad */);
+
+ cancelAnimationFrame(rAFID);
+}
+
+if (getPlatform() != "mac" && getPlatform() != "android") {
+ ok(true, "Skipping test because double-tap-zoom isn't allowed on " + getPlatform());
+ subtestDone();
+} else {
+ waitUntilApzStable()
+ .then(test)
+ .then(subtestDone, subtestFailed);
+}
+</script>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_bug1806400-3.html b/gfx/layers/apz/test/mochitest/helper_bug1806400-3.html
new file mode 100644
index 0000000000..2e5398119c
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_bug1806400-3.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<meta name="viewport" content="width=device-width; initial-scale=1.0">
+<title>Tests that :active state is changed on a scrollable container without any touch event listeners</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="apz_test_utils.js"></script>
+<script src="apz_test_native_event_utils.js"></script>
+<style>
+#scrollable {
+ height: 50vh;
+ width: 50vw;
+ background: yellow;
+ overflow: scroll;
+}
+
+#scrollabletarget {
+ height: 200vh;
+ width: 200vh;
+ background: green;
+}
+
+#scrollabletarget:active {
+ background: red;
+}
+
+</style>
+<div id="scrollable">
+ <div id="scrollabletarget">
+ </div>
+</div>
+<script>
+async function test() {
+ ok(!scrollabletarget.matches(":active"), "should not be active initially");
+
+ await synthesizeNativeTap(scrollabletarget, 50, 50);
+
+ // In JS there's no way to ensure `APZStateChange::eStartTouch` notification
+ // has been processed. So we wait for `:active` state change here.
+ await SimpleTest.promiseWaitForCondition(
+ () => scrollabletarget.matches(":active"),
+ "Waiting for :active state change");
+ ok(scrollabletarget.matches(":active"), "should be active");
+}
+waitUntilApzStable()
+.then(test)
+.then(subtestDone, subtestFailed);
+</script>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_bug1806400-4.html b/gfx/layers/apz/test/mochitest/helper_bug1806400-4.html
new file mode 100644
index 0000000000..46fa95b651
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_bug1806400-4.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+<meta name="viewport" content="width=device-width; initial-scale=0.4">
+<title>Tests that double-tap-to-zoom never activates elements inside non scrollable container</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="apz_test_utils.js"></script>
+<script src="apz_test_native_event_utils.js"></script>
+<style>
+#nonscrollabletarget {
+ height: 300px;
+ width: 300px;
+ background: green;
+}
+
+#nonscrollabletarget:active {
+ background: red;
+}
+
+</style>
+<div id="nonscrollabletarget">
+</div>
+<script>
+async function test() {
+ ok(!nonscrollabletarget.matches(":active"), "should not be active initially");
+
+ let rAFID = requestAnimationFrame(function ensureInactive() {
+ let isActive = nonscrollabletarget.matches(":active");
+ ok(!isActive, "Element activation should never happen!");
+ if (!isActive) {
+ rAFID = requestAnimationFrame(ensureInactive);
+ }
+ });
+
+ await doubleTapOn(nonscrollabletarget, 50, 50, false /* useTouchpad */);
+
+ cancelAnimationFrame(rAFID);
+}
+
+if (getPlatform() != "mac" && getPlatform() != "android") {
+ ok(true, "Skipping test because double-tap-zoom isn't allowed on " + getPlatform());
+ subtestDone();
+} else {
+ waitUntilApzStable()
+ .then(test)
+ .then(subtestDone, subtestFailed);
+}
+</script>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_bug1806400.html b/gfx/layers/apz/test/mochitest/helper_bug1806400.html
new file mode 100644
index 0000000000..03be0c8535
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_bug1806400.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<meta name="viewport" content="width=device-width; initial-scale=1.0">
+<title>Tests that :active state is changed with `touchstart` event listener</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="apz_test_utils.js"></script>
+<script src="apz_test_native_event_utils.js"></script>
+<style>
+ #button {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+<button id="button">Button</button>
+<script>
+async function test() {
+ // Set up an active touchstart event listner.
+ let eventPromise = promiseOneEvent(document.documentElement, "touchstart");
+ await promiseApzFlushedRepaints();
+
+ await synthesizeNativeTouch(button, 10, 10, SpecialPowers.DOMWindowUtils.TOUCH_CONTACT);
+ await eventPromise;
+
+ // In JS there's no way to ensure `APZStateChange::eStartTouch` notification
+ // has been processed. So we wait for `:active` state change here.
+ await SimpleTest.promiseWaitForCondition(
+ () => button.matches(":active"),
+ "Waiting for :active state change");
+ ok(button.matches(":active"), "should be active");
+
+ eventPromise = promiseOneEvent(button, "touchend");
+ await synthesizeNativeTouch(button, 10, 10, SpecialPowers.DOMWindowUtils.TOUCH_REMOVE);
+ await eventPromise;
+
+ // Same as above. We need to wait for not `:active` state here.
+ await SimpleTest.promiseWaitForCondition(
+ () => !button.matches(":active"),
+ "Waiting for :active state change");
+ ok(!button.matches(":active"), "should not be active");
+}
+
+if (getPlatform() == "windows") {
+ // Bug 1875916. On Windows synthesizeNativeTouch(TOUCH_REMOVE) causes
+ // `InjectTouchInput failure` with ERROR_TIMEOUT.
+ ok(true, "Test doesn't need to run on Windows");
+ subtestDone();
+} else {
+ waitUntilApzStable()
+ .then(test)
+ .then(subtestDone, subtestFailed);
+}
+</script>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_hittest_iframe_perspective-4.html b/gfx/layers/apz/test/mochitest/helper_hittest_iframe_perspective-4.html
new file mode 100644
index 0000000000..96ea0d3b09
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/helper_hittest_iframe_perspective-4.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1888904
+-->
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width; initial-scale=1.0">
+ <title>Test that events are delivered with correct coordinates to an iframe inide a no-op perspective transform</title>
+ <script src="apz_test_native_event_utils.js"></script>
+ <script src="apz_test_utils.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ }
+ iframe {
+ border: 0;
+ background-color: blue;
+ }
+ .modal-dialog {
+ position: absolute;
+ top: 500px;
+ left: 500px;
+ transform: translate(-50%, -50%);
+ border: 1px solid black;
+ }
+ .item {
+ perspective: 1000px;
+ transform: translate3d(0, 0, 0);
+ }
+ .g-recaptcha {
+ transform-origin: 0 0;
+ transform: scale(0.91);
+ }
+ </style>
+</head>
+<body>
+ <div class="modal-dialog">
+ <div class="item">
+ <div class="g-recaptcha">
+ <iframe id="iframe" src="https://example.com/tests/gfx/layers/apz/test/mochitest/helper_hittest_iframe_perspective_child.html"></iframe>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <script type="application/javascript">
+async function test() {
+ // Wait for iframe to receive content transforms.
+ await SpecialPowers.spawn(iframe, [], async () => {
+ await SpecialPowers.contentTransformsReceived(content.window);
+ });
+
+ let clickCoordsInChild = {
+ offsetX: 0,
+ offsetY: 0
+ };
+ let childMessagePromise = new Promise(resolve => {
+ window.addEventListener("message", event => {
+ let data = JSON.parse(event.data);
+ if ("type" in data && data.type == "got-mouse-down") {
+ clickCoordsInChild = data.coords;
+ resolve();
+ }
+ })
+ });
+ await synthesizeNativeMouseEventWithAPZ({
+ type: "click",
+ target: iframe,
+ offsetX: 100,
+ offsetY: 100
+ });
+ await childMessagePromise;
+ is(clickCoordsInChild.offsetX, 110 /* 100 / 0.91 */, "x coordinate is correct");
+ is(clickCoordsInChild.offsetY, 110 /* 100 / 0.91 */, "y coordinate is correct");
+}
+
+waitUntilApzStable()
+.then(test)
+.then(subtestDone, subtestFailed);
+
+ </script>
+</body>
+</html>
diff --git a/gfx/layers/apz/test/mochitest/helper_hittest_pointerevents_svg.html b/gfx/layers/apz/test/mochitest/helper_hittest_pointerevents_svg.html
index 22b880736d..3b8a7cef3e 100644
--- a/gfx/layers/apz/test/mochitest/helper_hittest_pointerevents_svg.html
+++ b/gfx/layers/apz/test/mochitest/helper_hittest_pointerevents_svg.html
@@ -132,10 +132,10 @@ async function test() {
`bottom left of scroller in testcase ${testId}`);
}
- // With the first two cases (circle masks) both WebRender and non-WebRender
- // emit dispatch-to-content regions for the right side, so for now we just
- // test for that. Eventually WebRender should be able to stop emitting DTC
- // and we can update this test to be more precise in that case.
+ // With the first two cases (circle masks) WebRender emits dispatch-to-content
+ // regions for the right side, so for now we just test for that.
+ // Eventually WebRender should be able to stop emitting DTC
+ // and we can update this test to be more precise.
// For the two rectangular test cases we get precise results rather than
// dispatch-to-content.
if (testId == 1 || testId == 2) {
diff --git a/gfx/layers/apz/test/mochitest/helper_scrollframe_activation_on_load.html b/gfx/layers/apz/test/mochitest/helper_scrollframe_activation_on_load.html
index 1947a89a8f..c3f02d23d9 100644
--- a/gfx/layers/apz/test/mochitest/helper_scrollframe_activation_on_load.html
+++ b/gfx/layers/apz/test/mochitest/helper_scrollframe_activation_on_load.html
@@ -41,9 +41,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1151663
let config = getHitTestConfig();
let heightMultiplier = SpecialPowers.getCharPref("apz.y_stationary_size_multiplier");
- // With WebRender, the effective height multiplier can be reduced
- // for alignment reasons. The reduction should be no more than a
- // factor of two.
+ // The effective height multiplier can be reduced for alignment reasons.
+ // The reduction should be no more than a factor of two.
heightMultiplier /= 2;
info("effective displayport height multipler is " + heightMultiplier);
diff --git a/gfx/layers/apz/test/mochitest/helper_touch_synthesized_mouseevents.html b/gfx/layers/apz/test/mochitest/helper_touch_synthesized_mouseevents.html
index 3930cec3c3..b3d7b4352a 100644
--- a/gfx/layers/apz/test/mochitest/helper_touch_synthesized_mouseevents.html
+++ b/gfx/layers/apz/test/mochitest/helper_touch_synthesized_mouseevents.html
@@ -76,6 +76,13 @@ async function test() {
promiseOneEvent(targetElem, "click"),
];
+ // Create a promise for :active state change since in the case where the
+ // target element is inside a scrollable container, APZ delays :active state
+ // change, it sometimes happens after all the relavant events above.
+ const activePromise = SimpleTest.promiseWaitForCondition(
+ () => targetElem.matches(":active"),
+ "Waiting for :active state change");
+
// Perform a tap gesture
await synthesizeNativeTap(targetElem, 50, 50);
@@ -88,7 +95,7 @@ async function test() {
// The value of ui.touch_activation.duration_ms should be set to
// a large value. If we did not delay sending the synthesized
// mouse events, this test will not timeout.
- await Promise.all(mouseEventPromises);
+ await Promise.all([...mouseEventPromises, activePromise]);
clearTimeout(failTimeout);
diff --git a/gfx/layers/apz/test/mochitest/test_group_hittest-3.html b/gfx/layers/apz/test/mochitest/test_group_hittest-3.html
index f5675ee790..eac0348b89 100644
--- a/gfx/layers/apz/test/mochitest/test_group_hittest-3.html
+++ b/gfx/layers/apz/test/mochitest/test_group_hittest-3.html
@@ -33,6 +33,7 @@ var prefs = [
var subtests = [
{"file": "helper_hittest_iframe_perspective.html", "prefs": prefs},
{"file": "helper_hittest_iframe_perspective-3.html", "prefs": prefs},
+ {"file": "helper_hittest_iframe_perspective-4.html", "prefs": prefs},
];
if (isApzEnabled()) {
diff --git a/gfx/layers/apz/test/mochitest/test_group_touchevents-5.html b/gfx/layers/apz/test/mochitest/test_group_touchevents-5.html
index 0eee77a3ae..e6e4eb40fb 100644
--- a/gfx/layers/apz/test/mochitest/test_group_touchevents-5.html
+++ b/gfx/layers/apz/test/mochitest/test_group_touchevents-5.html
@@ -9,6 +9,12 @@
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript">
+// Increase the tap timeouts so the double-tap is still detected in case of
+// random delays during testing.
+var doubletap_prefs = [
+ ["ui.click_hold_context_menus.delay", 10000],
+ ["apz.max_tap_time", 10000],
+];
var subtests = [
// tests that scrolling doesn't cause extra SchedulePaint calls
@@ -23,6 +29,18 @@ var subtests = [
{"file": "helper_bug1719855.html?prevent=contextmenu"},
{"file": "helper_bug1719855.html"},
{"file": "helper_bug1724759.html"},
+ {"file": "helper_bug1806400.html", "prefs": [
+ // This test uses `SimpleTest.promiseWaitForCondition` which waits for the
+ // given condition up to 3s, to avoid opening context menu during the time
+ // span use a longer `ui.click_hold_context_menus.delay` here.
+ ["ui.click_hold_context_menus.delay", 10000],
+ ["ui.touch_activation.duration_ms", 1000]
+ ]},
+ {"file": "helper_bug1806400-2.html", "prefs": doubletap_prefs},
+ {"file": "helper_bug1806400-3.html", "prefs": [
+ ["ui.touch_activation.duration_ms", 90000]
+ ]},
+ {"file": "helper_bug1806400-4.html", "prefs": doubletap_prefs},
// Add new subtests here. If this starts timing out because it's taking too
// long, create a test_group_touchevents-6.html file. Refer to 1423011#c57
// for more details.
diff --git a/gfx/layers/apz/test/mochitest/test_layerization.html b/gfx/layers/apz/test/mochitest/test_layerization.html
index 0ff76de317..e97971b456 100644
--- a/gfx/layers/apz/test/mochitest/test_layerization.html
+++ b/gfx/layers/apz/test/mochitest/test_layerization.html
@@ -64,9 +64,8 @@ let config = getHitTestConfig();
let activateAllScrollFrames = config.activateAllScrollFrames;
let heightMultiplier = SpecialPowers.getCharPref("apz.y_stationary_size_multiplier");
-// With WebRender, the effective height multiplier can be reduced
-// for alignment reasons. The reduction should be no more than a
-// factor of two.
+// The effective height multiplier can be reduced for alignment reasons.
+// The reduction should be no more than a factor of two.
heightMultiplier /= 2;
info("effective displayport height multipler is " + heightMultiplier);
diff --git a/gfx/layers/apz/test/mochitest/test_scroll_inactive_bug1190112.html b/gfx/layers/apz/test/mochitest/test_scroll_inactive_bug1190112.html
index de54cf93fe..dd18e078b6 100644
--- a/gfx/layers/apz/test/mochitest/test_scroll_inactive_bug1190112.html
+++ b/gfx/layers/apz/test/mochitest/test_scroll_inactive_bug1190112.html
@@ -523,8 +523,7 @@ async function test() {
// Scroll inner again
// Tick the refresh driver once to make sure the compositor has sent the
// updated scroll offset for the outer scroller to WebRender, so that the
- // hit-test in sendWheelAndPaint takes it into account. (This isn't needed
- // if using non-WR layers, but doesn't hurt either).
+ // hit-test in sendWheelAndPaint takes it into account.
var dwu = SpecialPowers.getDOMWindowUtils(window);
dwu.advanceTimeAndRefresh(16);
dwu.restoreNormalRefresh();
diff --git a/gfx/layers/apz/util/APZEventState.cpp b/gfx/layers/apz/util/APZEventState.cpp
index 5db6a08429..c205b09ca2 100644
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -27,6 +27,7 @@
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/layers/APZCCallbackHelper.h"
+#include "mozilla/layers/APZUtils.h"
#include "mozilla/layers/IAPZCTreeManager.h"
#include "mozilla/widget/nsAutoRollup.h"
#include "nsCOMPtr.h"
@@ -101,7 +102,7 @@ APZEventState::APZEventState(nsIWidget* aWidget,
mContentReceivedInputBlockCallback(std::move(aCallback)),
mPendingTouchPreventedResponse(false),
mPendingTouchPreventedBlockId(0),
- mEndTouchIsClick(false),
+ mEndTouchState(apz::SingleTapState::NotClick),
mFirstTouchCancelled(false),
mTouchEndCancelled(false),
mReceivedNonTouchStart(false),
@@ -349,11 +350,13 @@ void APZEventState::ProcessTouchEvent(
case eTouchEnd:
if (isTouchPrevented) {
mTouchEndCancelled = true;
- mEndTouchIsClick = false;
+ mEndTouchState = apz::SingleTapState::NotClick;
}
[[fallthrough]];
case eTouchCancel:
- mActiveElementManager->HandleTouchEndEvent(mEndTouchIsClick);
+ if (mActiveElementManager->HandleTouchEndEvent(mEndTouchState)) {
+ mEndTouchState = apz::SingleTapState::NotClick;
+ }
[[fallthrough]];
case eTouchMove: {
if (!mReceivedNonTouchStart) {
@@ -515,13 +518,13 @@ void APZEventState::ProcessAPZStateChange(ViewID aViewId,
break;
}
case APZStateChange::eStartTouch: {
- bool canBePan = aArg;
- mActiveElementManager->HandleTouchStart(canBePan);
+ bool canBePanOrZoom = aArg;
+ mActiveElementManager->HandleTouchStart(canBePanOrZoom);
// If this is a non-scrollable content, set a timer for the amount of
// time specified by ui.touch_activation.duration_ms to clear the
// active element state.
- APZES_LOG("%s: can-be-pan=%d", __FUNCTION__, aArg);
- if (!canBePan) {
+ APZES_LOG("%s: can-be-pan-or-zoom=%d", __FUNCTION__, aArg);
+ if (!canBePanOrZoom) {
MOZ_ASSERT(aInputBlockId.isSome());
}
break;
@@ -532,8 +535,10 @@ void APZEventState::ProcessAPZStateChange(ViewID aViewId,
break;
}
case APZStateChange::eEndTouch: {
- mEndTouchIsClick = aArg;
- mActiveElementManager->HandleTouchEnd();
+ mEndTouchState = static_cast<apz::SingleTapState>(aArg);
+ if (mActiveElementManager->HandleTouchEnd(mEndTouchState)) {
+ mEndTouchState = apz::SingleTapState::NotClick;
+ }
break;
}
}
diff --git a/gfx/layers/apz/util/APZEventState.h b/gfx/layers/apz/util/APZEventState.h
index 52febc0424..3a57e9e6c6 100644
--- a/gfx/layers/apz/util/APZEventState.h
+++ b/gfx/layers/apz/util/APZEventState.h
@@ -39,6 +39,10 @@ namespace layers {
class ActiveElementManager;
+namespace apz {
+enum class SingleTapState : uint8_t;
+} // namespace apz
+
typedef std::function<void(uint64_t /* input block id */,
bool /* prevent default */)>
ContentReceivedInputBlockCallback;
@@ -106,7 +110,7 @@ class APZEventState final {
bool mPendingTouchPreventedResponse;
ScrollableLayerGuid mPendingTouchPreventedGuid;
uint64_t mPendingTouchPreventedBlockId;
- bool mEndTouchIsClick;
+ apz::SingleTapState mEndTouchState;
bool mFirstTouchCancelled;
bool mTouchEndCancelled;
// Set to true when we have received any one of
diff --git a/gfx/layers/apz/util/ActiveElementManager.cpp b/gfx/layers/apz/util/ActiveElementManager.cpp
index f2d981e9f0..c92fec783a 100644
--- a/gfx/layers/apz/util/ActiveElementManager.cpp
+++ b/gfx/layers/apz/util/ActiveElementManager.cpp
@@ -10,6 +10,8 @@
#include "mozilla/StaticPrefs_ui.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Document.h"
+#include "mozilla/layers/APZEventState.h"
+#include "mozilla/layers/APZUtils.h"
#include "nsITimer.h"
static mozilla::LazyLogModule sApzAemLog("apz.activeelement");
@@ -21,7 +23,7 @@ namespace layers {
class DelayedClearElementActivation final : public nsITimerCallback,
public nsINamed {
private:
- explicit DelayedClearElementActivation(nsCOMPtr<dom::Element>& aTarget,
+ explicit DelayedClearElementActivation(RefPtr<dom::Element>& aTarget,
const nsCOMPtr<nsITimer>& aTimer)
: mTarget(aTarget)
// Hold the reference count until we are called back.
@@ -33,7 +35,7 @@ class DelayedClearElementActivation final : public nsITimerCallback,
NS_DECL_ISUPPORTS
static RefPtr<DelayedClearElementActivation> Create(
- nsCOMPtr<dom::Element>& aTarget);
+ RefPtr<dom::Element>& aTarget);
NS_IMETHOD Notify(nsITimer*) override;
@@ -56,11 +58,12 @@ class DelayedClearElementActivation final : public nsITimerCallback,
mTimer = nullptr;
}
}
+ dom::Element* GetTarget() const { return mTarget; }
private:
~DelayedClearElementActivation() = default;
- nsCOMPtr<dom::Element> mTarget;
+ RefPtr<dom::Element> mTarget;
nsCOMPtr<nsITimer> mTimer;
bool mProcessedSingleTap;
};
@@ -77,7 +80,7 @@ static nsPresContext* GetPresContextFor(nsIContent* aContent) {
}
RefPtr<DelayedClearElementActivation> DelayedClearElementActivation::Create(
- nsCOMPtr<dom::Element>& aTarget) {
+ RefPtr<dom::Element>& aTarget) {
nsCOMPtr<nsITimer> timer = NS_NewTimer();
if (!timer) {
return nullptr;
@@ -137,7 +140,11 @@ void DelayedClearElementActivation::ClearGlobalActiveContent() {
NS_IMPL_ISUPPORTS(DelayedClearElementActivation, nsITimerCallback, nsINamed)
ActiveElementManager::ActiveElementManager()
- : mCanBePan(false), mCanBePanSet(false), mSetActiveTask(nullptr) {}
+ : mCanBePanOrZoom(false),
+ mCanBePanOrZoomSet(false),
+ mSingleTapBeforeActivation(false),
+ mSingleTapState(apz::SingleTapState::NotClick),
+ mSetActiveTask(nullptr) {}
ActiveElementManager::~ActiveElementManager() = default;
@@ -156,9 +163,9 @@ void ActiveElementManager::SetTargetElement(dom::EventTarget* aTarget) {
TriggerElementActivation();
}
-void ActiveElementManager::HandleTouchStart(bool aCanBePan) {
- AEM_LOG("Touch start, aCanBePan: %d\n", aCanBePan);
- if (mCanBePanSet) {
+void ActiveElementManager::HandleTouchStart(bool aCanBePanOrZoom) {
+ AEM_LOG("Touch start, aCanBePanOrZoom: %d\n", aCanBePanOrZoom);
+ if (mCanBePanOrZoomSet) {
// Multiple fingers on screen (since HandleTouchEnd clears mCanBePanSet).
AEM_LOG("Multiple fingers on-screen, clearing touch block state\n");
CancelTask();
@@ -167,16 +174,29 @@ void ActiveElementManager::HandleTouchStart(bool aCanBePan) {
return;
}
- mCanBePan = aCanBePan;
- mCanBePanSet = true;
+ mCanBePanOrZoom = aCanBePanOrZoom;
+ mCanBePanOrZoomSet = true;
TriggerElementActivation();
}
void ActiveElementManager::TriggerElementActivation() {
+ // Reset mSingleTapState here either when HandleTouchStart() or
+ // SetTargetElement() gets called.
+ // NOTE: It's possible that ProcessSingleTap() gets called in between
+ // HandleTouchStart() and SetTargetElement() calls. I.e.,
+ // mSingleTapBeforeActivation is true, in such cases it doesn't matter that
+ // mSingleTapState was reset once and referred it in ProcessSingleTap() and
+ // then reset here again because in ProcessSingleTap() `NotYetDetermined` is
+ // the only one state we need to care, and it should NOT happen in the
+ // scenario. In other words the case where we need to care `NotYetDetermined`
+ // is when ProcessSingleTap() gets called later than any other events and
+ // notifications.
+ mSingleTapState = apz::SingleTapState::NotClick;
+
// Both HandleTouchStart() and SetTargetElement() call this. They can be
- // called in either order. One will set mCanBePanSet, and the other, mTarget.
- // We want to actually trigger the activation once both are set.
- if (!(mTarget && mCanBePanSet)) {
+ // called in either order. One will set mCanBePanOrZoomSet, and the other,
+ // mTarget. We want to actually trigger the activation once both are set.
+ if (!(mTarget && mCanBePanOrZoomSet)) {
return;
}
@@ -190,10 +210,13 @@ void ActiveElementManager::TriggerElementActivation() {
// If the touch cannot be a pan, make mTarget :active right away.
// Otherwise, wait a bit to see if the user will pan or not.
- if (!mCanBePan) {
+ if (!mCanBePanOrZoom) {
SetActive(mTarget);
if (mDelayedClearElementActivation) {
+ if (mSingleTapBeforeActivation) {
+ mDelayedClearElementActivation->MarkSingleTapProcessed();
+ }
mDelayedClearElementActivation->StartTimer();
}
} else {
@@ -210,6 +233,10 @@ void ActiveElementManager::TriggerElementActivation() {
task.forget(), StaticPrefs::ui_touch_activation_delay_ms());
AEM_LOG("Scheduling mSetActiveTask %p\n", mSetActiveTask.get());
}
+ AEM_LOG(
+ "Got both touch-end event and end touch notiication, clearing pan "
+ "state\n");
+ mCanBePanOrZoomSet = false;
}
void ActiveElementManager::ClearActivation() {
@@ -218,43 +245,70 @@ void ActiveElementManager::ClearActivation() {
ResetActive();
}
-void ActiveElementManager::HandleTouchEndEvent(bool aWasClick) {
- AEM_LOG("Touch end event, aWasClick: %d\n", aWasClick);
+bool ActiveElementManager::HandleTouchEndEvent(apz::SingleTapState aState) {
+ AEM_LOG("Touch end event, state: %hhu\n", static_cast<uint8_t>(aState));
+
+ mTouchEndState += TouchEndState::GotTouchEndEvent;
+ return MaybeChangeActiveState(aState);
+}
+
+bool ActiveElementManager::HandleTouchEnd(apz::SingleTapState aState) {
+ AEM_LOG("Touch end\n");
+
+ mTouchEndState += TouchEndState::GotTouchEndNotification;
+ return MaybeChangeActiveState(aState);
+}
+
+bool ActiveElementManager::MaybeChangeActiveState(apz::SingleTapState aState) {
+ if (mTouchEndState !=
+ TouchEndStates(TouchEndState::GotTouchEndEvent,
+ TouchEndState::GotTouchEndNotification)) {
+ return false;
+ }
- // If the touch was a click, make mTarget :active right away.
- // nsEventStateManager will reset the active element when processing
- // the mouse-down event generated by the click.
CancelTask();
- if (aWasClick) {
+
+ mSingleTapState = aState;
+
+ if (aState == apz::SingleTapState::WasClick) {
// Scrollbar thumbs use a different mechanism for their active
// highlight (the "active" attribute), so don't set the active state
// on them because nothing will clear it.
- if (!(mTarget && mTarget->IsXULElement(nsGkAtoms::thumb))) {
+ if (mCanBePanOrZoom &&
+ !(mTarget && mTarget->IsXULElement(nsGkAtoms::thumb))) {
SetActive(mTarget);
}
} else {
- // We might reach here if mCanBePan was false on touch-start and
+ // We might reach here if mCanBePanOrZoom was false on touch-start and
// so we set the element active right away. Now it turns out the
// action was not a click so we need to reset the active element.
ResetActive();
}
ResetTouchBlockState();
-}
-
-void ActiveElementManager::HandleTouchEnd() {
- AEM_LOG("Touch end, clearing pan state\n");
- mCanBePanSet = false;
+ return true;
}
void ActiveElementManager::ProcessSingleTap() {
if (!mDelayedClearElementActivation) {
+ // We have not received touch-start notification yet. We will have to run
+ // MarkSingleTapProcessed() when we receive the touch-start notification.
+ mSingleTapBeforeActivation = true;
return;
}
+ if (mSingleTapState == apz::SingleTapState::NotYetDetermined) {
+ // If we got `NotYetDetermined`, which means at the moment we don't know for
+ // sure whether double-tapping will be incoming or not, but now we are sure
+ // that no double-tapping will happen, thus it's time to activate the target
+ // element.
+ if (auto* target = mDelayedClearElementActivation->GetTarget()) {
+ SetActive(target);
+ }
+ }
mDelayedClearElementActivation->MarkSingleTapProcessed();
- if (mCanBePan) {
+ if (mCanBePanOrZoom) {
// In the case that we have not started the delayed reset of the element
// activation state, start the timer now.
mDelayedClearElementActivation->StartTimer();
@@ -297,7 +351,14 @@ void ActiveElementManager::ResetActive() {
void ActiveElementManager::ResetTouchBlockState() {
mTarget = nullptr;
- mCanBePanSet = false;
+ mCanBePanOrZoomSet = false;
+ mTouchEndState.clear();
+ mSingleTapBeforeActivation = false;
+ // NOTE: Do not reset mSingleTapState here since it will be necessary in
+ // ProcessSingleTap() to tell whether we need to activate the target element
+ // because on environments where double-tap is enabled ProcessSingleTap()
+ // gets called after both of touch-end event and end touch notiication
+ // arrived.
}
void ActiveElementManager::SetActiveTask(
diff --git a/gfx/layers/apz/util/ActiveElementManager.h b/gfx/layers/apz/util/ActiveElementManager.h
index f8a6f07261..1f2e1e4aad 100644
--- a/gfx/layers/apz/util/ActiveElementManager.h
+++ b/gfx/layers/apz/util/ActiveElementManager.h
@@ -9,6 +9,7 @@
#include "nsCOMPtr.h"
#include "nsISupportsImpl.h"
+#include "mozilla/EnumSet.h"
namespace mozilla {
@@ -23,6 +24,10 @@ namespace layers {
class DelayedClearElementActivation;
+namespace apz {
+enum class SingleTapState : uint8_t;
+} // namespace apz
+
/**
* Manages setting and clearing the ':active' CSS pseudostate in the presence
* of touch input.
@@ -46,9 +51,9 @@ class ActiveElementManager final {
/**
* Handle a touch-start state notification from APZ. This notification
* may be delayed until after touch listeners have responded to the APZ.
- * @param aCanBePan whether the touch can be a pan
+ * @param aCanBePanOrZoom whether the touch can be a pan or double-tap-to-zoom
*/
- void HandleTouchStart(bool aCanBePan);
+ void HandleTouchStart(bool aCanBePanOrZoom);
/**
* Clear the active element.
*/
@@ -57,12 +62,12 @@ class ActiveElementManager final {
* Handle a touch-end or touch-cancel event.
* @param aWasClick whether the touch was a click
*/
- void HandleTouchEndEvent(bool aWasClick);
+ bool HandleTouchEndEvent(apz::SingleTapState aState);
/**
* Handle a touch-end state notification from APZ. This notification may be
* delayed until after touch listeners have responded to the APZ.
*/
- void HandleTouchEnd();
+ bool HandleTouchEnd(apz::SingleTapState aState);
/**
* Possibly clear active element sate in response to a single tap.
*/
@@ -76,17 +81,39 @@ class ActiveElementManager final {
/**
* The target of the first touch point in the current touch block.
*/
- nsCOMPtr<dom::Element> mTarget;
+ RefPtr<dom::Element> mTarget;
/**
- * Whether the current touch block can be a pan. Set in HandleTouchStart().
+ * Whether the current touch block can be a pan or double-tap-to-zoom. Set in
+ * HandleTouchStart().
*/
- bool mCanBePan;
+ bool mCanBePanOrZoom;
/**
- * Whether mCanBePan has been set for the current touch block.
+ * Whether mCanBePanOrZoom has been set for the current touch block.
* We need to keep track of this to allow HandleTouchStart() and
* SetTargetElement() to be called in either order.
*/
- bool mCanBePanSet;
+ bool mCanBePanOrZoomSet;
+
+ bool mSingleTapBeforeActivation;
+
+ enum class TouchEndState : uint8_t {
+ GotTouchEndNotification,
+ GotTouchEndEvent,
+ };
+ using TouchEndStates = EnumSet<TouchEndState>;
+
+ /**
+ * A flag tracks whether `APZStateChange::eEndTouch` notification has arrived
+ * and whether `eTouchEnd` event has arrived.
+ */
+ TouchEndStates mTouchEndState;
+
+ /**
+ * A tri-state variable to represent the single tap state when both of
+ * `APZStateChange::eEndTouch` notification and `eTouchEnd` event arrived.
+ */
+ apz::SingleTapState mSingleTapState;
+
/**
* A task for calling SetActive() after a timeout.
*/
@@ -103,6 +130,8 @@ class ActiveElementManager final {
void ResetTouchBlockState();
void SetActiveTask(const nsCOMPtr<dom::Element>& aTarget);
void CancelTask();
+ // Returns true if the function changed the active element state.
+ bool MaybeChangeActiveState(apz::SingleTapState aState);
};
} // namespace layers
diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp
index 4187e48955..12a34ff92f 100644
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -1546,12 +1546,7 @@ TextureClient::TextureClient(TextureData* aData, TextureFlags aFlags,
mUpdated(false),
mAddedToCompositableClient(false),
mFwdTransactionId(0),
- mSerial(++sSerialCounter)
-#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
- ,
- mPoolTracker(nullptr)
-#endif
-{
+ mSerial(++sSerialCounter) {
mData->FillInfo(mInfo);
mFlags |= mData->GetTextureFlags();
}
diff --git a/gfx/layers/client/TextureClient.h b/gfx/layers/client/TextureClient.h
index ac0a755698..cf1701e3f0 100644
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -44,12 +44,6 @@ struct ID3D11Device;
namespace mozilla {
-// When defined, we track which pool the tile came from and test for
-// any inconsistencies. This can be defined in release build as well.
-#ifdef DEBUG
-# define GFX_DEBUG_TRACK_CLIENTS_IN_POOL 1
-#endif
-
namespace layers {
class AndroidHardwareBufferTextureData;
@@ -68,9 +62,6 @@ class GPUVideoTextureData;
class TextureClient;
class ITextureClientRecycleAllocator;
class SharedSurfaceTextureData;
-#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
-class TextureClientPool;
-#endif
class TextureForwarder;
struct RemoteTextureOwnerId;
@@ -696,11 +687,6 @@ class TextureClient : public AtomicRefCountedWithFinalize<TextureClient> {
static void TextureClientRecycleCallback(TextureClient* aClient,
void* aClosure);
- // Internal helpers for creating texture clients using the actual forwarder
- // instead of KnowsCompositor. TextureClientPool uses these to let it cache
- // texture clients per-process instead of per ShadowLayerForwarder, but
- // everyone else should use the public functions instead.
- friend class TextureClientPool;
static already_AddRefed<TextureClient> CreateForDrawing(
TextureForwarder* aAllocator, gfx::SurfaceFormat aFormat,
gfx::IntSize aSize, KnowsCompositor* aKnowsCompositor,
@@ -795,12 +781,6 @@ class TextureClient : public AtomicRefCountedWithFinalize<TextureClient> {
friend void TestTextureClientYCbCr(TextureClient*, PlanarYCbCrData&);
friend already_AddRefed<TextureHost> CreateTextureHostWithBackend(
TextureClient*, ISurfaceAllocator*, LayersBackend&);
-
-#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
- public:
- // Pointer to the pool this tile came from.
- TextureClientPool* mPoolTracker;
-#endif
};
/**
diff --git a/gfx/layers/client/TextureClientPool.cpp b/gfx/layers/client/TextureClientPool.cpp
deleted file mode 100644
index 3eb0c908b6..0000000000
--- a/gfx/layers/client/TextureClientPool.cpp
+++ /dev/null
@@ -1,307 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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/. */
-
-#include "TextureClientPool.h"
-#include "CompositableClient.h"
-#include "mozilla/layers/CompositableForwarder.h"
-#include "mozilla/layers/TextureForwarder.h"
-#include "mozilla/StaticPrefs_layers.h"
-
-#include "nsComponentManagerUtils.h"
-
-#define TCP_LOG(...)
-// #define TCP_LOG(...) printf_stderr(__VA_ARGS__);
-
-namespace mozilla {
-namespace layers {
-
-// We want to shrink to our maximum size of N unused tiles
-// after a timeout to allow for short-term budget requirements
-static void ShrinkCallback(nsITimer* aTimer, void* aClosure) {
- static_cast<TextureClientPool*>(aClosure)->ShrinkToMaximumSize();
-}
-
-// After a certain amount of inactivity, let's clear the pool so that
-// we don't hold onto tiles needlessly. In general, allocations are
-// cheap enough that re-allocating isn't an issue unless we're allocating
-// at an inopportune time (e.g. mid-animation).
-static void ClearCallback(nsITimer* aTimer, void* aClosure) {
- static_cast<TextureClientPool*>(aClosure)->Clear();
-}
-
-TextureClientPool::TextureClientPool(
- KnowsCompositor* aKnowsCompositor, gfx::SurfaceFormat aFormat,
- gfx::IntSize aSize, TextureFlags aFlags, uint32_t aShrinkTimeoutMsec,
- uint32_t aClearTimeoutMsec, uint32_t aInitialPoolSize,
- uint32_t aPoolUnusedSize, TextureForwarder* aAllocator)
- : mKnowsCompositor(aKnowsCompositor),
- mFormat(aFormat),
- mSize(aSize),
- mFlags(aFlags),
- mShrinkTimeoutMsec(aShrinkTimeoutMsec),
- mClearTimeoutMsec(aClearTimeoutMsec),
- mInitialPoolSize(aInitialPoolSize),
- mPoolUnusedSize(aPoolUnusedSize),
- mOutstandingClients(0),
- mSurfaceAllocator(aAllocator),
- mDestroyed(false) {
- TCP_LOG("TexturePool %p created with maximum unused texture clients %u\n",
- this, mInitialPoolSize);
- mShrinkTimer = NS_NewTimer();
- mClearTimer = NS_NewTimer();
- if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
- gfxWarning() << "Creating texture pool for SurfaceFormat::UNKNOWN format";
- }
-}
-
-TextureClientPool::~TextureClientPool() {
- mShrinkTimer->Cancel();
- mClearTimer->Cancel();
-}
-
-#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
-static bool TestClientPool(const char* what, TextureClient* aClient,
- TextureClientPool* aPool) {
- if (!aClient || !aPool) {
- return false;
- }
-
- TextureClientPool* actual = aClient->mPoolTracker;
- bool ok = (actual == aPool);
- if (ok) {
- ok = (aClient->GetFormat() == aPool->GetFormat());
- }
-
- if (!ok) {
- if (actual) {
- gfxCriticalError() << "Pool error(" << what << "): " << aPool << "-"
- << aPool->GetFormat() << ", " << actual << "-"
- << actual->GetFormat() << ", " << aClient->GetFormat();
- MOZ_CRASH("GFX: Crashing with actual");
- } else {
- gfxCriticalError() << "Pool error(" << what << "): " << aPool << "-"
- << aPool->GetFormat() << ", nullptr, "
- << aClient->GetFormat();
- MOZ_CRASH("GFX: Crashing without actual");
- }
- }
- return ok;
-}
-#endif
-
-already_AddRefed<TextureClient> TextureClientPool::GetTextureClient() {
- // Try to fetch a client from the pool
- RefPtr<TextureClient> textureClient;
-
- // We initially allocate mInitialPoolSize for our pool. If we run
- // out of TextureClients, we allocate additional TextureClients to try and
- // keep around mPoolUnusedSize
- if (mTextureClients.empty()) {
- AllocateTextureClient();
- }
-
- if (mTextureClients.empty()) {
- // All our allocations failed, return nullptr
- return nullptr;
- }
-
- mOutstandingClients++;
- textureClient = mTextureClients.top();
- mTextureClients.pop();
-#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
- if (textureClient) {
- textureClient->mPoolTracker = this;
- }
- DebugOnly<bool> ok = TestClientPool("fetch", textureClient, this);
- MOZ_ASSERT(ok);
-#endif
- TCP_LOG("TexturePool %p giving %p from pool; size %u outstanding %u\n", this,
- textureClient.get(), mTextureClients.size(), mOutstandingClients);
-
- return textureClient.forget();
-}
-
-void TextureClientPool::AllocateTextureClient() {
- TCP_LOG("TexturePool %p allocating TextureClient, outstanding %u\n", this,
- mOutstandingClients);
-
- TextureAllocationFlags allocFlags = ALLOC_DEFAULT;
-
- RefPtr<TextureClient> newClient;
- if (StaticPrefs::layers_force_shmem_tiles_AtStartup()) {
- // gfx::BackendType::NONE means use the content backend
- newClient = TextureClient::CreateForRawBufferAccess(
- mSurfaceAllocator, mFormat, mSize, gfx::BackendType::NONE, GetBackend(),
- mFlags, allocFlags);
- } else {
- newClient = TextureClient::CreateForDrawing(
- mSurfaceAllocator, mFormat, mSize, mKnowsCompositor,
- BackendSelector::Content, mFlags, allocFlags);
- }
-
- if (newClient) {
- mTextureClients.push(newClient);
- }
-}
-
-void TextureClientPool::ResetTimers() {
- // Shrink down if we're beyond our maximum size
- if (mShrinkTimeoutMsec &&
- mTextureClients.size() + mTextureClientsDeferred.size() >
- mPoolUnusedSize) {
- TCP_LOG("TexturePool %p scheduling a shrink-to-max-size\n", this);
- mShrinkTimer->InitWithNamedFuncCallback(
- ShrinkCallback, this, mShrinkTimeoutMsec, nsITimer::TYPE_ONE_SHOT,
- "layers::TextureClientPool::ResetTimers");
- }
-
- // Clear pool after a period of inactivity to reduce memory consumption
- if (mClearTimeoutMsec) {
- TCP_LOG("TexturePool %p scheduling a clear\n", this);
- mClearTimer->InitWithNamedFuncCallback(
- ClearCallback, this, mClearTimeoutMsec, nsITimer::TYPE_ONE_SHOT,
- "layers::TextureClientPool::ResetTimers");
- }
-}
-
-void TextureClientPool::ReturnTextureClient(TextureClient* aClient) {
- if (!aClient || mDestroyed) {
- return;
- }
-#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
- DebugOnly<bool> ok = TestClientPool("return", aClient, this);
- MOZ_ASSERT(ok);
-#endif
- // Add the client to the pool:
- MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
- mOutstandingClients--;
- mTextureClients.push(aClient);
- TCP_LOG("TexturePool %p had client %p returned; size %u outstanding %u\n",
- this, aClient, mTextureClients.size(), mOutstandingClients);
-
- ResetTimers();
-}
-
-void TextureClientPool::ReturnTextureClientDeferred(TextureClient* aClient) {
- if (!aClient || mDestroyed) {
- return;
- }
- MOZ_ASSERT(aClient->HasReadLock());
-#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
- DebugOnly<bool> ok = TestClientPool("defer", aClient, this);
- MOZ_ASSERT(ok);
-#endif
- mTextureClientsDeferred.push_back(aClient);
- TCP_LOG(
- "TexturePool %p had client %p defer-returned, size %u outstanding %u\n",
- this, aClient, mTextureClientsDeferred.size(), mOutstandingClients);
-
- ResetTimers();
-}
-
-void TextureClientPool::ShrinkToMaximumSize() {
- // We're over our desired maximum size, immediately shrink down to the
- // maximum.
- //
- // We cull from the deferred TextureClients first, as we can't reuse those
- // until they get returned.
- uint32_t totalUnusedTextureClients =
- mTextureClients.size() + mTextureClientsDeferred.size();
-
- // If we have > mInitialPoolSize outstanding, then we want to keep around
- // mPoolUnusedSize at a maximum. If we have fewer than mInitialPoolSize
- // outstanding, then keep around the entire initial pool size.
- uint32_t targetUnusedClients;
- if (mOutstandingClients > mInitialPoolSize) {
- targetUnusedClients = mPoolUnusedSize;
- } else {
- targetUnusedClients = mInitialPoolSize;
- }
-
- TCP_LOG(
- "TexturePool %p shrinking to maximum unused size %u; current pool size "
- "%u; total outstanding %u\n",
- this, targetUnusedClients, totalUnusedTextureClients,
- mOutstandingClients);
-
- while (totalUnusedTextureClients > targetUnusedClients) {
- if (!mTextureClientsDeferred.empty()) {
- mOutstandingClients--;
- TCP_LOG("TexturePool %p dropped deferred client %p; %u remaining\n", this,
- mTextureClientsDeferred.front().get(),
- mTextureClientsDeferred.size() - 1);
- mTextureClientsDeferred.pop_front();
- } else {
- TCP_LOG("TexturePool %p dropped non-deferred client %p; %u remaining\n",
- this, mTextureClients.top().get(), mTextureClients.size() - 1);
- mTextureClients.pop();
- }
- totalUnusedTextureClients--;
- }
-}
-
-void TextureClientPool::ReturnDeferredClients() {
- if (mTextureClientsDeferred.empty()) {
- return;
- }
-
- TCP_LOG("TexturePool %p returning %u deferred clients to pool\n", this,
- mTextureClientsDeferred.size());
-
- ReturnUnlockedClients();
- ShrinkToMaximumSize();
-}
-
-void TextureClientPool::ReturnUnlockedClients() {
- for (auto it = mTextureClientsDeferred.begin();
- it != mTextureClientsDeferred.end();) {
- MOZ_ASSERT((*it)->GetNonBlockingReadLockCount() >= 1);
- // Last count is held by the lock itself.
- if (!(*it)->IsReadLocked()) {
- mTextureClients.push(*it);
- it = mTextureClientsDeferred.erase(it);
-
- MOZ_ASSERT(mOutstandingClients > 0);
- mOutstandingClients--;
- } else {
- it++;
- }
- }
-}
-
-void TextureClientPool::ReportClientLost() {
- MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
- mOutstandingClients--;
- TCP_LOG("TexturePool %p getting report client lost; down to %u outstanding\n",
- this, mOutstandingClients);
-}
-
-void TextureClientPool::Clear() {
- TCP_LOG("TexturePool %p getting cleared\n", this);
- while (!mTextureClients.empty()) {
- TCP_LOG("TexturePool %p releasing client %p\n", this,
- mTextureClients.top().get());
- mTextureClients.pop();
- }
- while (!mTextureClientsDeferred.empty()) {
- MOZ_ASSERT(mOutstandingClients > 0);
- mOutstandingClients--;
- TCP_LOG("TexturePool %p releasing deferred client %p\n", this,
- mTextureClientsDeferred.front().get());
- mTextureClientsDeferred.pop_front();
- }
-}
-
-void TextureClientPool::Destroy() {
- Clear();
- mDestroyed = true;
- mInitialPoolSize = 0;
- mPoolUnusedSize = 0;
- mKnowsCompositor = nullptr;
-}
-
-} // namespace layers
-} // namespace mozilla
diff --git a/gfx/layers/client/TextureClientPool.h b/gfx/layers/client/TextureClientPool.h
deleted file mode 100644
index d557ca9a03..0000000000
--- a/gfx/layers/client/TextureClientPool.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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/. */
-
-#ifndef MOZILLA_GFX_TEXTURECLIENTPOOL_H
-#define MOZILLA_GFX_TEXTURECLIENTPOOL_H
-
-#include "mozilla/gfx/Types.h"
-#include "mozilla/gfx/Point.h"
-#include "mozilla/RefPtr.h"
-#include "mozilla/layers/KnowsCompositor.h"
-#include "TextureClient.h"
-#include "nsITimer.h"
-#include <stack>
-#include <list>
-
-namespace mozilla {
-namespace layers {
-
-class ISurfaceAllocator;
-class TextureForwarder;
-class TextureReadLock;
-
-class TextureClientAllocator {
- protected:
- virtual ~TextureClientAllocator() = default;
-
- public:
- NS_INLINE_DECL_REFCOUNTING(TextureClientAllocator)
-
- virtual already_AddRefed<TextureClient> GetTextureClient() = 0;
-
- /**
- * Return a TextureClient that is not yet ready to be reused, but will be
- * imminently.
- */
- virtual void ReturnTextureClientDeferred(TextureClient* aClient) = 0;
-
- virtual void ReportClientLost() = 0;
-};
-
-class TextureClientPool final : public TextureClientAllocator {
- virtual ~TextureClientPool();
-
- public:
- TextureClientPool(KnowsCompositor* aKnowsCompositor,
- gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
- TextureFlags aFlags, uint32_t aShrinkTimeoutMsec,
- uint32_t aClearTimeoutMsec, uint32_t aInitialPoolSize,
- uint32_t aPoolUnusedSize, TextureForwarder* aAllocator);
-
- /**
- * Gets an allocated TextureClient of size and format that are determined
- * by the initialisation parameters given to the pool. This will either be
- * a cached client that was returned to the pool, or a newly allocated
- * client if one isn't available.
- *
- * All clients retrieved by this method should be returned using the return
- * functions, or reported lost so that the pool can manage its size correctly.
- */
- already_AddRefed<TextureClient> GetTextureClient() override;
-
- /**
- * Return a TextureClient that is no longer being used and is ready for
- * immediate re-use or destruction.
- */
- void ReturnTextureClient(TextureClient* aClient);
-
- /**
- * Return a TextureClient that is not yet ready to be reused, but will be
- * imminently.
- */
- void ReturnTextureClientDeferred(TextureClient* aClient) override;
-
- /**
- * Return any clients to the pool that were previously returned in
- * ReturnTextureClientDeferred.
- */
- void ReturnDeferredClients();
-
- /**
- * Attempt to shrink the pool so that there are no more than
- * mInitialPoolSize outstanding.
- */
- void ShrinkToMaximumSize();
-
- /**
- * Report that a client retrieved via GetTextureClient() has become
- * unusable, so that it will no longer be tracked.
- */
- void ReportClientLost() override;
-
- /**
- * Calling this will cause the pool to attempt to relinquish any unused
- * clients.
- */
- void Clear();
-
- LayersBackend GetBackend() const {
- return mKnowsCompositor->GetCompositorBackendType();
- }
- int32_t GetMaxTextureSize() const {
- return mKnowsCompositor->GetMaxTextureSize();
- }
- gfx::SurfaceFormat GetFormat() { return mFormat; }
- TextureFlags GetFlags() const { return mFlags; }
-
- /**
- * Clear the pool and put it in a state where it won't recycle any new
- * texture.
- */
- void Destroy();
-
- private:
- void ReturnUnlockedClients();
-
- /// Allocate a single TextureClient to be returned from the pool.
- void AllocateTextureClient();
-
- /// Reset and/or initialise timers for shrinking/clearing the pool.
- void ResetTimers();
-
- /// KnowsCompositor passed to the TextureClient for buffer creation.
- RefPtr<KnowsCompositor> mKnowsCompositor;
-
- /// Format is passed to the TextureClient for buffer creation.
- gfx::SurfaceFormat mFormat;
-
- /// The width and height of the tiles to be used.
- gfx::IntSize mSize;
-
- /// Flags passed to the TextureClient for buffer creation.
- const TextureFlags mFlags;
-
- /// How long to wait after a TextureClient is returned before trying
- /// to shrink the pool to its maximum size of mPoolUnusedSize.
- uint32_t mShrinkTimeoutMsec;
-
- /// How long to wait after a TextureClient is returned before trying
- /// to clear the pool.
- uint32_t mClearTimeoutMsec;
-
- // The initial number of unused texture clients to seed the pool with
- // on construction
- uint32_t mInitialPoolSize;
-
- // How many unused texture clients to try and keep around if we go over
- // the initial allocation
- uint32_t mPoolUnusedSize;
-
- /// This is a total number of clients in the wild and in the stack of
- /// deferred clients (see below). So, the total number of clients in
- /// existence is always mOutstandingClients + the size of mTextureClients.
- uint32_t mOutstandingClients;
-
- std::stack<RefPtr<TextureClient>> mTextureClients;
-
- std::list<RefPtr<TextureClient>> mTextureClientsDeferred;
- RefPtr<nsITimer> mShrinkTimer;
- RefPtr<nsITimer> mClearTimer;
- // This mSurfaceAllocator owns us, so no need to hold a ref to it
- TextureForwarder* mSurfaceAllocator;
-
- // Keep track of whether this pool has been destroyed or not. If it has,
- // we won't accept returns of TextureClients anymore, and the refcounting
- // should take care of their destruction.
- bool mDestroyed;
-};
-
-} // namespace layers
-} // namespace mozilla
-
-#endif /* MOZILLA_GFX_TEXTURECLIENTPOOL_H */
diff --git a/gfx/layers/client/TextureRecorded.cpp b/gfx/layers/client/TextureRecorded.cpp
index da4ca4f8f3..b3596efdc3 100644
--- a/gfx/layers/client/TextureRecorded.cpp
+++ b/gfx/layers/client/TextureRecorded.cpp
@@ -33,7 +33,7 @@ RecordedTextureData::~RecordedTextureData() {
// We need the translator to drop its reference for the DrawTarget first,
// because the TextureData might need to destroy its DrawTarget within a lock.
mSnapshot = nullptr;
- mSnapshotWrapper = nullptr;
+ DetachSnapshotWrapper();
mDT = nullptr;
mCanvasChild->CleanupTexture(mTextureId);
mCanvasChild->RecordEvent(RecordedTextureDestruction(
@@ -92,10 +92,25 @@ bool RecordedTextureData::Lock(OpenMode aMode) {
return true;
}
+void RecordedTextureData::DetachSnapshotWrapper(bool aInvalidate,
+ bool aRelease) {
+ if (mSnapshotWrapper) {
+ // If the snapshot only has one ref, then we don't need to worry about
+ // copying before invalidation since it is about to be deleted. Otherwise,
+ // we need to ensure any internal data is appropriately copied before
+ // shmems are potentially overwritten if there are still existing users.
+ mCanvasChild->DetachSurface(mSnapshotWrapper,
+ aInvalidate && !mSnapshotWrapper->hasOneRef());
+ if (aRelease) {
+ mSnapshotWrapper = nullptr;
+ }
+ }
+}
+
void RecordedTextureData::Unlock() {
if ((mLockedMode == OpenMode::OPEN_READ_WRITE) &&
mCanvasChild->ShouldCacheDataSurface()) {
- mSnapshotWrapper = nullptr;
+ DetachSnapshotWrapper();
mSnapshot = mDT->Snapshot();
mDT->DetachAllSnapshots();
mCanvasChild->RecordEvent(RecordedCacheDataSurface(mSnapshot.get()));
@@ -108,11 +123,9 @@ void RecordedTextureData::Unlock() {
already_AddRefed<gfx::DrawTarget> RecordedTextureData::BorrowDrawTarget() {
if (mLockedMode & OpenMode::OPEN_WRITE) {
+ // The snapshot will be invalidated.
mSnapshot = nullptr;
- if (mSnapshotWrapper) {
- mCanvasChild->DetachSurface(mSnapshotWrapper);
- mSnapshotWrapper = nullptr;
- }
+ DetachSnapshotWrapper(true);
}
return do_AddRef(mDT);
}
@@ -122,18 +135,22 @@ void RecordedTextureData::EndDraw() {
MOZ_ASSERT(mLockedMode == OpenMode::OPEN_READ_WRITE);
if (mCanvasChild->ShouldCacheDataSurface()) {
- mSnapshotWrapper = nullptr;
+ DetachSnapshotWrapper();
mSnapshot = mDT->Snapshot();
mCanvasChild->RecordEvent(RecordedCacheDataSurface(mSnapshot.get()));
}
}
already_AddRefed<gfx::SourceSurface> RecordedTextureData::BorrowSnapshot() {
- if (mSnapshotWrapper && (!mDT || !mDT->IsDirty())) {
- // The DT is unmodified since the last time snapshot was borrowed, so it
- // is safe to reattach the snapshot for shmem readbacks.
- mCanvasChild->AttachSurface(mSnapshotWrapper);
- return do_AddRef(mSnapshotWrapper);
+ if (mSnapshotWrapper) {
+ if (!mDT || !mDT->IsDirty()) {
+ // The DT is unmodified since the last time snapshot was borrowed, so it
+ // is safe to reattach the snapshot for shmem readbacks.
+ mCanvasChild->AttachSurface(mSnapshotWrapper);
+ return do_AddRef(mSnapshotWrapper);
+ }
+
+ DetachSnapshotWrapper();
}
// There are some failure scenarios where we have no DrawTarget and
@@ -153,9 +170,10 @@ already_AddRefed<gfx::SourceSurface> RecordedTextureData::BorrowSnapshot() {
void RecordedTextureData::ReturnSnapshot(
already_AddRefed<gfx::SourceSurface> aSnapshot) {
RefPtr<gfx::SourceSurface> snapshot = aSnapshot;
- if (mSnapshotWrapper) {
- mCanvasChild->DetachSurface(mSnapshotWrapper);
- }
+ // The snapshot needs to be marked detached but we keep the wrapper around
+ // so that it can be reused without repeatedly creating it and accidentally
+ // reading back data for each new instantiation.
+ DetachSnapshotWrapper(false, false);
}
void RecordedTextureData::Deallocate(LayersIPCChannel* aAllocator) {}
diff --git a/gfx/layers/client/TextureRecorded.h b/gfx/layers/client/TextureRecorded.h
index 56e504fb54..9e4e69e78d 100644
--- a/gfx/layers/client/TextureRecorded.h
+++ b/gfx/layers/client/TextureRecorded.h
@@ -58,6 +58,8 @@ class RecordedTextureData final : public TextureData {
~RecordedTextureData() override;
+ void DetachSnapshotWrapper(bool aInvalidate = false, bool aRelease = true);
+
int64_t mTextureId;
RefPtr<CanvasChild> mCanvasChild;
gfx::IntSize mSize;
diff --git a/gfx/layers/ipc/CanvasChild.cpp b/gfx/layers/ipc/CanvasChild.cpp
index 515463cd8e..a25d5e6799 100644
--- a/gfx/layers/ipc/CanvasChild.cpp
+++ b/gfx/layers/ipc/CanvasChild.cpp
@@ -133,6 +133,16 @@ class SourceSurfaceCanvasRecording final : public gfx::SourceSurface {
void AttachSurface() { mDetached = false; }
void DetachSurface() { mDetached = true; }
+ void InvalidateDataSurface() {
+ if (mDataSourceSurface && mMayInvalidate) {
+ // This must be the only reference to the data left.
+ MOZ_ASSERT(mDataSourceSurface->hasOneRef());
+ mDataSourceSurface =
+ gfx::Factory::CopyDataSourceSurface(mDataSourceSurface);
+ mMayInvalidate = false;
+ }
+ }
+
already_AddRefed<gfx::SourceSurface> ExtractSubrect(
const gfx::IntRect& aRect) final {
return mRecordedSurface->ExtractSubrect(aRect);
@@ -142,8 +152,8 @@ class SourceSurfaceCanvasRecording final : public gfx::SourceSurface {
void EnsureDataSurfaceOnMainThread() {
// The data can only be retrieved on the main thread.
if (!mDataSourceSurface && NS_IsMainThread()) {
- mDataSourceSurface =
- mCanvasChild->GetDataSurface(mTextureId, mRecordedSurface, mDetached);
+ mDataSourceSurface = mCanvasChild->GetDataSurface(
+ mTextureId, mRecordedSurface, mDetached, mMayInvalidate);
}
}
@@ -167,6 +177,7 @@ class SourceSurfaceCanvasRecording final : public gfx::SourceSurface {
RefPtr<CanvasDrawEventRecorder> mRecorder;
RefPtr<gfx::DataSourceSurface> mDataSourceSurface;
bool mDetached = false;
+ bool mMayInvalidate = false;
};
class CanvasDataShmemHolder {
@@ -420,6 +431,7 @@ already_AddRefed<gfx::DrawTargetRecording> CanvasChild::CreateDrawTarget(
gfx::BackendType::SKIA, gfx::IntSize(1, 1), aFormat);
RefPtr<gfx::DrawTargetRecording> dt = MakeAndAddRef<gfx::DrawTargetRecording>(
mRecorder, aTextureId, aTextureOwnerId, dummyDt, aSize);
+ dt->SetOptimizeTransform(true);
mTextureInfo.insert({aTextureId, {}});
@@ -483,7 +495,8 @@ int64_t CanvasChild::CreateCheckpoint() {
}
already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
- int64_t aTextureId, const gfx::SourceSurface* aSurface, bool aDetached) {
+ int64_t aTextureId, const gfx::SourceSurface* aSurface, bool aDetached,
+ bool& aMayInvalidate) {
NS_ASSERT_OWNINGTHREAD(CanvasChild);
MOZ_ASSERT(aSurface);
@@ -527,6 +540,7 @@ already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
gfx::Factory::CreateWrappingDataSourceSurface(
shmemPtr, stride, ssSize, ssFormat, ReleaseDataShmemHolder,
closure);
+ aMayInvalidate = true;
return dataSurface.forget();
}
}
@@ -556,6 +570,7 @@ already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
RefPtr<gfx::DataSourceSurface> dataSurface =
gfx::Factory::CreateWrappingDataSourceSurface(
data, stride, ssSize, ssFormat, ReleaseDataShmemHolder, closure);
+ aMayInvalidate = false;
return dataSurface.forget();
}
@@ -593,10 +608,14 @@ void CanvasChild::AttachSurface(const RefPtr<gfx::SourceSurface>& aSurface) {
}
}
-void CanvasChild::DetachSurface(const RefPtr<gfx::SourceSurface>& aSurface) {
+void CanvasChild::DetachSurface(const RefPtr<gfx::SourceSurface>& aSurface,
+ bool aInvalidate) {
if (auto* surface =
static_cast<SourceSurfaceCanvasRecording*>(aSurface.get())) {
surface->DetachSurface();
+ if (aInvalidate) {
+ surface->InvalidateDataSurface();
+ }
}
}
diff --git a/gfx/layers/ipc/CanvasChild.h b/gfx/layers/ipc/CanvasChild.h
index e22109f406..a0cf22b0ec 100644
--- a/gfx/layers/ipc/CanvasChild.h
+++ b/gfx/layers/ipc/CanvasChild.h
@@ -22,7 +22,7 @@ class ThreadSafeWorkerRef;
namespace gfx {
class DrawTargetRecording;
class SourceSurface;
-}
+} // namespace gfx
namespace layers {
class CanvasDrawEventRecorder;
@@ -132,7 +132,8 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
/**
* The DrawTargetRecording is about to change, so detach the old snapshot.
*/
- void DetachSurface(const RefPtr<gfx::SourceSurface>& aSurface);
+ void DetachSurface(const RefPtr<gfx::SourceSurface>& aSurface,
+ bool aInvalidate = false);
/**
* Get DataSourceSurface from the translated equivalent version of aSurface in
@@ -141,11 +142,13 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
* @param aSurface the SourceSurface in this process for which we need a
* DataSourceSurface
* @param aDetached whether the surface is old
+ * @param aMayInvalidate whether the data may be invalidated by future changes
* @returns a DataSourceSurface created from data for aSurface retrieve from
* GPU process
*/
already_AddRefed<gfx::DataSourceSurface> GetDataSurface(
- int64_t aTextureId, const gfx::SourceSurface* aSurface, bool aDetached);
+ int64_t aTextureId, const gfx::SourceSurface* aSurface, bool aDetached,
+ bool& aMayInvalidate);
bool RequiresRefresh(int64_t aTextureId) const;
diff --git a/gfx/layers/ipc/CanvasTranslator.cpp b/gfx/layers/ipc/CanvasTranslator.cpp
index 4a184f48d8..3fd7a4c4c4 100644
--- a/gfx/layers/ipc/CanvasTranslator.cpp
+++ b/gfx/layers/ipc/CanvasTranslator.cpp
@@ -21,6 +21,7 @@
#include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/layers/SharedSurfacesParent.h"
#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/VideoBridgeParent.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/SyncRunnable.h"
#include "mozilla/TaskQueue.h"
@@ -112,6 +113,8 @@ static bool CreateAndMapShmem(RefPtr<ipc::SharedMemoryBasic>& aShmem,
return true;
}
+StaticRefPtr<gfx::SharedContextWebgl> CanvasTranslator::sSharedContext;
+
bool CanvasTranslator::EnsureSharedContextWebgl() {
if (!mSharedContext || mSharedContext->IsContextLost()) {
if (mSharedContext) {
@@ -121,7 +124,14 @@ bool CanvasTranslator::EnsureSharedContextWebgl() {
mRemoteTextureOwner->ClearRecycledTextures();
}
}
- mSharedContext = gfx::SharedContextWebgl::Create();
+ // Check if the global shared context is still valid. If not, instantiate
+ // a new one before we try to use it.
+ if (!sSharedContext || sSharedContext->IsContextLost()) {
+ sSharedContext = gfx::SharedContextWebgl::Create();
+ }
+ mSharedContext = sSharedContext;
+ // If we can't get a new context, then the only thing left to do is block
+ // new canvases.
if (!mSharedContext || mSharedContext->IsContextLost()) {
mSharedContext = nullptr;
BlockCanvas();
@@ -131,6 +141,13 @@ bool CanvasTranslator::EnsureSharedContextWebgl() {
return true;
}
+void CanvasTranslator::Shutdown() {
+ if (sSharedContext) {
+ gfx::CanvasRenderThread::Dispatch(NS_NewRunnableFunction(
+ "CanvasTranslator::Shutdown", []() { sSharedContext = nullptr; }));
+ }
+}
+
mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator(
TextureType aTextureType, TextureType aWebglTextureType,
gfx::BackendType aBackendType, Handle&& aReadHandle,
@@ -1144,6 +1161,13 @@ void CanvasTranslator::ClearTextureInfo() {
mTextureInfo.clear();
mDrawTargets.Clear();
mSharedContext = nullptr;
+ // If the global shared context's ref is the last ref left, then clear out
+ // any internal caches and textures from the context, but still keep it
+ // alive. This saves on startup costs while not contributing significantly
+ // to memory usage.
+ if (sSharedContext && sSharedContext->hasOneRef()) {
+ sSharedContext->ClearCaches();
+ }
mBaseDT = nullptr;
if (mReferenceTextureData) {
mReferenceTextureData->Unlock();
@@ -1163,6 +1187,46 @@ already_AddRefed<gfx::SourceSurface> CanvasTranslator::LookupExternalSurface(
return mSharedSurfacesHolder->Get(wr::ToExternalImageId(aKey));
}
+// Check if the surface descriptor describes a GPUVideo texture for which we
+// only have an opaque source/handle from SurfaceDescriptorRemoteDecoder to
+// derive the actual texture from.
+static bool SDIsNullRemoteDecoder(const SurfaceDescriptor& sd) {
+ return sd.type() == SurfaceDescriptor::TSurfaceDescriptorGPUVideo &&
+ sd.get_SurfaceDescriptorGPUVideo()
+ .get_SurfaceDescriptorRemoteDecoder()
+ .subdesc()
+ .type() == RemoteDecoderVideoSubDescriptor::Tnull_t;
+}
+
+already_AddRefed<gfx::SourceSurface>
+CanvasTranslator::LookupSourceSurfaceFromSurfaceDescriptor(
+ const SurfaceDescriptor& aDesc) {
+ if (!SDIsNullRemoteDecoder(aDesc)) {
+ return nullptr;
+ }
+
+ const auto& sdrd = aDesc.get_SurfaceDescriptorGPUVideo()
+ .get_SurfaceDescriptorRemoteDecoder();
+ RefPtr<VideoBridgeParent> parent =
+ VideoBridgeParent::GetSingleton(sdrd.source());
+ if (!parent) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ gfxCriticalNote << "TexUnpackSurface failed to get VideoBridgeParent";
+ return nullptr;
+ }
+ RefPtr<TextureHost> texture =
+ parent->LookupTexture(mContentId, sdrd.handle());
+ if (!texture) {
+ MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+ gfxCriticalNote << "TexUnpackSurface failed to get TextureHost";
+ return nullptr;
+ }
+
+ RefPtr<gfx::DataSourceSurface> surf = texture->GetAsSurface();
+
+ return surf.forget();
+}
+
void CanvasTranslator::CheckpointReached() { CheckAndSignalWriter(); }
void CanvasTranslator::PauseTranslation() {
diff --git a/gfx/layers/ipc/CanvasTranslator.h b/gfx/layers/ipc/CanvasTranslator.h
index 5258e0c529..e2b6c587b4 100644
--- a/gfx/layers/ipc/CanvasTranslator.h
+++ b/gfx/layers/ipc/CanvasTranslator.h
@@ -219,6 +219,9 @@ class CanvasTranslator final : public gfx::InlineTranslator,
already_AddRefed<gfx::SourceSurface> LookupExternalSurface(
uint64_t aKey) final;
+ already_AddRefed<gfx::SourceSurface> LookupSourceSurfaceFromSurfaceDescriptor(
+ const SurfaceDescriptor& aDesc) final;
+
/**
* Gets the cached DataSourceSurface, if it exists, associated with a
* SourceSurface from another process.
@@ -274,6 +277,8 @@ class CanvasTranslator final : public gfx::InlineTranslator,
void GetDataSurface(uint64_t aSurfaceRef);
+ static void Shutdown();
+
private:
~CanvasTranslator();
@@ -333,6 +338,7 @@ class CanvasTranslator final : public gfx::InlineTranslator,
#if defined(XP_WIN)
RefPtr<ID3D11Device> mDevice;
#endif
+ static StaticRefPtr<gfx::SharedContextWebgl> sSharedContext;
RefPtr<gfx::SharedContextWebgl> mSharedContext;
RefPtr<RemoteTextureOwnerClient> mRemoteTextureOwner;
diff --git a/gfx/layers/ipc/CompositorBridgeChild.cpp b/gfx/layers/ipc/CompositorBridgeChild.cpp
index 070e6d673e..83374e3d30 100644
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -18,8 +18,7 @@
#include "mozilla/layers/CanvasChild.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/layers/PTextureChild.h"
-#include "mozilla/layers/TextureClient.h" // for TextureClient
-#include "mozilla/layers/TextureClientPool.h" // for TextureClientPool
+#include "mozilla/layers/TextureClient.h" // for TextureClient
#include "mozilla/layers/WebRenderBridgeChild.h"
#include "mozilla/layers/SyncObject.h" // for SyncObjectClient
#include "mozilla/gfx/CanvasManagerChild.h"
@@ -135,10 +134,6 @@ void CompositorBridgeChild::Destroy() {
// happens.
RefPtr<CompositorBridgeChild> selfRef = this;
- for (size_t i = 0; i < mTexturePools.Length(); i++) {
- mTexturePools[i]->Destroy();
- }
-
if (mSectionAllocator) {
delete mSectionAllocator;
mSectionAllocator = nullptr;
@@ -275,9 +270,6 @@ bool CompositorBridgeChild::CompositorIsInGPUProcess() {
mozilla::ipc::IPCResult CompositorBridgeChild::RecvDidComposite(
const LayersId& aId, const nsTArray<TransactionId>& aTransactionIds,
const TimeStamp& aCompositeStart, const TimeStamp& aCompositeEnd) {
- // Hold a reference to keep texture pools alive. See bug 1387799
- const auto texturePools = mTexturePools.Clone();
-
for (const auto& id : aTransactionIds) {
if (mLayerManager) {
MOZ_ASSERT(!aId.IsValid());
@@ -293,10 +285,6 @@ mozilla::ipc::IPCResult CompositorBridgeChild::RecvDidComposite(
}
}
- for (size_t i = 0; i < texturePools.Length(); i++) {
- texturePools[i]->ReturnDeferredClients();
- }
-
return IPC_OK();
}
diff --git a/gfx/layers/ipc/CompositorBridgeChild.h b/gfx/layers/ipc/CompositorBridgeChild.h
index 7e0a4799fe..7ac7ccc197 100644
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -45,7 +45,6 @@ class CompositorManagerChild;
class CompositorOptions;
class WebRenderLayerManager;
class TextureClient;
-class TextureClientPool;
struct FrameMetrics;
struct FwdTransactionCounter;
@@ -233,8 +232,6 @@ class CompositorBridgeChild final : public PCompositorBridgeChild,
nsCOMPtr<nsISerialEventTarget> mThread;
- AutoTArray<RefPtr<TextureClientPool>, 2> mTexturePools;
-
uint64_t mProcessToken;
FixedSizeSmallShmemSectionAllocator* mSectionAllocator;
diff --git a/gfx/layers/ipc/CompositorBridgeParent.cpp b/gfx/layers/ipc/CompositorBridgeParent.cpp
index 3982791357..767ea47b2f 100644
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1777,20 +1777,20 @@ int32_t RecordContentFrameTime(
ContentFrameMarker{});
}
- mozilla::glean::gfx_content_frame_time::from_paint.AccumulateSamples(
- {static_cast<unsigned long long>(fracLatencyNorm)});
+ mozilla::glean::gfx_content_frame_time::from_paint.AccumulateSingleSample(
+ static_cast<unsigned long long>(fracLatencyNorm));
if (!(aTxnId == VsyncId()) && aVsyncStart) {
latencyMs = (aCompositeEnd - aVsyncStart).ToMilliseconds();
latencyNorm = latencyMs / aVsyncRate.ToMilliseconds();
fracLatencyNorm = lround(latencyNorm * 100.0);
int32_t result = fracLatencyNorm;
- mozilla::glean::gfx_content_frame_time::from_vsync.AccumulateSamples(
- {static_cast<unsigned long long>(fracLatencyNorm)});
+ mozilla::glean::gfx_content_frame_time::from_vsync.AccumulateSingleSample(
+ static_cast<unsigned long long>(fracLatencyNorm));
if (aContainsSVGGroup) {
- mozilla::glean::gfx_content_frame_time::with_svg.AccumulateSamples(
- {static_cast<unsigned long long>(fracLatencyNorm)});
+ mozilla::glean::gfx_content_frame_time::with_svg.AccumulateSingleSample(
+ static_cast<unsigned long long>(fracLatencyNorm));
}
// Record CONTENT_FRAME_TIME_REASON.
@@ -1889,8 +1889,8 @@ int32_t RecordContentFrameTime(
fracLatencyNorm = lround(latencyNorm * 100.0);
}
mozilla::glean::gfx_content_frame_time::without_resource_upload
- .AccumulateSamples(
- {static_cast<unsigned long long>(fracLatencyNorm)});
+ .AccumulateSingleSample(
+ static_cast<unsigned long long>(fracLatencyNorm));
if (aStats) {
latencyMs -= (double(aStats->gpu_cache_upload_time) / 1000000.0);
@@ -1898,8 +1898,8 @@ int32_t RecordContentFrameTime(
fracLatencyNorm = lround(latencyNorm * 100.0);
}
mozilla::glean::gfx_content_frame_time::without_resource_upload
- .AccumulateSamples(
- {static_cast<unsigned long long>(fracLatencyNorm)});
+ .AccumulateSingleSample(
+ static_cast<unsigned long long>(fracLatencyNorm));
}
return result;
}
diff --git a/gfx/layers/ipc/LayersMessageUtils.h b/gfx/layers/ipc/LayersMessageUtils.h
index a4e9557ac3..a0da6154d5 100644
--- a/gfx/layers/ipc/LayersMessageUtils.h
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -18,6 +18,7 @@
#include "ipc/IPCMessageUtils.h"
#include "mozilla/ScrollSnapInfo.h"
#include "mozilla/ServoBindings.h"
+#include "mozilla/dom/WebGLIpdl.h"
#include "mozilla/ipc/ByteBuf.h"
#include "mozilla/ipc/ProtocolMessageUtils.h"
#include "mozilla/layers/APZInputBridge.h"
@@ -48,15 +49,11 @@ namespace IPC {
template <>
struct ParamTraits<mozilla::layers::LayersId>
- : public PlainOldDataSerializer<mozilla::layers::LayersId> {};
+ : public ParamTraits_TiedFields<mozilla::layers::LayersId> {};
template <typename T>
struct ParamTraits<mozilla::layers::BaseTransactionId<T>>
- : public PlainOldDataSerializer<mozilla::layers::BaseTransactionId<T>> {};
-
-template <>
-struct ParamTraits<mozilla::VsyncId>
- : public PlainOldDataSerializer<mozilla::VsyncId> {};
+ : public ParamTraits_TiedFields<mozilla::layers::BaseTransactionId<T>> {};
template <>
struct ParamTraits<mozilla::VsyncEvent> {
@@ -419,7 +416,7 @@ struct ParamTraits<mozilla::StyleScrollSnapStop>
template <>
struct ParamTraits<mozilla::ScrollSnapTargetId>
- : public PlainOldDataSerializer<mozilla::ScrollSnapTargetId> {};
+ : public ParamTraits_IsEnumCase<mozilla::ScrollSnapTargetId> {};
template <>
struct ParamTraits<mozilla::SnapPoint> {
@@ -495,26 +492,12 @@ struct ParamTraits<mozilla::ScrollSnapInfo> {
};
template <>
-struct ParamTraits<mozilla::layers::OverscrollBehaviorInfo> {
- // Not using PlainOldDataSerializer so we get enum validation
- // for the members.
-
- typedef mozilla::layers::OverscrollBehaviorInfo paramType;
-
- static void Write(MessageWriter* aWriter, const paramType& aParam) {
- WriteParam(aWriter, aParam.mBehaviorX);
- WriteParam(aWriter, aParam.mBehaviorY);
- }
-
- static bool Read(MessageReader* aReader, paramType* aResult) {
- return (ReadParam(aReader, &aResult->mBehaviorX) &&
- ReadParam(aReader, &aResult->mBehaviorY));
- }
-};
+struct ParamTraits<mozilla::layers::OverscrollBehaviorInfo>
+ : public ParamTraits_TiedFields<mozilla::layers::OverscrollBehaviorInfo> {};
template <typename T>
struct ParamTraits<mozilla::ScrollGeneration<T>>
- : PlainOldDataSerializer<mozilla::ScrollGeneration<T>> {};
+ : public ParamTraits_TiedFields<mozilla::ScrollGeneration<T>> {};
template <>
struct ParamTraits<mozilla::ScrollUpdateType>
diff --git a/gfx/layers/ipc/SharedSurfacesMemoryReport.h b/gfx/layers/ipc/SharedSurfacesMemoryReport.h
index 81baf1349f..31e27bccad 100644
--- a/gfx/layers/ipc/SharedSurfacesMemoryReport.h
+++ b/gfx/layers/ipc/SharedSurfacesMemoryReport.h
@@ -12,6 +12,7 @@
#include "base/process.h"
#include "ipc/IPCMessageUtils.h"
#include "ipc/IPCMessageUtilsSpecializations.h"
+#include "mozilla/dom/WebGLIpdl.h"
#include "mozilla/gfx/Point.h" // for IntSize
namespace mozilla {
@@ -26,8 +27,16 @@ class SharedSurfacesMemoryReport final {
int32_t mStride;
uint32_t mConsumers;
bool mCreatorRef;
+ PaddingField<bool, 3> _padding;
+
+ auto MutTiedFields() {
+ return std::tie(mCreatorPid, mSize, mStride, mConsumers, mCreatorRef,
+ _padding);
+ }
};
+ auto MutTiedFields() { return std::tie(mSurfaces); }
+
std::unordered_map<uint64_t, SurfaceEntry> mSurfaces;
};
@@ -37,21 +46,13 @@ class SharedSurfacesMemoryReport final {
namespace IPC {
template <>
-struct ParamTraits<mozilla::layers::SharedSurfacesMemoryReport> {
- typedef mozilla::layers::SharedSurfacesMemoryReport paramType;
-
- static void Write(MessageWriter* aWriter, const paramType& aParam) {
- WriteParam(aWriter, aParam.mSurfaces);
- }
-
- static bool Read(MessageReader* aReader, paramType* aResult) {
- return ReadParam(aReader, &aResult->mSurfaces);
- }
-};
+struct ParamTraits<mozilla::layers::SharedSurfacesMemoryReport>
+ : public ParamTraits_TiedFields<
+ mozilla::layers::SharedSurfacesMemoryReport> {};
template <>
struct ParamTraits<mozilla::layers::SharedSurfacesMemoryReport::SurfaceEntry>
- : public PlainOldDataSerializer<
+ : public ParamTraits_TiedFields<
mozilla::layers::SharedSurfacesMemoryReport::SurfaceEntry> {};
} // namespace IPC
diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build
index 384611b68b..176e6b54a4 100644
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -125,7 +125,6 @@ EXPORTS.mozilla.layers += [
"client/GPUVideoTextureClient.h",
"client/ImageClient.h",
"client/TextureClient.h",
- "client/TextureClientPool.h",
"client/TextureClientRecycleAllocator.h",
"client/TextureClientSharedSurface.h",
"client/TextureRecorded.h",
@@ -354,7 +353,6 @@ UNIFIED_SOURCES += [
"client/CompositableClient.cpp",
"client/GPUVideoTextureClient.cpp",
"client/ImageClient.cpp",
- "client/TextureClientPool.cpp",
"client/TextureClientRecycleAllocator.cpp",
"client/TextureClientSharedSurface.cpp",
"client/TextureRecorded.cpp",
diff --git a/gfx/layers/wr/StackingContextHelper.cpp b/gfx/layers/wr/StackingContextHelper.cpp
index d5ffbf26a0..2c0627f63f 100644
--- a/gfx/layers/wr/StackingContextHelper.cpp
+++ b/gfx/layers/wr/StackingContextHelper.cpp
@@ -272,5 +272,14 @@ Maybe<gfx::Matrix4x4> StackingContextHelper::GetDeferredTransformMatrix()
}
}
+void StackingContextHelper::ClearDeferredTransformItem() const {
+ mDeferredTransformItem = nullptr;
+}
+
+void StackingContextHelper::RestoreDeferredTransformItem(
+ nsDisplayTransform* aItem) const {
+ mDeferredTransformItem = aItem;
+}
+
} // namespace layers
} // namespace mozilla
diff --git a/gfx/layers/wr/StackingContextHelper.h b/gfx/layers/wr/StackingContextHelper.h
index 368449a2fd..8239b2db0d 100644
--- a/gfx/layers/wr/StackingContextHelper.h
+++ b/gfx/layers/wr/StackingContextHelper.h
@@ -56,6 +56,12 @@ class MOZ_RAII StackingContextHelper {
nsDisplayTransform* GetDeferredTransformItem() const;
Maybe<gfx::Matrix4x4> GetDeferredTransformMatrix() const;
+ // Functions for temporarily clearing and restoring the deferred
+ // transform item during WebRender display list building. These are
+ // used to ensure deferred transforms are not applied in duplicate
+ // to nested nodes in the WebRenderScrollData tree.
+ void ClearDeferredTransformItem() const;
+ void RestoreDeferredTransformItem(nsDisplayTransform* aItem) const;
bool AffectsClipPositioning() const { return mAffectsClipPositioning; }
Maybe<wr::WrSpatialId> ReferenceFrameId() const { return mReferenceFrameId; }
@@ -105,19 +111,19 @@ class MOZ_RAII StackingContextHelper {
// item (i.e. the closest ancestor nsDisplayTransform item of the item that
// created this StackingContextHelper). And then we use
// mDeferredAncestorTransform to store the product of all the other transforms
- // that were deferred. As a result, there is an invariant here that if
- // mDeferredTransformItem is nullptr, mDeferredAncestorTransform will also
- // be Nothing(). Note that we can only do this if the nsDisplayTransform items
- // share the same ASR. If we are processing an nsDisplayTransform item with a
- // different ASR than the previously-deferred item, we assume that the
- // previously-deferred transform will get sent to APZ as part of a separate
- // WebRenderLayerScrollData item, and so we don't need to bother with any
- // merging. (The merging probably wouldn't even make sense because the
- // coordinate spaces might be different in the face of async scrolling). This
- // behaviour of forcing a WebRenderLayerScrollData item to be generated when
- // the ASR changes is implemented in
+ // that were deferred. Note that this means we only need to look at
+ // mDeferredAncestorTransform if mDeferredTransformItem is set. Note that we
+ // can only do this if the nsDisplayTransform items share the same ASR. If we
+ // are processing an nsDisplayTransform item with a different ASR than the
+ // previously-deferred item, we assume that the previously-deferred transform
+ // will get sent to APZ as part of a separate WebRenderLayerScrollData item,
+ // and so we don't need to bother with any merging. (The merging probably
+ // wouldn't even make sense because the coordinate spaces might be different
+ // in the face of async scrolling). This behaviour of forcing a
+ // WebRenderLayerScrollData item to be generated when the ASR changes is
+ // implemented in
// WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList.
- nsDisplayTransform* mDeferredTransformItem;
+ mutable nsDisplayTransform* mDeferredTransformItem;
Maybe<gfx::Matrix4x4> mDeferredAncestorTransform;
bool mRasterizeLocally;
diff --git a/gfx/layers/wr/WebRenderBridgeParent.cpp b/gfx/layers/wr/WebRenderBridgeParent.cpp
index 83139b6af6..68e0c7af8c 100644
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -1919,6 +1919,10 @@ mozilla::ipc::IPCResult WebRenderBridgeParent::RecvClearCachedResources() {
wr::AsUint64(mPipelineId), wr::AsUint64(mApi->GetId()),
IsRootWebRenderBridgeParent());
+ if (!IsRootWebRenderBridgeParent()) {
+ mApi->FlushPendingWrTransactionEventsWithoutWait();
+ }
+
// Clear resources
wr::TransactionBuilder txn(mApi);
txn.SetLowPriority(true);
@@ -2618,17 +2622,17 @@ void WebRenderBridgeParent::ScheduleGenerateFrame(wr::RenderReasons aReasons) {
}
void WebRenderBridgeParent::FlushRendering(wr::RenderReasons aReasons,
- bool aWaitForPresent) {
+ bool aBlocking) {
if (mDestroyed) {
return;
}
- // This gets called during e.g. window resizes, so we need to flush the
- // scene (which has the display list at the new window size).
- FlushSceneBuilds();
- FlushFrameGeneration(aReasons);
- if (aWaitForPresent) {
+ if (aBlocking) {
+ FlushSceneBuilds();
+ FlushFrameGeneration(aReasons);
FlushFramePresentation();
+ } else {
+ ScheduleGenerateFrame(aReasons);
}
}
diff --git a/gfx/layers/wr/WebRenderBridgeParent.h b/gfx/layers/wr/WebRenderBridgeParent.h
index d8d80d1047..2c23779fb2 100644
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -244,7 +244,7 @@ class WebRenderBridgeParent final : public PWebRenderBridgeParent,
return aFontKey.mNamespace == mIdNamespace;
}
- void FlushRendering(wr::RenderReasons aReasons, bool aWaitForPresent = true);
+ void FlushRendering(wr::RenderReasons aReasons, bool aBlocking = true);
/**
* Schedule generating WebRender frame definitely at next composite timing.
diff --git a/gfx/layers/wr/WebRenderCommandBuilder.cpp b/gfx/layers/wr/WebRenderCommandBuilder.cpp
index e1bb2e1127..d7c7468f56 100644
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -1882,9 +1882,8 @@ struct NewLayerData {
ScrollableLayerGuid::ViewID mDeferredId = ScrollableLayerGuid::NULL_SCROLL_ID;
bool mTransformShouldGetOwnLayer = false;
- void ComputeDeferredTransformInfo(
- const StackingContextHelper& aSc, nsDisplayItem* aItem,
- nsDisplayTransform* aLastDeferredTransform) {
+ void ComputeDeferredTransformInfo(const StackingContextHelper& aSc,
+ nsDisplayItem* aItem) {
// See the comments on StackingContextHelper::mDeferredTransformItem
// for an overview of what deferred transforms are.
// In the case where we deferred a transform, but have a child display
@@ -1900,14 +1899,6 @@ struct NewLayerData {
// that we deferred, and a child WebRenderLayerScrollData item that
// holds the scroll metadata for the child's ASR.
mDeferredItem = aSc.GetDeferredTransformItem();
- // If this deferred transform is already slated to be emitted onto an
- // ancestor layer, do not emit it on this layer as well. Note that it's
- // sufficient to check the most recently deferred item here, because
- // there's only one per stacking context, and we emit it when changing
- // stacking contexts.
- if (mDeferredItem == aLastDeferredTransform) {
- mDeferredItem = nullptr;
- }
if (mDeferredItem) {
// It's possible the transform's ASR is not only an ancestor of
// the item's ASR, but an ancestor of stopAtAsr. In such cases,
@@ -2071,10 +2062,7 @@ void WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(
newLayerData->mLayerCountBeforeRecursing = mLayerScrollData.size();
newLayerData->mStopAtAsr =
mAsrStack.empty() ? nullptr : mAsrStack.back();
- newLayerData->ComputeDeferredTransformInfo(
- aSc, item,
- mDeferredTransformStack.empty() ? nullptr
- : mDeferredTransformStack.back());
+ newLayerData->ComputeDeferredTransformInfo(aSc, item);
// Ensure our children's |stopAtAsr| is not be an ancestor of our
// |stopAtAsr|, otherwise we could get cyclic scroll metadata
@@ -2096,10 +2084,12 @@ void WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(
mAsrStack.push_back(stopAtAsrForChildren);
// If we're going to emit a deferred transform onto this layer,
- // keep track of that so descendant layers know not to emit the
- // same deferred transform.
+ // clear the deferred transform from the StackingContextHelper
+ // while we are building the subtree of descendant layers.
+ // This ensures that the deferred transform is not applied in
+ // duplicate to any of our descendant layers.
if (newLayerData->mDeferredItem) {
- mDeferredTransformStack.push_back(newLayerData->mDeferredItem);
+ aSc.ClearDeferredTransformItem();
}
}
}
@@ -2143,8 +2133,7 @@ void WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(
mAsrStack.pop_back();
if (newLayerData->mDeferredItem) {
- MOZ_ASSERT(!mDeferredTransformStack.empty());
- mDeferredTransformStack.pop_back();
+ aSc.RestoreDeferredTransformItem(newLayerData->mDeferredItem);
}
const ActiveScrolledRoot* stopAtAsr = newLayerData->mStopAtAsr;
diff --git a/gfx/layers/wr/WebRenderCommandBuilder.h b/gfx/layers/wr/WebRenderCommandBuilder.h
index 68c9a4ce63..8f17a73e3f 100644
--- a/gfx/layers/wr/WebRenderCommandBuilder.h
+++ b/gfx/layers/wr/WebRenderCommandBuilder.h
@@ -208,9 +208,6 @@ class WebRenderCommandBuilder final {
// need this so that WebRenderLayerScrollData items that deeper in the
// tree don't duplicate scroll metadata that their ancestors already have.
std::vector<const ActiveScrolledRoot*> mAsrStack;
- // A similar stack to track the deferred transform that we decided to emit
- // most recently.
- std::vector<nsDisplayTransform*> mDeferredTransformStack;
const ActiveScrolledRoot* mLastAsr;
WebRenderUserDataRefTable mWebRenderUserDatas;
diff --git a/gfx/layers/wr/WebRenderLayerManager.cpp b/gfx/layers/wr/WebRenderLayerManager.cpp
index 66b98a7db3..c29517c696 100644
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -96,6 +96,7 @@ bool WebRenderLayerManager::Initialize(
}
mWrChild = static_cast<WebRenderBridgeChild*>(bridge);
+ mHasFlushedThisChild = false;
TextureFactoryIdentifier textureFactoryIdentifier;
wr::MaybeIdNamespace idNamespace;
@@ -694,24 +695,30 @@ void WebRenderLayerManager::FlushRendering(wr::RenderReasons aReasons) {
}
MOZ_ASSERT(mWidget);
- // If value of IsResizingNativeWidget() is nothing, we assume that resizing
- // might happen.
- bool resizing = mWidget && mWidget->IsResizingNativeWidget().valueOr(true);
+ // If widget bounds size is different from the last flush, consider
+ // this to be a resize. It's important to use GetClientSize here,
+ // because that has extra plumbing to support initial display cases
+ // where the widget doesn't yet have real bounds.
+ LayoutDeviceIntSize widgetSize = mWidget->GetClientSize();
+ bool resizing = widgetSize != mFlushWidgetSize;
+ mFlushWidgetSize = widgetSize;
if (resizing) {
aReasons = aReasons | wr::RenderReasons::RESIZE;
}
- // Limit async FlushRendering to !resizing and Win DComp.
- // XXX relax the limitation
- if (WrBridge()->GetCompositorUseDComp() && !resizing) {
- cBridge->SendFlushRenderingAsync(aReasons);
- } else if (mWidget->SynchronouslyRepaintOnResize() ||
- StaticPrefs::layers_force_synchronous_resize()) {
+ // Check for the conditions where we we force a sync flush. The first
+ // flush for this child should always be sync. Resizes should be
+ // sometimes be sync. Everything else can be async.
+ if (!mHasFlushedThisChild ||
+ (resizing && (mWidget->SynchronouslyRepaintOnResize() ||
+ StaticPrefs::layers_force_synchronous_resize()))) {
cBridge->SendFlushRendering(aReasons);
} else {
cBridge->SendFlushRenderingAsync(aReasons);
}
+
+ mHasFlushedThisChild = true;
}
void WebRenderLayerManager::WaitOnTransactionProcessed() {
diff --git a/gfx/layers/wr/WebRenderLayerManager.h b/gfx/layers/wr/WebRenderLayerManager.h
index 31fc9b6678..5ee2cc76a5 100644
--- a/gfx/layers/wr/WebRenderLayerManager.h
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -226,6 +226,7 @@ class WebRenderLayerManager final : public WindowRenderer {
nsIWidget* MOZ_NON_OWNING_REF mWidget;
RefPtr<WebRenderBridgeChild> mWrChild;
+ bool mHasFlushedThisChild;
RefPtr<TransactionIdAllocator> mTransactionIdAllocator;
TransactionId mLatestTransactionId;
@@ -273,6 +274,8 @@ class WebRenderLayerManager final : public WindowRenderer {
UniquePtr<wr::DisplayListBuilder> mDLBuilder;
ScrollUpdatesMap mPendingScrollUpdates;
+
+ LayoutDeviceIntSize mFlushWidgetSize;
};
} // namespace layers