4ac36f80be
bug:18667472 Previously, we were allocating per-frame temporary paths within the PlaybackStateStruct, but these are not safe as layers allocate these transiently. Instead, move these to the OpenGLRenderer, which has better define lifecycle. Additionally, don't store SkPath objects directly in vector, since they are then subject to relocation. Change-Id: I8187ef542fcd5b030502bb75eb123ee26c0daa96
347 lines
14 KiB
C++
347 lines
14 KiB
C++
/*
|
|
* Copyright (C) 2013 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#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>
|
|
|
|
#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 getEdgeExtraAndUpdateSpike(Vector2* currentSpike,
|
|
const Vector3& secondVertex, const Vector3& centroid) {
|
|
Vector2 secondSpike = {secondVertex.x - centroid.x, secondVertex.y - centroid.y};
|
|
secondSpike.normalize();
|
|
|
|
int result = ShadowTessellator::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.
|
|
*
|
|
* @param isCasterOpaque Whether the caster is opaque.
|
|
* @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 centroid3d The centroid of the shadow caster.
|
|
* @param heightFactor The factor showing the higher the object, the lighter the
|
|
* shadow.
|
|
* @param geomFactor The factor scaling the geometry expansion along the normal.
|
|
*
|
|
* @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* casterVertices, int casterVertexCount, const Vector3& centroid3d,
|
|
float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) {
|
|
shadowVertexBuffer.setMode(VertexBuffer::kIndices);
|
|
|
|
// 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);
|
|
|
|
// Preparing all the output data.
|
|
int totalVertexCount, totalIndexCount, totalUmbraCount;
|
|
computeBufferSize(&totalVertexCount, &totalIndexCount, &totalUmbraCount,
|
|
casterVertexCount, isCasterOpaque);
|
|
AlphaVertex* shadowVertices =
|
|
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;
|
|
|
|
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 = ShadowTessellator::getExtraVertexNumber(currentNormal,
|
|
previousNormal, CORNER_RADIANS_DIVISOR);
|
|
|
|
float expansionDist = innerVertex.z * heightFactor * geomFactor;
|
|
const int cornerSlicesNumber = extraVerticesNumber + 1; // Minimal as 1.
|
|
#if DEBUG_SHADOW
|
|
ALOGD("cornerSlicesNumber is %d", cornerSlicesNumber);
|
|
#endif
|
|
|
|
// 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;
|
|
}
|
|
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], casterVertices[i].x,
|
|
casterVertices[i].y,
|
|
getTransformedAlphaFromAlpha(currentAlpha));
|
|
|
|
const Vector3& innerStart = casterVertices[i];
|
|
|
|
// 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;
|
|
|
|
// Compute the angle and see how many extra points we need.
|
|
int extraVerticesNumber = getEdgeExtraAndUpdateSpike(¤tSpike,
|
|
innerNext, centroid3d);
|
|
#if DEBUG_SHADOW
|
|
ALOGD("extraVerticesNumber %d for edge %d", extraVerticesNumber, i);
|
|
#endif
|
|
// 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);
|
|
|
|
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));
|
|
}
|
|
}
|
|
currentAlpha = nextAlpha;
|
|
}
|
|
|
|
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);
|
|
shadowVertexBuffer.computeBounds<AlphaVertex>();
|
|
|
|
ShadowTessellator::checkOverflow(vertexBufferIndex, totalVertexCount, "Ambient Vertex Buffer");
|
|
ShadowTessellator::checkOverflow(indexBufferIndex, totalIndexCount, "Ambient Index Buffer");
|
|
ShadowTessellator::checkOverflow(umbraIndex, totalUmbraCount, "Ambient 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
|
|
}; // namespace android
|