Chet Haase 259b696b00 Fix scaled-view droppings artifact
Sometimes views that are scaled leave behind rows/columns on the
screen as they move/scale around.

The problem was that the pivot point around which the scale takes place
(in the default case of scaling around the center of the view)
was getting truncated to integer coordinates in the display list.
Meanwhile, the pivot point at the Java level was using the true float
values, resulting in a mis-match between the invalidation rectangle
(computed at the Java level) and the drawing-operation rectangle (computed
at the native level).

This only occurred when views had odd bounds (thus the integer representation
of the center differed from the float representation of the center), and only
when some other drawing operation would expand the clip rect to allow the
incorrect drawing operation (using the wrong pivot point) to draw outside of
its clip boundaries.

Issue #8617023 7x7 screen not updated correctly

Change-Id: If88889b9450d34535df732b78077a29b1f24802d
2013-05-03 15:32:48 -07:00

526 lines
18 KiB
C++

/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <SkCanvas.h>
#include "Debug.h"
#include "DisplayList.h"
#include "DisplayListOp.h"
#include "DisplayListLogBuffer.h"
namespace android {
namespace uirenderer {
void DisplayList::outputLogBuffer(int fd) {
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
if (logBuffer.isEmpty()) {
return;
}
FILE *file = fdopen(fd, "a");
fprintf(file, "\nRecent DisplayList operations\n");
logBuffer.outputCommands(file);
String8 cachesLog;
Caches::getInstance().dumpMemoryUsage(cachesLog);
fprintf(file, "\nCaches:\n%s", cachesLog.string());
fprintf(file, "\n");
fflush(file);
}
DisplayList::DisplayList(const DisplayListRenderer& recorder) :
mTransformMatrix(NULL), mTransformCamera(NULL), mTransformMatrix3D(NULL),
mStaticMatrix(NULL), mAnimationMatrix(NULL) {
initFromDisplayListRenderer(recorder);
}
DisplayList::~DisplayList() {
clearResources();
}
void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) {
if (displayList) {
DISPLAY_LIST_LOGD("Deferring display list destruction");
Caches::getInstance().deleteDisplayListDeferred(displayList);
}
}
void DisplayList::clearResources() {
mDisplayListData = NULL;
mClipRectOp = NULL;
mSaveLayerOp = NULL;
mSaveOp = NULL;
mRestoreToCountOp = NULL;
delete mTransformMatrix;
delete mTransformCamera;
delete mTransformMatrix3D;
delete mStaticMatrix;
delete mAnimationMatrix;
mTransformMatrix = NULL;
mTransformCamera = NULL;
mTransformMatrix3D = NULL;
mStaticMatrix = NULL;
mAnimationMatrix = NULL;
Caches& caches = Caches::getInstance();
caches.unregisterFunctors(mFunctorCount);
caches.resourceCache.lock();
for (size_t i = 0; i < mBitmapResources.size(); i++) {
caches.resourceCache.decrementRefcountLocked(mBitmapResources.itemAt(i));
}
for (size_t i = 0; i < mOwnedBitmapResources.size(); i++) {
SkBitmap* bitmap = mOwnedBitmapResources.itemAt(i);
caches.resourceCache.decrementRefcountLocked(bitmap);
caches.resourceCache.destructorLocked(bitmap);
}
for (size_t i = 0; i < mFilterResources.size(); i++) {
caches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i));
}
for (size_t i = 0; i < mShaders.size(); i++) {
caches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i));
caches.resourceCache.destructorLocked(mShaders.itemAt(i));
}
for (size_t i = 0; i < mSourcePaths.size(); i++) {
caches.resourceCache.decrementRefcountLocked(mSourcePaths.itemAt(i));
}
for (size_t i = 0; i < mLayers.size(); i++) {
caches.resourceCache.decrementRefcountLocked(mLayers.itemAt(i));
}
caches.resourceCache.unlock();
for (size_t i = 0; i < mPaints.size(); i++) {
delete mPaints.itemAt(i);
}
for (size_t i = 0; i < mRegions.size(); i++) {
delete mRegions.itemAt(i);
}
for (size_t i = 0; i < mPaths.size(); i++) {
delete mPaths.itemAt(i);
}
for (size_t i = 0; i < mMatrices.size(); i++) {
delete mMatrices.itemAt(i);
}
mBitmapResources.clear();
mOwnedBitmapResources.clear();
mFilterResources.clear();
mShaders.clear();
mSourcePaths.clear();
mPaints.clear();
mRegions.clear();
mPaths.clear();
mMatrices.clear();
mLayers.clear();
}
void DisplayList::reset() {
clearResources();
init();
}
void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing) {
if (reusing) {
// re-using display list - clear out previous allocations
clearResources();
}
init();
mDisplayListData = recorder.getDisplayListData();
mSize = mDisplayListData->allocator.usedSize();
if (mSize == 0) {
return;
}
// allocate reusable ops for state-deferral
LinearAllocator& alloc = mDisplayListData->allocator;
mClipRectOp = new (alloc) ClipRectOp();
mSaveLayerOp = new (alloc) SaveLayerOp();
mSaveOp = new (alloc) SaveOp();
mRestoreToCountOp = new (alloc) RestoreToCountOp();
mFunctorCount = recorder.getFunctorCount();
Caches& caches = Caches::getInstance();
caches.registerFunctors(mFunctorCount);
caches.resourceCache.lock();
const Vector<SkBitmap*>& bitmapResources = recorder.getBitmapResources();
for (size_t i = 0; i < bitmapResources.size(); i++) {
SkBitmap* resource = bitmapResources.itemAt(i);
mBitmapResources.add(resource);
caches.resourceCache.incrementRefcountLocked(resource);
}
const Vector<SkBitmap*> &ownedBitmapResources = recorder.getOwnedBitmapResources();
for (size_t i = 0; i < ownedBitmapResources.size(); i++) {
SkBitmap* resource = ownedBitmapResources.itemAt(i);
mOwnedBitmapResources.add(resource);
caches.resourceCache.incrementRefcountLocked(resource);
}
const Vector<SkiaColorFilter*>& filterResources = recorder.getFilterResources();
for (size_t i = 0; i < filterResources.size(); i++) {
SkiaColorFilter* resource = filterResources.itemAt(i);
mFilterResources.add(resource);
caches.resourceCache.incrementRefcountLocked(resource);
}
const Vector<SkiaShader*>& shaders = recorder.getShaders();
for (size_t i = 0; i < shaders.size(); i++) {
SkiaShader* resource = shaders.itemAt(i);
mShaders.add(resource);
caches.resourceCache.incrementRefcountLocked(resource);
}
const SortedVector<SkPath*>& sourcePaths = recorder.getSourcePaths();
for (size_t i = 0; i < sourcePaths.size(); i++) {
mSourcePaths.add(sourcePaths.itemAt(i));
caches.resourceCache.incrementRefcountLocked(sourcePaths.itemAt(i));
}
const Vector<Layer*>& layers = recorder.getLayers();
for (size_t i = 0; i < layers.size(); i++) {
mLayers.add(layers.itemAt(i));
caches.resourceCache.incrementRefcountLocked(layers.itemAt(i));
}
caches.resourceCache.unlock();
mPaints.appendVector(recorder.getPaints());
mRegions.appendVector(recorder.getRegions());
mPaths.appendVector(recorder.getPaths());
mMatrices.appendVector(recorder.getMatrices());
}
void DisplayList::init() {
mSize = 0;
mIsRenderable = true;
mFunctorCount = 0;
mLeft = 0;
mTop = 0;
mRight = 0;
mBottom = 0;
mClipToBounds = true;
mAlpha = 1;
mHasOverlappingRendering = true;
mTranslationX = 0;
mTranslationY = 0;
mRotation = 0;
mRotationX = 0;
mRotationY= 0;
mScaleX = 1;
mScaleY = 1;
mPivotX = 0;
mPivotY = 0;
mCameraDistance = 0;
mMatrixDirty = false;
mMatrixFlags = 0;
mPrevWidth = -1;
mPrevHeight = -1;
mWidth = 0;
mHeight = 0;
mPivotExplicitlySet = false;
mCaching = false;
}
size_t DisplayList::getSize() {
return mSize;
}
/**
* This function is a simplified version of replay(), where we simply retrieve and log the
* display list. This function should remain in sync with the replay() function.
*/
void DisplayList::output(uint32_t level) {
ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
mName.string(), isRenderable());
ALOGD("%*s%s %d", level * 2, "", "Save",
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
outputViewProperties(level);
int flags = DisplayListOp::kOpLogFlag_Recurse;
for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
mDisplayListData->displayListOps[i]->output(level, flags);
}
ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
}
float DisplayList::getPivotX() {
updateMatrix();
return mPivotX;
}
float DisplayList::getPivotY() {
updateMatrix();
return mPivotY;
}
void DisplayList::updateMatrix() {
if (mMatrixDirty) {
if (!mTransformMatrix) {
mTransformMatrix = new SkMatrix();
}
if (mMatrixFlags == 0 || mMatrixFlags == TRANSLATION) {
mTransformMatrix->reset();
} else {
if (!mPivotExplicitlySet) {
if (mWidth != mPrevWidth || mHeight != mPrevHeight) {
mPrevWidth = mWidth;
mPrevHeight = mHeight;
mPivotX = mPrevWidth / 2.0f;
mPivotY = mPrevHeight / 2.0f;
}
}
if ((mMatrixFlags & ROTATION_3D) == 0) {
mTransformMatrix->setTranslate(mTranslationX, mTranslationY);
mTransformMatrix->preRotate(mRotation, mPivotX, mPivotY);
mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY);
} else {
if (!mTransformCamera) {
mTransformCamera = new Sk3DView();
mTransformMatrix3D = new SkMatrix();
}
mTransformMatrix->reset();
mTransformCamera->save();
mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY);
mTransformCamera->rotateX(mRotationX);
mTransformCamera->rotateY(mRotationY);
mTransformCamera->rotateZ(-mRotation);
mTransformCamera->getMatrix(mTransformMatrix3D);
mTransformMatrix3D->preTranslate(-mPivotX, -mPivotY);
mTransformMatrix3D->postTranslate(mPivotX + mTranslationX,
mPivotY + mTranslationY);
mTransformMatrix->postConcat(*mTransformMatrix3D);
mTransformCamera->restore();
}
}
mMatrixDirty = false;
}
}
void DisplayList::outputViewProperties(const int level) {
updateMatrix();
if (mLeft != 0 || mTop != 0) {
ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mLeft, mTop);
}
if (mStaticMatrix) {
ALOGD("%*sConcatMatrix (static) %p: " MATRIX_STRING,
level * 2, "", mStaticMatrix, MATRIX_ARGS(mStaticMatrix));
}
if (mAnimationMatrix) {
ALOGD("%*sConcatMatrix (animation) %p: " MATRIX_STRING,
level * 2, "", mAnimationMatrix, MATRIX_ARGS(mStaticMatrix));
}
if (mMatrixFlags != 0) {
if (mMatrixFlags == TRANSLATION) {
ALOGD("%*sTranslate %f, %f", level * 2, "", mTranslationX, mTranslationY);
} else {
ALOGD("%*sConcatMatrix %p: " MATRIX_STRING,
level * 2, "", mTransformMatrix, MATRIX_ARGS(mTransformMatrix));
}
}
if (mAlpha < 1) {
if (mCaching) {
ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mAlpha);
} else if (!mHasOverlappingRendering) {
ALOGD("%*sScaleAlpha %.2f", level * 2, "", mAlpha);
} else {
int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
if (mClipToBounds) {
flags |= SkCanvas::kClipToLayer_SaveFlag;
}
ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
(float) 0, (float) 0, (float) mRight - mLeft, (float) mBottom - mTop,
(int)(mAlpha * 255), flags);
}
}
if (mClipToBounds && !mCaching) {
ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
(float) mRight - mLeft, (float) mBottom - mTop);
}
}
/*
* For property operations, we pass a savecount of 0, since the operations aren't part of the
* displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
* base saveCount (i.e., how RestoreToCount uses saveCount + mCount)
*/
#define PROPERTY_SAVECOUNT 0
template <class T>
void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler,
const int level) {
#if DEBUG_DISPLAY_LIST
outputViewProperties(level);
#endif
updateMatrix();
if (mLeft != 0 || mTop != 0) {
renderer.translate(mLeft, mTop);
}
if (mStaticMatrix) {
renderer.concatMatrix(mStaticMatrix);
} else if (mAnimationMatrix) {
renderer.concatMatrix(mAnimationMatrix);
}
if (mMatrixFlags != 0) {
if (mMatrixFlags == TRANSLATION) {
renderer.translate(mTranslationX, mTranslationY);
} else {
renderer.concatMatrix(mTransformMatrix);
}
}
if (mAlpha < 1) {
if (mCaching) {
renderer.setOverrideLayerAlpha(mAlpha);
} else if (!mHasOverlappingRendering) {
renderer.scaleAlpha(mAlpha);
} else {
// TODO: should be able to store the size of a DL at record time and not
// have to pass it into this call. In fact, this information might be in the
// location/size info that we store with the new native transform data.
int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
if (mClipToBounds) {
saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
}
handler(mSaveLayerOp->reinit(0, 0, mRight - mLeft, mBottom - mTop,
mAlpha * 255, SkXfermode::kSrcOver_Mode, saveFlags), PROPERTY_SAVECOUNT,
mClipToBounds);
}
}
if (mClipToBounds && !mCaching) {
handler(mClipRectOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op),
PROPERTY_SAVECOUNT, mClipToBounds);
}
}
class DeferOperationHandler {
public:
DeferOperationHandler(DeferStateStruct& deferStruct, int level)
: mDeferStruct(deferStruct), mLevel(level) {}
inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
}
private:
DeferStateStruct& mDeferStruct;
const int mLevel;
};
void DisplayList::defer(DeferStateStruct& deferStruct, const int level) {
DeferOperationHandler handler(deferStruct, level);
iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level);
}
class ReplayOperationHandler {
public:
ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
: mReplayStruct(replayStruct), mLevel(level) {}
inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
mReplayStruct.mRenderer.eventMark(operation->name());
#endif
operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
}
private:
ReplayStateStruct& mReplayStruct;
const int mLevel;
};
void DisplayList::replay(ReplayStateStruct& replayStruct, const int level) {
ReplayOperationHandler handler(replayStruct, level);
replayStruct.mRenderer.startMark(mName.string());
iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level);
replayStruct.mRenderer.endMark();
DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
replayStruct.mDrawGlStatus);
}
/**
* This function serves both defer and replay modes, and will organize the displayList's component
* operations for a single frame:
*
* Every 'simple' operation that affects just the matrix and alpha (or other factors of
* DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
* defer logic) and operations in displayListOps are issued through the 'handler' which handles the
* defer vs replay logic, per operation
*/
template <class T>
void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
if (mSize == 0 || mAlpha <= 0) {
DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
return;
}
#if DEBUG_DISPLAY_LIST
Rect* clipRect = renderer.getClipRect();
DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
clipRect->right, clipRect->bottom);
#endif
int restoreTo = renderer.getSaveCount();
handler(mSaveOp->reinit(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
PROPERTY_SAVECOUNT, mClipToBounds);
DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
setViewProperties<T>(renderer, handler, level + 1);
if (mClipToBounds && renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) {
DISPLAY_LIST_LOGD("%*sRestoreToCount %d", level * 2, "", restoreTo);
handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds);
return;
}
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
int saveCount = renderer.getSaveCount() - 1;
for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
DisplayListOp *op = mDisplayListData->displayListOps[i];
handler(op, saveCount, mClipToBounds);
logBuffer.writeCommand(level, op->name());
}
DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds);
renderer.restoreToCount(restoreTo);
renderer.setOverrideLayerAlpha(1.0f);
}
}; // namespace uirenderer
}; // namespace android