implement display projection clipping in h/w composer

- cropping to the projection's "viewport" is "simply"
  accomplished by intersecting it with the window crop
  expressed in layerstack space.

Bug: 7149437
Change-Id: I0e90b3f37945292314b5d78a8f134935967e8053
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 572e7c8..db2b20e 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -281,6 +281,14 @@
 }
 
 Rect LayerBase::computeCrop(const sp<const DisplayDevice>& hw) const {
+    /*
+     * The way we compute the crop (aka. texture coordinates when we have a
+     * Layer) produces a different output from the GL code in
+     * drawWithOpenGL() due to HWC being limited to integers. The difference
+     * can be large if getContentTransform() contains a large scale factor.
+     * See comments in drawWithOpenGL() for more details.
+     */
+
     // the content crop is the area of the content that gets scaled to the
     // layer's size.
     Rect crop(getContentCrop());
@@ -288,7 +296,21 @@
     // the active.crop is the area of the window that gets cropped, but not
     // scaled in any ways.
     const State& s(drawingState());
-    Rect activeCrop(s.active.crop);
+
+    // apply the projection's clipping to the window crop in
+    // layerstack space, and convert-back to layer space.
+    // if there are no window scaling (or content scaling) involved,
+    // this operation will map to full pixels in the buffer.
+    // NOTE: should we revert to GL composition if a scaling is involved
+    // since it cannot be represented in the HWC API?
+    Rect activeCrop(s.transform.transform(s.active.crop));
+    activeCrop.intersect(hw->getViewport(), &activeCrop);
+    activeCrop = s.transform.inverse().transform(activeCrop);
+
+    // paranoia: make sure the window-crop is constrained in the
+    // window's bounds
+    activeCrop.intersect(Rect(s.active.w, s.active.h), &activeCrop);
+
     if (!activeCrop.isEmpty()) {
         // Transform the window crop to match the buffer coordinate system,
         // which means using the inverse of the current transform set on the
@@ -350,6 +372,7 @@
     // here we're guaranteed that the layer's transform preserves rects
 
     Rect frame(s.transform.transform(computeBounds()));
+    frame.intersect(hw->getViewport(), &frame);
     const Transform& tr(hw->getTransform());
     layer.setFrame(tr.transform(frame));
     layer.setCrop(computeCrop(hw));
@@ -464,10 +487,22 @@
         GLfloat v;
     };
 
-    Rect win(s.active.w, s.active.h);
-    if (!s.active.crop.isEmpty()) {
-        win.intersect(s.active.crop, &win);
-    }
+
+    /*
+     * NOTE: the way we compute the texture coordinates here produces
+     * different results than when we take the HWC path -- in the later case
+     * the "source crop" is rounded to texel boundaries.
+     * This can produce significantly different results when the texture
+     * is scaled by a large amount.
+     *
+     * The GL code below is more logical (imho), and the difference with
+     * HWC is due to a limitation of the HWC API to integers -- a question
+     * is suspend is wether we should ignore this problem or revert to
+     * GL composition when a buffer scaling is applied (maybe with some
+     * minimal value)? Or, we could make GL behave like HWC -- but this feel
+     * like more of a hack.
+     */
+    const Rect win(computeBounds());
 
     GLfloat left   = GLfloat(win.left)   / GLfloat(s.active.w);
     GLfloat top    = GLfloat(win.top)    / GLfloat(s.active.h);