summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libs/hwui/AmbientShadow.cpp10
-rw-r--r--libs/hwui/AmbientShadow.h2
-rw-r--r--libs/hwui/Android.mk1
-rw-r--r--libs/hwui/Caches.cpp4
-rw-r--r--libs/hwui/Caches.h2
-rw-r--r--libs/hwui/DisplayListOp.h29
-rw-r--r--libs/hwui/OpenGLRenderer.cpp134
-rw-r--r--libs/hwui/OpenGLRenderer.h23
-rw-r--r--libs/hwui/PathCache.cpp17
-rw-r--r--libs/hwui/PathCache.h25
-rw-r--r--libs/hwui/PathTessellator.cpp64
-rw-r--r--libs/hwui/PathTessellator.h16
-rw-r--r--libs/hwui/Properties.h2
-rw-r--r--libs/hwui/Rect.h7
-rw-r--r--libs/hwui/ShadowTessellator.cpp15
-rw-r--r--libs/hwui/ShadowTessellator.h4
-rw-r--r--libs/hwui/SpotShadow.cpp4
-rw-r--r--libs/hwui/SpotShadow.h2
-rw-r--r--libs/hwui/StatefulBaseRenderer.cpp4
-rw-r--r--libs/hwui/StatefulBaseRenderer.h1
-rw-r--r--libs/hwui/TessellationCache.cpp476
-rw-r--r--libs/hwui/TessellationCache.h193
-rw-r--r--libs/hwui/VertexBuffer.h28
-rw-r--r--libs/hwui/thread/TaskManager.cpp6
-rw-r--r--libs/hwui/utils/Macros.h7
-rw-r--r--libs/hwui/utils/MathUtils.h4
26 files changed, 867 insertions, 213 deletions
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index c1af5f5..937b7c6 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -44,18 +44,18 @@ namespace uirenderer {
* @param shadowVertexBuffer Return an floating point array of (x, y, a)
* triangle strips mode.
*/
-VertexBufferMode AmbientShadow::createAmbientShadow(bool isCasterOpaque,
+void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
const Vector3* vertices, int vertexCount, const Vector3& centroid3d,
float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) {
const int rays = SHADOW_RAY_COUNT;
- VertexBufferMode mode = kVertexBufferMode_OnePolyRingShadow;
+ VertexBuffer::Mode mode = VertexBuffer::kOnePolyRingShadow;
// Validate the inputs.
if (vertexCount < 3 || heightFactor <= 0 || rays <= 0
|| geomFactor <= 0) {
#if DEBUG_SHADOW
ALOGW("Invalid input for createAmbientShadow(), early return!");
#endif
- return mode; // vertex buffer is empty, so any mode doesn't matter.
+ return;
}
Vector<Vector2> dir; // TODO: use C++11 unique_ptr
@@ -127,7 +127,7 @@ VertexBufferMode AmbientShadow::createAmbientShadow(bool isCasterOpaque,
// If caster isn't opaque, we need to to fill the umbra by storing the umbra's
// centroid in the innermost ring of vertices.
if (!isCasterOpaque) {
- mode = kVertexBufferMode_TwoPolyRingShadow;
+ mode = VertexBuffer::kTwoPolyRingShadow;
float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor);
AlphaVertex centroidXYA;
AlphaVertex::set(&centroidXYA, centroid2d.x, centroid2d.y, centroidAlpha);
@@ -135,6 +135,7 @@ VertexBufferMode AmbientShadow::createAmbientShadow(bool isCasterOpaque,
shadowVertices[2 * rays + rayIndex] = centroidXYA;
}
}
+ shadowVertexBuffer.setMode(mode);
#if DEBUG_SHADOW
for (int i = 0; i < SHADOW_VERTEX_COUNT; i++) {
@@ -142,7 +143,6 @@ VertexBufferMode AmbientShadow::createAmbientShadow(bool isCasterOpaque,
shadowVertices[i].y, shadowVertices[i].alpha);
}
#endif
- return mode;
}
/**
diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h
index 451bfbe..68df246 100644
--- a/libs/hwui/AmbientShadow.h
+++ b/libs/hwui/AmbientShadow.h
@@ -35,7 +35,7 @@ namespace uirenderer {
*/
class AmbientShadow {
public:
- static VertexBufferMode createAmbientShadow(bool isCasterOpaque, const Vector3* poly,
+ static void createAmbientShadow(bool isCasterOpaque, const Vector3* poly,
int polyLength, const Vector3& centroid3d, float heightFactor,
float geomFactor, VertexBuffer& shadowVertexBuffer);
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 442f327..900a72e 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -51,6 +51,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
SpotShadow.cpp \
StatefulBaseRenderer.cpp \
Stencil.cpp \
+ TessellationCache.cpp \
Texture.cpp \
TextureCache.cpp \
TextDropShadowCache.cpp
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 77ef637..6fd9999 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -273,6 +273,8 @@ void Caches::dumpMemoryUsage(String8 &log) {
gradientCache.getSize(), gradientCache.getMaxSize());
log.appendFormat(" PathCache %8d / %8d\n",
pathCache.getSize(), pathCache.getMaxSize());
+ log.appendFormat(" TessellationCache %8d / %8d\n",
+ tessellationCache.getSize(), tessellationCache.getMaxSize());
log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(),
dropShadowCache.getMaxSize());
log.appendFormat(" PatchCache %8d / %8d\n",
@@ -295,6 +297,7 @@ void Caches::dumpMemoryUsage(String8 &log) {
total += renderBufferCache.getSize();
total += gradientCache.getSize();
total += pathCache.getSize();
+ total += tessellationCache.getSize();
total += dropShadowCache.getSize();
total += patchCache.getSize();
for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
@@ -358,6 +361,7 @@ void Caches::flush(FlushMode mode) {
fontRenderer->flush();
textureCache.flush();
pathCache.clear();
+ tessellationCache.clear();
// fall through
case kFlushMode_Layers:
layerCache.clear();
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 5367663..b4b5927 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -43,6 +43,7 @@
#include "PatchCache.h"
#include "ProgramCache.h"
#include "PathCache.h"
+#include "TessellationCache.h"
#include "TextDropShadowCache.h"
#include "FboCache.h"
#include "ResourceCache.h"
@@ -326,6 +327,7 @@ public:
ProgramCache programCache;
PathCache pathCache;
PatchCache patchCache;
+ TessellationCache tessellationCache;
TextDropShadowCache dropShadowCache;
FboCache fboCache;
ResourceCache resourceCache;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index ea3e7a8..3281116 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1114,6 +1114,15 @@ public:
OP_LOG("Draw RoundRect "RECT_STRING", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
}
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
+ DrawStrokableOp::onDefer(renderer, deferInfo, state);
+ if (!mPaint->getPathEffect()) {
+ renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix,
+ mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy, mPaint);
+ }
+ }
+
virtual const char* name() { return "DrawRoundRect"; }
private:
@@ -1533,9 +1542,23 @@ public:
}
}
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
+ renderer.getCaches().tessellationCache.precacheShadows(&state.mMatrix,
+ renderer.getLocalClipBounds(), isCasterOpaque(), &mOutline,
+ &mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius());
+ }
+
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawShadow(mTransformXY, mTransformZ,
- mCasterAlpha, mCasterUnclipped, &mOutline);
+ TessellationCache::vertexBuffer_pair_t buffers;
+ Matrix4 drawTransform;
+ renderer.getMatrix(&drawTransform);
+ renderer.getCaches().tessellationCache.getShadowBuffers(&drawTransform,
+ renderer.getLocalClipBounds(), isCasterOpaque(), &mOutline,
+ &mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius(),
+ buffers);
+
+ return renderer.drawShadow(mCasterAlpha, buffers.first, buffers.second);
}
virtual void output(int level, uint32_t logFlags) const {
@@ -1545,6 +1568,8 @@ public:
virtual const char* name() { return "DrawShadow"; }
private:
+ bool isCasterOpaque() { return mCasterAlpha >= 1.0f && mCasterUnclipped; }
+
const mat4 mTransformXY;
const mat4 mTransformZ;
const float mCasterAlpha;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 71836dd..31f399a 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -287,6 +287,7 @@ void OpenGLRenderer::finish() {
// of the current frame
if (getTargetFbo() == 0) {
mCaches.pathCache.trim();
+ mCaches.tessellationCache.trim();
}
if (!suppressErrorChecks()) {
@@ -2390,7 +2391,7 @@ status_t OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry*
return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawVertexBuffer(VertexBufferMode mode,
+status_t OpenGLRenderer::drawVertexBuffer(float translateX, float translateY,
const VertexBuffer& vertexBuffer, const SkPaint* paint, bool useOffset) {
// not missing call to quickReject/dirtyLayer, always done at a higher level
if (!vertexBuffer.getVertexCount()) {
@@ -2398,6 +2399,9 @@ status_t OpenGLRenderer::drawVertexBuffer(VertexBufferMode mode,
return DrawGlInfo::kStatusDone;
}
+ const Rect& bounds = vertexBuffer.getBounds();
+ dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
+
int color = paint->getColor();
bool isAA = paint->isAntiAlias();
@@ -2409,7 +2413,7 @@ status_t OpenGLRenderer::drawVertexBuffer(VertexBufferMode mode,
setupDrawShader(getShader(paint));
setupDrawBlending(paint, isAA);
setupDrawProgram();
- setupDrawModelView(kModelViewMode_Translate, useOffset, 0, 0, 0, 0);
+ setupDrawModelView(kModelViewMode_Translate, useOffset, translateX, translateY, 0, 0);
setupDrawColorUniforms(getShader(paint));
setupDrawColorFilterUniforms(getColorFilter(paint));
setupDrawShaderUniforms(getShader(paint));
@@ -2429,13 +2433,14 @@ status_t OpenGLRenderer::drawVertexBuffer(VertexBufferMode mode,
glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
}
- if (mode == kVertexBufferMode_Standard) {
+ const VertexBuffer::Mode mode = vertexBuffer.getMode();
+ if (mode == VertexBuffer::kStandard) {
mCaches.unbindIndicesBuffer();
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
- } else if (mode == kVertexBufferMode_OnePolyRingShadow) {
+ } else if (mode == VertexBuffer::kOnePolyRingShadow) {
mCaches.bindShadowIndicesBuffer();
glDrawElements(GL_TRIANGLE_STRIP, ONE_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
- } else if (mode == kVertexBufferMode_TwoPolyRingShadow) {
+ } else if (mode == VertexBuffer::kTwoPolyRingShadow) {
mCaches.bindShadowIndicesBuffer();
glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
}
@@ -2460,14 +2465,7 @@ status_t OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint
VertexBuffer vertexBuffer;
// TODO: try clipping large paths to viewport
PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer);
-
- if (hasLayer()) {
- SkRect bounds = path.getBounds();
- PathTessellator::expandBoundsForStroke(bounds, paint);
- dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform());
- }
-
- return drawVertexBuffer(kVertexBufferMode_Standard, vertexBuffer, paint);
+ return drawVertexBuffer(vertexBuffer, paint);
}
/**
@@ -2487,18 +2485,15 @@ status_t OpenGLRenderer::drawLines(const float* points, int count, const SkPaint
count &= ~0x3; // round down to nearest four
VertexBuffer buffer;
- SkRect bounds;
- PathTessellator::tessellateLines(points, count, paint, *currentTransform(), bounds, buffer);
+ PathTessellator::tessellateLines(points, count, paint, *currentTransform(), buffer);
+ const Rect& bounds = buffer.getBounds();
- // can't pass paint, since style would be checked for outset. outset done by tessellation.
- if (quickRejectSetupScissor(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
+ if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
return DrawGlInfo::kStatusDone;
}
- dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform());
-
bool useOffset = !paint->isAntiAlias();
- return drawVertexBuffer(kVertexBufferMode_Standard, buffer, paint, useOffset);
+ return drawVertexBuffer(buffer, paint, useOffset);
}
status_t OpenGLRenderer::drawPoints(const float* points, int count, const SkPaint* paint) {
@@ -2507,18 +2502,15 @@ status_t OpenGLRenderer::drawPoints(const float* points, int count, const SkPain
count &= ~0x1; // round down to nearest two
VertexBuffer buffer;
- SkRect bounds;
- PathTessellator::tessellatePoints(points, count, paint, *currentTransform(), bounds, buffer);
+ PathTessellator::tessellatePoints(points, count, paint, *currentTransform(), buffer);
- // can't pass paint, since style would be checked for outset. outset done by tessellation.
- if (quickRejectSetupScissor(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
+ const Rect& bounds = buffer.getBounds();
+ if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
return DrawGlInfo::kStatusDone;
}
- dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform());
-
bool useOffset = !paint->isAntiAlias();
- return drawVertexBuffer(kVertexBufferMode_Standard, buffer, paint, useOffset);
+ return drawVertexBuffer(buffer, paint, useOffset);
}
status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
@@ -2564,16 +2556,9 @@ status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float
return drawShape(left, top, texture, p);
}
- SkPath path;
- SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
- if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
- float outset = p->getStrokeWidth() / 2;
- rect.outset(outset, outset);
- rx += outset;
- ry += outset;
- }
- path.addRoundRect(rect, rx, ry);
- return drawConvexPath(path, p);
+ const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(*currentTransform(),
+ right - left, bottom - top, rx, ry, p);
+ return drawVertexBuffer(left, top, *vertexBuffer, p);
}
status_t OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p) {
@@ -3192,8 +3177,8 @@ static void mapPointFakeZ(Vector3& point, const mat4& transformXY, const mat4& t
transformXY.mapPoint(point.x, point.y);
}
-status_t OpenGLRenderer::drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
- float casterAlpha, bool casterUnclipped, const SkPath* casterPerimeter) {
+status_t OpenGLRenderer::drawShadow(float casterAlpha,
+ const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
// TODO: use quickRejectWithScissor. For now, always force enable scissor.
@@ -3202,77 +3187,14 @@ status_t OpenGLRenderer::drawShadow(const mat4& casterTransformXY, const mat4& c
SkPaint paint;
paint.setAntiAlias(true); // want to use AlphaVertex
- // tessellate caster outline into a 2d polygon
- Vector<Vertex> casterVertices2d;
- const float casterRefinementThresholdSquared = 20.0f; // TODO: experiment with this value
- PathTessellator::approximatePathOutlineVertices(*casterPerimeter,
- casterRefinementThresholdSquared, casterVertices2d);
- if (!ShadowTessellator::isClockwisePath(*casterPerimeter)) {
- ShadowTessellator::reverseVertexArray(casterVertices2d.editArray(),
- casterVertices2d.size());
- }
-
- if (casterVertices2d.size() == 0) {
- // empty caster polygon computed from path
- return DrawGlInfo::kStatusDone;
- }
-
- // map 2d caster poly into 3d
- const int casterVertexCount = casterVertices2d.size();
- Vector3 casterPolygon[casterVertexCount];
- float minZ = FLT_MAX;
- float maxZ = -FLT_MAX;
- for (int i = 0; i < casterVertexCount; i++) {
- const Vertex& point2d = casterVertices2d[i];
- casterPolygon[i] = Vector3(point2d.x, point2d.y, 0);
- mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
- minZ = fmin(minZ, casterPolygon[i].z);
- maxZ = fmax(maxZ, casterPolygon[i].z);
- }
-
- // map the centroid of the caster into 3d
- Vector2 centroid = ShadowTessellator::centroid2d(
- reinterpret_cast<const Vector2*>(casterVertices2d.array()),
- casterVertexCount);
- Vector3 centroid3d(centroid.x, centroid.y, 0);
- mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ);
-
- // if the caster intersects the z=0 plane, lift it in Z so it doesn't
- if (minZ < SHADOW_MIN_CASTER_Z) {
- float casterLift = SHADOW_MIN_CASTER_Z - minZ;
- for (int i = 0; i < casterVertexCount; i++) {
- casterPolygon[i].z += casterLift;
- }
- centroid3d.z += casterLift;
- }
-
- // Check whether we want to draw the shadow at all by checking the caster's
- // bounds against clip.
- // We only have ortho projection, so we can just ignore the Z in caster for
- // simple rejection calculation.
- Rect localClip = mSnapshot->getLocalClip();
- Rect casterBounds(casterPerimeter->getBounds());
- casterTransformXY.mapRect(casterBounds);
-
- bool isCasterOpaque = (casterAlpha == 1.0f) && casterUnclipped;
- // draw caster's shadows
- if (mCaches.propertyAmbientShadowStrength > 0) {
+ if (ambientShadowVertexBuffer && mCaches.propertyAmbientShadowStrength > 0) {
paint.setARGB(casterAlpha * mCaches.propertyAmbientShadowStrength, 0, 0, 0);
- VertexBuffer ambientShadowVertexBuffer;
- VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateAmbientShadow(
- isCasterOpaque, casterPolygon, casterVertexCount, centroid3d,
- casterBounds, localClip, maxZ, ambientShadowVertexBuffer);
- drawVertexBuffer(vertexBufferMode, ambientShadowVertexBuffer, &paint);
+ drawVertexBuffer(*ambientShadowVertexBuffer, &paint);
}
- if (mCaches.propertySpotShadowStrength > 0) {
+ if (spotShadowVertexBuffer && mCaches.propertySpotShadowStrength > 0) {
paint.setARGB(casterAlpha * mCaches.propertySpotShadowStrength, 0, 0, 0);
- VertexBuffer spotShadowVertexBuffer;
- VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateSpotShadow(
- isCasterOpaque, casterPolygon, casterVertexCount,
- *currentTransform(), mLightCenter, mLightRadius, casterBounds, localClip,
- spotShadowVertexBuffer);
- drawVertexBuffer(vertexBufferMode, spotShadowVertexBuffer, &paint);
+ drawVertexBuffer(*spotShadowVertexBuffer, &paint);
}
return DrawGlInfo::kStatusDrew;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index fc27947..b9b369f 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -111,12 +111,6 @@ enum ModelViewMode {
kModelViewMode_TranslateAndScale = 1,
};
-enum VertexBufferMode {
- kVertexBufferMode_Standard = 0,
- kVertexBufferMode_OnePolyRingShadow = 1,
- kVertexBufferMode_TwoPolyRingShadow = 2
-};
-
///////////////////////////////////////////////////////////////////////////////
// Renderer
///////////////////////////////////////////////////////////////////////////////
@@ -213,8 +207,8 @@ public:
DrawOpMode drawOpMode = kDrawOpMode_Immediate);
virtual status_t drawRects(const float* rects, int count, const SkPaint* paint);
- status_t drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
- float casterAlpha, bool casterUnclipped, const SkPath* casterPerimeter);
+ status_t drawShadow(float casterAlpha,
+ const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer);
virtual void resetPaintFilter();
virtual void setupPaintFilter(int clearBits, int setBits);
@@ -348,6 +342,9 @@ public:
}
#endif
+ const Vector3& getLightCenter() const { return mLightCenter; }
+ float getLightRadius() const { return mLightRadius; }
+
protected:
/**
* Perform the setup specific to a frame. This method does not
@@ -661,10 +658,18 @@ private:
* @param paint The paint to render with
* @param useOffset Offset the vertexBuffer (used in drawing non-AA lines)
*/
- status_t drawVertexBuffer(VertexBufferMode mode, const VertexBuffer& vertexBuffer,
+ status_t drawVertexBuffer(float translateX, float translateY, const VertexBuffer& vertexBuffer,
const SkPaint* paint, bool useOffset = false);
/**
+ * Convenience for translating method
+ */
+ status_t drawVertexBuffer(const VertexBuffer& vertexBuffer,
+ const SkPaint* paint, bool useOffset = false) {
+ return drawVertexBuffer(0.0f, 0.0f, vertexBuffer, paint, useOffset);
+ }
+
+ /**
* Renders the convex hull defined by the specified path as a strip of polygons.
*
* @param path The hull of the path to draw
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 5a49f38..ab6b742 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -74,10 +74,6 @@ hash_t PathDescription::hash() const {
return JenkinsHashWhiten(hash);
}
-int PathDescription::compare(const PathDescription& rhs) const {
- return memcmp(this, &rhs, sizeof(PathDescription));
-}
-
///////////////////////////////////////////////////////////////////////////////
// Utilities
///////////////////////////////////////////////////////////////////////////////
@@ -163,14 +159,7 @@ PathCache::PathCache():
} else {
INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_PATH_CACHE_SIZE);
}
- init();
-}
-PathCache::~PathCache() {
- mCache.clear();
-}
-
-void PathCache::init() {
mCache.setOnEntryRemovedListener(this);
GLint maxTextureSize;
@@ -180,6 +169,10 @@ void PathCache::init() {
mDebugEnabled = readDebugLevel() & kDebugCaches;
}
+PathCache::~PathCache() {
+ mCache.clear();
+}
+
///////////////////////////////////////////////////////////////////////////////
// Size management
///////////////////////////////////////////////////////////////////////////////
@@ -341,7 +334,7 @@ PathCache::PathProcessor::PathProcessor(Caches& caches):
}
void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
- sp<PathTask> t = static_cast<PathTask* >(task.get());
+ PathTask* t = static_cast<PathTask*>(task.get());
ATRACE_NAME("pathPrecache");
float left, top, offset;
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 847853a..6177ff1 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -26,6 +26,7 @@
#include "Debug.h"
#include "Properties.h"
#include "Texture.h"
+#include "utils/Macros.h"
#include "utils/Pair.h"
class SkBitmap;
@@ -107,6 +108,7 @@ enum ShapeType {
};
struct PathDescription {
+ DESCRIPTION_TYPE(PathDescription);
ShapeType type;
SkPaint::Join join;
SkPaint::Cap cap;
@@ -148,29 +150,6 @@ struct PathDescription {
PathDescription(ShapeType shapeType, const SkPaint* paint);
hash_t hash() const;
-
- int compare(const PathDescription& rhs) const;
-
- bool operator==(const PathDescription& other) const {
- return compare(other) == 0;
- }
-
- bool operator!=(const PathDescription& other) const {
- return compare(other) != 0;
- }
-
- friend inline int strictly_order_type(
- const PathDescription& lhs, const PathDescription& rhs) {
- return lhs.compare(rhs) < 0;
- }
-
- friend inline int compare_type(const PathDescription& lhs, const PathDescription& rhs) {
- return lhs.compare(rhs);
- }
-
- friend inline hash_t hash_type(const PathDescription& entry) {
- return entry.hash();
- }
};
/**
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 4ef2158..c9921ba 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -57,15 +57,17 @@ namespace uirenderer {
#define ROUND_CAP_THRESH 0.25f
#define PI 3.1415926535897932f
-/**
- * Note: this function doesn't account for the AA case with sub-pixel line thickness (not just 0 <
- * width < 1.0, canvas scale factors in as well) so this can't be used for points/lines
- */
-void PathTessellator::expandBoundsForStroke(SkRect& bounds, const SkPaint* paint) {
- if (paint->getStyle() != SkPaint::kFill_Style) {
- float outset = paint->getStrokeWidth() * 0.5f;
- if (outset == 0) outset = 0.5f; // account for hairline
- bounds.outset(outset, outset);
+void PathTessellator::extractTessellationScales(const Matrix4& transform,
+ float* scaleX, float* scaleY) {
+ *scaleX = 1.0f;
+ *scaleY = 1.0f;
+ if (CC_UNLIKELY(!transform.isPureTranslate())) {
+ float m00 = transform.data[Matrix4::kScaleX];
+ float m01 = transform.data[Matrix4::kSkewY];
+ float m10 = transform.data[Matrix4::kSkewX];
+ float m11 = transform.data[Matrix4::kScaleY];
+ *scaleX = sqrt(m00 * m00 + m01 * m01);
+ *scaleY = sqrt(m10 * m10 + m11 * m11);
}
}
@@ -94,18 +96,15 @@ public:
halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) {
// compute inverse scales
if (CC_UNLIKELY(!transform.isPureTranslate())) {
- float m00 = transform.data[Matrix4::kScaleX];
- float m01 = transform.data[Matrix4::kSkewY];
- float m10 = transform.data[Matrix4::kSkewX];
- float m11 = transform.data[Matrix4::kScaleY];
- float scaleX = sqrt(m00 * m00 + m01 * m01);
- float scaleY = sqrt(m10 * m10 + m11 * m11);
+ float scaleX, scaleY;
+ PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
}
if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
2 * halfStrokeWidth < inverseScaleX) {
+ // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline.
maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
halfStrokeWidth = 0.0f;
}
@@ -159,10 +158,10 @@ public:
* Outset the bounds of point data (for line endpoints or points) to account for AA stroke
* geometry.
*/
- void expandBoundsForStrokeAA(SkRect& bounds) const {
+ void expandBoundsForStroke(Rect* bounds) const {
float outset = halfStrokeWidth;
if (outset == 0) outset = 0.5f;
- bounds.outset(outset * inverseScaleX + Vertex::GeometryFudgeFactor(),
+ bounds->outset(outset * inverseScaleX + Vertex::GeometryFudgeFactor(),
outset * inverseScaleY + Vertex::GeometryFudgeFactor());
}
};
@@ -778,21 +777,25 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
}
}
+
+ Rect bounds(path.getBounds());
+ paintInfo.expandBoundsForStroke(&bounds);
+ vertexBuffer.setBounds(bounds);
}
-static void expandRectToCoverVertex(SkRect& rect, float x, float y) {
- rect.fLeft = fminf(rect.fLeft, x);
- rect.fTop = fminf(rect.fTop, y);
- rect.fRight = fmaxf(rect.fRight, x);
- rect.fBottom = fmaxf(rect.fBottom, y);
+static void expandRectToCoverVertex(Rect& rect, float x, float y) {
+ rect.left = fminf(rect.left, x);
+ rect.top = fminf(rect.top, y);
+ rect.right = fmaxf(rect.right, x);
+ rect.bottom = fmaxf(rect.bottom, y);
}
-static void expandRectToCoverVertex(SkRect& rect, const Vertex& vertex) {
+static void expandRectToCoverVertex(Rect& rect, const Vertex& vertex) {
expandRectToCoverVertex(rect, vertex.x, vertex.y);
}
template <class TYPE>
static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
- const float* points, int count, SkRect& bounds) {
+ const float* points, int count, Rect& bounds) {
bounds.set(points[0], points[1], points[0], points[1]);
int numPoints = count / 2;
@@ -807,7 +810,7 @@ static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
}
void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint,
- const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer) {
+ const mat4& transform, VertexBuffer& vertexBuffer) {
const PaintInfo paintInfo(paint, transform);
// determine point shape
@@ -830,6 +833,7 @@ void PathTessellator::tessellatePoints(const float* points, int count, const SkP
if (!outlineVertices.size()) return;
+ Rect bounds;
// tessellate, then duplicate outline across points
int numPoints = count / 2;
VertexBuffer tempBuffer;
@@ -843,12 +847,12 @@ void PathTessellator::tessellatePoints(const float* points, int count, const SkP
}
// expand bounds from vertex coords to pixel data
- paintInfo.expandBoundsForStrokeAA(bounds);
-
+ paintInfo.expandBoundsForStroke(&bounds);
+ vertexBuffer.setBounds(bounds);
}
void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint,
- const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer) {
+ const mat4& transform, VertexBuffer& vertexBuffer) {
ATRACE_CALL();
const PaintInfo paintInfo(paint, transform);
@@ -868,6 +872,7 @@ void PathTessellator::tessellateLines(const float* points, int count, const SkPa
tempVertices.push();
tempVertices.push();
Vertex* tempVerticesData = tempVertices.editArray();
+ Rect bounds;
bounds.set(points[0], points[1], points[0], points[1]);
for (int i = 0; i < count; i += 4) {
Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]);
@@ -892,7 +897,8 @@ void PathTessellator::tessellateLines(const float* points, int count, const SkPa
}
// expand bounds from vertex coords to pixel data
- paintInfo.expandBoundsForStrokeAA(bounds);
+ paintInfo.expandBoundsForStroke(&bounds);
+ vertexBuffer.setBounds(bounds);
}
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h
index a215b7a..f033470 100644
--- a/libs/hwui/PathTessellator.h
+++ b/libs/hwui/PathTessellator.h
@@ -29,7 +29,15 @@ namespace uirenderer {
class PathTessellator {
public:
- static void expandBoundsForStroke(SkRect& bounds, const SkPaint* paint);
+ /**
+ * Populates scaleX and scaleY with the 'tessellation scale' of the transform - the effective X
+ * and Y scales that tessellation will take into account when generating the 1.0 pixel thick
+ * ramp.
+ *
+ * Two instances of the same shape (size, paint, etc.) will only generate the same vertices if
+ * their tessellation scales are equal.
+ */
+ static void extractTessellationScales(const Matrix4& transform, float* scaleX, float* scaleY);
/**
* Populates a VertexBuffer with a tessellated approximation of the input convex path, as a single
@@ -54,11 +62,10 @@ public:
* @param paint The paint the points will be drawn with indicating AA, stroke width & cap
* @param transform The transform the points will be drawn with, used to drive stretch-aware path
* vertex approximation, and correct AA ramp offsetting
- * @param bounds An output rectangle, which returns the total area covered by the output buffer
* @param vertexBuffer The output buffer
*/
static void tessellatePoints(const float* points, int count, const SkPaint* paint,
- const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer);
+ const mat4& transform, VertexBuffer& vertexBuffer);
/**
* Populates a VertexBuffer with a tessellated approximation of lines as a single triangle
@@ -69,11 +76,10 @@ public:
* @param paint The paint the lines will be drawn with indicating AA, stroke width & cap
* @param transform The transform the points will be drawn with, used to drive stretch-aware path
* vertex approximation, and correct AA ramp offsetting
- * @param bounds An output rectangle, which returns the total area covered by the output buffer
* @param vertexBuffer The output buffer
*/
static void tessellateLines(const float* points, int count, const SkPaint* paint,
- const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer);
+ const mat4& transform, VertexBuffer& vertexBuffer);
/**
* Approximates a convex, CW outline into a Vector of 2d vertices.
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 12241b8..feaee8e 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -176,6 +176,7 @@ enum DebugLevel {
#define PROPERTY_RENDER_BUFFER_CACHE_SIZE "ro.hwui.r_buffer_cache_size"
#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
#define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size"
+#define PROPERTY_VERTEX_CACHE_SIZE "ro.hwui.vertex_cache_size"
#define PROPERTY_PATCH_CACHE_SIZE "ro.hwui.patch_cache_size"
#define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size"
#define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size"
@@ -222,6 +223,7 @@ enum DebugLevel {
#define DEFAULT_LAYER_CACHE_SIZE 16.0f
#define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f
#define DEFAULT_PATH_CACHE_SIZE 10.0f
+#define DEFAULT_VERTEX_CACHE_SIZE 1.0f
#define DEFAULT_PATCH_CACHE_SIZE 128 // in kB
#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 2ddbbd7..846ebdc 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -186,6 +186,13 @@ public:
bottom += delta;
}
+ void outset(float xdelta, float ydelta) {
+ left -= xdelta;
+ top -= ydelta;
+ right += xdelta;
+ bottom += ydelta;
+ }
+
/**
* Similar to snapToPixelBoundaries, but estimates bounds conservatively to handle GL rounding
* errors.
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 2f714a1..30c6f5d 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -34,7 +34,7 @@ static inline T max(T a, T b) {
return a > b ? a : b;
}
-VertexBufferMode ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque,
+void ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque,
const Vector3* casterPolygon, int casterVertexCount,
const Vector3& centroid3d, const Rect& casterBounds,
const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer) {
@@ -57,16 +57,15 @@ VertexBufferMode ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque,
#if DEBUG_SHADOW
ALOGD("Ambient shadow is out of clip rect!");
#endif
- return kVertexBufferMode_OnePolyRingShadow;
+ return;
}
- return AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon,
+ AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon,
casterVertexCount, centroid3d, heightFactor, geomFactor,
shadowVertexBuffer);
-
}
-VertexBufferMode ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
+void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
const Vector3* casterPolygon, int casterVertexCount,
const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius,
const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer) {
@@ -107,19 +106,17 @@ VertexBufferMode ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
#if DEBUG_SHADOW
ALOGD("Spot shadow is out of clip rect!");
#endif
- return kVertexBufferMode_OnePolyRingShadow;
+ return;
}
- VertexBufferMode mode = SpotShadow::createSpotShadow(isCasterOpaque,
+ SpotShadow::createSpotShadow(isCasterOpaque,
casterPolygon, casterVertexCount, adjustedLightCenter, lightRadius,
lightVertexCount, shadowVertexBuffer);
-
#if DEBUG_SHADOW
if(shadowVertexBuffer.getVertexCount() <= 0) {
ALOGD("Spot shadow generation failed %d", shadowVertexBuffer.getVertexCount());
}
#endif
- return mode;
}
void ShadowTessellator::generateShadowIndices(uint16_t* shadowIndices) {
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
index a1606ad..cb65df5 100644
--- a/libs/hwui/ShadowTessellator.h
+++ b/libs/hwui/ShadowTessellator.h
@@ -66,12 +66,12 @@ namespace uirenderer {
class ShadowTessellator {
public:
- static VertexBufferMode tessellateAmbientShadow(bool isCasterOpaque,
+ static void tessellateAmbientShadow(bool isCasterOpaque,
const Vector3* casterPolygon, int casterVertexCount,
const Vector3& centroid3d, const Rect& casterBounds,
const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer);
- static VertexBufferMode tessellateSpotShadow(bool isCasterOpaque,
+ static void tessellateSpotShadow(bool isCasterOpaque,
const Vector3* casterPolygon, int casterVertexCount,
const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius,
const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer);
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index 3ebe7b4..06f6204 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -500,14 +500,14 @@ void SpotShadow::computeLightPolygon(int points, const Vector3& lightCenter,
* empty strip if error.
*
*/
-VertexBufferMode SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3* poly,
+void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3* poly,
int polyLength, const Vector3& lightCenter, float lightSize,
int lightVertexCount, VertexBuffer& retStrips) {
Vector3 light[lightVertexCount * 3];
computeLightPolygon(lightVertexCount, lightCenter, lightSize, light);
computeSpotShadow(isCasterOpaque, light, lightVertexCount, lightCenter, poly,
polyLength, retStrips);
- return kVertexBufferMode_TwoPolyRingShadow;
+ retStrips.setMode(VertexBuffer::kTwoPolyRingShadow);
}
/**
diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h
index fb3e6d5..d65ea89 100644
--- a/libs/hwui/SpotShadow.h
+++ b/libs/hwui/SpotShadow.h
@@ -26,7 +26,7 @@ namespace uirenderer {
class SpotShadow {
public:
- static VertexBufferMode createSpotShadow(bool isCasterOpaque, const Vector3* poly,
+ static void createSpotShadow(bool isCasterOpaque, const Vector3* poly,
int polyLength, const Vector3& lightCenter, float lightSize,
int lightVertexCount, VertexBuffer& retStrips);
diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp
index 90039e9..f2e28e1 100644
--- a/libs/hwui/StatefulBaseRenderer.cpp
+++ b/libs/hwui/StatefulBaseRenderer.cpp
@@ -101,6 +101,10 @@ void StatefulBaseRenderer::restoreToCount(int saveCount) {
// Matrix
///////////////////////////////////////////////////////////////////////////////
+void StatefulBaseRenderer::getMatrix(Matrix4* matrix) const {
+ matrix->load(*(mSnapshot->transform));
+}
+
void StatefulBaseRenderer::getMatrix(SkMatrix* matrix) const {
mSnapshot->transform->copyTo(*matrix);
}
diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h
index 057006b..dbb1d85 100644
--- a/libs/hwui/StatefulBaseRenderer.h
+++ b/libs/hwui/StatefulBaseRenderer.h
@@ -69,6 +69,7 @@ public:
// int alpha, SkXfermode::Mode mode, int flags);
// Matrix
+ void getMatrix(Matrix4* outMatrix) const;
virtual void getMatrix(SkMatrix* outMatrix) const;
virtual void translate(float dx, float dy, float dz = 0.0f);
virtual void rotate(float degrees);
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
new file mode 100644
index 0000000..41cc9d2
--- /dev/null
+++ b/libs/hwui/TessellationCache.cpp
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include <utils/JenkinsHash.h>
+#include <utils/Trace.h>
+
+#include "Caches.h"
+#include "OpenGLRenderer.h"
+#include "PathTessellator.h"
+#include "ShadowTessellator.h"
+#include "TessellationCache.h"
+
+#include "thread/Signal.h"
+#include "thread/Task.h"
+#include "thread/TaskProcessor.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache entries
+///////////////////////////////////////////////////////////////////////////////
+
+TessellationCache::Description::Description()
+ : type(kNone)
+ , cap(SkPaint::kDefault_Cap)
+ , style(SkPaint::kFill_Style)
+ , strokeWidth(1.0f) {
+ memset(&shape, 0, sizeof(Shape));
+}
+
+TessellationCache::Description::Description(Type type)
+ : type(type)
+ , cap(SkPaint::kDefault_Cap)
+ , style(SkPaint::kFill_Style)
+ , strokeWidth(1.0f) {
+ memset(&shape, 0, sizeof(Shape));
+}
+
+TessellationCache::Description::Description(Type type, const SkPaint* paint)
+ : type(type)
+ , cap(paint->getStrokeCap())
+ , style(paint->getStyle())
+ , strokeWidth(paint->getStrokeWidth()) {
+ memset(&shape, 0, sizeof(Shape));
+}
+
+hash_t TessellationCache::Description::hash() const {
+ uint32_t hash = JenkinsHashMix(0, type);
+ hash = JenkinsHashMix(hash, cap);
+ hash = JenkinsHashMix(hash, style);
+ hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
+ hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape));
+ return JenkinsHashWhiten(hash);
+}
+
+TessellationCache::ShadowDescription::ShadowDescription()
+ : nodeKey(NULL) {
+ memset(&matrixData, 0, 16 * sizeof(float));
+}
+
+TessellationCache::ShadowDescription::ShadowDescription(const void* nodeKey, const Matrix4* drawTransform)
+ : nodeKey(nodeKey) {
+ memcpy(&matrixData, drawTransform->data, 16 * sizeof(float));
+}
+
+hash_t TessellationCache::ShadowDescription::hash() const {
+ uint32_t hash = JenkinsHashMixBytes(0, (uint8_t*) &nodeKey, sizeof(const void*));
+ hash = JenkinsHashMixBytes(hash, (uint8_t*) &matrixData, 16 * sizeof(float));
+ return JenkinsHashWhiten(hash);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// General purpose tessellation task processing
+///////////////////////////////////////////////////////////////////////////////
+
+class TessellationCache::TessellationTask : public Task<VertexBuffer*> {
+public:
+ TessellationTask(Tessellator tessellator, const Description& description,
+ const SkPaint* paint)
+ : tessellator(tessellator)
+ , description(description)
+ , paint(*paint) {
+ }
+
+ ~TessellationTask() {}
+
+ Tessellator tessellator;
+ Description description;
+
+ //copied, since input paint may not be immutable
+ const SkPaint paint;
+};
+
+class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> {
+public:
+ TessellationProcessor(Caches& caches)
+ : TaskProcessor<VertexBuffer*>(&caches.tasks) {}
+ ~TessellationProcessor() {}
+
+ virtual void onProcess(const sp<Task<VertexBuffer*> >& task) {
+ TessellationTask* t = static_cast<TessellationTask*>(task.get());
+ ATRACE_NAME("shape tessellation");
+ VertexBuffer* buffer = t->tessellator(t->description, t->paint);
+ t->setResult(buffer);
+ }
+};
+
+struct TessellationCache::Buffer {
+public:
+ Buffer(const sp<Task<VertexBuffer*> >& task)
+ : mTask(task)
+ , mBuffer(NULL) {
+ }
+
+ ~Buffer() {
+ mTask.clear();
+ delete mBuffer;
+ }
+
+ unsigned int getSize() {
+ blockOnPrecache();
+ return mBuffer->getSize();
+ }
+
+ const VertexBuffer* getVertexBuffer() {
+ blockOnPrecache();
+ return mBuffer;
+ }
+
+private:
+ void blockOnPrecache() {
+ if (mTask != NULL) {
+ mBuffer = mTask->getResult();
+ LOG_ALWAYS_FATAL_IF(mBuffer == NULL, "Failed to precache");
+ mTask.clear();
+ }
+ }
+ sp<Task<VertexBuffer*> > mTask;
+ VertexBuffer* mBuffer;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Shadow tessellation task processing
+///////////////////////////////////////////////////////////////////////////////
+
+class ShadowTask : public Task<TessellationCache::vertexBuffer_pair_t*> {
+public:
+ ShadowTask(const Matrix4* drawTransform, const Rect& localClip, bool opaque,
+ const SkPath* casterPerimeter, const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius)
+ : drawTransform(drawTransform)
+ , localClip(localClip)
+ , opaque(opaque)
+ , casterPerimeter(casterPerimeter)
+ , transformXY(transformXY)
+ , transformZ(transformZ)
+ , lightCenter(lightCenter)
+ , lightRadius(lightRadius) {
+ }
+
+ ~ShadowTask() {
+ TessellationCache::vertexBuffer_pair_t* bufferPair = getResult();
+ delete bufferPair->getFirst();
+ delete bufferPair->getSecond();
+ delete bufferPair;
+ }
+
+ // Note - only the localClip is deep copied, since other pointers point at Allocator controlled
+ // objects, which are safe for the entire frame
+ const Matrix4* drawTransform;
+ const Rect localClip;
+ bool opaque;
+ const SkPath* casterPerimeter;
+ const Matrix4* transformXY;
+ const Matrix4* transformZ;
+ const Vector3 lightCenter;
+ const float lightRadius;
+};
+
+static void mapPointFakeZ(Vector3& point, const mat4* transformXY, const mat4* transformZ) {
+ // map z coordinate with true 3d matrix
+ point.z = transformZ->mapZ(point);
+
+ // map x,y coordinates with draw/Skia matrix
+ transformXY->mapPoint(point.x, point.y);
+}
+
+static void tessellateShadows(
+ const Matrix4* drawTransform, const Rect* localClip,
+ bool isCasterOpaque, const SkPath* casterPerimeter,
+ const Matrix4* casterTransformXY, const Matrix4* casterTransformZ,
+ const Vector3& lightCenter, float lightRadius,
+ VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer) {
+
+ // tessellate caster outline into a 2d polygon
+ Vector<Vertex> casterVertices2d;
+ const float casterRefinementThresholdSquared = 20.0f; // TODO: experiment with this value
+ PathTessellator::approximatePathOutlineVertices(*casterPerimeter,
+ casterRefinementThresholdSquared, casterVertices2d);
+ if (!ShadowTessellator::isClockwisePath(*casterPerimeter)) {
+ ShadowTessellator::reverseVertexArray(casterVertices2d.editArray(),
+ casterVertices2d.size());
+ }
+
+ if (casterVertices2d.size() == 0) return;
+
+ // map 2d caster poly into 3d
+ const int casterVertexCount = casterVertices2d.size();
+ Vector3 casterPolygon[casterVertexCount];
+ float minZ = FLT_MAX;
+ float maxZ = -FLT_MAX;
+ for (int i = 0; i < casterVertexCount; i++) {
+ const Vertex& point2d = casterVertices2d[i];
+ casterPolygon[i] = Vector3(point2d.x, point2d.y, 0);
+ mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
+ minZ = fmin(minZ, casterPolygon[i].z);
+ maxZ = fmax(maxZ, casterPolygon[i].z);
+ }
+
+ // map the centroid of the caster into 3d
+ Vector2 centroid = ShadowTessellator::centroid2d(
+ reinterpret_cast<const Vector2*>(casterVertices2d.array()),
+ casterVertexCount);
+ Vector3 centroid3d(centroid.x, centroid.y, 0);
+ mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ);
+
+ // if the caster intersects the z=0 plane, lift it in Z so it doesn't
+ if (minZ < SHADOW_MIN_CASTER_Z) {
+ float casterLift = SHADOW_MIN_CASTER_Z - minZ;
+ for (int i = 0; i < casterVertexCount; i++) {
+ casterPolygon[i].z += casterLift;
+ }
+ centroid3d.z += casterLift;
+ }
+
+ // Check whether we want to draw the shadow at all by checking the caster's bounds against clip.
+ // We only have ortho projection, so we can just ignore the Z in caster for
+ // simple rejection calculation.
+ Rect casterBounds(casterPerimeter->getBounds());
+ casterTransformXY->mapRect(casterBounds);
+
+ // actual tessellation of both shadows
+ ShadowTessellator::tessellateAmbientShadow(
+ isCasterOpaque, casterPolygon, casterVertexCount, centroid3d,
+ casterBounds, *localClip, maxZ, ambientBuffer);
+
+ ShadowTessellator::tessellateSpotShadow(
+ isCasterOpaque, casterPolygon, casterVertexCount,
+ *drawTransform, lightCenter, lightRadius, casterBounds, *localClip,
+ spotBuffer);
+
+ // TODO: set ambientBuffer & spotBuffer's bounds for correct layer damage
+}
+
+class ShadowProcessor : public TaskProcessor<TessellationCache::vertexBuffer_pair_t*> {
+public:
+ ShadowProcessor(Caches& caches)
+ : TaskProcessor<TessellationCache::vertexBuffer_pair_t*>(&caches.tasks) {}
+ ~ShadowProcessor() {}
+
+ virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t*> >& task) {
+ ShadowTask* t = static_cast<ShadowTask*>(task.get());
+ ATRACE_NAME("shadow tessellation");
+
+ VertexBuffer* ambientBuffer = new VertexBuffer;
+ VertexBuffer* spotBuffer = new VertexBuffer;
+ tessellateShadows(t->drawTransform, &t->localClip, t->opaque, t->casterPerimeter,
+ t->transformXY, t->transformZ, t->lightCenter, t->lightRadius,
+ *ambientBuffer, *spotBuffer);
+
+ t->setResult(new TessellationCache::vertexBuffer_pair_t(ambientBuffer, spotBuffer));
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache constructor/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+TessellationCache::TessellationCache()
+ : mSize(0)
+ , mMaxSize(MB(DEFAULT_VERTEX_CACHE_SIZE))
+ , mCache(LruCache<Description, Buffer*>::kUnlimitedCapacity)
+ , mShadowCache(LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_VERTEX_CACHE_SIZE, property, NULL) > 0) {
+ INIT_LOGD(" Setting %s cache size to %sMB", name, property);
+ setMaxSize(MB(atof(property)));
+ } else {
+ INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_VERTEX_CACHE_SIZE);
+ }
+
+ mCache.setOnEntryRemovedListener(&mBufferRemovedListener);
+ mShadowCache.setOnEntryRemovedListener(&mBufferPairRemovedListener);
+ mDebugEnabled = readDebugLevel() & kDebugCaches;
+}
+
+TessellationCache::~TessellationCache() {
+ mCache.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t TessellationCache::getSize() {
+ LruCache<Description, Buffer*>::Iterator iter(mCache);
+ uint32_t size = 0;
+ while (iter.next()) {
+ size += iter.value()->getSize();
+ }
+ return size;
+}
+
+uint32_t TessellationCache::getMaxSize() {
+ return mMaxSize;
+}
+
+void TessellationCache::setMaxSize(uint32_t maxSize) {
+ mMaxSize = maxSize;
+ while (mSize > mMaxSize) {
+ mCache.removeOldest();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+
+void TessellationCache::trim() {
+ uint32_t size = getSize();
+ while (size > mMaxSize) {
+ size -= mCache.peekOldestValue()->getSize();
+ mCache.removeOldest();
+ }
+ mShadowCache.clear();
+}
+
+void TessellationCache::clear() {
+ mCache.clear();
+ mShadowCache.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void TessellationCache::BufferRemovedListener::operator()(Description& description,
+ Buffer*& buffer) {
+ delete buffer;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Shadows
+///////////////////////////////////////////////////////////////////////////////
+
+void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
+ bool opaque, const SkPath* casterPerimeter,
+ const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius) {
+ ShadowDescription key(casterPerimeter, drawTransform);
+
+ sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque,
+ casterPerimeter, transformXY, transformZ, lightCenter, lightRadius);
+ if (mShadowProcessor == NULL) {
+ mShadowProcessor = new ShadowProcessor(Caches::getInstance());
+ }
+ mShadowProcessor->add(task);
+
+ task->incStrong(NULL); // not using sp<>s, so manually ref while in the cache
+ mShadowCache.put(key, task.get());
+}
+
+void TessellationCache::getShadowBuffers(const Matrix4* drawTransform, const Rect& localClip,
+ bool opaque, const SkPath* casterPerimeter,
+ const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius, vertexBuffer_pair_t& outBuffers) {
+ ShadowDescription key(casterPerimeter, drawTransform);
+ ShadowTask* task = static_cast<ShadowTask*>(mShadowCache.get(key));
+ if (!task) {
+ precacheShadows(drawTransform, localClip, opaque, casterPerimeter,
+ transformXY, transformZ, lightCenter, lightRadius);
+ task = static_cast<ShadowTask*>(mShadowCache.get(key));
+ }
+ LOG_ALWAYS_FATAL_IF(task == NULL, "shadow not precached");
+ outBuffers = *(task->getResult());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Tessellation precaching
+///////////////////////////////////////////////////////////////////////////////
+
+static VertexBuffer* tessellatePath(const SkPath& path, const SkPaint* paint,
+ float scaleX, float scaleY) {
+ VertexBuffer* buffer = new VertexBuffer();
+ Matrix4 matrix;
+ matrix.loadScale(scaleX, scaleY, 1);
+ PathTessellator::tessellatePath(path, paint, matrix, *buffer);
+ return buffer;
+}
+
+TessellationCache::Buffer* TessellationCache::getOrCreateBuffer(
+ const Description& entry, Tessellator tessellator, const SkPaint* paint) {
+ Buffer* buffer = mCache.get(entry);
+ if (!buffer) {
+ // not cached, enqueue a task to fill the buffer
+ sp<TessellationTask> task = new TessellationTask(tessellator, entry, paint);
+ buffer = new Buffer(task);
+
+ if (mProcessor == NULL) {
+ mProcessor = new TessellationProcessor(Caches::getInstance());
+ }
+ mProcessor->add(task);
+ mCache.put(entry, buffer);
+ }
+ return buffer;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Rounded rects
+///////////////////////////////////////////////////////////////////////////////
+
+static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description,
+ const SkPaint& paint) {
+ SkRect rect = SkRect::MakeWH(description.shape.roundRect.mWidth,
+ description.shape.roundRect.mHeight);
+ float rx = description.shape.roundRect.mRx;
+ float ry = description.shape.roundRect.mRy;
+ if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) {
+ float outset = paint.getStrokeWidth() / 2;
+ rect.outset(outset, outset);
+ rx += outset;
+ ry += outset;
+ }
+ SkPath path;
+ path.addRoundRect(rect, rx, ry);
+ return tessellatePath(path, &paint,
+ description.shape.roundRect.mScaleX, description.shape.roundRect.mScaleY);
+}
+
+TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(const Matrix4& transform,
+ float width, float height, float rx, float ry, const SkPaint* paint) {
+ Description entry(Description::kRoundRect, paint);
+ entry.shape.roundRect.mWidth = width;
+ entry.shape.roundRect.mHeight = height;
+ entry.shape.roundRect.mRx = rx;
+ entry.shape.roundRect.mRy = ry;
+ PathTessellator::extractTessellationScales(transform,
+ &entry.shape.roundRect.mScaleX, &entry.shape.roundRect.mScaleY);
+
+ return getOrCreateBuffer(entry, &tessellateRoundRect, paint);
+}
+const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform,
+ float width, float height, float rx, float ry, const SkPaint* paint) {
+ return getRoundRectBuffer(transform, width, height, rx, ry, paint)->getVertexBuffer();
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h
new file mode 100644
index 0000000..8f37230
--- /dev/null
+++ b/libs/hwui/TessellationCache.h
@@ -0,0 +1,193 @@
+/*
+ * 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_TESSELLATION_CACHE_H
+#define ANDROID_HWUI_TESSELLATION_CACHE_H
+
+#include <utils/LruCache.h>
+#include <utils/Mutex.h>
+#include <utils/Vector.h>
+
+#include "Debug.h"
+#include "utils/Macros.h"
+#include "utils/Pair.h"
+#include "VertexBuffer.h"
+
+class SkBitmap;
+class SkCanvas;
+class SkPaint;
+class SkPath;
+struct SkRect;
+
+namespace android {
+namespace uirenderer {
+
+class Caches;
+
+///////////////////////////////////////////////////////////////////////////////
+// Classes
+///////////////////////////////////////////////////////////////////////////////
+
+class TessellationCache {
+public:
+ typedef Pair<VertexBuffer*, VertexBuffer*> vertexBuffer_pair_t;
+
+ struct Description {
+ DESCRIPTION_TYPE(Description);
+ enum Type {
+ kNone,
+ kRoundRect,
+ kAmbientShadow,
+ kSpotShadow
+ };
+
+ Type type;
+ SkPaint::Cap cap;
+ SkPaint::Style style;
+ float strokeWidth;
+ union Shape {
+ struct RoundRect {
+ float mScaleX;
+ float mScaleY;
+ float mWidth;
+ float mHeight;
+ float mRx;
+ float mRy;
+ } roundRect;
+ } shape;
+
+ Description();
+ Description(Type type);
+ Description(Type type, const SkPaint* paint);
+ hash_t hash() const;
+ };
+
+ struct ShadowDescription {
+ DESCRIPTION_TYPE(ShadowDescription);
+ const void* nodeKey;
+ float matrixData[16];
+
+ ShadowDescription();
+ ShadowDescription(const void* nodeKey, const Matrix4* drawTransform);
+ hash_t hash() const;
+ };
+
+ TessellationCache();
+ ~TessellationCache();
+
+ /**
+ * Clears the cache. This causes all TessellationBuffers to be deleted.
+ */
+ void clear();
+
+ /**
+ * Sets the maximum size of the cache in bytes.
+ */
+ void setMaxSize(uint32_t maxSize);
+ /**
+ * Returns the maximum size of the cache in bytes.
+ */
+ uint32_t getMaxSize();
+ /**
+ * Returns the current size of the cache in bytes.
+ */
+ uint32_t getSize();
+
+ /**
+ * Trims the contents of the cache, removing items until it's under its
+ * specified limit.
+ *
+ * Trimming is used for caches that support pre-caching from a worker
+ * thread. During pre-caching the maximum limit of the cache can be
+ * exceeded for the duration of the frame. It is therefore required to
+ * trim the cache at the end of the frame to keep the total amount of
+ * memory used under control.
+ *
+ * Also removes transient Shadow VertexBuffers, which aren't cached between frames.
+ */
+ void trim();
+
+ // TODO: precache/get for Oval, Lines, Points, etc.
+
+ void precacheRoundRect(const Matrix4& transform,
+ float width, float height, float rx, float ry, const SkPaint* paint) {
+ getRoundRectBuffer(transform, width, height, rx, ry, paint);
+ }
+ const VertexBuffer* getRoundRect(const Matrix4& transform,
+ float width, float height, float rx, float ry, const SkPaint* paint);
+
+ void precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
+ bool opaque, const SkPath* casterPerimeter,
+ const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius);
+
+ void getShadowBuffers(const Matrix4* drawTransform, const Rect& localClip,
+ bool opaque, const SkPath* casterPerimeter,
+ const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius,
+ vertexBuffer_pair_t& outBuffers);
+
+private:
+ class Buffer;
+ class TessellationTask;
+ class TessellationProcessor;
+
+
+ typedef VertexBuffer* (*Tessellator)(const Description&, const SkPaint&);
+
+ Buffer* getRoundRectBuffer(const Matrix4& transform,
+ float width, float height, float rx, float ry, const SkPaint* paint);
+
+ Buffer* getOrCreateBuffer(const Description& entry,
+ Tessellator tessellator, const SkPaint* paint);
+
+ uint32_t mSize;
+ uint32_t mMaxSize;
+
+ bool mDebugEnabled;
+
+ mutable Mutex mLock;
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // General tessellation caching
+ ///////////////////////////////////////////////////////////////////////////////
+ sp<TaskProcessor<VertexBuffer*> > mProcessor;
+ LruCache<Description, Buffer*> mCache;
+ class BufferRemovedListener : public OnEntryRemoved<Description, Buffer*> {
+ void operator()(Description& description, Buffer*& buffer);
+ };
+ BufferRemovedListener mBufferRemovedListener;
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // Shadow tessellation caching
+ ///////////////////////////////////////////////////////////////////////////////
+ sp<TaskProcessor<vertexBuffer_pair_t*> > mShadowProcessor;
+
+ // holds a pointer, and implicit strong ref to each shadow task of the frame
+ LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*> mShadowCache;
+ class BufferPairRemovedListener : public OnEntryRemoved<ShadowDescription, Task<vertexBuffer_pair_t*>*> {
+ void operator()(ShadowDescription& description, Task<vertexBuffer_pair_t*>*& bufferPairTask) {
+ bufferPairTask->decStrong(NULL);
+ }
+ };
+ BufferPairRemovedListener mBufferPairRemovedListener;
+
+}; // class TessellationCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_PATH_CACHE_H
diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h
index 8b6872e..55d566b 100644
--- a/libs/hwui/VertexBuffer.h
+++ b/libs/hwui/VertexBuffer.h
@@ -23,10 +23,18 @@ namespace uirenderer {
class VertexBuffer {
public:
- VertexBuffer():
- mBuffer(0),
- mVertexCount(0),
- mCleanupMethod(NULL)
+ enum Mode {
+ kStandard = 0,
+ kOnePolyRingShadow = 1,
+ kTwoPolyRingShadow = 2
+ };
+
+ VertexBuffer()
+ : mBuffer(0)
+ , mVertexCount(0)
+ , mByteCount(0)
+ , mMode(kStandard)
+ , mCleanupMethod(NULL)
{}
~VertexBuffer() {
@@ -37,7 +45,7 @@ public:
This should be the only method used by the Tessellator. Subsequent calls to
alloc will allocate space within the first allocation (useful if you want to
eventually allocate multiple regions within a single VertexBuffer, such as
- with PathTessellator::tesselateLines())
+ with PathTessellator::tessellateLines())
*/
template <class TYPE>
TYPE* alloc(int vertexCount) {
@@ -52,6 +60,7 @@ public:
return reallocBuffer;
}
mVertexCount = vertexCount;
+ mByteCount = mVertexCount * sizeof(TYPE);
mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount];
mCleanupMethod = &(cleanup<TYPE>);
@@ -71,7 +80,13 @@ public:
}
const void* getBuffer() const { return mBuffer; }
+ const Rect& getBounds() const { return mBounds; }
unsigned int getVertexCount() const { return mVertexCount; }
+ unsigned int getSize() const { return mByteCount; }
+ Mode getMode() const { return mMode; }
+
+ void setBounds(Rect bounds) { mBounds = bounds; }
+ void setMode(Mode mode) { mMode = mode; }
template <class TYPE>
void createDegenerateSeparators(int allocSize) {
@@ -88,8 +103,11 @@ private:
delete[] (TYPE*)buffer;
}
+ Rect mBounds;
void* mBuffer;
unsigned int mVertexCount;
+ unsigned int mByteCount;
+ Mode mMode;
void* mReallocBuffer; // used for multi-allocation
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index 189895c..3d2b0d9 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -16,9 +16,10 @@
#include <sys/sysinfo.h>
+#include "TaskManager.h"
#include "Task.h"
#include "TaskProcessor.h"
-#include "TaskManager.h"
+#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
@@ -31,7 +32,8 @@ TaskManager::TaskManager() {
// Get the number of available CPUs. This value does not change over time.
int cpuCount = sysconf(_SC_NPROCESSORS_CONF);
- for (int i = 0; i < cpuCount / 2; i++) {
+ int workerCount = MathUtils::max(1, cpuCount / 2);
+ for (int i = 0; i < workerCount; i++) {
String8 name;
name.appendFormat("hwuiTask%d", i + 1);
mThreads.add(new WorkerThread(name));
diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
index 14a3ec0..5b7c87c 100644
--- a/libs/hwui/utils/Macros.h
+++ b/libs/hwui/utils/Macros.h
@@ -21,5 +21,12 @@
Type(const Type&); \
void operator=(const Type&)
+#define DESCRIPTION_TYPE(Type) \
+ int compare(const Type& rhs) const { return memcmp(this, &rhs, sizeof(Type));} \
+ bool operator==(const Type& other) const { return compare(other) == 0; } \
+ bool operator!=(const Type& other) const { return compare(other) != 0; } \
+ friend inline int strictly_order_type(const Type& lhs, const Type& rhs) { return lhs.compare(rhs) < 0; } \
+ friend inline int compare_type(const Type& lhs, const Type& rhs) { return lhs.compare(rhs); } \
+ friend inline hash_t hash_type(const Type& entry) { return entry.hash(); }
#endif /* MACROS_H */
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 997acde2..65f1663 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -38,6 +38,10 @@ public:
return isZero(valueA - valueB);
}
+ inline static int max(int a, int b) {
+ return a > b ? a : b;
+ }
+
inline static int min(int a, int b) {
return a < b ? a : b;
}