summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/hwui/Android.mk1
-rw-r--r--libs/hwui/CanvasState.cpp60
-rw-r--r--libs/hwui/CanvasState.h24
-rw-r--r--libs/hwui/ClipArea.cpp20
-rw-r--r--libs/hwui/ClipArea.h2
-rw-r--r--libs/hwui/DeferredDisplayList.cpp24
-rw-r--r--libs/hwui/DeferredDisplayList.h10
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp3
-rw-r--r--libs/hwui/DisplayListOp.h22
-rw-r--r--libs/hwui/FontRenderer.cpp8
-rw-r--r--libs/hwui/FontRenderer.h2
-rw-r--r--libs/hwui/Glop.h4
-rw-r--r--libs/hwui/GlopBuilder.cpp3
-rw-r--r--libs/hwui/GlopBuilder.h5
-rw-r--r--libs/hwui/Layer.cpp3
-rw-r--r--libs/hwui/LayerRenderer.cpp2
-rw-r--r--libs/hwui/OpenGLRenderer.cpp71
-rwxr-xr-xlibs/hwui/OpenGLRenderer.h61
-rw-r--r--libs/hwui/Rect.h47
-rw-r--r--libs/hwui/RenderNode.cpp9
-rw-r--r--libs/hwui/RenderProperties.cpp7
-rw-r--r--libs/hwui/RenderProperties.h2
-rw-r--r--libs/hwui/ShadowTessellator.cpp4
-rw-r--r--libs/hwui/Snapshot.cpp6
-rw-r--r--libs/hwui/Snapshot.h6
-rw-r--r--libs/hwui/SpotShadow.cpp2
-rw-r--r--libs/hwui/font/Font.cpp2
-rw-r--r--libs/hwui/renderstate/RenderState.cpp4
-rw-r--r--libs/hwui/renderstate/RenderState.h2
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp63
-rw-r--r--libs/hwui/renderthread/CanvasContext.h6
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp9
-rw-r--r--libs/hwui/renderthread/RenderProxy.h2
-rw-r--r--libs/hwui/tests/Benchmark.h54
-rw-r--r--libs/hwui/tests/TestContext.cpp23
-rw-r--r--libs/hwui/tests/TestContext.h2
-rw-r--r--libs/hwui/tests/TreeContentAnimation.cpp385
-rw-r--r--libs/hwui/tests/how_to_run.txt14
-rw-r--r--libs/hwui/tests/main.cpp476
-rw-r--r--libs/hwui/utils/PaintUtils.h40
-rw-r--r--libs/hwui/utils/TinyHashMap.h70
41 files changed, 872 insertions, 688 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index cc0943f52fdb..eaade34a1db6 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -209,6 +209,7 @@ endif
LOCAL_SRC_FILES += \
tests/TestContext.cpp \
+ tests/TreeContentAnimation.cpp \
tests/main.cpp
include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index c128ca775155..eca71c6e0e8d 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -28,10 +28,21 @@ CanvasState::CanvasState(CanvasStateClient& renderer)
, mWidth(-1)
, mHeight(-1)
, mSaveCount(1)
- , mFirstSnapshot(new Snapshot)
, mCanvas(renderer)
- , mSnapshot(mFirstSnapshot) {
+ , mSnapshot(&mFirstSnapshot) {
+}
+
+CanvasState::~CanvasState() {
+ // First call freeSnapshot on all but mFirstSnapshot
+ // to invoke all the dtors
+ freeAllSnapshots();
+ // Now actually release the memory
+ while (mSnapshotPool) {
+ void* temp = mSnapshotPool;
+ mSnapshotPool = mSnapshotPool->previous;
+ free(temp);
+ }
}
void CanvasState::initializeSaveStack(
@@ -41,11 +52,12 @@ void CanvasState::initializeSaveStack(
if (mWidth != viewportWidth || mHeight != viewportHeight) {
mWidth = viewportWidth;
mHeight = viewportHeight;
- mFirstSnapshot->initializeViewport(viewportWidth, viewportHeight);
+ mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight);
mCanvas.onViewportInitialized();
}
- mSnapshot = new Snapshot(mFirstSnapshot,
+ freeAllSnapshots();
+ mSnapshot = allocSnapshot(&mFirstSnapshot,
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
mSnapshot->fbo = mCanvas.getTargetFbo();
@@ -53,6 +65,38 @@ void CanvasState::initializeSaveStack(
mSaveCount = 1;
}
+Snapshot* CanvasState::allocSnapshot(Snapshot* previous, int savecount) {
+ void* memory;
+ if (mSnapshotPool) {
+ memory = mSnapshotPool;
+ mSnapshotPool = mSnapshotPool->previous;
+ mSnapshotPoolCount--;
+ } else {
+ memory = malloc(sizeof(Snapshot));
+ }
+ return new (memory) Snapshot(previous, savecount);
+}
+
+void CanvasState::freeSnapshot(Snapshot* snapshot) {
+ snapshot->~Snapshot();
+ // Arbitrary number, just don't let this grown unbounded
+ if (mSnapshotPoolCount > 10) {
+ free((void*) snapshot);
+ } else {
+ snapshot->previous = mSnapshotPool;
+ mSnapshotPool = snapshot;
+ mSnapshotPoolCount++;
+ }
+}
+
+void CanvasState::freeAllSnapshots() {
+ while (mSnapshot != &mFirstSnapshot) {
+ Snapshot* temp = mSnapshot;
+ mSnapshot = mSnapshot->previous;
+ freeSnapshot(temp);
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Save (layer)
///////////////////////////////////////////////////////////////////////////////
@@ -64,7 +108,7 @@ void CanvasState::initializeSaveStack(
* stack, and ensures restoreToCount() doesn't call back into subclass overrides.
*/
int CanvasState::saveSnapshot(int flags) {
- mSnapshot = new Snapshot(mSnapshot, flags);
+ mSnapshot = allocSnapshot(mSnapshot, flags);
return mSaveCount++;
}
@@ -76,14 +120,16 @@ int CanvasState::save(int flags) {
* Guaranteed to restore without side-effects.
*/
void CanvasState::restoreSnapshot() {
- sp<Snapshot> toRemove = mSnapshot;
- sp<Snapshot> toRestore = mSnapshot->previous;
+ Snapshot* toRemove = mSnapshot;
+ Snapshot* toRestore = mSnapshot->previous;
mSaveCount--;
mSnapshot = toRestore;
// subclass handles restore implementation
mCanvas.onSnapshotRestored(*toRemove, *toRestore);
+
+ freeSnapshot(toRemove);
}
void CanvasState::restore() {
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
index f0fb9ba8b324..be57f44210ef 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -17,12 +17,12 @@
#ifndef ANDROID_HWUI_CANVAS_STATE_H
#define ANDROID_HWUI_CANVAS_STATE_H
+#include "Snapshot.h"
+
#include <SkMatrix.h>
#include <SkPath.h>
#include <SkRegion.h>
-#include "Snapshot.h"
-
namespace android {
namespace uirenderer {
@@ -74,6 +74,7 @@ public:
class CanvasState {
public:
CanvasState(CanvasStateClient& renderer);
+ ~CanvasState();
/**
* Initializes the first snapshot, computing the projection matrix,
@@ -157,11 +158,15 @@ public:
int getHeight() const { return mHeight; }
bool clipIsSimple() const { return currentSnapshot()->clipIsSimple(); }
- inline const Snapshot* currentSnapshot() const { return mSnapshot.get(); }
- inline Snapshot* writableSnapshot() { return mSnapshot.get(); }
- inline const Snapshot* firstSnapshot() const { return mFirstSnapshot.get(); }
+ inline const Snapshot* currentSnapshot() const { return mSnapshot; }
+ inline Snapshot* writableSnapshot() { return mSnapshot; }
+ inline const Snapshot* firstSnapshot() const { return &mFirstSnapshot; }
private:
+ Snapshot* allocSnapshot(Snapshot* previous, int savecount);
+ void freeSnapshot(Snapshot* snapshot);
+ void freeAllSnapshots();
+
/// indicates that the clip has been changed since the last time it was consumed
bool mDirtyClip;
@@ -172,13 +177,18 @@ private:
int mSaveCount;
/// Base state
- sp<Snapshot> mFirstSnapshot;
+ Snapshot mFirstSnapshot;
/// Host providing callbacks
CanvasStateClient& mCanvas;
/// Current state
- sp<Snapshot> mSnapshot;
+ Snapshot* mSnapshot;
+
+ // Pool of allocated snapshots to re-use
+ // NOTE: The dtors have already been invoked!
+ Snapshot* mSnapshotPool = nullptr;
+ int mSnapshotPoolCount = 0;
}; // class CanvasState
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index 8e7efb4e35d6..a9d1e4284d2e 100644
--- a/libs/hwui/ClipArea.cpp
+++ b/libs/hwui/ClipArea.cpp
@@ -23,14 +23,6 @@
namespace android {
namespace uirenderer {
-static bool intersect(Rect& r, const Rect& r2) {
- bool hasIntersection = r.intersect(r2);
- if (!hasIntersection) {
- r.setEmpty();
- }
- return hasIntersection;
-}
-
static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) {
Vertex v = {x, y};
transform.mapPoint(v.x, v.y);
@@ -67,9 +59,8 @@ bool TransformedRectangle::canSimplyIntersectWith(
return mTransform == other.mTransform;
}
-bool TransformedRectangle::intersectWith(const TransformedRectangle& other) {
- Rect translatedBounds(other.mBounds);
- return intersect(mBounds, translatedBounds);
+void TransformedRectangle::intersectWith(const TransformedRectangle& other) {
+ mBounds.doIntersect(other.mBounds);
}
bool TransformedRectangle::isEmpty() const {
@@ -146,7 +137,7 @@ Rect RectangleList::calculateBounds() const {
if (index == 0) {
bounds = tr.transformedBounds();
} else {
- bounds.intersect(tr.transformedBounds());
+ bounds.doIntersect(tr.transformedBounds());
}
}
return bounds;
@@ -275,10 +266,7 @@ void ClipArea::rectangleModeClipRectWithTransform(const Rect& r,
if (transform->rectToRect()) {
Rect transformed(r);
transform->mapRect(transformed);
- bool hasIntersection = mClipRect.intersect(transformed);
- if (!hasIntersection) {
- mClipRect.setEmpty();
- }
+ mClipRect.doIntersect(transformed);
return;
}
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
index 38fefe5ab097..f88fd92e234d 100644
--- a/libs/hwui/ClipArea.h
+++ b/libs/hwui/ClipArea.h
@@ -33,7 +33,7 @@ public:
TransformedRectangle(const Rect& bounds, const Matrix4& transform);
bool canSimplyIntersectWith(const TransformedRectangle& other) const;
- bool intersectWith(const TransformedRectangle& other);
+ void intersectWith(const TransformedRectangle& other);
bool isEmpty() const;
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index a81ffb9f59fa..0c29a9e928a2 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -44,6 +44,12 @@ namespace uirenderer {
#define DEBUG_COLOR_MERGEDBATCH 0x5f7f7fff
#define DEBUG_COLOR_MERGEDBATCH_SOLO 0x5f7fff7f
+static bool avoidOverdraw() {
+ // Don't avoid overdraw when visualizing it, since that makes it harder to
+ // debug where it's coming from, and when the problem occurs.
+ return !Properties::debugOverdraw;
+};
+
/////////////////////////////////////////////////////////////////////////////////
// Operation Batches
/////////////////////////////////////////////////////////////////////////////////
@@ -218,7 +224,10 @@ public:
// if paints are equal, then modifiers + paint attribs don't need to be compared
if (op->mPaint == mOps[0].op->mPaint) return true;
- if (op->getPaintAlpha() != mOps[0].op->getPaintAlpha()) return false;
+ if (PaintUtils::getAlphaDirect(op->mPaint)
+ != PaintUtils::getAlphaDirect(mOps[0].op->mPaint)) {
+ return false;
+ }
if (op->mPaint && mOps[0].op->mPaint &&
op->mPaint->getColorFilter() != mOps[0].op->mPaint->getColorFilter()) {
@@ -495,7 +504,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
&& mSaveStack.empty()
&& !state->mRoundRectClipState;
- if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() &&
+ if (CC_LIKELY(avoidOverdraw()) && mBatches.size() &&
state->mClipSideFlags != kClipSide_ConservativeFull &&
deferInfo.opaqueOverBounds && state->mBounds.contains(mBounds)) {
// avoid overdraw by resetting drawing state + discarding drawing ops
@@ -533,7 +542,11 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
if (deferInfo.mergeable) {
// Try to merge with any existing batch with same mergeId.
- if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) {
+ std::unordered_map<mergeid_t, DrawBatch*>& mergingBatch
+ = mMergingBatches[deferInfo.batchId];
+ auto getResult = mergingBatch.find(deferInfo.mergeId);
+ if (getResult != mergingBatch.end()) {
+ targetBatch = getResult->second;
if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) {
targetBatch = nullptr;
}
@@ -577,7 +590,8 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
if (deferInfo.mergeable) {
targetBatch = new MergingDrawBatch(deferInfo,
renderer.getViewportWidth(), renderer.getViewportHeight());
- mMergingBatches[deferInfo.batchId].put(deferInfo.mergeId, targetBatch);
+ mMergingBatches[deferInfo.batchId].insert(
+ std::make_pair(deferInfo.mergeId, targetBatch));
} else {
targetBatch = new DrawBatch(deferInfo);
mBatchLookup[deferInfo.batchId] = targetBatch;
@@ -642,7 +656,7 @@ void DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
// save and restore so that reordering doesn't affect final state
renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
- if (CC_LIKELY(mAvoidOverdraw)) {
+ if (CC_LIKELY(avoidOverdraw())) {
for (unsigned int i = 1; i < mBatches.size(); i++) {
if (mBatches[i] && mBatches[i]->coversBounds(mBounds)) {
discardDrawingBatches(i - 1);
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 4f2dca5f3ee1..7873fbdd342a 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -17,9 +17,10 @@
#ifndef ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
#define ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
+#include <unordered_map>
+
#include <utils/Errors.h>
#include <utils/LinearAllocator.h>
-#include <utils/TinyHashMap.h>
#include "Matrix.h"
#include "OpenGLRenderer.h"
@@ -82,8 +83,8 @@ public:
class DeferredDisplayList {
friend struct DeferStateStruct; // used to give access to allocator
public:
- DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) :
- mBounds(bounds), mAvoidOverdraw(avoidOverdraw) {
+ DeferredDisplayList(const Rect& bounds)
+ : mBounds(bounds) {
clear();
}
~DeferredDisplayList() { clear(); }
@@ -151,7 +152,6 @@ private:
// layer space bounds of rendering
Rect mBounds;
- const bool mAvoidOverdraw;
/**
* At defer time, stores the *defer time* savecount of save/saveLayer ops that were deferred, so
@@ -177,7 +177,7 @@ private:
* MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
* collide, which avoids the need to resolve mergeid collisions.
*/
- TinyHashMap<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count];
+ std::unordered_map<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count];
LinearAllocator mAllocator;
};
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 38f2363f3532..70383340fc8d 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -47,7 +47,8 @@ DeferredLayerUpdater::~DeferredLayerUpdater() {
}
void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
- OpenGLRenderer::getAlphaAndModeDirect(paint, &mAlpha, &mMode);
+ mAlpha = PaintUtils::getAlphaDirect(paint);
+ mMode = PaintUtils::getXfermodeDirect(paint);
SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr;
SkRefCnt_SafeAssign(mColorFilter, colorFilter);
}
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index dc5cb8b349f1..ddfc533f9d77 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -172,10 +172,6 @@ public:
void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; }
bool getQuickRejected() { return mQuickRejected; }
- inline int getPaintAlpha() const {
- return OpenGLRenderer::getAlphaDirect(mPaint);
- }
-
virtual bool hasTextShadow() const {
return false;
}
@@ -213,7 +209,7 @@ protected:
if (state.mAlpha != 1.0f) return false;
- SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint);
return (mode == SkXfermode::kSrcOver_Mode ||
mode == SkXfermode::kSrc_Mode);
@@ -249,8 +245,8 @@ public:
virtual bool getLocalBounds(Rect& localBounds) override {
localBounds.set(mLocalBounds);
- OpenGLRenderer::TextShadow textShadow;
- if (OpenGLRenderer::getTextShadow(mPaint, &textShadow)) {
+ PaintUtils::TextShadow textShadow;
+ if (PaintUtils::getTextShadow(mPaint, &textShadow)) {
Rect shadow(mLocalBounds);
shadow.translate(textShadow.dx, textShadow.dx);
shadow.outset(textShadow.radius);
@@ -372,8 +368,8 @@ public:
private:
bool isSaveLayerAlpha() const {
- SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
- int alpha = OpenGLRenderer::getAlphaDirect(mPaint);
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint);
+ int alpha = PaintUtils::getAlphaDirect(mPaint);
return alpha < 255 && mode == SkXfermode::kSrcOver_Mode;
}
@@ -691,7 +687,7 @@ public:
// TODO: support clipped bitmaps by handling them in SET_TEXTURE
deferInfo.mergeable = state.mMatrix.isSimple() && state.mMatrix.positiveScale() &&
!state.mClipSideFlags &&
- OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
+ PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
(mBitmap->colorType() != kAlpha_8_SkColorType);
}
@@ -895,7 +891,7 @@ public:
deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch;
deferInfo.mergeId = getAtlasEntry(renderer) ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap;
deferInfo.mergeable = state.mMatrix.isPureTranslate() &&
- OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+ PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) && mBitmap->isOpaque();
}
@@ -1241,7 +1237,7 @@ public:
}
virtual bool hasTextShadow() const override {
- return OpenGLRenderer::hasTextShadow(mPaint);
+ return PaintUtils::hasTextShadow(mPaint);
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
@@ -1330,7 +1326,7 @@ public:
deferInfo.mergeable = state.mMatrix.isPureTranslate()
&& !hasDecorations
- && OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+ && PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
}
virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 4b9d4f90675c..ccf0b48cd4be 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -667,14 +667,6 @@ bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, cons
return mDrawn;
}
-void FontRenderer::removeFont(const Font* font) {
- mActiveFonts.remove(font->getDescription());
-
- if (mCurrentFont == font) {
- mCurrentFont = nullptr;
- }
-}
-
void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
uint32_t intRadius = Blur::convertRadiusToInt(radius);
#ifdef ANDROID_ENABLE_RENDERSCRIPT
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 936c838bd6e4..8172312e9a43 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -147,8 +147,6 @@ private:
float x3, float y3, float u3, float v3,
float x4, float y4, float u4, float v4, CacheTexture* texture);
- void removeFont(const Font* font);
-
void checkTextureUpdate();
void setTextureDirty() {
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index fa20b0807a88..4785ea48cddc 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -135,10 +135,6 @@ struct Glop {
} fill;
struct Transform {
- // Orthographic projection matrix for current FBO
- // TODO: move out of Glop, since this is static per FBO
- Matrix4 ortho;
-
// modelView transform, accounting for delta between mesh transform and content of the mesh
// often represents x/y offsets within command, or scaling for mesh unit size
Matrix4 modelView;
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 69559a77c3a0..fa166ae5ca5a 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -461,11 +461,10 @@ GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) {
// Transform
////////////////////////////////////////////////////////////////////////////////
-void GlopBuilder::setTransform(const Matrix4& ortho, const Matrix4& canvas,
+void GlopBuilder::setTransform(const Matrix4& canvas,
const int transformFlags) {
TRIGGER_STAGE(kTransformStage);
- mOutGlop->transform.ortho = ortho;
mOutGlop->transform.canvas = canvas;
mOutGlop->transform.transformFlags = transformFlags;
}
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index 549bb21e5f8d..8d05570dd206 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -71,7 +71,7 @@ public:
GlopBuilder& setFillTextureLayer(Layer& layer, float alpha);
GlopBuilder& setTransform(const Snapshot& snapshot, const int transformFlags) {
- setTransform(snapshot.getOrthoMatrix(), *snapshot.transform, transformFlags);
+ setTransform(*snapshot.transform, transformFlags);
return *this;
}
@@ -102,8 +102,7 @@ private:
void setFill(int color, float alphaScale,
SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
const SkShader* shader, const SkColorFilter* colorFilter);
- void setTransform(const Matrix4& ortho, const Matrix4& canvas,
- const int transformFlags);
+ void setTransform(const Matrix4& canvas, const int transformFlags);
enum StageFlags {
kInitialStage = 0,
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 8d8528961794..f99d92b89420 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -170,7 +170,8 @@ void Layer::updateDeferred(RenderNode* renderNode, int left, int top, int right,
}
void Layer::setPaint(const SkPaint* paint) {
- OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
+ alpha = PaintUtils::getAlphaDirect(paint);
+ mode = PaintUtils::getXfermodeDirect(paint);
setColorFilter((paint) ? paint->getColorFilter() : nullptr);
}
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index c63b5597f284..227271d83cf8 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -58,7 +58,7 @@ void LayerRenderer::prepareDirty(int viewportWidth, int viewportHeight,
mLayer->region.clear();
dirty.set(0.0f, 0.0f, width, height);
} else {
- dirty.intersect(0.0f, 0.0f, width, height);
+ dirty.doIntersect(0.0f, 0.0f, width, height);
android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom);
mLayer->region.subtractSelf(r);
}
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index a401ce119021..cd03ac407d81 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -488,7 +488,8 @@ void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool
currentTransform()->mapRect(bounds);
// Layers only make sense if they are in the framebuffer's bounds
- if (bounds.intersect(mState.currentClipRect())) {
+ bounds.doIntersect(mState.currentClipRect());
+ if (!bounds.isEmpty()) {
// We cannot work with sub-pixels in this case
bounds.snapToPixelBoundaries();
@@ -497,23 +498,20 @@ void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool
// of the framebuffer
const Snapshot& previous = *(currentSnapshot()->previous);
Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight());
- if (!bounds.intersect(previousViewport)) {
- bounds.setEmpty();
- } else if (fboLayer) {
+
+ bounds.doIntersect(previousViewport);
+ if (!bounds.isEmpty() && fboLayer) {
clip.set(bounds);
mat4 inverse;
inverse.loadInverse(*currentTransform());
inverse.mapRect(clip);
clip.snapToPixelBoundaries();
- if (clip.intersect(untransformedBounds)) {
+ clip.doIntersect(untransformedBounds);
+ if (!clip.isEmpty()) {
clip.translate(-untransformedBounds.left, -untransformedBounds.top);
bounds.set(untransformedBounds);
- } else {
- clip.setEmpty();
}
}
- } else {
- bounds.setEmpty();
}
}
@@ -540,7 +538,7 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float
Rect bounds(left, top, right, bottom);
Rect clip;
calculateLayerBoundsAndClip(bounds, clip, true);
- updateSnapshotIgnoreForLayer(bounds, clip, true, getAlphaDirect(paint));
+ updateSnapshotIgnoreForLayer(bounds, clip, true, PaintUtils::getAlphaDirect(paint));
if (!mState.currentlyIgnored()) {
writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f);
@@ -615,7 +613,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto
Rect clip;
Rect bounds(left, top, right, bottom);
calculateLayerBoundsAndClip(bounds, clip, fboLayer);
- updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, getAlphaDirect(paint));
+ updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, PaintUtils::getAlphaDirect(paint));
// Bail out if we won't draw in this snapshot
if (mState.currentlyIgnored()) {
@@ -1038,7 +1036,8 @@ void OpenGLRenderer::dirtyLayer(const float left, const float top,
}
void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
- if (CC_LIKELY(!bounds.isEmpty() && bounds.intersect(mState.currentClipRect()))) {
+ bounds.doIntersect(mState.currentClipRect());
+ if (!bounds.isEmpty()) {
bounds.snapToPixelBoundaries();
android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
if (!dirty.isEmpty()) {
@@ -1112,7 +1111,8 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
// is used, it should more closely duplicate the quickReject logic (in how it uses
// snapToPixelBoundaries)
- if (!clippedBounds.intersect(currentClip)) {
+ clippedBounds.doIntersect(currentClip);
+ if (clippedBounds.isEmpty()) {
// quick rejected
return true;
}
@@ -1242,9 +1242,8 @@ void OpenGLRenderer::drawRectangleList(const RectangleList& rectangleList) {
Rect bounds = tr.getBounds();
if (transform.rectToRect()) {
transform.mapRect(bounds);
- if (!bounds.intersect(scissorBox)) {
- bounds.setEmpty();
- } else {
+ bounds.doIntersect(scissorBox);
+ if (!bounds.isEmpty()) {
handlePointNoTransform(rectangleVertices, bounds.left, bounds.top);
handlePointNoTransform(rectangleVertices, bounds.right, bounds.top);
handlePointNoTransform(rectangleVertices, bounds.left, bounds.bottom);
@@ -1405,7 +1404,7 @@ void OpenGLRenderer::renderGlop(const Glop& glop, GlopRenderType type) {
setStencilFromClip();
}
- mRenderState.render(glop);
+ mRenderState.render(glop, currentSnapshot()->getOrthoMatrix());
if (type == GlopRenderType::Standard && !mRenderState.stencil().isWriteEnabled()) {
// TODO: specify more clearly when a draw should dirty the layer.
// is writing to the stencil the only time we should ignore this?
@@ -1431,10 +1430,7 @@ void OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t
return;
}
- // Don't avoid overdraw when visualizing, since that makes it harder to
- // debug where it's coming from, and when the problem occurs.
- bool avoidOverdraw = !Properties::debugOverdraw;
- DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw);
+ DeferredDisplayList deferredList(mState.currentClipRect());
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
renderNode->defer(deferStruct, 0);
@@ -1958,8 +1954,8 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
FontRenderer& fontRenderer, int alpha, float x, float y) {
mCaches.textureState().activateTexture(0);
- TextShadow textShadow;
- if (!getTextShadow(paint, &textShadow)) {
+ PaintUtils::TextShadow textShadow;
+ if (!PaintUtils::getTextShadow(paint, &textShadow)) {
LOG_ALWAYS_FATAL("failed to query shadow attributes");
}
@@ -1987,8 +1983,10 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
renderGlop(glop);
}
+// TODO: remove this, once mState.currentlyIgnored captures snapshot alpha
bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
- float alpha = (hasTextShadow(paint) ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha;
+ float alpha = (PaintUtils::hasTextShadow(paint)
+ ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha;
return MathUtils::isZero(alpha)
&& PaintUtils::getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
}
@@ -2017,11 +2015,10 @@ void OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
fontRenderer.setFont(paint, SkMatrix::I());
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
+ int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
- if (CC_UNLIKELY(hasTextShadow(paint))) {
+ if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
alpha, 0.0f, 0.0f);
}
@@ -2162,13 +2159,12 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float
y = floorf(y + transform.getTranslateY() + 0.5f);
}
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
+ int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
- if (CC_UNLIKELY(hasTextShadow(paint))) {
+ if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
fontRenderer.setFont(paint, SkMatrix::I());
drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
alpha, oldX, oldY);
@@ -2238,9 +2234,8 @@ void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
fontRenderer.setFont(paint, SkMatrix::I());
fontRenderer.setTextureFiltering(true);
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
+ int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
TextDrawFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint);
const Rect* clip = &writableSnapshot()->getLocalClip();
@@ -2530,12 +2525,6 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot
renderGlop(glop);
}
-void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha,
- SkXfermode::Mode* mode) const {
- getAlphaAndModeDirect(paint, alpha, mode);
- *alpha *= currentSnapshot()->alpha;
-}
-
float OpenGLRenderer::getLayerAlpha(const Layer* layer) const {
return (layer->getAlpha() / 255.0f) * currentSnapshot()->alpha;
}
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 910af5705705..400c225b53a0 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -260,57 +260,6 @@ public:
void endMark() const;
/**
- * Gets the alpha and xfermode out of a paint object. If the paint is null
- * alpha will be 255 and the xfermode will be SRC_OVER. This method does
- * not multiply the paint's alpha by the current snapshot's alpha, and does
- * not replace the alpha with the overrideLayerAlpha
- *
- * @param paint The paint to extract values from
- * @param alpha Where to store the resulting alpha
- * @param mode Where to store the resulting xfermode
- */
- static inline void getAlphaAndModeDirect(const SkPaint* paint, int* alpha,
- SkXfermode::Mode* mode) {
- *mode = getXfermodeDirect(paint);
- *alpha = getAlphaDirect(paint);
- }
-
- static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) {
- if (!paint) return SkXfermode::kSrcOver_Mode;
- return PaintUtils::getXfermode(paint->getXfermode());
- }
-
- static inline int getAlphaDirect(const SkPaint* paint) {
- if (!paint) return 255;
- return paint->getAlpha();
- }
-
- struct TextShadow {
- SkScalar radius;
- float dx;
- float dy;
- SkColor color;
- };
-
- static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) {
- SkDrawLooper::BlurShadowRec blur;
- if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) {
- if (textShadow) {
- textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma);
- textShadow->dx = blur.fOffset.fX;
- textShadow->dy = blur.fOffset.fY;
- textShadow->color = blur.fColor;
- }
- return true;
- }
- return false;
- }
-
- static inline bool hasTextShadow(const SkPaint* paint) {
- return getTextShadow(paint, nullptr);
- }
-
- /**
* Build the best transform to use to rasterize text given a full
* transform matrix, and whether filteration is needed.
*
@@ -493,16 +442,6 @@ protected:
void drawTextureLayer(Layer* layer, const Rect& rect);
/**
- * Gets the alpha and xfermode out of a paint object. If the paint is null
- * alpha will be 255 and the xfermode will be SRC_OVER. Accounts for snapshot alpha.
- *
- * @param paint The paint to extract values from
- * @param alpha Where to store the resulting alpha
- * @param mode Where to store the resulting xfermode
- */
- inline void getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const;
-
- /**
* Gets the alpha from a layer, accounting for snapshot alpha
*
* @param layer The layer from which the alpha is extracted
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 4c4cd3da3be4..50199db75640 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -125,25 +125,32 @@ public:
}
bool intersects(float l, float t, float r, float b) const {
- return !intersectWith(l, t, r, b).isEmpty();
+ float tempLeft = std::max(left, l);
+ float tempTop = std::max(top, t);
+ float tempRight = std::min(right, r);
+ float tempBottom = std::min(bottom, b);
+
+ return ((tempLeft < tempRight) && (tempTop < tempBottom)); // !isEmpty
}
bool intersects(const Rect& r) const {
return intersects(r.left, r.top, r.right, r.bottom);
}
- bool intersect(float l, float t, float r, float b) {
- Rect tmp(l, t, r, b);
- intersectWith(tmp);
- if (!tmp.isEmpty()) {
- set(tmp);
- return true;
- }
- return false;
+ /**
+ * This method is named 'doIntersect' instead of 'intersect' so as not to be confused with
+ * SkRect::intersect / android.graphics.Rect#intersect behavior, which do not modify the object
+ * if the intersection of the rects would be empty.
+ */
+ void doIntersect(float l, float t, float r, float b) {
+ left = std::max(left, l);
+ top = std::max(top, t);
+ right = std::min(right, r);
+ bottom = std::min(bottom, b);
}
- bool intersect(const Rect& r) {
- return intersect(r.left, r.top, r.right, r.bottom);
+ void doIntersect(const Rect& r) {
+ doIntersect(r.left, r.top, r.right, r.bottom);
}
inline bool contains(float l, float t, float r, float b) const {
@@ -271,24 +278,6 @@ public:
void dump(const char* label = nullptr) const {
ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom);
}
-
-private:
- void intersectWith(Rect& tmp) const {
- tmp.left = std::max(left, tmp.left);
- tmp.top = std::max(top, tmp.top);
- tmp.right = std::min(right, tmp.right);
- tmp.bottom = std::min(bottom, tmp.bottom);
- }
-
- Rect intersectWith(float l, float t, float r, float b) const {
- Rect tmp;
- tmp.left = std::max(left, l);
- tmp.top = std::max(top, t);
- tmp.right = std::min(right, r);
- tmp.bottom = std::min(bottom, b);
- return tmp;
- }
-
}; // class Rect
}; // namespace uirenderer
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index ddc7ecd329b6..bf1b4d0b0d0e 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -725,7 +725,9 @@ template <class T>
void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
if (properties().getAlpha() <= 0.0f
|| properties().getOutline().getAlpha() <= 0.0f
- || !properties().getOutline().getPath()) {
+ || !properties().getOutline().getPath()
+ || properties().getScaleX() == 0
+ || properties().getScaleY() == 0) {
// no shadow to draw
return;
}
@@ -915,7 +917,10 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
const bool useViewProperties = (!mLayer || drawLayer);
if (useViewProperties) {
const Outline& outline = properties().getOutline();
- if (properties().getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty())) {
+ if (properties().getAlpha() <= 0
+ || (outline.getShouldClip() && outline.isEmpty())
+ || properties().getScaleX() == 0
+ || properties().getScaleY() == 0) {
DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", handler.level() * 2, "",
this, getName());
return;
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index ad74bff8dc25..ce1bd6ab8b03 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -52,11 +52,8 @@ bool LayerProperties::setColorFilter(SkColorFilter* filter) {
bool LayerProperties::setFromPaint(const SkPaint* paint) {
bool changed = false;
- SkXfermode::Mode mode;
- int alpha;
- OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
- changed |= setAlpha(static_cast<uint8_t>(alpha));
- changed |= setXferMode(mode);
+ changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint)));
+ changed |= setXferMode(PaintUtils::getXfermodeDirect(paint));
changed |= setColorFilter(paint ? paint->getColorFilter() : nullptr);
return changed;
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 71589c802749..f824cc020196 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -549,7 +549,7 @@ public:
if (flags & CLIP_TO_BOUNDS) {
outRect->set(0, 0, getWidth(), getHeight());
if (flags & CLIP_TO_CLIP_BOUNDS) {
- outRect->intersect(mPrimitiveFields.mClipBounds);
+ outRect->doIntersect(mPrimitiveFields.mClipBounds);
}
} else {
outRect->set(mPrimitiveFields.mClipBounds);
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 220936551a60..eb0fa74f5af0 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -73,8 +73,8 @@ void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
}
#if DEBUG_SHADOW
- ALOGD("light center %f %f %f",
- adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z);
+ ALOGD("light center %f %f %f %d",
+ adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z, lightRadius);
#endif
// light position (because it's in local space) needs to compensate for receiver transform
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 4d60b8dd0e7c..0a58f4b42e4c 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -44,7 +44,7 @@ Snapshot::Snapshot()
* Copies the specified snapshot/ The specified snapshot is stored as
* the previous snapshot.
*/
-Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags)
+Snapshot::Snapshot(Snapshot* s, int saveFlags)
: flags(0)
, previous(s)
, layer(s->layer)
@@ -148,7 +148,7 @@ void Snapshot::buildScreenSpaceTransform(Matrix4* outTransform) const {
const Snapshot* current = this;
do {
snapshotList.push(current);
- current = current->previous.get();
+ current = current->previous;
} while (current);
// traverse the list, adding in each transform that contributes to the total transform
@@ -240,7 +240,7 @@ bool Snapshot::isIgnored() const {
void Snapshot::dump() const {
ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d",
- this, flags, previous.get(), getViewportHeight(), isIgnored(), !mClipArea->isSimple());
+ this, flags, previous, getViewportHeight(), isIgnored(), !mClipArea->isSimple());
const Rect& clipRect(mClipArea->getClipRect());
ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d",
clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, mClipArea->isSimple());
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index cf8f11c80058..aeeda965c48f 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -83,11 +83,11 @@ public:
* Each snapshot has a link to a previous snapshot, indicating the previous
* state of the renderer.
*/
-class Snapshot: public LightRefBase<Snapshot> {
+class Snapshot {
public:
Snapshot();
- Snapshot(const sp<Snapshot>& s, int saveFlags);
+ Snapshot(Snapshot* s, int saveFlags);
/**
* Various flags set on ::flags.
@@ -229,7 +229,7 @@ public:
/**
* Previous snapshot.
*/
- sp<Snapshot> previous;
+ Snapshot* previous;
/**
* A pointer to the currently active layer.
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index 9b0a1aadf0bf..bdce73c79993 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -1051,7 +1051,7 @@ void SpotShadow::dumpPolygon(const Vector2* poly, int polyLength, const char* po
*/
void SpotShadow::dumpPolygon(const Vector3* poly, int polyLength, const char* polyName) {
for (int i = 0; i < polyLength; i++) {
- ALOGD("polygon %s i %d x %f y %f", polyName, i, poly[i].x, poly[i].y);
+ ALOGD("polygon %s i %d x %f y %f z %f", polyName, i, poly[i].x, poly[i].y, poly[i].z);
}
}
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index fb0753bbc76c..d680f990a0be 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -61,8 +61,6 @@ Font::FontDescription::FontDescription(const SkPaint* paint, const SkMatrix& ras
}
Font::~Font() {
- mState->removeFont(this);
-
for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
delete mCachedGlyphs.valueAt(i);
}
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index c5126def683c..dfa70ace2f44 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -208,7 +208,7 @@ void RenderState::postDecStrong(VirtualLightRefBase* object) {
// Render
///////////////////////////////////////////////////////////////////////////////
-void RenderState::render(const Glop& glop) {
+void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) {
const Glop::Mesh& mesh = glop.mesh;
const Glop::Mesh::Vertices& vertices = mesh.vertices;
const Glop::Mesh::Indices& indices = mesh.indices;
@@ -223,7 +223,7 @@ void RenderState::render(const Glop& glop) {
fill.program->setColor(fill.color);
}
- fill.program->set(glop.transform.ortho,
+ fill.program->set(orthoMatrix,
glop.transform.modelView,
glop.transform.meshTransform(),
glop.transform.transformFlags & TransformFlags::OffsetByFudgeFactor);
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index 4fd792c1b503..9ae084506f1d 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -84,7 +84,7 @@ public:
// more thinking...
void postDecStrong(VirtualLightRefBase* object);
- void render(const Glop& glop);
+ void render(const Glop& glop, const Matrix4& orthoMatrix);
AssetAtlas& assetAtlas() { return mAssetAtlas; }
Blend& blend() { return *mBlend; }
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9dc5b45a7738..38f6e539693e 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -62,7 +62,7 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
, mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
, mJankTracker(thread.timeLord().frameIntervalNanos())
, mProfiler(mFrames)
- , mContentOverdrawProtectionBounds(0, 0, 0, 0) {
+ , mContentDrawBounds(0, 0, 0, 0) {
mRenderNodes.emplace_back(rootRenderNode);
mRenderThread.renderState().registerCanvasContext(this);
mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
@@ -309,7 +309,7 @@ void CanvasContext::draw() {
Rect outBounds;
// It there are multiple render nodes, they are as follows:
// #0 - backdrop
- // #1 - content (with - and clipped to - bounds mContentOverdrawProtectionBounds)
+ // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
// #2 - frame
// Usually the backdrop cannot be seen since it will be entirely covered by the content. While
// resizing however it might become partially visible. The following render loop will crop the
@@ -317,66 +317,72 @@ void CanvasContext::draw() {
// against the backdrop (since that indicates a shrinking of the window) and then the frame
// around everything.
// The bounds of the backdrop against which the content should be clipped.
- Rect backdropBounds = mContentOverdrawProtectionBounds;
+ Rect backdropBounds = mContentDrawBounds;
+ // Usually the contents bounds should be mContentDrawBounds - however - we will
+ // move it towards the fixed edge to give it a more stable appearance (for the moment).
+ Rect contentBounds;
// If there is no content bounds we ignore the layering as stated above and start with 2.
- int layer = mContentOverdrawProtectionBounds.isEmpty() ? 2 : 0;
+ int layer = (mContentDrawBounds.isEmpty() || mRenderNodes.size() <= 2) ? 2 : 0;
// Draw all render nodes. Note that
for (const sp<RenderNode>& node : mRenderNodes) {
if (layer == 0) { // Backdrop.
- // Draw the backdrop clipped to the inverse content bounds.
+ // Draw the backdrop clipped to the inverse content bounds, but assume that the content
+ // was moved to the upper left corner.
const RenderProperties& properties = node->properties();
Rect targetBounds(properties.getLeft(), properties.getTop(),
properties.getRight(), properties.getBottom());
+ // Move the content bounds towards the fixed corner of the backdrop.
+ const int x = targetBounds.left;
+ const int y = targetBounds.top;
+ contentBounds.set(x, y, x + mContentDrawBounds.getWidth(),
+ y + mContentDrawBounds.getHeight());
// Remember the intersection of the target bounds and the intersection bounds against
// which we have to crop the content.
- backdropBounds.intersect(targetBounds);
+ backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
+ backdropBounds.doIntersect(targetBounds);
// Check if we have to draw something on the left side ...
- if (targetBounds.left < mContentOverdrawProtectionBounds.left) {
+ if (targetBounds.left < contentBounds.left) {
mCanvas->save(SkCanvas::kClip_SaveFlag);
if (mCanvas->clipRect(targetBounds.left, targetBounds.top,
- mContentOverdrawProtectionBounds.left, targetBounds.bottom,
+ contentBounds.left, targetBounds.bottom,
SkRegion::kIntersect_Op)) {
mCanvas->drawRenderNode(node.get(), outBounds);
}
// Reduce the target area by the area we have just painted.
- targetBounds.left = std::min(mContentOverdrawProtectionBounds.left,
- targetBounds.right);
+ targetBounds.left = std::min(contentBounds.left, targetBounds.right);
mCanvas->restore();
}
// ... or on the right side ...
- if (targetBounds.right > mContentOverdrawProtectionBounds.right &&
+ if (targetBounds.right > contentBounds.right &&
!targetBounds.isEmpty()) {
mCanvas->save(SkCanvas::kClip_SaveFlag);
- if (mCanvas->clipRect(mContentOverdrawProtectionBounds.right, targetBounds.top,
+ if (mCanvas->clipRect(contentBounds.right, targetBounds.top,
targetBounds.right, targetBounds.bottom,
SkRegion::kIntersect_Op)) {
mCanvas->drawRenderNode(node.get(), outBounds);
}
// Reduce the target area by the area we have just painted.
- targetBounds.right = std::max(targetBounds.left,
- mContentOverdrawProtectionBounds.right);
+ targetBounds.right = std::max(targetBounds.left, contentBounds.right);
mCanvas->restore();
}
// ... or at the top ...
- if (targetBounds.top < mContentOverdrawProtectionBounds.top &&
+ if (targetBounds.top < contentBounds.top &&
!targetBounds.isEmpty()) {
mCanvas->save(SkCanvas::kClip_SaveFlag);
if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right,
- mContentOverdrawProtectionBounds.top,
+ contentBounds.top,
SkRegion::kIntersect_Op)) {
mCanvas->drawRenderNode(node.get(), outBounds);
}
// Reduce the target area by the area we have just painted.
- targetBounds.top = std::min(mContentOverdrawProtectionBounds.top,
- targetBounds.bottom);
+ targetBounds.top = std::min(contentBounds.top, targetBounds.bottom);
mCanvas->restore();
}
// ... or at the bottom.
- if (targetBounds.bottom > mContentOverdrawProtectionBounds.bottom &&
+ if (targetBounds.bottom > contentBounds.bottom &&
!targetBounds.isEmpty()) {
mCanvas->save(SkCanvas::kClip_SaveFlag);
- if (mCanvas->clipRect(targetBounds.left,
- mContentOverdrawProtectionBounds.bottom, targetBounds.right,
+ if (mCanvas->clipRect(targetBounds.left, contentBounds.bottom, targetBounds.right,
targetBounds.bottom, SkRegion::kIntersect_Op)) {
mCanvas->drawRenderNode(node.get(), outBounds);
}
@@ -384,10 +390,17 @@ void CanvasContext::draw() {
}
} else if (layer == 1) { // Content
// It gets cropped against the bounds of the backdrop to stay inside.
- mCanvas->save(SkCanvas::kClip_SaveFlag);
- if (mCanvas->clipRect(backdropBounds.left, backdropBounds.top,
- backdropBounds.right, backdropBounds.bottom,
- SkRegion::kIntersect_Op)) {
+ mCanvas->save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+
+ // We shift and clip the content to match its final location in the window.
+ const float left = mContentDrawBounds.left;
+ const float top = mContentDrawBounds.top;
+ const float dx = backdropBounds.left - left;
+ const float dy = backdropBounds.top - top;
+ const float width = backdropBounds.getWidth();
+ const float height = backdropBounds.getHeight();
+ mCanvas->translate(dx, dy);
+ if (mCanvas->clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op)) {
mCanvas->drawRenderNode(node.get(), outBounds);
}
mCanvas->restore();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 1c3845cac504..e0cbabdc933a 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -126,8 +126,8 @@ public:
mRenderNodes.end());
}
- void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
- mContentOverdrawProtectionBounds.set(left, top, right, bottom);
+ void setContentDrawBounds(int left, int top, int right, int bottom) {
+ mContentDrawBounds.set(left, top, right, bottom);
}
private:
@@ -167,7 +167,7 @@ private:
std::set<RenderNode*> mPrefetechedLayers;
// Stores the bounds of the main content.
- Rect mContentOverdrawProtectionBounds;
+ Rect mContentDrawBounds;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index f43a769890a4..26aae90d5990 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -529,15 +529,14 @@ void RenderProxy::drawRenderNode(RenderNode* node) {
staticPostAndWait(task);
}
-CREATE_BRIDGE5(setContentOverdrawProtectionBounds, CanvasContext* context, int left, int top,
+CREATE_BRIDGE5(setContentDrawBounds, CanvasContext* context, int left, int top,
int right, int bottom) {
- args->context->setContentOverdrawProtectionBounds(args->left, args->top, args->right,
- args->bottom);
+ args->context->setContentDrawBounds(args->left, args->top, args->right, args->bottom);
return nullptr;
}
-void RenderProxy::setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
- SETUP_TASK(setContentOverdrawProtectionBounds);
+void RenderProxy::setContentDrawBounds(int left, int top, int right, int bottom) {
+ SETUP_TASK(setContentDrawBounds);
args->context = mContext;
args->left = left;
args->top = top;
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 046f24ac3f81..d1b62f1f64a6 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -109,7 +109,7 @@ public:
ANDROID_API void addRenderNode(RenderNode* node, bool placeFront);
ANDROID_API void removeRenderNode(RenderNode* node);
ANDROID_API void drawRenderNode(RenderNode* node);
- ANDROID_API void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom);
+ ANDROID_API void setContentDrawBounds(int left, int top, int right, int bottom);
private:
RenderThread& mRenderThread;
diff --git a/libs/hwui/tests/Benchmark.h b/libs/hwui/tests/Benchmark.h
new file mode 100644
index 000000000000..e16310e034be
--- /dev/null
+++ b/libs/hwui/tests/Benchmark.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+#ifndef TESTS_BENCHMARK_H
+#define TESTS_BENCHMARK_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+struct BenchmarkOptions {
+ int count;
+};
+
+typedef void (*BenchmarkFunctor)(const BenchmarkOptions&);
+
+struct BenchmarkInfo {
+ std::string name;
+ std::string description;
+ BenchmarkFunctor functor;
+};
+
+class Benchmark {
+public:
+ Benchmark(const BenchmarkInfo& info) {
+ registerBenchmark(info);
+ }
+
+private:
+ Benchmark() = delete;
+ Benchmark(const Benchmark&) = delete;
+ Benchmark& operator=(const Benchmark&) = delete;
+
+ static void registerBenchmark(const BenchmarkInfo& info);
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TESTS_BENCHMARK_H */
diff --git a/libs/hwui/tests/TestContext.cpp b/libs/hwui/tests/TestContext.cpp
index cebe7650dea2..ba763a8def62 100644
--- a/libs/hwui/tests/TestContext.cpp
+++ b/libs/hwui/tests/TestContext.cpp
@@ -22,16 +22,35 @@ namespace test {
static const int IDENT_DISPLAYEVENT = 1;
-static DisplayInfo getBuiltInDisplay() {
+static android::DisplayInfo DUMMY_DISPLAY {
+ 1080, //w
+ 1920, //h
+ 320.0, // xdpi
+ 320.0, // ydpi
+ 60.0, // fps
+ 2.0, // density
+ 0, // orientation
+ false, // secure?
+ 0, // appVsyncOffset
+ 0, // presentationDeadline
+ 0, // colorTransform
+};
+
+DisplayInfo getBuiltInDisplay() {
+#if !HWUI_NULL_GPU
DisplayInfo display;
sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
ISurfaceComposer::eDisplayIdMain));
status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &display);
LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n");
return display;
+#else
+ return DUMMY_DISPLAY;
+#endif
}
-android::DisplayInfo gDisplay = getBuiltInDisplay();
+// Initialize to a dummy default
+android::DisplayInfo gDisplay = DUMMY_DISPLAY;
TestContext::TestContext() {
mLooper = new Looper(true);
diff --git a/libs/hwui/tests/TestContext.h b/libs/hwui/tests/TestContext.h
index 7b30fc1dc7ce..2bbe5dffd9b8 100644
--- a/libs/hwui/tests/TestContext.h
+++ b/libs/hwui/tests/TestContext.h
@@ -32,6 +32,8 @@ namespace test {
extern DisplayInfo gDisplay;
#define dp(x) ((x) * android::uirenderer::test::gDisplay.density)
+DisplayInfo getBuiltInDisplay();
+
class TestContext {
public:
TestContext();
diff --git a/libs/hwui/tests/TreeContentAnimation.cpp b/libs/hwui/tests/TreeContentAnimation.cpp
new file mode 100644
index 000000000000..a59261c14fc5
--- /dev/null
+++ b/libs/hwui/tests/TreeContentAnimation.cpp
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2015 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 <cutils/log.h>
+#include <gui/Surface.h>
+#include <ui/PixelFormat.h>
+
+#include <AnimationContext.h>
+#include <DisplayListCanvas.h>
+#include <RenderNode.h>
+#include <renderthread/RenderProxy.h>
+#include <renderthread/RenderTask.h>
+
+#include "Benchmark.h"
+#include "TestContext.h"
+
+#include "protos/hwui.pb.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <vector>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::test;
+
+class ContextFactory : public IContextFactory {
+public:
+ virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
+ return new AnimationContext(clock);
+ }
+};
+
+static DisplayListCanvas* startRecording(RenderNode* node) {
+ DisplayListCanvas* renderer = new DisplayListCanvas(
+ node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
+ return renderer;
+}
+
+static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
+ node->setStagingDisplayList(renderer->finishRecording());
+ delete renderer;
+}
+
+class TreeContentAnimation {
+public:
+ virtual ~TreeContentAnimation() {}
+ int frameCount = 150;
+ virtual int getFrameCount() { return frameCount; }
+ virtual void setFrameCount(int fc) {
+ if (fc > 0) {
+ frameCount = fc;
+ }
+ }
+ virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
+ virtual void doFrame(int frameNr) = 0;
+
+ template <class T>
+ static void run(const BenchmarkOptions& opts) {
+ // Switch to the real display
+ gDisplay = getBuiltInDisplay();
+
+ T animation;
+ animation.setFrameCount(opts.count);
+
+ TestContext testContext;
+
+ // create the native surface
+ const int width = gDisplay.w;
+ const int height = gDisplay.h;
+ sp<Surface> surface = testContext.surface();
+
+ RenderNode* rootNode = new RenderNode();
+ rootNode->incStrong(nullptr);
+ rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
+ rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ rootNode->mutateStagingProperties().setClipToBounds(false);
+ rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+
+ ContextFactory factory;
+ std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
+ proxy->loadSystemProperties();
+ proxy->initialize(surface);
+ float lightX = width / 2.0;
+ proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
+ proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
+
+ android::uirenderer::Rect DUMMY;
+
+ DisplayListCanvas* renderer = startRecording(rootNode);
+ animation.createContent(width, height, renderer);
+ endRecording(renderer, rootNode);
+
+ // Do a few cold runs then reset the stats so that the caches are all hot
+ for (int i = 0; i < 3; i++) {
+ testContext.waitForVsync();
+ proxy->syncAndDrawFrame();
+ }
+ proxy->resetProfileInfo();
+
+ for (int i = 0; i < animation.getFrameCount(); i++) {
+ testContext.waitForVsync();
+
+ ATRACE_NAME("UI-Draw Frame");
+ nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
+ UiFrameInfoBuilder(proxy->frameInfo())
+ .setVsync(vsync, vsync);
+ animation.doFrame(i);
+ proxy->syncAndDrawFrame();
+ }
+
+ proxy->dumpProfileInfo(STDOUT_FILENO, 0);
+ rootNode->decStrong(nullptr);
+ }
+};
+
+class ShadowGridAnimation : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+ for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+ sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->mutateStagingProperties().setElevation(dp(16));
+ node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
+ node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _ShadowGrid(BenchmarkInfo{
+ "shadowgrid",
+ "A grid of rounded rects that cast a shadow. Simplified scenario of an "
+ "Android TV-style launcher interface. High CPU/GPU load.",
+ TreeContentAnimation::run<ShadowGridAnimation>
+});
+
+class ShadowGrid2Animation : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
+ for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
+ sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->mutateStagingProperties().setElevation(dp(16));
+ node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
+ node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _ShadowGrid2(BenchmarkInfo{
+ "shadowgrid2",
+ "A dense grid of rounded rects that cast a shadow. This is a higher CPU load "
+ "variant of shadowgrid. Very high CPU load, high GPU load.",
+ TreeContentAnimation::run<ShadowGrid2Animation>
+});
+
+class RectGridAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ card = createCard(40, 40, 200, 200);
+ renderer->drawRenderNode(card.get());
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
+
+ SkRegion region;
+ for (int xOffset = 0; xOffset < width; xOffset+=2) {
+ for (int yOffset = 0; yOffset < height; yOffset+=2) {
+ region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
+ }
+ }
+
+ SkPaint paint;
+ paint.setColor(0xff00ffff);
+ renderer->drawRegion(region, paint);
+
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _RectGrid(BenchmarkInfo{
+ "rectgrid",
+ "A dense grid of 1x1 rects that should visually look like a single rect. "
+ "Low CPU/GPU load.",
+ TreeContentAnimation::run<RectGridAnimation>
+});
+
+class OvalAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ card = createCard(40, 40, 400, 400);
+ renderer->drawRenderNode(card.get());
+
+ renderer->insertReorderBarrier(false);
+ }
+
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(0xFF000000);
+ renderer->drawOval(0, 0, width, height, paint);
+
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _Oval(BenchmarkInfo{
+ "oval",
+ "Draws 1 oval.",
+ TreeContentAnimation::run<OvalAnimation>
+});
+
+class PartialDamageTest : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ static SkColor COLORS[] = {
+ 0xFFF44336,
+ 0xFF9C27B0,
+ 0xFF2196F3,
+ 0xFF4CAF50,
+ };
+
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+
+ for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+ for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+ sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
+ COLORS[static_cast<int>((y / dp(116))) % 4]);
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ cards[0]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[0]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(cards[0].get());
+ renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
+ SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, cards[0].get());
+ }
+
+ static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
+ int startA = (start >> 24) & 0xff;
+ int startR = (start >> 16) & 0xff;
+ int startG = (start >> 8) & 0xff;
+ int startB = start & 0xff;
+
+ int endA = (end >> 24) & 0xff;
+ int endR = (end >> 16) & 0xff;
+ int endG = (end >> 8) & 0xff;
+ int endB = end & 0xff;
+
+ return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
+ (int)((startR + (int)(fraction * (endR - startR))) << 16) |
+ (int)((startG + (int)(fraction * (endG - startG))) << 8) |
+ (int)((startB + (int)(fraction * (endB - startB))));
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _PartialDamage(BenchmarkInfo{
+ "partialdamage",
+ "Tests the partial invalidation path. Draws a grid of rects and animates 1 "
+ "of them, should be low CPU & GPU load if EGL_EXT_buffer_age or "
+ "EGL_KHR_partial_update is supported by the device & are enabled in hwui.",
+ TreeContentAnimation::run<PartialDamageTest>
+});
diff --git a/libs/hwui/tests/how_to_run.txt b/libs/hwui/tests/how_to_run.txt
index 85900eff2f78..b051768f3262 100644
--- a/libs/hwui/tests/how_to_run.txt
+++ b/libs/hwui/tests/how_to_run.txt
@@ -2,16 +2,4 @@ mmm -j8 frameworks/base/libs/hwui/ &&
adb push $OUT/data/local/tmp/hwuitest /data/local/tmp/hwuitest &&
adb shell /data/local/tmp/hwuitest
-
-Command arguments:
-hwuitest [testname]
-
-Default test is 'shadowgrid'
-
-List of tests:
-
-shadowgrid: creates a grid of rounded rects that cast shadows, high CPU & GPU load
-
-rectgrid: creates a grid of 1x1 rects
-
-oval: draws 1 oval
+Pass --help to get help
diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp
index 0bbf08c76c90..aee84de3ae7b 100644
--- a/libs/hwui/tests/main.cpp
+++ b/libs/hwui/tests/main.cpp
@@ -14,385 +14,181 @@
* limitations under the License.
*/
-#include <cutils/log.h>
-#include <gui/Surface.h>
-#include <ui/PixelFormat.h>
-
-#include <AnimationContext.h>
-#include <DisplayListCanvas.h>
-#include <RenderNode.h>
-#include <renderthread/RenderProxy.h>
-#include <renderthread/RenderTask.h>
-
-#include "TestContext.h"
+#include "Benchmark.h"
#include "protos/hwui.pb.h"
+#include <getopt.h>
#include <stdio.h>
+#include <string>
#include <unistd.h>
+#include <unordered_map>
+#include <vector>
using namespace android;
using namespace android::uirenderer;
-using namespace android::uirenderer::renderthread;
-using namespace android::uirenderer::test;
-
-class ContextFactory : public IContextFactory {
-public:
- virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
- return new AnimationContext(clock);
- }
-};
-static DisplayListCanvas* startRecording(RenderNode* node) {
- DisplayListCanvas* renderer = new DisplayListCanvas(
- node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
- return renderer;
+// Not a static global because we need to force the map to be constructed
+// before we try to add things to it.
+std::unordered_map<std::string, BenchmarkInfo>& testMap() {
+ static std::unordered_map<std::string, BenchmarkInfo> testMap;
+ return testMap;
}
-static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
- node->setStagingDisplayList(renderer->finishRecording());
- delete renderer;
+void Benchmark::registerBenchmark(const BenchmarkInfo& info) {
+ testMap()[info.name] = info;
}
-class TreeContentAnimation {
-public:
- virtual ~TreeContentAnimation() {}
- int frameCount = 150;
- virtual int getFrameCount() { return frameCount; }
- virtual void setFrameCount(int fc) {
- if (fc > 0) {
- frameCount = fc;
- }
- }
- virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
- virtual void doFrame(int frameNr) = 0;
-
- template <class T>
- static void run(int frameCount) {
- T animation;
- animation.setFrameCount(frameCount);
-
- TestContext testContext;
-
- // create the native surface
- const int width = gDisplay.w;
- const int height = gDisplay.h;
- sp<Surface> surface = testContext.surface();
-
- RenderNode* rootNode = new RenderNode();
- rootNode->incStrong(nullptr);
- rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
- rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- rootNode->mutateStagingProperties().setClipToBounds(false);
- rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
-
- ContextFactory factory;
- std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
- proxy->loadSystemProperties();
- proxy->initialize(surface);
- float lightX = width / 2.0;
- proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
- proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
-
- android::uirenderer::Rect DUMMY;
-
- DisplayListCanvas* renderer = startRecording(rootNode);
- animation.createContent(width, height, renderer);
- endRecording(renderer, rootNode);
-
- // Do a few cold runs then reset the stats so that the caches are all hot
- for (int i = 0; i < 3; i++) {
- testContext.waitForVsync();
- proxy->syncAndDrawFrame();
- }
- proxy->resetProfileInfo();
-
- for (int i = 0; i < animation.getFrameCount(); i++) {
- testContext.waitForVsync();
-
- ATRACE_NAME("UI-Draw Frame");
- nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
- UiFrameInfoBuilder(proxy->frameInfo())
- .setVsync(vsync, vsync);
- animation.doFrame(i);
- proxy->syncAndDrawFrame();
- }
-
- proxy->dumpProfileInfo(STDOUT_FILENO, 0);
- rootNode->decStrong(nullptr);
- }
-};
-
-class ShadowGridAnimation : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
+static int gFrameCount = 150;
+static int gRepeatCount = 1;
+static std::vector<BenchmarkInfo> gRunTests;
+
+static void printHelp() {
+ printf("\
+USAGE: hwuitest [OPTIONS] <TESTNAME>\n\
+\n\
+OPTIONS:\n\
+ -c, --count=NUM NUM loops a test should run (example, number of frames)\n\
+ -r, --runs=NUM Repeat the test(s) NUM times\n\
+ -h, --help Display this help\n\
+ --list List all tests\n\
+\n");
+}
- for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
- for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
- sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
- renderer->drawRenderNode(card.get());
- cards.push_back(card);
+static void listTests() {
+ printf("Tests: \n");
+ for (auto&& test : testMap()) {
+ auto&& info = test.second;
+ const char* col1 = info.name.c_str();
+ int dlen = info.description.length();
+ const char* col2 = info.description.c_str();
+ // World's best line breaking algorithm.
+ do {
+ int toPrint = dlen;
+ if (toPrint > 50) {
+ char* found = (char*) memrchr(col2, ' ', 50);
+ if (found) {
+ toPrint = found - col2;
+ } else {
+ toPrint = 50;
+ }
}
- }
-
- renderer->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- for (size_t ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
- cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
- cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->mutateStagingProperties().setElevation(dp(16));
- node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
- node->mutateStagingProperties().mutableOutline().setShouldClip(true);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-class ShadowGrid2Animation : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
-
- for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
- for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
- sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
- renderer->drawRenderNode(card.get());
- cards.push_back(card);
+ printf("%-20s %.*s\n", col1, toPrint, col2);
+ col1 = "";
+ col2 += toPrint;
+ dlen -= toPrint;
+ while (*col2 == ' ') {
+ col2++; dlen--;
}
- }
-
- renderer->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- for (size_t ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
- cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
- cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->mutateStagingProperties().setElevation(dp(16));
- node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
- node->mutateStagingProperties().mutableOutline().setShouldClip(true);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-class RectGridAnimation : public TreeContentAnimation {
-public:
- sp<RenderNode> card;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
-
- card = createCard(40, 40, 200, 200);
- renderer->drawRenderNode(card.get());
-
- renderer->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- card->mutateStagingProperties().setTranslationX(curFrame);
- card->mutateStagingProperties().setTranslationY(curFrame);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ } while (dlen > 0);
+ printf("\n");
}
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
-
- SkRegion region;
- for (int xOffset = 0; xOffset < width; xOffset+=2) {
- for (int yOffset = 0; yOffset < height; yOffset+=2) {
- region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
- }
- }
-
- SkPaint paint;
- paint.setColor(0xff00ffff);
- renderer->drawRegion(region, paint);
+}
- endRecording(renderer, node.get());
- return node;
- }
+static const struct option LONG_OPTIONS[] = {
+ { "frames", required_argument, nullptr, 'f' },
+ { "repeat", required_argument, nullptr, 'r' },
+ { "help", no_argument, nullptr, 'h' },
+ { "list", no_argument, nullptr, 'l' },
+ { 0, 0, 0, 0 }
};
-class OvalAnimation : public TreeContentAnimation {
-public:
- sp<RenderNode> card;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
-
- card = createCard(40, 40, 400, 400);
- renderer->drawRenderNode(card.get());
+static const char* SHORT_OPTIONS = "c:r:h";
- renderer->insertReorderBarrier(false);
- }
+void parseOptions(int argc, char* argv[]) {
+ int c;
+ // temporary variable
+ int count;
+ bool error = false;
+ opterr = 0;
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- card->mutateStagingProperties().setTranslationX(curFrame);
- card->mutateStagingProperties().setTranslationY(curFrame);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ while (true) {
- DisplayListCanvas* renderer = startRecording(node.get());
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setColor(0xFF000000);
- renderer->drawOval(0, 0, width, height, paint);
+ c = getopt_long(argc, argv, SHORT_OPTIONS, LONG_OPTIONS, &option_index);
- endRecording(renderer, node.get());
- return node;
- }
-};
+ if (c == -1)
+ break;
-class PartialInvalTest : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- static SkColor COLORS[] = {
- 0xFFF44336,
- 0xFF9C27B0,
- 0xFF2196F3,
- 0xFF4CAF50,
- };
+ switch (c) {
+ case 0:
+ // Option set a flag, don't need to do anything
+ // (although none of the current LONG_OPTIONS do this...)
+ break;
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ case 'l':
+ listTests();
+ exit(EXIT_SUCCESS);
+ break;
- for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
- for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
- sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
- COLORS[static_cast<int>((y / dp(116))) % 4]);
- renderer->drawRenderNode(card.get());
- cards.push_back(card);
+ case 'c':
+ count = atoi(optarg);
+ if (!count) {
+ fprintf(stderr, "Invalid frames argument '%s'\n", optarg);
+ error = true;
+ } else {
+ gFrameCount = (count > 0 ? count : INT_MAX);
+ }
+ break;
+
+ case 'r':
+ count = atoi(optarg);
+ if (!count) {
+ fprintf(stderr, "Invalid repeat argument '%s'\n", optarg);
+ error = true;
+ } else {
+ gRepeatCount = (count > 0 ? count : INT_MAX);
}
+ break;
+
+ case 'h':
+ printHelp();
+ exit(EXIT_SUCCESS);
+ break;
+
+ case '?':
+ fprintf(stderr, "Unrecognized option '%s'\n", argv[optind - 1]);
+ // fall-through
+ default:
+ error = true;
+ break;
}
}
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- cards[0]->mutateStagingProperties().setTranslationX(curFrame);
- cards[0]->mutateStagingProperties().setTranslationY(curFrame);
- cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- DisplayListCanvas* renderer = startRecording(cards[0].get());
- renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
- SkXfermode::kSrcOver_Mode);
- endRecording(renderer, cards[0].get());
+ if (error) {
+ fprintf(stderr, "Try 'hwuitest --help' for more information.\n");
+ exit(EXIT_FAILURE);
}
- static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
- int startA = (start >> 24) & 0xff;
- int startR = (start >> 16) & 0xff;
- int startG = (start >> 8) & 0xff;
- int startB = start & 0xff;
-
- int endA = (end >> 24) & 0xff;
- int endR = (end >> 16) & 0xff;
- int endG = (end >> 8) & 0xff;
- int endB = end & 0xff;
-
- return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
- (int)((startR + (int)(fraction * (endR - startR))) << 16) |
- (int)((startG + (int)(fraction * (endG - startG))) << 8) |
- (int)((startB + (int)(fraction * (endB - startB))));
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-struct cstr_cmp {
- bool operator()(const char *a, const char *b) const {
- return std::strcmp(a, b) < 0;
+ /* Print any remaining command line arguments (not options). */
+ if (optind < argc) {
+ do {
+ const char* test = argv[optind++];
+ auto pos = testMap().find(test);
+ if (pos == testMap().end()) {
+ fprintf(stderr, "Unknown test '%s'\n", test);
+ exit(EXIT_FAILURE);
+ } else {
+ gRunTests.push_back(pos->second);
+ }
+ } while (optind < argc);
+ } else {
+ gRunTests.push_back(testMap()["shadowgrid"]);
}
-};
-
-typedef void (*testProc)(int);
-
-std::map<const char*, testProc, cstr_cmp> gTestMap {
- {"shadowgrid", TreeContentAnimation::run<ShadowGridAnimation>},
- {"shadowgrid2", TreeContentAnimation::run<ShadowGrid2Animation>},
- {"rectgrid", TreeContentAnimation::run<RectGridAnimation> },
- {"oval", TreeContentAnimation::run<OvalAnimation> },
- {"partialinval", TreeContentAnimation::run<PartialInvalTest> },
-};
+}
int main(int argc, char* argv[]) {
- const char* testName = argc > 1 ? argv[1] : "shadowgrid";
- testProc proc = gTestMap[testName];
- if(!proc) {
- printf("Error: couldn't find test %s\n", testName);
- return 1;
- }
- int loopCount = 1;
- if (argc > 2) {
- loopCount = atoi(argv[2]);
- if (!loopCount) {
- printf("Invalid loop count!\n");
- return 1;
- }
- }
- int frameCount = 150;
- if (argc > 3) {
- frameCount = atoi(argv[3]);
- if (frameCount < 1) {
- printf("Invalid frame count!\n");
- return 1;
+ parseOptions(argc, argv);
+
+ BenchmarkOptions opts;
+ opts.count = gFrameCount;
+ for (int i = 0; i < gRepeatCount; i++) {
+ for (auto&& test : gRunTests) {
+ test.functor(opts);
}
}
- if (loopCount < 0) {
- loopCount = INT_MAX;
- }
- for (int i = 0; i < loopCount; i++) {
- proc(frameCount);
- }
printf("Success!\n");
return 0;
}
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index ba02f5f1a77d..d00236ed955a 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -16,12 +16,19 @@
#ifndef PAINT_UTILS_H
#define PAINT_UTILS_H
+#include <utils/Blur.h>
+
#include <SkColorFilter.h>
+#include <SkDrawLooper.h>
#include <SkXfermode.h>
namespace android {
namespace uirenderer {
+/**
+ * Utility methods for accessing data within SkPaint, and providing defaults
+ * with optional SkPaint pointers.
+ */
class PaintUtils {
public:
@@ -73,6 +80,39 @@ public:
return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
}
+ struct TextShadow {
+ SkScalar radius;
+ float dx;
+ float dy;
+ SkColor color;
+ };
+
+ static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) {
+ SkDrawLooper::BlurShadowRec blur;
+ if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) {
+ if (textShadow) {
+ textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma);
+ textShadow->dx = blur.fOffset.fX;
+ textShadow->dy = blur.fOffset.fY;
+ textShadow->color = blur.fColor;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ static inline bool hasTextShadow(const SkPaint* paint) {
+ return getTextShadow(paint, nullptr);
+ }
+
+ static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) {
+ return paint ? getXfermode(paint->getXfermode()) : SkXfermode::kSrcOver_Mode;
+ }
+
+ static inline int getAlphaDirect(const SkPaint* paint) {
+ return paint ? paint->getAlpha() : 255;
+ }
+
}; // class PaintUtils
} /* namespace uirenderer */
diff --git a/libs/hwui/utils/TinyHashMap.h b/libs/hwui/utils/TinyHashMap.h
deleted file mode 100644
index 4ff9a42f6d5d..000000000000
--- a/libs/hwui/utils/TinyHashMap.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_HWUI_TINYHASHMAP_H
-#define ANDROID_HWUI_TINYHASHMAP_H
-
-#include <utils/BasicHashtable.h>
-
-namespace android {
-namespace uirenderer {
-
-/**
- * A very simple hash map that doesn't allow duplicate keys, overwriting the older entry.
- */
-template <typename TKey, typename TValue>
-class TinyHashMap {
-public:
- typedef key_value_pair_t<TKey, TValue> TEntry;
-
- /**
- * Puts an entry in the hash, removing any existing entry with the same key
- */
- void put(TKey key, TValue value) {
- hash_t hash = android::hash_type(key);
-
- ssize_t index = mTable.find(-1, hash, key);
- if (index != -1) {
- mTable.removeAt(index);
- }
-
- TEntry initEntry(key, value);
- mTable.add(hash, initEntry);
- }
-
- /**
- * Return true if key is in the map, in which case stores the value in the output ref
- */
- bool get(TKey key, TValue& outValue) {
- hash_t hash = android::hash_type(key);
- ssize_t index = mTable.find(-1, hash, key);
- if (index == -1) {
- return false;
- }
- outValue = mTable.entryAt(index).value;
- return true;
- }
-
- void clear() { mTable.clear(); }
-
-private:
- BasicHashtable<TKey, TEntry> mTable;
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_TINYHASHMAP_H