Tessellate on worker threads
Tessellate and cache (where possible) shadow and round rect tessellation tasks. Change-Id: I2cfda8e11d83d51ea74af871235cf26e8f831d40
This commit is contained in:
@@ -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(¢roidXYA, 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
|
||||
SpotShadow.cpp \
|
||||
StatefulBaseRenderer.cpp \
|
||||
Stencil.cpp \
|
||||
TessellationCache.cpp \
|
||||
Texture.cpp \
|
||||
TextureCache.cpp \
|
||||
TextDropShadowCache.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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,9 +658,17 @@ 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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
476
libs/hwui/TessellationCache.cpp
Normal file
476
libs/hwui/TessellationCache.cpp
Normal file
@@ -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
|
||||
193
libs/hwui/TessellationCache.h
Normal file
193
libs/hwui/TessellationCache.h
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user