Bug 866232 - Add visible region tracking and hit-testing code for APZCTreeManager. r=BenWa, mattwoodrow

This commit is contained in:
Kartikaya Gupta 2013-07-30 14:03:41 -04:00
parent be119cfa6c
commit c40b9b29dd
5 changed files with 201 additions and 202 deletions

View File

@ -4,8 +4,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "APZCTreeManager.h"
#include "AsyncCompositionManager.h" // for ViewTransform
#include "LayerManagerComposite.h" // for AsyncCompositionManager.h
#include "Compositor.h"
#define APZC_LOG(args...)
// #define APZC_LOG(args...) printf_stderr(args)
namespace mozilla {
namespace layers {
@ -60,6 +65,7 @@ APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor, Laye
}
for (int i = apzcsToDestroy.Length() - 1; i >= 0; i--) {
APZC_LOG("Destroying APZC at %p\n", apzcsToDestroy[i].get());
apzcsToDestroy[i]->Destroy();
}
}
@ -97,10 +103,19 @@ APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor,
controller->SetPrevSibling(nullptr);
controller->SetLastChild(nullptr);
}
APZC_LOG("Using APZC %p for layer %p with identifiers %lld %lld\n", controller, aLayer, aLayersId, container->GetFrameMetrics().mScrollId);
controller->NotifyLayersUpdated(container->GetFrameMetrics(),
aIsFirstPaint && (aLayersId == aFirstPaintLayersId));
gfx3DMatrix transform = container->GetEffectiveTransform();
LayerRect visible = container->GetFrameMetrics().mViewport * container->GetFrameMetrics().LayersPixelsPerCSSPixel();
gfxRect transformed = transform.TransformBounds(gfxRect(visible.x, visible.y, visible.width, visible.height));
controller->SetVisibleRegion(transformed);
APZC_LOG("Setting rect(%f %f %f %f) as visible region for %p\n", transformed.x, transformed.y,
transformed.width, transformed.height,
controller);
// Bind the APZC instance into the tree of APZCs
if (aNextSibling) {
aNextSibling->SetPrevSibling(controller);
@ -311,9 +326,16 @@ already_AddRefed<AsyncPanZoomController>
APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint)
{
MonitorAutoLock lock(mTreeLock);
// TODO: Do a hit test on the tree of
// APZC instances and return the right one.
return nullptr;
nsRefPtr<AsyncPanZoomController> target;
// The root may have siblings, so check those too
gfxPoint point(aPoint.x, aPoint.y);
for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
target = GetAPZCAtPoint(apzc, point);
if (target) {
break;
}
}
return target.forget();
}
AsyncPanZoomController*
@ -333,5 +355,24 @@ APZCTreeManager::FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableL
return nullptr;
}
AsyncPanZoomController*
APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc, gfxPoint aHitTestPoint)
{
// This walks the tree in depth-first, reverse order, so that it encounters
// APZCs front-to-back on the screen.
ViewTransform apzcTransform = aApzc->GetCurrentAsyncTransform();
gfxPoint untransformed = gfx3DMatrix(apzcTransform).Inverse().ProjectPoint(aHitTestPoint);
for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
AsyncPanZoomController* match = GetAPZCAtPoint(child, untransformed);
if (match) {
return match;
}
}
if (aApzc->VisibleRegionContains(untransformed)) {
return aApzc;
}
return nullptr;
}
}
}

View File

@ -228,8 +228,9 @@ private:
*/
already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScrollableLayerGuid& aGuid);
already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint);
/* Recursive helper */
/* Recursive helpers */
AsyncPanZoomController* FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid);
AsyncPanZoomController* GetAPZCAtPoint(AsyncPanZoomController* aApzc, gfxPoint aHitTestPoint);
/**
* Recursive helper function to build the APZC tree. The tree of APZC instances has

View File

@ -1465,55 +1465,6 @@ void AsyncPanZoomController::SendAsyncScrollEvent() {
controller->SendAsyncScrollDOMEvent(contentRect, scrollableSize);
}
static void GetAPZCAtPointOnSubtree(const ContainerLayer& aLayerIn,
const gfxPoint& aPoint,
AsyncPanZoomController** aApzcOut,
LayerIntPoint* aRelativePointOut)
{
// Making layers const correct is very slow because it requires
// a near clobber of the tree. Once const correct is further along
// remove this cast.
ContainerLayer& aLayer = const_cast<ContainerLayer&>(aLayerIn);
gfx3DMatrix transform = aLayer.GetLocalTransform().Inverse();
gfxPoint layerPoint = transform.Transform(aPoint);
// iterate over the children first. They are better match then the parent
Layer* currLayer = aLayer.GetLastChild();
while (currLayer) {
if (currLayer->AsContainerLayer()) {
GetAPZCAtPointOnSubtree(*currLayer->AsContainerLayer(), layerPoint, aApzcOut, aRelativePointOut);
}
if (*aApzcOut) {
return;
}
currLayer = currLayer->GetPrevSibling();
}
if (aLayer.GetFrameMetrics().IsScrollable()) {
const FrameMetrics& frame = aLayer.GetFrameMetrics();
LayerRect layerViewport = frame.mViewport * frame.LayersPixelsPerCSSPixel();
bool intersect = layerViewport.Contains(layerPoint.x, layerPoint.y);
if (intersect) {
*aApzcOut = aLayer.GetAsyncPanZoomController();
*aRelativePointOut = LayerIntPoint(NS_lround(layerPoint.x), NS_lround(layerPoint.y));
}
}
}
void AsyncPanZoomController::GetAPZCAtPoint(const ContainerLayer& aLayerTree,
const ScreenIntPoint& aPoint,
AsyncPanZoomController** aApzcOut,
LayerIntPoint* aRelativePointOut)
{
*aApzcOut = nullptr;
gfxPoint point(aPoint.x, aPoint.y);
GetAPZCAtPointOnSubtree(aLayerTree, point, aApzcOut, aRelativePointOut);
}
void AsyncPanZoomController::UpdateScrollOffset(const CSSPoint& aScrollOffset)
{
MonitorAutoLock monitor(mMonitor);

View File

@ -271,17 +271,6 @@ public:
*/
static void SetFrameTime(const TimeStamp& aMilliseconds);
/**
* Transform and intersect aPoint with the layer tree returning the appropriate
* AsyncPanZoomController for this point.
* aRelativePointOut Return the point transformed into the layer coordinates
* relative to the scroll origin for this layer.
*/
static void GetAPZCAtPoint(const ContainerLayer& aLayerTree,
const ScreenIntPoint& aPoint,
AsyncPanZoomController** aApzcOut,
LayerIntPoint* aRelativePointOut);
/**
* Update mFrameMetrics.mScrollOffset to the given offset.
* This is necessary in cases where a scroll is not caused by user
@ -649,6 +638,23 @@ public:
private:
nsRefPtr<AsyncPanZoomController> mLastChild;
nsRefPtr<AsyncPanZoomController> mPrevSibling;
/* The functions and members in this section are used to maintain the
* area that this APZC instance is responsible for. This is used when
* hit-testing to see which APZC instance should handle touch events.
*/
public:
void SetVisibleRegion(gfxRect rect) { mVisibleRegion = rect; }
bool VisibleRegionContains(const gfxPoint& aPoint) const {
return mVisibleRegion.Contains(aPoint.x, aPoint.y);
}
private:
/* This is the viewport of the layer that this APZC corresponds to, but
* post-transform. In other words, it is in the coordinate space of its
* parent layer. */
gfxRect mVisibleRegion;
};
}

View File

@ -339,143 +339,143 @@ CreateTestLayerTree(nsRefPtr<LayerManager>& aLayerManager, nsTArray<nsRefPtr<Lay
return CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, aLayerManager, aLayers);
}
TEST(AsyncPanZoomController, GetAPZCAtPoint) {
nsTArray<nsRefPtr<Layer> > layers;
nsRefPtr<LayerManager> lm;
nsRefPtr<Layer> root = CreateTestLayerTree(lm, layers);
TimeStamp testStartTime = TimeStamp::Now();
AsyncPanZoomController::SetFrameTime(testStartTime);
nsRefPtr<MockContentController> mcc = new MockContentController();
nsRefPtr<AsyncPanZoomController> apzcMain = new AsyncPanZoomController(mcc);
nsRefPtr<AsyncPanZoomController> apzcSub3 = new AsyncPanZoomController(mcc);
nsRefPtr<AsyncPanZoomController> apzcSub4 = new AsyncPanZoomController(mcc);
nsRefPtr<AsyncPanZoomController> apzcSub7 = new AsyncPanZoomController(mcc);
apzcMain->NotifyLayersUpdated(TestFrameMetrics(), true);
nsIntRect layerBound;
ScreenIntPoint touchPoint(20, 20);
AsyncPanZoomController* apzcOut;
LayerIntPoint relativePointOut;
FrameMetrics scrollable;
// No APZC attached so hit testing will return no APZC at (20,20)
AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
&apzcOut, &relativePointOut);
AsyncPanZoomController* nullAPZC = nullptr;
EXPECT_EQ(apzcOut, nullAPZC);
// Now we have a root APZC that will match the page
scrollable.mScrollId = FrameMetrics::ROOT_SCROLL_ID;
layerBound = root->GetVisibleRegion().GetBounds();
scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
layerBound.width, layerBound.height);
root->AsContainerLayer()->SetFrameMetrics(scrollable);
root->AsContainerLayer()->SetAsyncPanZoomController(apzcMain);
AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
&apzcOut, &relativePointOut);
EXPECT_EQ(apzcOut, apzcMain.get());
EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
// Now we have a sub APZC with a better fit
scrollable.mScrollId = FrameMetrics::START_SCROLL_ID;
layerBound = layers[3]->GetVisibleRegion().GetBounds();
scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
layerBound.width, layerBound.height);
layers[3]->AsContainerLayer()->SetFrameMetrics(scrollable);
layers[3]->AsContainerLayer()->SetAsyncPanZoomController(apzcSub3);
AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
&apzcOut, &relativePointOut);
EXPECT_EQ(apzcOut, apzcSub3.get());
EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
// Now test hit testing when we have two scrollable layers
touchPoint = ScreenIntPoint(15,15);
AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
&apzcOut, &relativePointOut);
EXPECT_EQ(apzcOut, apzcSub3.get()); // We haven't bound apzcSub4 yet
scrollable.mScrollId++;
layerBound = layers[4]->GetVisibleRegion().GetBounds();
scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
layerBound.width, layerBound.height);
layers[4]->AsContainerLayer()->SetFrameMetrics(scrollable);
layers[4]->AsContainerLayer()->SetAsyncPanZoomController(apzcSub4);
AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
&apzcOut, &relativePointOut);
EXPECT_EQ(apzcOut, apzcSub4.get());
EXPECT_EQ(LayerIntPoint(15, 15), relativePointOut);
// Hit test ouside the reach of apzc3/4 but inside apzcMain
touchPoint = ScreenIntPoint(90,90);
AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
&apzcOut, &relativePointOut);
EXPECT_EQ(apzcOut, apzcMain.get());
EXPECT_EQ(LayerIntPoint(90, 90), relativePointOut);
// Hit test ouside the reach of any layer
touchPoint = ScreenIntPoint(1000,10);
AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
&apzcOut, &relativePointOut);
EXPECT_EQ(apzcOut, nullAPZC);
touchPoint = ScreenIntPoint(-1000,10);
AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
&apzcOut, &relativePointOut);
EXPECT_EQ(apzcOut, nullAPZC);
// Test layer transform
gfx3DMatrix transform;
transform.ScalePost(0.1, 0.1, 1);
root->SetBaseTransform(transform);
touchPoint = ScreenIntPoint(50,50); // This point is now outside the root layer
AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
&apzcOut, &relativePointOut);
EXPECT_EQ(apzcOut, nullAPZC);
touchPoint = ScreenIntPoint(2,2);
AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
&apzcOut, &relativePointOut);
EXPECT_EQ(apzcOut, apzcSub4.get());
EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
// Scale layer[4] outside the range
layers[4]->SetBaseTransform(transform);
// layer 4 effective visible screenrect: (0.05, 0.05, 0.2, 0.2)
// Does not contain (2, 2)
AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
&apzcOut, &relativePointOut);
EXPECT_EQ(apzcOut, apzcSub3.get());
EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
// Transformation chain to layer 7
scrollable.mScrollId++;
layerBound = layers[7]->GetVisibleRegion().GetBounds();
scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
layerBound.width, layerBound.height);
layers[7]->AsContainerLayer()->SetFrameMetrics(scrollable);
layers[7]->AsContainerLayer()->SetAsyncPanZoomController(apzcSub7);
gfx3DMatrix translateTransform;
translateTransform.Translate(gfxPoint3D(10, 10, 0));
layers[5]->SetBaseTransform(translateTransform);
gfx3DMatrix translateTransform2;
translateTransform2.Translate(gfxPoint3D(-20, 0, 0));
layers[6]->SetBaseTransform(translateTransform2);
gfx3DMatrix translateTransform3;
translateTransform3.ScalePost(1,15,1);
layers[7]->SetBaseTransform(translateTransform3);
touchPoint = ScreenIntPoint(1,45);
// layer 7 effective visible screenrect (0,16,4,60) but clipped by parent layers
AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
&apzcOut, &relativePointOut);
EXPECT_EQ(apzcOut, apzcSub7.get());
EXPECT_EQ(LayerIntPoint(20, 29), relativePointOut);
}
//TEST(AsyncPanZoomController, GetAPZCAtPoint) {
// nsTArray<nsRefPtr<Layer> > layers;
// nsRefPtr<LayerManager> lm;
// nsRefPtr<Layer> root = CreateTestLayerTree(lm, layers);
//
// TimeStamp testStartTime = TimeStamp::Now();
// AsyncPanZoomController::SetFrameTime(testStartTime);
//
// nsRefPtr<MockContentController> mcc = new MockContentController();
// nsRefPtr<AsyncPanZoomController> apzcMain = new AsyncPanZoomController(mcc);
// nsRefPtr<AsyncPanZoomController> apzcSub3 = new AsyncPanZoomController(mcc);
// nsRefPtr<AsyncPanZoomController> apzcSub4 = new AsyncPanZoomController(mcc);
// nsRefPtr<AsyncPanZoomController> apzcSub7 = new AsyncPanZoomController(mcc);
// apzcMain->NotifyLayersUpdated(TestFrameMetrics(), true);
//
// nsIntRect layerBound;
// ScreenIntPoint touchPoint(20, 20);
// AsyncPanZoomController* apzcOut;
// LayerIntPoint relativePointOut;
//
// FrameMetrics scrollable;
//
// // No APZC attached so hit testing will return no APZC at (20,20)
// AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
// &apzcOut, &relativePointOut);
//
// AsyncPanZoomController* nullAPZC = nullptr;
// EXPECT_EQ(apzcOut, nullAPZC);
//
// // Now we have a root APZC that will match the page
// scrollable.mScrollId = FrameMetrics::ROOT_SCROLL_ID;
// layerBound = root->GetVisibleRegion().GetBounds();
// scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
// layerBound.width, layerBound.height);
// root->AsContainerLayer()->SetFrameMetrics(scrollable);
// root->AsContainerLayer()->SetAsyncPanZoomController(apzcMain);
// AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
// &apzcOut, &relativePointOut);
// EXPECT_EQ(apzcOut, apzcMain.get());
// EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
//
// // Now we have a sub APZC with a better fit
// scrollable.mScrollId = FrameMetrics::START_SCROLL_ID;
// layerBound = layers[3]->GetVisibleRegion().GetBounds();
// scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
// layerBound.width, layerBound.height);
// layers[3]->AsContainerLayer()->SetFrameMetrics(scrollable);
// layers[3]->AsContainerLayer()->SetAsyncPanZoomController(apzcSub3);
// AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
// &apzcOut, &relativePointOut);
// EXPECT_EQ(apzcOut, apzcSub3.get());
// EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
//
// // Now test hit testing when we have two scrollable layers
// touchPoint = ScreenIntPoint(15,15);
// AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
// &apzcOut, &relativePointOut);
// EXPECT_EQ(apzcOut, apzcSub3.get()); // We haven't bound apzcSub4 yet
// scrollable.mScrollId++;
// layerBound = layers[4]->GetVisibleRegion().GetBounds();
// scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
// layerBound.width, layerBound.height);
// layers[4]->AsContainerLayer()->SetFrameMetrics(scrollable);
// layers[4]->AsContainerLayer()->SetAsyncPanZoomController(apzcSub4);
// AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
// &apzcOut, &relativePointOut);
// EXPECT_EQ(apzcOut, apzcSub4.get());
// EXPECT_EQ(LayerIntPoint(15, 15), relativePointOut);
//
// // Hit test ouside the reach of apzc3/4 but inside apzcMain
// touchPoint = ScreenIntPoint(90,90);
// AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
// &apzcOut, &relativePointOut);
// EXPECT_EQ(apzcOut, apzcMain.get());
// EXPECT_EQ(LayerIntPoint(90, 90), relativePointOut);
//
// // Hit test ouside the reach of any layer
// touchPoint = ScreenIntPoint(1000,10);
// AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
// &apzcOut, &relativePointOut);
// EXPECT_EQ(apzcOut, nullAPZC);
// touchPoint = ScreenIntPoint(-1000,10);
// AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
// &apzcOut, &relativePointOut);
// EXPECT_EQ(apzcOut, nullAPZC);
//
// // Test layer transform
// gfx3DMatrix transform;
// transform.ScalePost(0.1, 0.1, 1);
// root->SetBaseTransform(transform);
//
// touchPoint = ScreenIntPoint(50,50); // This point is now outside the root layer
// AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
// &apzcOut, &relativePointOut);
// EXPECT_EQ(apzcOut, nullAPZC);
//
// touchPoint = ScreenIntPoint(2,2);
// AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
// &apzcOut, &relativePointOut);
// EXPECT_EQ(apzcOut, apzcSub4.get());
// EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
//
// // Scale layer[4] outside the range
// layers[4]->SetBaseTransform(transform);
// // layer 4 effective visible screenrect: (0.05, 0.05, 0.2, 0.2)
// // Does not contain (2, 2)
// AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
// &apzcOut, &relativePointOut);
// EXPECT_EQ(apzcOut, apzcSub3.get());
// EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
//
// // Transformation chain to layer 7
// scrollable.mScrollId++;
// layerBound = layers[7]->GetVisibleRegion().GetBounds();
// scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
// layerBound.width, layerBound.height);
// layers[7]->AsContainerLayer()->SetFrameMetrics(scrollable);
// layers[7]->AsContainerLayer()->SetAsyncPanZoomController(apzcSub7);
//
// gfx3DMatrix translateTransform;
// translateTransform.Translate(gfxPoint3D(10, 10, 0));
// layers[5]->SetBaseTransform(translateTransform);
//
// gfx3DMatrix translateTransform2;
// translateTransform2.Translate(gfxPoint3D(-20, 0, 0));
// layers[6]->SetBaseTransform(translateTransform2);
//
// gfx3DMatrix translateTransform3;
// translateTransform3.ScalePost(1,15,1);
// layers[7]->SetBaseTransform(translateTransform3);
//
// touchPoint = ScreenIntPoint(1,45);
// // layer 7 effective visible screenrect (0,16,4,60) but clipped by parent layers
// AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
// &apzcOut, &relativePointOut);
// EXPECT_EQ(apzcOut, apzcSub7.get());
// EXPECT_EQ(LayerIntPoint(20, 29), relativePointOut);
//}