diff options
author | Mathias Agopian <mathias@google.com> | 2013-03-04 22:12:23 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-03-04 22:12:24 +0000 |
commit | f59d2f9070621e8f36b2ded3ec1d07bd3aec6150 (patch) | |
tree | 811f00a95b568369170c3c1bbbdfa93b24a7e72a | |
parent | 006a601f22b275bcabf608f630b321df027d56d1 (diff) | |
parent | 3da1672acbe6a84f1d69f1e21096115c60826aea (diff) | |
download | frameworks_native-f59d2f9070621e8f36b2ded3ec1d07bd3aec6150.zip frameworks_native-f59d2f9070621e8f36b2ded3ec1d07bd3aec6150.tar.gz frameworks_native-f59d2f9070621e8f36b2ded3ec1d07bd3aec6150.tar.bz2 |
Merge changes I0e90b3f3,Ib7769bde,I4c25f34f,I1ec6400a into jb-mr2-dev
* changes:
implement display projection clipping in h/w composer
refactor the crop region for hwc is calculated/set
apply the projection's viewport to the visibleregion passed to hwc
set correct crop rectangle in LayerBase::setCrop
-rw-r--r-- | services/surfaceflinger/DisplayDevice.cpp | 125 | ||||
-rw-r--r-- | services/surfaceflinger/DisplayDevice.h | 6 | ||||
-rw-r--r-- | services/surfaceflinger/Layer.cpp | 47 | ||||
-rw-r--r-- | services/surfaceflinger/Layer.h | 4 | ||||
-rw-r--r-- | services/surfaceflinger/LayerBase.cpp | 119 | ||||
-rw-r--r-- | services/surfaceflinger/LayerBase.h | 15 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 2 | ||||
-rw-r--r-- | services/surfaceflinger/Transform.cpp | 38 | ||||
-rw-r--r-- | services/surfaceflinger/Transform.h | 2 |
9 files changed, 242 insertions, 116 deletions
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 9466944..f9cc341 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -287,7 +287,8 @@ void DisplayDevice::setVisibleLayersSortedByZ(const Vector< sp<LayerBase> >& lay mSecureLayerVisible = false; size_t count = layers.size(); for (size_t i=0 ; i<count ; i++) { - if (layers[i]->isSecure()) { + const sp<LayerBase>& layer(layers[i]); + if (layer->isSecure()) { mSecureLayerVisible = true; } } @@ -365,74 +366,72 @@ status_t DisplayDevice::orientationToTransfrom( } void DisplayDevice::setProjection(int orientation, - const Rect& viewport, const Rect& frame) { - mOrientation = orientation; - mViewport = viewport; - mFrame = frame; - updateGeometryTransform(); -} + const Rect& newViewport, const Rect& newFrame) { + Rect viewport(newViewport); + Rect frame(newFrame); -void DisplayDevice::updateGeometryTransform() { - int w = mDisplayWidth; - int h = mDisplayHeight; - Transform TL, TP, R, S; - if (DisplayDevice::orientationToTransfrom( - mOrientation, w, h, &R) == NO_ERROR) { - dirtyRegion.set(bounds()); - - Rect viewport(mViewport); - Rect frame(mFrame); - - if (!frame.isValid()) { - // the destination frame can be invalid if it has never been set, - // in that case we assume the whole display frame. - frame = Rect(w, h); - } + const int w = mDisplayWidth; + const int h = mDisplayHeight; - if (viewport.isEmpty()) { - // viewport can be invalid if it has never been set, in that case - // we assume the whole display size. - // it's also invalid to have an empty viewport, so we handle that - // case in the same way. - viewport = Rect(w, h); - if (R.getOrientation() & Transform::ROT_90) { - // viewport is always specified in the logical orientation - // of the display (ie: post-rotation). - swap(viewport.right, viewport.bottom); - } - } + Transform R; + DisplayDevice::orientationToTransfrom(orientation, w, h, &R); - float src_width = viewport.width(); - float src_height = viewport.height(); - float dst_width = frame.width(); - float dst_height = frame.height(); - if (src_width != dst_width || src_height != dst_height) { - float sx = dst_width / src_width; - float sy = dst_height / src_height; - S.set(sx, 0, 0, sy); - } + if (!frame.isValid()) { + // the destination frame can be invalid if it has never been set, + // in that case we assume the whole display frame. + frame = Rect(w, h); + } - float src_x = viewport.left; - float src_y = viewport.top; - float dst_x = frame.left; - float dst_y = frame.top; - TL.set(-src_x, -src_y); - TP.set(dst_x, dst_y); - - // The viewport and frame are both in the logical orientation. - // Apply the logical translation, scale to physical size, apply the - // physical translation and finally rotate to the physical orientation. - mGlobalTransform = R * TP * S * TL; - - const uint8_t type = mGlobalTransform.getType(); - mNeedsFiltering = (!mGlobalTransform.preserveRects() || - (type >= Transform::SCALE)); - - mScissor = mGlobalTransform.transform(mViewport); - if (mScissor.isEmpty()) { - mScissor.set(getBounds()); + if (viewport.isEmpty()) { + // viewport can be invalid if it has never been set, in that case + // we assume the whole display size. + // it's also invalid to have an empty viewport, so we handle that + // case in the same way. + viewport = Rect(w, h); + if (R.getOrientation() & Transform::ROT_90) { + // viewport is always specified in the logical orientation + // of the display (ie: post-rotation). + swap(viewport.right, viewport.bottom); } } + + dirtyRegion.set(getBounds()); + + Transform TL, TP, S; + float src_width = viewport.width(); + float src_height = viewport.height(); + float dst_width = frame.width(); + float dst_height = frame.height(); + if (src_width != dst_width || src_height != dst_height) { + float sx = dst_width / src_width; + float sy = dst_height / src_height; + S.set(sx, 0, 0, sy); + } + + float src_x = viewport.left; + float src_y = viewport.top; + float dst_x = frame.left; + float dst_y = frame.top; + TL.set(-src_x, -src_y); + TP.set(dst_x, dst_y); + + // The viewport and frame are both in the logical orientation. + // Apply the logical translation, scale to physical size, apply the + // physical translation and finally rotate to the physical orientation. + mGlobalTransform = R * TP * S * TL; + + const uint8_t type = mGlobalTransform.getType(); + mNeedsFiltering = (!mGlobalTransform.preserveRects() || + (type >= Transform::SCALE)); + + mScissor = mGlobalTransform.transform(viewport); + if (mScissor.isEmpty()) { + mScissor.set(getBounds()); + } + + mOrientation = orientation; + mViewport = viewport; + mFrame = frame; } void DisplayDevice::dump(String8& result, char* buffer, size_t SIZE) const { diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index bb6eb70..c7534af 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -105,8 +105,8 @@ public: int getOrientation() const { return mOrientation; } const Transform& getTransform() const { return mGlobalTransform; } - const Rect& getViewport() const { return mViewport; } - const Rect& getFrame() const { return mFrame; } + const Rect getViewport() const { return mViewport; } + const Rect getFrame() const { return mFrame; } const Rect& getScissor() const { return mScissor; } bool needsFiltering() const { return mNeedsFiltering; } @@ -197,8 +197,6 @@ private: static status_t orientationToTransfrom(int orientation, int w, int h, Transform* tr); - void updateGeometryTransform(); - uint32_t mLayerStack; int mOrientation; // user-provided visible area of the layer stack diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 439acb5..c9f1eb5 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -19,7 +19,6 @@ #include <stdlib.h> #include <stdint.h> #include <sys/types.h> -#include <math.h> #include <cutils/compiler.h> #include <cutils/native_handle.h> @@ -200,48 +199,27 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h, return NO_ERROR; } -Rect Layer::computeBufferCrop() const { - // Start with the SurfaceFlingerConsumer's buffer crop... +Rect Layer::getContentCrop() const { + // this is the crop rectangle that applies to the buffer + // itself (as opposed to the window) Rect crop; if (!mCurrentCrop.isEmpty()) { + // if the buffer crop is defined, we use that crop = mCurrentCrop; - } else if (mActiveBuffer != NULL){ - crop = Rect(mActiveBuffer->getWidth(), mActiveBuffer->getHeight()); + } else if (mActiveBuffer != NULL) { + // otherwise we use the whole buffer + crop = mActiveBuffer->getBounds(); } else { + // if we don't have a buffer yet, we use an empty/invalid crop crop.makeInvalid(); - return crop; } - - // ... then reduce that in the same proportions as the window crop reduces - // the window size. - const State& s(drawingState()); - if (!s.active.crop.isEmpty()) { - // Transform the window crop to match the buffer coordinate system, - // which means using the inverse of the current transform set on the - // SurfaceFlingerConsumer. - uint32_t invTransform = mCurrentTransform; - int winWidth = s.active.w; - int winHeight = s.active.h; - if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | - NATIVE_WINDOW_TRANSFORM_FLIP_H; - winWidth = s.active.h; - winHeight = s.active.w; - } - Rect winCrop = s.active.crop.transform(invTransform, - s.active.w, s.active.h); - - float xScale = float(crop.width()) / float(winWidth); - float yScale = float(crop.height()) / float(winHeight); - crop.left += int(ceilf(float(winCrop.left) * xScale)); - crop.top += int(ceilf(float(winCrop.top) * yScale)); - crop.right -= int(ceilf(float(winWidth - winCrop.right) * xScale)); - crop.bottom -= int(ceilf(float(winHeight - winCrop.bottom) * yScale)); - } - return crop; } +uint32_t Layer::getContentTransform() const { + return mCurrentTransform; +} + void Layer::setGeometry( const sp<const DisplayDevice>& hw, HWComposer::HWCLayerInterface& layer) @@ -278,7 +256,6 @@ void Layer::setGeometry( } else { layer.setTransform(finalTransform); } - layer.setCrop(computeBufferCrop()); } void Layer::setPerFrameData(const sp<const DisplayDevice>& hw, diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index a82767b..242493d 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -103,6 +103,9 @@ public: // the current orientation of the display device. virtual void updateTransformHint(const sp<const DisplayDevice>& hw) const; + virtual Rect getContentCrop() const; + virtual uint32_t getContentTransform() const; + protected: virtual void onFirstRef(); virtual void dump(String8& result, char* scratch, size_t size) const; @@ -115,7 +118,6 @@ private: uint32_t getEffectiveUsage(uint32_t usage) const; bool isCropped() const; - Rect computeBufferCrop() const; static bool getOpacityForFormat(uint32_t format); // Interface implementation for SurfaceFlingerConsumer::FrameAvailableListener diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp index dfdbf30..db2b20e 100644 --- a/services/surfaceflinger/LayerBase.cpp +++ b/services/surfaceflinger/LayerBase.cpp @@ -17,6 +17,7 @@ #include <stdlib.h> #include <stdint.h> #include <sys/types.h> +#include <math.h> #include <utils/Errors.h> #include <utils/Log.h> @@ -259,7 +260,7 @@ Rect LayerBase::computeBounds() const { if (!s.active.crop.isEmpty()) { win.intersect(s.active.crop, &win); } - return s.transform.transform(win); + return win; } Region LayerBase::latchBuffer(bool& recomputeVisibleRegions) { @@ -267,6 +268,83 @@ Region LayerBase::latchBuffer(bool& recomputeVisibleRegions) { return result; } + +Rect LayerBase::getContentCrop() const { + // regular layers just use their active area as the content crop + const State& s(drawingState()); + return Rect(s.active.w, s.active.h); +} + +uint32_t LayerBase::getContentTransform() const { + // regular layers don't have a content transform + return 0; +} + +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()); + + // the active.crop is the area of the window that gets cropped, but not + // scaled in any ways. + const State& s(drawingState()); + + // 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 + // SurfaceFlingerConsumer. + uint32_t invTransform = getContentTransform(); + int winWidth = s.active.w; + int winHeight = s.active.h; + if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | + NATIVE_WINDOW_TRANSFORM_FLIP_H; + winWidth = s.active.h; + winHeight = s.active.w; + } + const Rect winCrop = activeCrop.transform( + invTransform, s.active.w, s.active.h); + + // the code below essentially performs a scaled intersection + // of crop and winCrop + float xScale = float(crop.width()) / float(winWidth); + float yScale = float(crop.height()) / float(winHeight); + + int insetL = int(ceilf( winCrop.left * xScale)); + int insetT = int(ceilf( winCrop.top * yScale)); + int insetR = int(ceilf((winWidth - winCrop.right ) * xScale)); + int insetB = int(ceilf((winHeight - winCrop.bottom) * yScale)); + + crop.left += insetL; + crop.top += insetT; + crop.right -= insetR; + crop.bottom -= insetB; + } + return crop; +} + void LayerBase::setGeometry( const sp<const DisplayDevice>& hw, HWComposer::HWCLayerInterface& layer) @@ -289,13 +367,15 @@ void LayerBase::setGeometry( HWC_BLENDING_COVERAGE); } - const Transform& tr = hw->getTransform(); - Rect transformedBounds(computeBounds()); - transformedBounds = tr.transform(transformedBounds); - // scaling is already applied in transformedBounds - layer.setFrame(transformedBounds); - layer.setCrop(transformedBounds.getBounds()); + // apply the layer's transform, followed by the display's global transform + // 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)); } void LayerBase::setPerFrameData(const sp<const DisplayDevice>& hw, @@ -303,8 +383,11 @@ void LayerBase::setPerFrameData(const sp<const DisplayDevice>& hw, // we have to set the visible region on every frame because // we currently free it during onLayerDisplayed(), which is called // after HWComposer::commit() -- every frame. + // Apply this display's projection's viewport to the visible region + // before giving it to the HWC HAL. const Transform& tr = hw->getTransform(); - layer.setVisibleRegionScreen(tr.transform(visibleRegion)); + Region visible = tr.transform(visibleRegion.intersect(hw->getViewport())); + layer.setVisibleRegionScreen(visible); } void LayerBase::setAcquireFence(const sp<const DisplayDevice>& hw, @@ -404,10 +487,22 @@ void LayerBase::drawWithOpenGL(const sp<const DisplayDevice>& hw, const Region& 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); diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h index c2624df..ecae2d9 100644 --- a/services/surfaceflinger/LayerBase.h +++ b/services/surfaceflinger/LayerBase.h @@ -260,6 +260,18 @@ public: */ virtual void updateTransformHint(const sp<const DisplayDevice>& hw) const { } + /** + * returns the rectangle that crops the content of the layer and scales it + * to the layer's size. + */ + virtual Rect getContentCrop() const; + + /* + * returns the transform bits (90 rotation / h-flip / v-flip) of the + * layer's content + */ + virtual uint32_t getContentTransform() const; + /** always call base class first */ virtual void dump(String8& result, char* scratch, size_t size) const; virtual void shortDump(String8& result, char* scratch, size_t size) const; @@ -282,6 +294,9 @@ public: void setFiltering(bool filtering); bool getFiltering() const; +private: + Rect computeCrop(const sp<const DisplayDevice>& hw) const; + protected: void clearWithOpenGL(const sp<const DisplayDevice>& hw, const Region& clip, GLclampf r, GLclampf g, GLclampf b, GLclampf alpha) const; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index ee3e93b..bdeffdf 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1384,7 +1384,7 @@ void SurfaceFlinger::computeVisibleRegions( // handle hidden surfaces by setting the visible region to empty if (CC_LIKELY(layer->isVisible())) { const bool translucent = !layer->isOpaque(); - Rect bounds(layer->computeBounds()); + Rect bounds(s.transform.transform(layer->computeBounds())); visibleRegion.set(bounds); if (!visibleRegion.isEmpty()) { // Remove the transparent area from the visible region diff --git a/services/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp index aca90e0..315720e 100644 --- a/services/surfaceflinger/Transform.cpp +++ b/services/surfaceflinger/Transform.cpp @@ -298,6 +298,44 @@ uint32_t Transform::type() const return mType; } +Transform Transform::inverse() const { + // our 3x3 matrix is always of the form of a 2x2 transformation + // followed by a translation: T*M, therefore: + // (T*M)^-1 = M^-1 * T^-1 + Transform result; + if (mType <= TRANSLATE) { + // 1 0 x + // 0 1 y + // 0 0 1 + result = *this; + result.mMatrix[2][0] = -result.mMatrix[2][0]; + result.mMatrix[2][1] = -result.mMatrix[2][1]; + } else { + // a c x + // b d y + // 0 0 1 + const mat33& M(mMatrix); + const float a = M[0][0]; + const float b = M[1][0]; + const float c = M[0][1]; + const float d = M[1][1]; + const float x = M[2][0]; + const float y = M[2][1]; + + Transform R, T; + const float idet = 1.0 / (a*d - b*c); + R.mMatrix[0][0] = d*idet; R.mMatrix[0][1] = -c*idet; + R.mMatrix[1][0] = -b*idet; R.mMatrix[1][1] = a*idet; + R.mType = mType &= ~TRANSLATE; + + T.mMatrix[2][0] = -x; + T.mMatrix[2][1] = -y; + T.mType = TRANSLATE; + result = R * T; + } + return result; +} + uint32_t Transform::getType() const { return type() & 0xFF; } diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h index 4fe261a..c4efade 100644 --- a/services/surfaceflinger/Transform.h +++ b/services/surfaceflinger/Transform.h @@ -80,6 +80,8 @@ public: Rect transform(const Rect& bounds) const; Transform operator * (const Transform& rhs) const; + Transform inverse() const; + // for debugging void dump(const char* name) const; |