diff options
Diffstat (limited to 'libs')
-rw-r--r-- | libs/hwui/AmbientShadow.cpp | 547 | ||||
-rw-r--r-- | libs/hwui/AmbientShadow.h | 15 | ||||
-rwxr-xr-x | libs/hwui/OpenGLRenderer.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/Vector.h | 17 | ||||
-rw-r--r-- | libs/hwui/VertexBuffer.h | 39 |
5 files changed, 348 insertions, 274 deletions
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp index 9cc83ed..7834ef8 100644 --- a/libs/hwui/AmbientShadow.cpp +++ b/libs/hwui/AmbientShadow.cpp @@ -16,6 +16,43 @@ #define LOG_TAG "OpenGLRenderer" +/** + * Extra vertices for the corner for smoother corner. + * Only for outer vertices. + * Note that we use such extra memory to avoid an extra loop. + */ +// For half circle, we could add EXTRA_VERTEX_PER_PI vertices. +// Set to 1 if we don't want to have any. +#define EXTRA_CORNER_VERTEX_PER_PI 12 + +// For the whole polygon, the sum of all the deltas b/t normals is 2 * M_PI, +// therefore, the maximum number of extra vertices will be twice bigger. +#define MAX_EXTRA_CORNER_VERTEX_NUMBER (2 * EXTRA_CORNER_VERTEX_PER_PI) + +// For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals. +#define CORNER_RADIANS_DIVISOR (M_PI / EXTRA_CORNER_VERTEX_PER_PI) + +/** + * Extra vertices for the Edge for interpolation artifacts. + * Same value for both inner and outer vertices. + */ +#define EXTRA_EDGE_VERTEX_PER_PI 50 + +#define MAX_EXTRA_EDGE_VERTEX_NUMBER (2 * EXTRA_EDGE_VERTEX_PER_PI) + +#define EDGE_RADIANS_DIVISOR (M_PI / EXTRA_EDGE_VERTEX_PER_PI) + +/** + * Other constants: + */ +// For the edge of the penumbra, the opacity is 0. +#define OUTER_OPACITY (0.0f) + +// Once the alpha difference is greater than this threshold, we will allocate extra +// edge vertices. +// If this is set to negative value, then all the edge will be tessellated. +#define ALPHA_THRESHOLD (0.1f / 255.0f) + #include <math.h> #include <utils/Log.h> #include <utils/Vector.h> @@ -23,11 +60,97 @@ #include "AmbientShadow.h" #include "ShadowTessellator.h" #include "Vertex.h" +#include "utils/MathUtils.h" namespace android { namespace uirenderer { /** + * Local utility functions. + */ +inline Vector2 getNormalFromVertices(const Vector3* vertices, int current, int next) { + // Convert from Vector3 to Vector2 first. + Vector2 currentVertex = { vertices[current].x, vertices[current].y }; + Vector2 nextVertex = { vertices[next].x, vertices[next].y }; + + return ShadowTessellator::calculateNormal(currentVertex, nextVertex); +} + +// The input z value will be converted to be non-negative inside. +// The output must be ranged from 0 to 1. +inline float getAlphaFromFactoredZ(float factoredZ) { + return 1.0 / (1 + MathUtils::max(factoredZ, 0.0f)); +} + +inline float getTransformedAlphaFromAlpha(float alpha) { + return acosf(1.0f - 2.0f * alpha); +} + +// The output is ranged from 0 to M_PI. +inline float getTransformedAlphaFromFactoredZ(float factoredZ) { + return getTransformedAlphaFromAlpha(getAlphaFromFactoredZ(factoredZ)); +} + +inline int getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2, + float divisor) { + // The formula is : + // extraNumber = floor(acos(dot(n1, n2)) / (M_PI / EXTRA_VERTEX_PER_PI)) + // The value ranges for each step are: + // dot( ) --- [-1, 1] + // acos( ) --- [0, M_PI] + // floor(...) --- [0, EXTRA_VERTEX_PER_PI] + float dotProduct = vector1.dot(vector2); + // TODO: Use look up table for the dotProduct to extraVerticesNumber + // computation, if needed. + float angle = acosf(dotProduct); + return (int) floor(angle / divisor); +} + +inline void checkOverflow(int used, int total, const char* bufferName) { + LOG_ALWAYS_FATAL_IF(used > total, "Error: %s overflow!!! used %d, total %d", + bufferName, used, total); +} + +inline int getEdgeExtraAndUpdateSpike(Vector2* currentSpike, + const Vector3& secondVertex, const Vector3& centroid) { + Vector2 secondSpike = {secondVertex.x - centroid.x, secondVertex.y - centroid.y}; + secondSpike.normalize(); + + int result = getExtraVertexNumber(secondSpike, *currentSpike, EDGE_RADIANS_DIVISOR); + *currentSpike = secondSpike; + return result; +} + +// Given the caster's vertex count, compute all the buffers size depending on +// whether or not the caster is opaque. +inline void computeBufferSize(int* totalVertexCount, int* totalIndexCount, + int* totalUmbraCount, int casterVertexCount, bool isCasterOpaque) { + // Compute the size of the vertex buffer. + int outerVertexCount = casterVertexCount * 2 + MAX_EXTRA_CORNER_VERTEX_NUMBER + + MAX_EXTRA_EDGE_VERTEX_NUMBER; + int innerVertexCount = casterVertexCount + MAX_EXTRA_EDGE_VERTEX_NUMBER; + *totalVertexCount = outerVertexCount + innerVertexCount; + + // Compute the size of the index buffer. + *totalIndexCount = 2 * outerVertexCount + 2; + + // Compute the size of the umber buffer. + // For translucent object, keep track of the umbra(inner) vertex in order to draw + // inside. We only need to store the index information. + *totalUmbraCount = 0; + if (!isCasterOpaque) { + // Add the centroid if occluder is translucent. + *totalVertexCount++; + *totalIndexCount += 2 * innerVertexCount + 1; + *totalUmbraCount = innerVertexCount; + } +} + +inline bool needsExtraForEdge(float firstAlpha, float secondAlpha) { + return abs(firstAlpha - secondAlpha) > ALPHA_THRESHOLD; +} + +/** * Calculate the shadows as a triangle strips while alpha value as the * shadow values. * @@ -43,290 +166,198 @@ namespace uirenderer { * * @param shadowVertexBuffer Return an floating point array of (x, y, a) * triangle strips mode. + * + * An simple illustration: + * For now let's mark the outer vertex as Pi, the inner as Vi, the centroid as C. + * + * First project the occluder to the Z=0 surface. + * Then we got all the inner vertices. And we compute the normal for each edge. + * According to the normal, we generate outer vertices. E.g: We generate P1 / P4 + * as extra corner vertices to make the corner looks round and smoother. + * + * Due to the fact that the alpha is not linear interpolated along the inner + * edge, when the alpha is different, we may add extra vertices such as P2.1, P2.2, + * V0.1, V0.2 to avoid the visual artifacts. + * + * (P3) + * (P2) (P2.1) (P2.2) | ' (P4) + * (P1)' | | | | ' + * ' | | | | ' + * (P0) ------------------------------------------------(P5) + * | (V0) (V0.1) (V0.2) |(V1) + * | | + * | | + * | (C) | + * | | + * | | + * | | + * | | + * (V3)-----------------------------------(V2) */ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, - const Vector3* vertices, int vertexCount, const Vector3& centroid3d, + const Vector3* casterVertices, int casterVertexCount, const Vector3& centroid3d, float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) { - const int rays = SHADOW_RAY_COUNT; - // Validate the inputs. - if (vertexCount < 3 || heightFactor <= 0 || rays <= 0 - || geomFactor <= 0) { -#if DEBUG_SHADOW - ALOGW("Invalid input for createAmbientShadow(), early return!"); -#endif - return; - } + shadowVertexBuffer.setMode(VertexBuffer::kIndices); - Vector<Vector2> dir; // TODO: use C++11 unique_ptr - dir.setCapacity(rays); - float rayDist[rays]; - float rayHeight[rays]; - calculateRayDirections(rays, vertices, vertexCount, centroid3d, dir.editArray()); - - // Calculate the length and height of the points along the edge. - // - // The math here is: - // Intersect each ray (starting from the centroid) with the polygon. - for (int i = 0; i < rays; i++) { - int edgeIndex; - float edgeFraction; - float rayDistance; - calculateIntersection(vertices, vertexCount, centroid3d, dir[i], edgeIndex, - edgeFraction, rayDistance); - rayDist[i] = rayDistance; - if (edgeIndex < 0 || edgeIndex >= vertexCount) { -#if DEBUG_SHADOW - ALOGW("Invalid edgeIndex!"); -#endif - edgeIndex = 0; - } - float h1 = vertices[edgeIndex].z; - float h2 = vertices[((edgeIndex + 1) % vertexCount)].z; - rayHeight[i] = h1 + edgeFraction * (h2 - h1); - } + // In order to computer the outer vertices in one loop, we need pre-compute + // the normal by the vertex (n - 1) to vertex 0, and the spike and alpha value + // for vertex 0. + Vector2 previousNormal = getNormalFromVertices(casterVertices, + casterVertexCount - 1 , 0); + Vector2 currentSpike = {casterVertices[0].x - centroid3d.x, + casterVertices[0].y - centroid3d.y}; + currentSpike.normalize(); + float currentAlpha = getAlphaFromFactoredZ(casterVertices[0].z * heightFactor); - // The output buffer length basically is roughly rays * layers, but since we - // need triangle strips, so we need to duplicate vertices to accomplish that. + // Preparing all the output data. + int totalVertexCount, totalIndexCount, totalUmbraCount; + computeBufferSize(&totalVertexCount, &totalIndexCount, &totalUmbraCount, + casterVertexCount, isCasterOpaque); AlphaVertex* shadowVertices = - shadowVertexBuffer.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT); - - // Calculate the vertex of the shadows. - // - // The math here is: - // Along the edges of the polygon, for each intersection point P (generated above), - // calculate the normal N, which should be perpendicular to the edge of the - // polygon (represented by the neighbor intersection points) . - // Shadow's vertices will be generated as : P + N * scale. - const Vector2 centroid2d = {centroid3d.x, centroid3d.y}; - for (int rayIndex = 0; rayIndex < rays; rayIndex++) { - Vector2 normal = {1.0f, 0.0f}; - calculateNormal(rays, rayIndex, dir.array(), rayDist, normal); - - // The vertex should be start from rayDist[i] then scale the - // normalizeNormal! - Vector2 intersection = dir[rayIndex] * rayDist[rayIndex] + - centroid2d; - - // outer ring of points, expanded based upon height of each ray intersection - float expansionDist = rayHeight[rayIndex] * heightFactor * - geomFactor; - AlphaVertex::set(&shadowVertices[rayIndex], - intersection.x + normal.x * expansionDist, - intersection.y + normal.y * expansionDist, - 0.0f); - - // inner ring of points - float opacity = 1.0 / (1 + rayHeight[rayIndex] * heightFactor); - // NOTE: Shadow alpha values are transformed when stored in alphavertices, - // so that they can be consumed directly by gFS_Main_ApplyVertexAlphaShadowInterp - float transformedOpacity = acos(1.0f - 2.0f * opacity); - AlphaVertex::set(&shadowVertices[rays + rayIndex], - intersection.x, - intersection.y, - transformedOpacity); - } + shadowVertexBuffer.alloc<AlphaVertex>(totalVertexCount); + int vertexBufferIndex = 0; + uint16_t* indexBuffer = shadowVertexBuffer.allocIndices<uint16_t>(totalIndexCount); + int indexBufferIndex = 0; + uint16_t umbraVertices[totalUmbraCount]; + int umbraIndex = 0; - if (isCasterOpaque) { - // skip inner ring, calc bounds over filled portion of buffer - shadowVertexBuffer.computeBounds<AlphaVertex>(2 * rays); - shadowVertexBuffer.setMode(VertexBuffer::kOnePolyRingShadow); - } else { - // If caster isn't opaque, we need to to fill the umbra by storing the umbra's - // centroid in the innermost ring of vertices. - float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor); - AlphaVertex centroidXYA; - AlphaVertex::set(¢roidXYA, centroid2d.x, centroid2d.y, centroidAlpha); - for (int rayIndex = 0; rayIndex < rays; rayIndex++) { - shadowVertices[2 * rays + rayIndex] = centroidXYA; - } - // calc bounds over entire buffer - shadowVertexBuffer.computeBounds<AlphaVertex>(); - shadowVertexBuffer.setMode(VertexBuffer::kTwoPolyRingShadow); - } + for (int i = 0; i < casterVertexCount; i++) { + // Corner: first figure out the extra vertices we need for the corner. + const Vector3& innerVertex = casterVertices[i]; + Vector2 currentNormal = getNormalFromVertices(casterVertices, i, + (i + 1) % casterVertexCount); + + int extraVerticesNumber = getExtraVertexNumber(currentNormal, previousNormal, + CORNER_RADIANS_DIVISOR); + float expansionDist = innerVertex.z * heightFactor * geomFactor; + const int cornerSlicesNumber = extraVerticesNumber + 1; // Minimal as 1. #if DEBUG_SHADOW - for (int i = 0; i < SHADOW_VERTEX_COUNT; i++) { - ALOGD("ambient shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x, - shadowVertices[i].y, shadowVertices[i].alpha); - } + ALOGD("cornerSlicesNumber is %d", cornerSlicesNumber); #endif -} -/** - * Generate an array of rays' direction vectors. - * To make sure the vertices generated are clockwise, the directions are from PI - * to -PI. - * - * @param rays The number of rays shooting out from the centroid. - * @param vertices Vertices of the polygon. - * @param vertexCount The number of vertices. - * @param centroid3d The centroid of the polygon. - * @param dir Return the array of ray vectors. - */ -void AmbientShadow::calculateRayDirections(const int rays, const Vector3* vertices, - const int vertexCount, const Vector3& centroid3d, Vector2* dir) { - // If we don't have enough rays, then fall back to the uniform distribution. - if (vertexCount * 2 > rays) { - float deltaAngle = 2 * M_PI / rays; - for (int i = 0; i < rays; i++) { - dir[i].x = cosf(M_PI - deltaAngle * i); - dir[i].y = sinf(M_PI - deltaAngle * i); + // Corner: fill the corner Vertex Buffer(VB) and Index Buffer(IB). + // We fill the inner vertex first, such that we can fill the index buffer + // inside the loop. + int currentInnerVertexIndex = vertexBufferIndex; + if (!isCasterOpaque) { + umbraVertices[umbraIndex++] = vertexBufferIndex; } - return; - } + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], casterVertices[i].x, + casterVertices[i].y, + getTransformedAlphaFromAlpha(currentAlpha)); - // If we have enough rays, then we assign each vertices a ray, and distribute - // the rest uniformly. - float rayThetas[rays]; - - const int uniformRayCount = rays - vertexCount; - const float deltaAngle = 2 * M_PI / uniformRayCount; - - // We have to generate all the vertices' theta anyway and we also need to - // find the minimal, so let's precompute it first. - // Since the incoming polygon is clockwise, we can find the dip to identify - // the minimal theta. - float polyThetas[vertexCount]; - int maxPolyThetaIndex = 0; - for (int i = 0; i < vertexCount; i++) { - polyThetas[i] = atan2(vertices[i].y - centroid3d.y, - vertices[i].x - centroid3d.x); - if (i > 0 && polyThetas[i] > polyThetas[i - 1]) { - maxPolyThetaIndex = i; - } - } + const Vector3& innerStart = casterVertices[i]; - // Both poly's thetas and uniform thetas are in decrease order(clockwise) - // from PI to -PI. - int polyThetaIndex = maxPolyThetaIndex; - float polyTheta = polyThetas[maxPolyThetaIndex]; - int uniformThetaIndex = 0; - float uniformTheta = M_PI; - for (int i = 0; i < rays; i++) { - // Compare both thetas and pick the smaller one and move on. - bool hasThetaCollision = abs(polyTheta - uniformTheta) < MINIMAL_DELTA_THETA; - if (polyTheta > uniformTheta || hasThetaCollision) { - if (hasThetaCollision) { - // Shift the uniformTheta to middle way between current polyTheta - // and next uniform theta. The next uniform theta can wrap around - // to exactly PI safely here. - // Note that neither polyTheta nor uniformTheta can be FLT_MAX - // due to the hasThetaCollision is true. - uniformTheta = (polyTheta + M_PI - deltaAngle * (uniformThetaIndex + 1)) / 2; -#if DEBUG_SHADOW - ALOGD("Shifted uniformTheta to %f", uniformTheta); -#endif - } - rayThetas[i] = polyTheta; - polyThetaIndex = (polyThetaIndex + 1) % vertexCount; - if (polyThetaIndex != maxPolyThetaIndex) { - polyTheta = polyThetas[polyThetaIndex]; - } else { - // out of poly points. - polyTheta = - FLT_MAX; - } - } else { - rayThetas[i] = uniformTheta; - uniformThetaIndex++; - if (uniformThetaIndex < uniformRayCount) { - uniformTheta = M_PI - deltaAngle * uniformThetaIndex; - } else { - // out of uniform points. - uniformTheta = - FLT_MAX; + // outerStart is the first outer vertex for this inner vertex. + // outerLast is the last outer vertex for this inner vertex. + Vector2 outerStart = {0, 0}; + Vector2 outerLast = {0, 0}; + // This will create vertices from [0, cornerSlicesNumber] inclusively, + // which means minimally 2 vertices even without the extra ones. + for (int j = 0; j <= cornerSlicesNumber; j++) { + Vector2 averageNormal = + previousNormal * (cornerSlicesNumber - j) + currentNormal * j; + averageNormal /= cornerSlicesNumber; + averageNormal.normalize(); + Vector2 outerVertex; + outerVertex.x = innerVertex.x + averageNormal.x * expansionDist; + outerVertex.y = innerVertex.y + averageNormal.y * expansionDist; + + indexBuffer[indexBufferIndex++] = vertexBufferIndex; + indexBuffer[indexBufferIndex++] = currentInnerVertexIndex; + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x, + outerVertex.y, OUTER_OPACITY); + + if (j == 0) { + outerStart = outerVertex; + } else if (j == cornerSlicesNumber) { + outerLast = outerVertex; } } - } + previousNormal = currentNormal; + + // Edge: first figure out the extra vertices needed for the edge. + const Vector3& innerNext = casterVertices[(i + 1) % casterVertexCount]; + float nextAlpha = getAlphaFromFactoredZ(innerNext.z * heightFactor); + if (needsExtraForEdge(currentAlpha, nextAlpha)) { + // TODO: See if we can / should cache this outer vertex across the loop. + Vector2 outerNext; + float expansionDist = innerNext.z * heightFactor * geomFactor; + outerNext.x = innerNext.x + currentNormal.x * expansionDist; + outerNext.y = innerNext.y + currentNormal.y * expansionDist; - for (int i = 0; i < rays; i++) { + // Compute the angle and see how many extra points we need. + int extraVerticesNumber = getEdgeExtraAndUpdateSpike(¤tSpike, + innerNext, centroid3d); #if DEBUG_SHADOW - ALOGD("No. %d : %f", i, rayThetas[i] * 180 / M_PI); + ALOGD("extraVerticesNumber %d for edge %d", extraVerticesNumber, i); #endif - // TODO: Fix the intersection precision problem and remvoe the delta added - // here. - dir[i].x = cosf(rayThetas[i] + MINIMAL_DELTA_THETA); - dir[i].y = sinf(rayThetas[i] + MINIMAL_DELTA_THETA); - } -} + // Edge: fill the edge's VB and IB. + // This will create vertices pair from [1, extraVerticesNumber - 1]. + // If there is no extra vertices created here, the edge will be drawn + // as just 2 triangles. + for (int k = 1; k < extraVerticesNumber; k++) { + int startWeight = extraVerticesNumber - k; + Vector2 currentOuter = + (outerLast * startWeight + outerNext * k) / extraVerticesNumber; + indexBuffer[indexBufferIndex++] = vertexBufferIndex; + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentOuter.x, + currentOuter.y, OUTER_OPACITY); -/** - * Calculate the intersection of a ray hitting the polygon. - * - * @param vertices The shadow caster's polygon, which is represented in a - * Vector3 array. - * @param vertexCount The length of caster's polygon in terms of number of vertices. - * @param start The starting point of the ray. - * @param dir The direction vector of the ray. - * - * @param outEdgeIndex Return the index of the segment (or index of the starting - * vertex) that ray intersect with. - * @param outEdgeFraction Return the fraction offset from the segment starting - * index. - * @param outRayDist Return the ray distance from centroid to the intersection. - */ -void AmbientShadow::calculateIntersection(const Vector3* vertices, int vertexCount, - const Vector3& start, const Vector2& dir, int& outEdgeIndex, - float& outEdgeFraction, float& outRayDist) { - float startX = start.x; - float startY = start.y; - float dirX = dir.x; - float dirY = dir.y; - // Start the search from the last edge from poly[len-1] to poly[0]. - int p1 = vertexCount - 1; - - for (int p2 = 0; p2 < vertexCount; p2++) { - float p1x = vertices[p1].x; - float p1y = vertices[p1].y; - float p2x = vertices[p2].x; - float p2y = vertices[p2].y; - - // The math here is derived from: - // f(t, v) = p1x * (1 - t) + p2x * t - (startX + dirX * v) = 0; - // g(t, v) = p1y * (1 - t) + p2y * t - (startY + dirY * v) = 0; - float div = (dirX * (p1y - p2y) + dirY * p2x - dirY * p1x); - if (div != 0) { - float t = (dirX * (p1y - startY) + dirY * startX - dirY * p1x) / (div); - if (t > 0 && t <= 1) { - float t2 = (p1x * (startY - p2y) - + p2x * (p1y - startY) - + startX * (p2y - p1y)) / div; - if (t2 > 0) { - outEdgeIndex = p1; - outRayDist = t2; - outEdgeFraction = t; - return; + if (!isCasterOpaque) { + umbraVertices[umbraIndex++] = vertexBufferIndex; } + Vector3 currentInner = + (innerStart * startWeight + innerNext * k) / extraVerticesNumber; + indexBuffer[indexBufferIndex++] = vertexBufferIndex; + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentInner.x, + currentInner.y, + getTransformedAlphaFromFactoredZ(currentInner.z * heightFactor)); } } - p1 = p2; + currentAlpha = nextAlpha; } - return; -}; -/** - * Calculate the normal at the intersection point between a ray and the polygon. - * - * @param rays The total number of rays. - * @param currentRayIndex The index of the ray which the normal is based on. - * @param dir The array of the all the rays directions. - * @param rayDist The pre-computed ray distances array. - * - * @param normal Return the normal. - */ -void AmbientShadow::calculateNormal(int rays, int currentRayIndex, - const Vector2* dir, const float* rayDist, Vector2& normal) { - int preIndex = (currentRayIndex - 1 + rays) % rays; - int postIndex = (currentRayIndex + 1) % rays; - Vector2 p1 = dir[preIndex] * rayDist[preIndex]; - Vector2 p2 = dir[postIndex] * rayDist[postIndex]; - - // Now the rays are going CW around the poly. - Vector2 delta = p2 - p1; - if (delta.length() != 0) { - delta.normalize(); - // Calculate the normal , which is CCW 90 rotate to the delta. - normal.x = - delta.y; - normal.y = delta.x; + indexBuffer[indexBufferIndex++] = 1; + indexBuffer[indexBufferIndex++] = 0; + + if (!isCasterOpaque) { + // Add the centroid as the last one in the vertex buffer. + float centroidOpacity = + getTransformedAlphaFromFactoredZ(centroid3d.z * heightFactor); + int centroidIndex = vertexBufferIndex; + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid3d.x, + centroid3d.y, centroidOpacity); + + for (int i = 0; i < umbraIndex; i++) { + // Note that umbraVertices[0] is always 0. + // So the start and the end of the umbra are using the "0". + // And penumbra ended with 0, so a degenerated triangle is formed b/t + // the umbra and penumbra. + indexBuffer[indexBufferIndex++] = umbraVertices[i]; + indexBuffer[indexBufferIndex++] = centroidIndex; + } + indexBuffer[indexBufferIndex++] = 0; } + + // At the end, update the real index and vertex buffer size. + shadowVertexBuffer.updateVertexCount(vertexBufferIndex); + shadowVertexBuffer.updateIndexCount(indexBufferIndex); + + checkOverflow(vertexBufferIndex, totalVertexCount, "Vertex Buffer"); + checkOverflow(indexBufferIndex, totalIndexCount, "Index Buffer"); + checkOverflow(umbraIndex, totalUmbraCount, "Umbra Buffer"); + +#if DEBUG_SHADOW + for (int i = 0; i < vertexBufferIndex; i++) { + ALOGD("vertexBuffer i %d, (%f, %f %f)", i, shadowVertices[i].x, shadowVertices[i].y, + shadowVertices[i].alpha); + } + for (int i = 0; i < indexBufferIndex; i++) { + ALOGD("indexBuffer i %d, indexBuffer[i] %d", i, indexBuffer[i]); + } +#endif } }; // namespace uirenderer diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h index 68df246..9660dc0 100644 --- a/libs/hwui/AmbientShadow.h +++ b/libs/hwui/AmbientShadow.h @@ -28,27 +28,12 @@ namespace uirenderer { /** * AmbientShadow is used to calculate the ambient shadow value around a polygon. - * - * TODO: calculateIntersection() now is O(N*M), where N is the number of - * polygon's vertics and M is the number of rays. In fact, by staring tracing - * the vertex from the previous intersection, the algorithm can be O(N + M); */ class AmbientShadow { public: static void createAmbientShadow(bool isCasterOpaque, const Vector3* poly, int polyLength, const Vector3& centroid3d, float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer); - -private: - static void calculateRayDirections(const int rays, const Vector3* vertices, - const int vertexCount, const Vector3& centroid3d, Vector2* dir); - - static void calculateIntersection(const Vector3* poly, int nbVertices, - const Vector3& start, const Vector2& dir, int& outEdgeIndex, - float& outEdgeFraction, float& outRayDist); - - static void calculateNormal(int rays, int currentRayIndex, const Vector2* dir, - const float* rayDist, Vector2& normal); }; // AmbientShadow }; // namespace uirenderer diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index bbf0551..0f36c06 100755 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -2417,6 +2417,10 @@ status_t OpenGLRenderer::drawVertexBuffer(float translateX, float translateY, } else if (mode == VertexBuffer::kTwoPolyRingShadow) { mCaches.bindShadowIndicesBuffer(); glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0); + } else if (mode == VertexBuffer::kIndices) { + mCaches.unbindIndicesBuffer(); + glDrawElements(GL_TRIANGLE_STRIP, vertexBuffer.getIndexCount(), GL_UNSIGNED_SHORT, + vertexBuffer.getIndices()); } if (isAA) { diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h index 2a9f01c..d033ed9 100644 --- a/libs/hwui/Vector.h +++ b/libs/hwui/Vector.h @@ -111,6 +111,23 @@ public: float y; float z; + Vector3 operator+(const Vector3& v) const { + return (Vector3){x + v.x, y + v.y, z + v.z}; + } + + Vector3 operator-(const Vector3& v) const { + return (Vector3){x - v.x, y - v.y, z - v.z}; + } + + Vector3 operator/(float s) const { + return (Vector3){x / s, y / s, z / s}; + } + + Vector3 operator*(float s) const { + return (Vector3){x * s, y * s, z * s}; + } + + void dump() { ALOGD("Vector3[%.2f, %.2f, %.2f]", x, y, z); } diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h index 3837f88..966fa4e 100644 --- a/libs/hwui/VertexBuffer.h +++ b/libs/hwui/VertexBuffer.h @@ -17,6 +17,7 @@ #ifndef ANDROID_HWUI_VERTEX_BUFFER_H #define ANDROID_HWUI_VERTEX_BUFFER_H +#include "utils/MathUtils.h" namespace android { namespace uirenderer { @@ -26,19 +27,27 @@ public: enum Mode { kStandard = 0, kOnePolyRingShadow = 1, - kTwoPolyRingShadow = 2 + kTwoPolyRingShadow = 2, + kIndices = 3 }; VertexBuffer() : mBuffer(0) + , mIndices(0) , mVertexCount(0) + , mIndexCount(0) + , mAllocatedVertexCount(0) + , mAllocatedIndexCount(0) , mByteCount(0) , mMode(kStandard) + , mReallocBuffer(0) , mCleanupMethod(NULL) + , mCleanupIndexMethod(NULL) {} ~VertexBuffer() { if (mCleanupMethod) mCleanupMethod(mBuffer); + if (mCleanupIndexMethod) mCleanupIndexMethod(mIndices); } /** @@ -59,6 +68,7 @@ public: mReallocBuffer = reallocBuffer + vertexCount; return reallocBuffer; } + mAllocatedVertexCount = vertexCount; mVertexCount = vertexCount; mByteCount = mVertexCount * sizeof(TYPE); mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount]; @@ -69,6 +79,17 @@ public: } template <class TYPE> + TYPE* allocIndices(int indexCount) { + mAllocatedIndexCount = indexCount; + mIndexCount = indexCount; + mIndices = (void*)new TYPE[indexCount]; + + mCleanupIndexMethod = &(cleanup<TYPE>); + + return (TYPE*)mIndices; + } + + template <class TYPE> void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) { int verticesToCopy = srcBuffer.getVertexCount(); @@ -103,9 +124,17 @@ public: } const void* getBuffer() const { return mBuffer; } + const void* getIndices() const { return mIndices; } const Rect& getBounds() const { return mBounds; } unsigned int getVertexCount() const { return mVertexCount; } unsigned int getSize() const { return mByteCount; } + unsigned int getIndexCount() const { return mIndexCount; } + void updateIndexCount(unsigned int newCount) { + mIndexCount = MathUtils::min(newCount, mAllocatedIndexCount); + } + void updateVertexCount(unsigned int newCount) { + newCount = MathUtils::min(newCount, mAllocatedVertexCount); + } Mode getMode() const { return mMode; } void setBounds(Rect bounds) { mBounds = bounds; } @@ -127,14 +156,22 @@ private: } Rect mBounds; + void* mBuffer; + void* mIndices; + unsigned int mVertexCount; + unsigned int mIndexCount; + unsigned int mAllocatedVertexCount; + unsigned int mAllocatedIndexCount; unsigned int mByteCount; + Mode mMode; void* mReallocBuffer; // used for multi-allocation void (*mCleanupMethod)(void*); + void (*mCleanupIndexMethod)(void*); }; }; // namespace uirenderer |