diff options
Diffstat (limited to 'libs')
-rw-r--r-- | libs/hwui/AmbientShadow.cpp | 306 | ||||
-rw-r--r-- | libs/hwui/AmbientShadow.h | 57 | ||||
-rw-r--r-- | libs/hwui/Android.mk | 2 | ||||
-rw-r--r-- | libs/hwui/Caches.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/Caches.h | 8 | ||||
-rw-r--r-- | libs/hwui/Debug.h | 6 | ||||
-rw-r--r-- | libs/hwui/DeferredDisplayList.h | 4 | ||||
-rw-r--r-- | libs/hwui/DisplayList.cpp | 264 | ||||
-rw-r--r-- | libs/hwui/DisplayList.h | 131 | ||||
-rw-r--r-- | libs/hwui/DisplayListOp.h | 66 | ||||
-rw-r--r-- | libs/hwui/DisplayListRenderer.cpp | 9 | ||||
-rw-r--r-- | libs/hwui/DisplayListRenderer.h | 1 | ||||
-rw-r--r-- | libs/hwui/FontRenderer.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/Layer.cpp | 9 | ||||
-rw-r--r-- | libs/hwui/Matrix.cpp | 95 | ||||
-rw-r--r-- | libs/hwui/Matrix.h | 14 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 62 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.h | 13 | ||||
-rw-r--r-- | libs/hwui/PathTessellator.h | 75 | ||||
-rw-r--r-- | libs/hwui/ShadowTessellator.cpp | 73 | ||||
-rw-r--r-- | libs/hwui/ShadowTessellator.h | 37 | ||||
-rw-r--r-- | libs/hwui/Vector.h | 15 | ||||
-rw-r--r-- | libs/hwui/VertexBuffer.h | 102 |
23 files changed, 1143 insertions, 214 deletions
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp new file mode 100644 index 0000000..923571e --- /dev/null +++ b/libs/hwui/AmbientShadow.cpp @@ -0,0 +1,306 @@ +/* + * 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" + +#include <math.h> +#include <utils/Log.h> + +#include "AmbientShadow.h" +#include "Vertex.h" + +namespace android { +namespace uirenderer { + +/** + * Calculate the shadows as a triangle strips while alpha value as the + * shadow values. + * + * @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 rays The number of rays shooting out from the centroid. + * @param layers The number of rings outside the polygon. + * @param strength The darkness of the shadow, the higher, the darker. + * @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. + */ +void AmbientShadow::createAmbientShadow(const Vector3* vertices, int vertexCount, + int rays, int layers, float strength, float heightFactor, float geomFactor, + VertexBuffer& shadowVertexBuffer) { + + // Validate the inputs. + if (strength <= 0 || heightFactor <= 0 || layers <= 0 || rays <= 0 + || geomFactor <= 0) { +#if DEBUG_SHADOW + ALOGE("Invalid input for createAmbientShadow(), early return!"); +#endif + return; + } + int rings = layers + 1; + int size = rays * rings; + Vector2 centroid; + calculatePolygonCentroid(vertices, vertexCount, centroid); + + Vector2 dir[rays]; + float rayDist[rays]; + float rayHeight[rays]; + calculateRayDirections(rays, dir); + + // 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, centroid, dir[i], edgeIndex, + edgeFraction, rayDistance); + rayDist[i] = rayDistance; + if (edgeIndex < 0 || edgeIndex >= vertexCount) { +#if DEBUG_SHADOW + ALOGE("Invalid edgeIndex!"); +#endif + edgeIndex = 0; + } + float h1 = vertices[edgeIndex].z; + float h2 = vertices[((edgeIndex + 1) % vertexCount)].z; + rayHeight[i] = h1 + edgeFraction * (h2 - h1); + } + + // The output buffer length basically is roughly rays * layers, but since we + // need triangle strips, so we need to duplicate vertices to accomplish that. + const int shadowVertexCount = (2 + rays + ((layers) * 2 * (rays + 1))); + AlphaVertex* shadowVertices = shadowVertexBuffer.alloc<AlphaVertex>(shadowVertexCount); + + // 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. + int currentIndex = 0; + for (int r = 0; r < layers; r++) { + int firstInLayer = currentIndex; + for (int i = 0; i < rays; i++) { + + Vector2 normal(1.0f, 0.0f); + calculateNormal(rays, i, dir, rayDist, normal); + + float opacity = strength * (0.5f) / (1 + rayHeight[i] / heightFactor); + + // The vertex should be start from rayDist[i] then scale the + // normalizeNormal! + Vector2 intersection = dir[i] * rayDist[i] + centroid; + + // Use 2 rings' vertices to complete one layer's strip + for (int j = r; j < (r + 2); j++) { + float jf = j / (float)(rings - 1); + + float expansionDist = rayHeight[i] / heightFactor * geomFactor * jf; + AlphaVertex::set(&shadowVertices[currentIndex], + intersection.x + normal.x * expansionDist, + intersection.y + normal.y * expansionDist, + (1 - jf) * opacity); + currentIndex++; + } + } + + // From one layer to the next, we need to duplicate the vertex to + // continue as a single strip. + shadowVertices[currentIndex] = shadowVertices[firstInLayer]; + currentIndex++; + shadowVertices[currentIndex] = shadowVertices[firstInLayer + 1]; + currentIndex++; + } + + // After all rings are done, we need to jump into the polygon. + // In order to keep everything in a strip, we need to duplicate the last one + // of the rings and the first one inside the polygon. + int lastInRings = currentIndex - 1; + shadowVertices[currentIndex] = shadowVertices[lastInRings]; + currentIndex++; + + // We skip one and fill it back after we finish the internal triangles. + currentIndex++; + int firstInternal = currentIndex; + + // Combine the internal area of the polygon into a triangle strip, too. + // The basic idea is zig zag between the intersection points. + // 0 -> (n - 1) -> 1 -> (n - 2) ... + for (int k = 0; k < rays; k++) { + int i = k / 2; + if ((k & 1) == 1) { // traverse the inside in a zig zag pattern for strips + i = rays - i - 1; + } + float cast = rayDist[i] * (1 + rayHeight[i] / heightFactor); + float opacity = strength * (0.5f) / (1 + rayHeight[i] / heightFactor); + float t = rayDist[i]; + + AlphaVertex::set(&shadowVertices[currentIndex], dir[i].x * t + centroid.x, + dir[i].y * t + centroid.y, opacity); + currentIndex++; + } + + currentIndex = firstInternal - 1; + shadowVertices[currentIndex] = shadowVertices[firstInternal]; +} + +/** + * Calculate the centroid of a given polygon. + * + * @param vertices The shadow caster's polygon, which is represented in a + * straight Vector3 array. + * @param vertexCount The length of caster's polygon in terms of number of vertices. + * + * @param centroid Return the centroid of the polygon. + */ +void AmbientShadow::calculatePolygonCentroid(const Vector3* vertices, int vertexCount, + Vector2& centroid) { + float sumx = 0; + float sumy = 0; + int p1 = vertexCount - 1; + float area = 0; + for (int p2 = 0; p2 < vertexCount; p2++) { + float x1 = vertices[p1].x; + float y1 = vertices[p1].y; + float x2 = vertices[p2].x; + float y2 = vertices[p2].y; + float a = (x1 * y2 - x2 * y1); + sumx += (x1 + x2) * a; + sumy += (y1 + y2) * a; + area += a; + p1 = p2; + } + + if (area == 0) { +#if DEBUG_SHADOW + ALOGE("Area is 0!"); +#endif + centroid.x = vertices[0].x; + centroid.y = vertices[0].y; + } else { + centroid.x = sumx / (3 * area); + centroid.y = sumy / (3 * area); + } +} + +/** + * Generate an array of rays' direction vectors. + * + * @param rays The number of rays shooting out from the centroid. + * @param dir Return the array of ray vectors. + */ +void AmbientShadow::calculateRayDirections(int rays, Vector2* dir) { + float deltaAngle = 2 * M_PI / rays; + + for (int i = 0; i < rays; i++) { + dir[i].x = sinf(deltaAngle * i); + dir[i].y = cosf(deltaAngle * i); + } +} + +/** + * 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 Vector2& 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; + } + } + } + p1 = p2; + } + 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 V (deltaX, deltaY) is the vector 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 V. + // 90 degrees CCW about z-axis: (x, y, z) -> (-y, x, z) + normal.x = -delta.y; + normal.y = delta.x; + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h new file mode 100644 index 0000000..079bdb7 --- /dev/null +++ b/libs/hwui/AmbientShadow.h @@ -0,0 +1,57 @@ + +/* + * 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_AMBIENT_SHADOW_H +#define ANDROID_HWUI_AMBIENT_SHADOW_H + +#include "Debug.h" +#include "Vector.h" +#include "VertexBuffer.h" + +namespace android { +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(const Vector3* poly, int polyLength, int rays, + int layers, float strength, float heightFactor, float geomFactor, + VertexBuffer& shadowVertexBuffer); + +private: + static void calculatePolygonCentroid(const Vector3* poly, int len, Vector2& centroid); + + static void calculateRayDirections(int rays, Vector2* dir); + + static void calculateIntersection(const Vector3* poly, int nbVertices, + const Vector2& 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 +}; // namespace android + +#endif // ANDROID_HWUI_AMBIENT_SHADOW_H diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 1f37925..962d726 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -10,6 +10,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) thread/TaskManager.cpp \ font/CacheTexture.cpp \ font/Font.cpp \ + AmbientShadow.cpp \ AssetAtlas.cpp \ FontRenderer.cpp \ GammaFontRenderer.cpp \ @@ -37,6 +38,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) ProgramCache.cpp \ RenderBufferCache.cpp \ ResourceCache.cpp \ + ShadowTessellator.cpp \ SkiaColorFilter.cpp \ SkiaShader.cpp \ Snapshot.cpp \ diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index f8d3589..5b751b9 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -473,7 +473,7 @@ bool Caches::unbindPixelBuffer() { // Meshes and textures /////////////////////////////////////////////////////////////////////////////// -void Caches::bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei stride) { +void Caches::bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) { if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) { GLuint slot = currentProgram->position; glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices); @@ -482,7 +482,7 @@ void Caches::bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei str } } -void Caches::bindTexCoordsVertexPointer(bool force, GLvoid* vertices, GLsizei stride) { +void Caches::bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) { if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) { GLuint slot = currentProgram->texCoords; glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices); diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index f8f2284..963965d 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -208,13 +208,13 @@ public: * Binds an attrib to the specified float vertex pointer. * Assumes a stride of gMeshStride and a size of 2. */ - void bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei stride = gMeshStride); + void bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride); /** * Binds an attrib to the specified float vertex pointer. * Assumes a stride of gMeshStride and a size of 2. */ - void bindTexCoordsVertexPointer(bool force, GLvoid* vertices, GLsizei stride = gMeshStride); + void bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride); /** * Resets the vertex pointers. @@ -379,9 +379,9 @@ private: GLuint mCurrentBuffer; GLuint mCurrentIndicesBuffer; GLuint mCurrentPixelBuffer; - void* mCurrentPositionPointer; + const void* mCurrentPositionPointer; GLsizei mCurrentPositionStride; - void* mCurrentTexCoordsPointer; + const void* mCurrentTexCoordsPointer; GLsizei mCurrentTexCoordsStride; bool mTexCoordsArrayEnabled; diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h index 786f12a..f619205 100644 --- a/libs/hwui/Debug.h +++ b/libs/hwui/Debug.h @@ -85,6 +85,12 @@ // Turn on to highlight drawing batches and merged batches with different colors #define DEBUG_MERGE_BEHAVIOR 0 +// Turn on to enable 3D support in the renderer (off by default until API for control exists) +#define DEBUG_ENABLE_3D 0 + +// Turn on to enable debugging shadow +#define DEBUG_SHADOW 0 + #if DEBUG_INIT #define INIT_LOGD(...) ALOGD(__VA_ARGS__) #else diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index 3dcbd0b..fca3588 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -79,13 +79,13 @@ public: }; class DeferredDisplayList { + friend class DeferStateStruct; // used to give access to allocator public: DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) : mBounds(bounds), mAvoidOverdraw(avoidOverdraw) { clear(); } ~DeferredDisplayList() { clear(); } - void reset(const Rect& bounds) { mBounds.set(bounds); } enum OpBatchId { kOpBatch_None = 0, // Don't batch @@ -120,6 +120,8 @@ public: void addDrawOp(OpenGLRenderer& renderer, DrawOp* op); private: + DeferredDisplayList(const DeferredDisplayList& other); // disallow copy + DeferredDisplayState* createState() { return new (mAllocator) DeferredDisplayState(); } diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index a3e4bb4..c616cd8 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -14,8 +14,12 @@ * limitations under the License. */ +#define ATRACE_TAG ATRACE_TAG_VIEW + #include <SkCanvas.h> +#include <utils/Trace.h> + #include "Debug.h" #include "DisplayList.h" #include "DisplayListOp.h" @@ -65,11 +69,6 @@ void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) { void DisplayList::clearResources() { mDisplayListData = NULL; - mClipRectOp = NULL; - mSaveLayerOp = NULL; - mSaveOp = NULL; - mRestoreToCountOp = NULL; - delete mTransformMatrix; delete mTransformCamera; delete mTransformMatrix3D; @@ -168,17 +167,6 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde return; } - // allocate reusable ops for state-deferral - LinearAllocator& alloc = mDisplayListData->allocator; - mClipRectOp = new (alloc) ClipRectOp(); - mSaveLayerOp = new (alloc) SaveLayerOp(); - mSaveOp = new (alloc) SaveOp(); - mRestoreToCountOp = new (alloc) RestoreToCountOp(); - if (CC_UNLIKELY(!mSaveOp)) { // temporary debug logging - ALOGW("Error: %s's SaveOp not allocated, size %d", getName(), mSize); - CRASH(); - } - mFunctorCount = recorder.getFunctorCount(); Caches& caches = Caches::getInstance(); @@ -253,6 +241,7 @@ void DisplayList::init() { mHasOverlappingRendering = true; mTranslationX = 0; mTranslationY = 0; + mTranslationZ = 0; mRotation = 0; mRotationX = 0; mRotationY= 0; @@ -269,6 +258,7 @@ void DisplayList::init() { mHeight = 0; mPivotExplicitlySet = false; mCaching = false; + mIs3dRoot = true; // TODO: setter, java side impl } size_t DisplayList::getSize() { @@ -320,27 +310,38 @@ void DisplayList::updateMatrix() { mPivotY = mPrevHeight / 2.0f; } } - if ((mMatrixFlags & ROTATION_3D) == 0) { + if (!DEBUG_ENABLE_3D && (mMatrixFlags & ROTATION_3D) == 0) { mTransformMatrix->setTranslate(mTranslationX, mTranslationY); mTransformMatrix->preRotate(mRotation, mPivotX, mPivotY); mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY); } else { - if (!mTransformCamera) { - mTransformCamera = new Sk3DView(); - mTransformMatrix3D = new SkMatrix(); + if (DEBUG_ENABLE_3D) { + mTransform.loadTranslate(mPivotX + mTranslationX, mPivotY + mTranslationY, + mTranslationZ); + mTransform.rotate(mRotationX, 1, 0, 0); + mTransform.rotate(mRotationY, 0, 1, 0); + mTransform.rotate(mRotation, 0, 0, 1); + mTransform.scale(mScaleX, mScaleY, 1); + mTransform.translate(-mPivotX, -mPivotY); + } else { + /* TODO: support this old transform approach, based on API level */ + if (!mTransformCamera) { + mTransformCamera = new Sk3DView(); + mTransformMatrix3D = new SkMatrix(); + } + mTransformMatrix->reset(); + mTransformCamera->save(); + mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY); + mTransformCamera->rotateX(mRotationX); + mTransformCamera->rotateY(mRotationY); + mTransformCamera->rotateZ(-mRotation); + mTransformCamera->getMatrix(mTransformMatrix3D); + mTransformMatrix3D->preTranslate(-mPivotX, -mPivotY); + mTransformMatrix3D->postTranslate(mPivotX + mTranslationX, + mPivotY + mTranslationY); + mTransformMatrix->postConcat(*mTransformMatrix3D); + mTransformCamera->restore(); } - mTransformMatrix->reset(); - mTransformCamera->save(); - mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY); - mTransformCamera->rotateX(mRotationX); - mTransformCamera->rotateY(mRotationY); - mTransformCamera->rotateZ(-mRotation); - mTransformCamera->getMatrix(mTransformMatrix3D); - mTransformMatrix3D->preTranslate(-mPivotX, -mPivotY); - mTransformMatrix3D->postTranslate(mPivotX + mTranslationX, - mPivotY + mTranslationY); - mTransformMatrix->postConcat(*mTransformMatrix3D); - mTransformCamera->restore(); } } mMatrixDirty = false; @@ -417,8 +418,13 @@ void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler, if (mMatrixFlags != 0) { if (mMatrixFlags == TRANSLATION) { renderer.translate(mTranslationX, mTranslationY); + renderer.translateZ(mTranslationZ); } else { +#if DEBUG_ENABLE_3D + renderer.concatMatrix(mTransform); +#else renderer.concatMatrix(mTransformMatrix); +#endif } } bool clipToBoundsNeeded = mCaching ? false : mClipToBounds; @@ -436,14 +442,107 @@ void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler, saveFlags |= SkCanvas::kClipToLayer_SaveFlag; clipToBoundsNeeded = false; // clipping done by saveLayer } - handler(mSaveLayerOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, - mAlpha * 255, SkXfermode::kSrcOver_Mode, saveFlags), PROPERTY_SAVECOUNT, - mClipToBounds); + + SaveLayerOp* op = new (handler.allocator()) SaveLayerOp( + 0, 0, mRight - mLeft, mBottom - mTop, + mAlpha * 255, SkXfermode::kSrcOver_Mode, saveFlags); + handler(op, PROPERTY_SAVECOUNT, mClipToBounds); } } if (clipToBoundsNeeded) { - handler(mClipRectOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op), - PROPERTY_SAVECOUNT, mClipToBounds); + ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0, + mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op); + handler(op, PROPERTY_SAVECOUNT, mClipToBounds); + } +} + +/** + * Apply property-based transformations to input matrix + */ +void DisplayList::applyViewPropertyTransforms(mat4& matrix) { + if (mLeft != 0 || mTop != 0) { + matrix.translate(mLeft, mTop); + } + if (mStaticMatrix) { + mat4 stat(*mStaticMatrix); + matrix.multiply(stat); + } else if (mAnimationMatrix) { + mat4 anim(*mAnimationMatrix); + matrix.multiply(anim); + } + if (mMatrixFlags != 0) { + if (mMatrixFlags == TRANSLATION) { + matrix.translate(mTranslationX, mTranslationY, mTranslationZ); + } else { +#if DEBUG_ENABLE_3D + matrix.multiply(mTransform); +#else + mat4 temp(*mTransformMatrix); + matrix.multiply(temp); +#endif + } + } +} + +/** + * Organizes the DisplayList hierarchy to prepare for Z-based draw order. + * + * This should be called before a call to defer() or drawDisplayList() + * + * Each DisplayList that serves as a 3d root builds its list of composited children, + * which are flagged to not draw in the standard draw loop. + */ +void DisplayList::computeOrdering() { + ATRACE_CALL(); + mat4::identity(); + for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) { + DrawDisplayListOp* childOp = mDisplayListData->children[i]; + childOp->mDisplayList->computeOrderingImpl(childOp, &m3dNodes, &mat4::identity()); + } +} + +void DisplayList::computeOrderingImpl( + DrawDisplayListOp* opState, + KeyedVector<float, Vector<DrawDisplayListOp*> >* compositedChildrenOf3dRoot, + const mat4* transformFrom3dRoot) { + // TODO: should avoid this calculation in most cases + opState->mTransformFrom3dRoot.load(*transformFrom3dRoot); + opState->mTransformFrom3dRoot.multiply(opState->mTransformFromParent); + + if (mTranslationZ != 0.0f) { // TODO: other signals, such as custom 4x4 matrix + // composited layer, insert into current 3d root and flag for out of order draw + opState->mSkipInOrderDraw = true; + + Vector3 pivot(mPivotX, mPivotY, 0.0f); + mat4 totalTransform(opState->mTransformFrom3dRoot); + applyViewPropertyTransforms(totalTransform); + totalTransform.mapPoint3d(pivot); + const float key = pivot.z; + + if (compositedChildrenOf3dRoot->indexOfKey(key) < 0) { + compositedChildrenOf3dRoot->add(key, Vector<DrawDisplayListOp*>()); + } + compositedChildrenOf3dRoot->editValueFor(key).push(opState); + } else { + // standard in order draw + opState->mSkipInOrderDraw = false; + } + + m3dNodes.clear(); + if (mIs3dRoot) { + // create a new 3d space for children by separating their ordering + compositedChildrenOf3dRoot = &m3dNodes; + transformFrom3dRoot = &mat4::identity(); + } else { + transformFrom3dRoot = &(opState->mTransformFrom3dRoot); + } + + if (mDisplayListData->children.size() > 0) { + for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) { + DrawDisplayListOp* childOp = mDisplayListData->children[i]; + childOp->mDisplayList->computeOrderingImpl(childOp, + compositedChildrenOf3dRoot, transformFrom3dRoot); + } } } @@ -454,6 +553,8 @@ public: inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds); } + inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); } + private: DeferStateStruct& mDeferStruct; const int mLevel; @@ -474,6 +575,8 @@ public: #endif operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds); } + inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); } + private: ReplayStateStruct& mReplayStruct; const int mLevel; @@ -490,11 +593,60 @@ void DisplayList::replay(ReplayStateStruct& replayStruct, const int level) { replayStruct.mDrawGlStatus); } +template <class T> +void DisplayList::iterate3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer, + T& handler, const int level) { + if (m3dNodes.size() == 0 || + (mode == kNegativeZChildren && m3dNodes.keyAt(0) > 0.0f) || + (mode == kPositiveZChildren && m3dNodes.keyAt(m3dNodes.size() - 1) < 0.0f)) { + // nothing to draw + return; + } + + LinearAllocator& alloc = handler.allocator(); + ClipRectOp* op = new (alloc) ClipRectOp(0, 0, mWidth, mHeight, + SkRegion::kIntersect_Op); // clip to 3d root bounds for now + handler(op, PROPERTY_SAVECOUNT, mClipToBounds); + int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + + for (int i = 0; i < m3dNodes.size(); i++) { + const float zValue = m3dNodes.keyAt(i); + + if (mode == kPositiveZChildren && zValue < 0.0f) continue; + if (mode == kNegativeZChildren && zValue > 0.0f) break; + + const Vector<DrawDisplayListOp*>& nodesAtZ = m3dNodes[i]; + for (int j = 0; j < nodesAtZ.size(); j++) { + DrawDisplayListOp* op = nodesAtZ[j]; + if (mode == kPositiveZChildren) { + /* draw shadow on renderer with parent matrix applied, passing in the child's total matrix + * + * TODO: + * -determine and pass background shape (and possibly drawable alpha) + * -view must opt-in to shadows + * -consider shadows for other content + */ + mat4 shadowMatrix(op->mTransformFrom3dRoot); + op->mDisplayList->applyViewPropertyTransforms(shadowMatrix); + DisplayListOp* shadowOp = new (alloc) DrawShadowOp(shadowMatrix, op->mDisplayList->mAlpha, + op->mDisplayList->getWidth(), op->mDisplayList->getHeight()); + handler(shadowOp, PROPERTY_SAVECOUNT, mClipToBounds); + } + + renderer.concatMatrix(op->mTransformFrom3dRoot); + op->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone + handler(op, renderer.getSaveCount() - 1, mClipToBounds); + op->mSkipInOrderDraw = true; + } + } + handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, mClipToBounds); +} + /** * This function serves both defer and replay modes, and will organize the displayList's component * operations for a single frame: * - * Every 'simple' operation that affects just the matrix and alpha (or other factors of + * Every 'simple' state operation that affects just the matrix and alpha (or other factors of * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom * defer logic) and operations in displayListOps are issued through the 'handler' which handles the * defer vs replay logic, per operation @@ -517,8 +669,9 @@ void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) clipRect->right, clipRect->bottom); #endif + LinearAllocator& alloc = handler.allocator(); int restoreTo = renderer.getSaveCount(); - handler(mSaveOp->reinit(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), + handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), PROPERTY_SAVECOUNT, mClipToBounds); DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "", @@ -526,30 +679,31 @@ void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) setViewProperties<T>(renderer, handler, level + 1); - if (mClipToBounds && renderer.quickRejectConservative(0, 0, mWidth, mHeight)) { - DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); - handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds); - renderer.restoreToCount(restoreTo); - renderer.setOverrideLayerAlpha(1.0f); - return; - } + bool quickRejected = mClipToBounds && renderer.quickRejectConservative(0, 0, mWidth, mHeight); + if (!quickRejected) { + // for 3d root, draw children with negative z values + iterate3dChildren(kNegativeZChildren, renderer, handler, level); - DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); - int saveCount = renderer.getSaveCount() - 1; - for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { - DisplayListOp *op = mDisplayListData->displayListOps[i]; + DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); + const int saveCountOffset = renderer.getSaveCount() - 1; + for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { + DisplayListOp *op = mDisplayListData->displayListOps[i]; #if DEBUG_DISPLAY_LIST - op->output(level + 1); + op->output(level + 1); #endif - logBuffer.writeCommand(level, op->name()); - handler(op, saveCount, mClipToBounds); + logBuffer.writeCommand(level, op->name()); + handler(op, saveCountOffset, mClipToBounds); + } + + // for 3d root, draw children with positive z values + iterate3dChildren(kPositiveZChildren, renderer, handler, level); } DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); - handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds); - renderer.restoreToCount(restoreTo); + handler(new (alloc) RestoreToCountOp(restoreTo), + PROPERTY_SAVECOUNT, mClipToBounds); renderer.setOverrideLayerAlpha(1.0f); } diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index f52181a..4bd79eb 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -26,6 +26,7 @@ #include <private/hwui/DrawGlInfo.h> +#include <utils/KeyedVector.h> #include <utils/LinearAllocator.h> #include <utils/RefBase.h> #include <utils/SortedVector.h> @@ -37,6 +38,8 @@ #include <androidfw/ResourceTypes.h> #include "Debug.h" +#include "Matrix.h" +#include "DeferredDisplayList.h" #define TRANSLATION 0x0001 #define ROTATION 0x0002 @@ -65,36 +68,70 @@ class ClipRectOp; class SaveLayerOp; class SaveOp; class RestoreToCountOp; +class DrawDisplayListOp; -struct DeferStateStruct { - DeferStateStruct(DeferredDisplayList& deferredList, OpenGLRenderer& renderer, int replayFlags) - : mDeferredList(deferredList), mRenderer(renderer), mReplayFlags(replayFlags) {} - DeferredDisplayList& mDeferredList; +/** + * Holds data used in the playback a tree of DisplayLists. + */ +class PlaybackStateStruct { +protected: + PlaybackStateStruct(OpenGLRenderer& renderer, int replayFlags, LinearAllocator* allocator) + : mRenderer(renderer), mReplayFlags(replayFlags), mAllocator(allocator){} + +public: OpenGLRenderer& mRenderer; const int mReplayFlags; + + // Allocator with the lifetime of a single frame. + // replay uses an Allocator owned by the struct, while defer shares the DeferredDisplayList's Allocator + LinearAllocator * const mAllocator; }; -struct ReplayStateStruct { +class DeferStateStruct : public PlaybackStateStruct { +public: + DeferStateStruct(DeferredDisplayList& deferredList, OpenGLRenderer& renderer, int replayFlags) + : PlaybackStateStruct(renderer, replayFlags, &(deferredList.mAllocator)), mDeferredList(deferredList) {} + + DeferredDisplayList& mDeferredList; +}; + +class ReplayStateStruct : public PlaybackStateStruct { +public: ReplayStateStruct(OpenGLRenderer& renderer, Rect& dirty, int replayFlags) - : mRenderer(renderer), mDirty(dirty), mReplayFlags(replayFlags), - mDrawGlStatus(DrawGlInfo::kStatusDone) {} - OpenGLRenderer& mRenderer; + : PlaybackStateStruct(renderer, replayFlags, &mReplayAllocator), + mDirty(dirty), mDrawGlStatus(DrawGlInfo::kStatusDone) {} + Rect& mDirty; - const int mReplayFlags; status_t mDrawGlStatus; + LinearAllocator mReplayAllocator; }; /** - * Refcounted structure that holds data used in display list stream + * Refcounted structure that holds the list of commands used in display list stream. */ class DisplayListData : public LightRefBase<DisplayListData> { public: + // allocator into which all ops were allocated LinearAllocator allocator; + + // pointers to all ops within display list, pointing into allocator data Vector<DisplayListOp*> displayListOps; + + // list of children display lists for quick, non-drawing traversal + Vector<DrawDisplayListOp*> children; }; /** - * Replays recorded drawing commands. + * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties. + * + * Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording + * functionality is split between DisplayListRenderer (which manages the recording), DisplayListData + * (which holds the actual data), and DisplayList (which holds properties and performs playback onto + * a renderer). + * + * Note that DisplayListData is swapped out from beneath an individual DisplayList when a view's + * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay + * attached. */ class DisplayList { public: @@ -113,6 +150,7 @@ public: void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false); + void computeOrdering(); void defer(DeferStateStruct& deferStruct, const int level); void replay(ReplayStateStruct& replayStruct, const int level); @@ -188,12 +226,7 @@ public: void setTranslationX(float translationX) { if (translationX != mTranslationX) { mTranslationX = translationX; - mMatrixDirty = true; - if (mTranslationX == 0.0f && mTranslationY == 0.0f) { - mMatrixFlags &= ~TRANSLATION; - } else { - mMatrixFlags |= TRANSLATION; - } + onTranslationUpdate(); } } @@ -204,12 +237,7 @@ public: void setTranslationY(float translationY) { if (translationY != mTranslationY) { mTranslationY = translationY; - mMatrixDirty = true; - if (mTranslationX == 0.0f && mTranslationY == 0.0f) { - mMatrixFlags &= ~TRANSLATION; - } else { - mMatrixFlags |= TRANSLATION; - } + onTranslationUpdate(); } } @@ -217,6 +245,17 @@ public: return mTranslationY; } + void setTranslationZ(float translationZ) { + if (translationZ != mTranslationZ) { + mTranslationZ = translationZ; + onTranslationUpdate(); + } + } + + float getTranslationZ() const { + return mTranslationZ; + } + void setRotation(float rotation) { if (rotation != mRotation) { mRotation = rotation; @@ -454,12 +493,36 @@ public: } private: + enum ChildrenSelectMode { + kNegativeZChildren, + kPositiveZChildren + }; + + void onTranslationUpdate() { + mMatrixDirty = true; + if (mTranslationX == 0.0f && mTranslationY == 0.0f && mTranslationZ == 0.0f) { + mMatrixFlags &= ~TRANSLATION; + } else { + mMatrixFlags |= TRANSLATION; + } + } + void outputViewProperties(const int level); + void applyViewPropertyTransforms(mat4& matrix); + + void computeOrderingImpl(DrawDisplayListOp* opState, + KeyedVector<float, Vector<DrawDisplayListOp*> >* compositedChildrenOf3dRoot, + const mat4* transformFromRoot); + template <class T> inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level); template <class T> + inline void iterate3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer, + T& handler, const int level); + + template <class T> inline void iterate(OpenGLRenderer& renderer, T& handler, const int level); void init(); @@ -509,7 +572,7 @@ private: bool mClipToBounds; float mAlpha; bool mHasOverlappingRendering; - float mTranslationX, mTranslationY; + float mTranslationX, mTranslationY, mTranslationZ; float mRotation, mRotationX, mRotationY; float mScaleX, mScaleY; float mPivotX, mPivotY; @@ -526,23 +589,17 @@ private: SkMatrix* mTransformMatrix3D; SkMatrix* mStaticMatrix; SkMatrix* mAnimationMatrix; + Matrix4 mTransform; bool mCaching; + bool mIs3dRoot; + /** - * State operations - needed to defer displayList property operations (for example, when setting - * an alpha causes a SaveLayerAlpha to occur). These operations point into mDisplayListData's - * allocation, or null if uninitialized. - * - * These are initialized (via friend re-constructors) when a displayList is issued in either - * replay or deferred mode. If replaying, the ops are not used until the next frame. If - * deferring, the ops may be stored in the DeferredDisplayList to be played back a second time. - * - * They should be used at most once per frame (one call to 'iterate') to avoid overwriting data + * Draw time state - these properties are only set and used during rendering */ - ClipRectOp* mClipRectOp; - SaveLayerOp* mSaveLayerOp; - SaveOp* mSaveOp; - RestoreToCountOp* mRestoreToCountOp; + + // for 3d roots, contains a z sorted list of all children items + KeyedVector<float, Vector<DrawDisplayListOp*> > m3dNodes; // TODO: good data structure }; // class DisplayList }; // namespace uirenderer diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 88077d4..1980b03 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -262,7 +262,6 @@ protected: /////////////////////////////////////////////////////////////////////////////// class SaveOp : public StateOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: SaveOp(int flags) : mFlags(flags) {} @@ -295,7 +294,6 @@ private: }; class RestoreToCountOp : public StateOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: RestoreToCountOp(int count) : mCount(count) {} @@ -328,7 +326,6 @@ private: }; class SaveLayerOp : public StateOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: SaveLayerOp(float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode, int flags) @@ -524,7 +521,6 @@ protected: }; class ClipRectOp : public ClipOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: ClipRectOp(float left, float top, float right, float bottom, SkRegion::Op op) : ClipOp(op), mArea(left, top, right, bottom) {} @@ -1100,7 +1096,7 @@ private: class DrawColorOp : public DrawOp { public: DrawColorOp(int color, SkXfermode::Mode mode) - : DrawOp(0), mColor(color), mMode(mode) {}; + : DrawOp(NULL), mColor(color), mMode(mode) {}; virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { return renderer.drawColor(mColor, mMode); @@ -1505,7 +1501,7 @@ private: class DrawFunctorOp : public DrawOp { public: DrawFunctorOp(Functor* functor) - : DrawOp(0), mFunctor(functor) {} + : DrawOp(NULL), mFunctor(functor) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { renderer.startMark("GL functor"); @@ -1525,20 +1521,21 @@ private: }; class DrawDisplayListOp : public DrawBoundedOp { + friend class DisplayList; // grant DisplayList access to info of child public: - DrawDisplayListOp(DisplayList* displayList, int flags) + DrawDisplayListOp(DisplayList* displayList, int flags, const mat4& transformFromParent) : DrawBoundedOp(0, 0, displayList->getWidth(), displayList->getHeight(), 0), - mDisplayList(displayList), mFlags(flags) {} + mDisplayList(displayList), mFlags(flags), mTransformFromParent(transformFromParent) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, bool useQuickReject) { - if (mDisplayList && mDisplayList->isRenderable()) { + if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) { mDisplayList->defer(deferStruct, level + 1); } } virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level, bool useQuickReject) { - if (mDisplayList && mDisplayList->isRenderable()) { + if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) { mDisplayList->replay(replayStruct, level + 1); } } @@ -1559,13 +1556,58 @@ public: private: DisplayList* mDisplayList; - int mFlags; + const int mFlags; + + /////////////////////////// + // Properties below are used by DisplayList::computeOrderingImpl() and iterate() + /////////////////////////// + /** + * Records transform vs parent, used for computing total transform without rerunning DL contents + */ + const mat4 mTransformFromParent; + + /** + * Holds the transformation between the 3d root ViewGroup and this DisplayList drawing + * instance. Represents any translations / transformations done within the drawing of the 3d + * root ViewGroup's draw, before the draw of the View represented by this DisplayList draw + * instance. + * + * Note: doesn't include any transformation recorded within the DisplayList and its properties. + */ + mat4 mTransformFrom3dRoot; + bool mSkipInOrderDraw; +}; + +/** + * Not a canvas operation, used only by 3d / z ordering logic in DisplayList::iterate() + */ +class DrawShadowOp : public DrawOp { +public: + DrawShadowOp(const mat4& casterTransform, float casterAlpha, float width, float height) + : DrawOp(NULL), mCasterTransform(casterTransform), mCasterAlpha(casterAlpha), + mWidth(width), mHeight(height) {} + + virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { + return renderer.drawShadow(mCasterTransform, mCasterAlpha, mWidth, mHeight); + } + + virtual void output(int level, uint32_t logFlags) const { + OP_LOG("DrawShadow of width %.2f, height %.2f", mWidth, mHeight); + } + + virtual const char* name() { return "DrawShadow"; } + +private: + const mat4 mCasterTransform; + const float mCasterAlpha; + const float mWidth; + const float mHeight; }; class DrawLayerOp : public DrawOp { public: DrawLayerOp(Layer* layer, float x, float y) - : DrawOp(0), mLayer(layer), mX(x), mY(y) {} + : DrawOp(NULL), mLayer(layer), mX(x), mY(y) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { return renderer.drawLayer(mLayer, mX, mY); diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index d024923..19a027d 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -111,6 +111,7 @@ DisplayList* DisplayListRenderer::getDisplayList(DisplayList* displayList) { } else { displayList->initFromDisplayListRenderer(*this, true); } + // TODO: should just avoid setting the DisplayList's DisplayListData displayList->setRenderable(mHasDrawOps); return displayList; } @@ -120,7 +121,8 @@ bool DisplayListRenderer::isDeferred() { } void DisplayListRenderer::setViewport(int width, int height) { - mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); + // TODO: DisplayListRenderer shouldn't have a projection matrix, as it should never be used + mViewProjMatrix.loadOrtho(0, width, height, 0, -1, 1); mWidth = width; mHeight = height; @@ -248,7 +250,10 @@ status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList, // resources cache, but we rely on the caller (UI toolkit) to // do the right thing for now - addDrawOp(new (alloc()) DrawDisplayListOp(displayList, flags)); + DrawDisplayListOp* op = new (alloc()) DrawDisplayListOp(displayList, flags, currentTransform()); + addDrawOp(op); + mDisplayListData->children.push(op); + return DrawGlInfo::kStatusDone; } diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index d233150..7269378 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -328,6 +328,7 @@ private: return patch; } + // TODO: move these to DisplayListData Vector<SkBitmap*> mBitmapResources; Vector<SkBitmap*> mOwnedBitmapResources; Vector<SkiaColorFilter*> mFilterResources; diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 48613fe..f7493a3 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -733,7 +733,9 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int if (mRs == 0) { mRs = new RSC::RS(); - if (!mRs->init(RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) { + // a null path is OK because there are no custom kernels used + // hence nothing gets cached by RS + if (!mRs->init("", RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) { ALOGE("blur RS failed to init"); } diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index bd371a3..742ffd4 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -194,11 +194,9 @@ void Layer::defer() { dirtyRect.set(0, 0, width, height); } - if (deferredList) { - deferredList->reset(dirtyRect); - } else { - deferredList = new DeferredDisplayList(dirtyRect); - } + delete deferredList; + deferredList = new DeferredDisplayList(dirtyRect); + DeferStateStruct deferredState(*deferredList, *renderer, DisplayList::kReplayFlag_ClipChildren); @@ -206,6 +204,7 @@ void Layer::defer() { renderer->setupFrameState(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); + displayList->computeOrdering(); displayList->defer(deferredState, 0); deferredUpdateScheduled = false; diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp index ba22071..4f5cd26 100644 --- a/libs/hwui/Matrix.cpp +++ b/libs/hwui/Matrix.cpp @@ -89,8 +89,9 @@ uint8_t Matrix4::getType() const { float m01 = data[kSkewX]; float m10 = data[kSkewY]; float m11 = data[kScaleY]; + float m32 = data[kTranslateZ]; - if (m01 != 0.0f || m10 != 0.0f) { + if (m01 != 0.0f || m10 != 0.0f || m32 != 0.0f) { mType |= kTypeAffine; } @@ -131,11 +132,13 @@ bool Matrix4::changesBounds() const { } bool Matrix4::isPureTranslate() const { - return getGeometryType() <= kTypeTranslate; + // NOTE: temporary hack to workaround ignoreTransform behavior with Z values + // TODO: separate this into isPure2dTranslate vs isPure3dTranslate + return getGeometryType() <= kTypeTranslate && (data[kTranslateZ] == 0.0f); } bool Matrix4::isSimple() const { - return getGeometryType() <= (kTypeScale | kTypeTranslate); + return getGeometryType() <= (kTypeScale | kTypeTranslate) && (data[kTranslateZ] == 0.0f); } bool Matrix4::isIdentity() const { @@ -369,6 +372,84 @@ void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) { mType = kTypeUnknown; } +// translated from android.opengl.Matrix#frustumM() +void Matrix4::loadFrustum(float left, float top, float right, float bottom, float near, float far) { + float r_width = 1.0f / (right - left); + float r_height = 1.0f / (top - bottom); + float r_depth = 1.0f / (near - far); + float x = 2.0f * (near * r_width); + float y = 2.0f * (near * r_height); + float A = (right + left) * r_width; + float B = (top + bottom) * r_height; + float C = (far + near) * r_depth; + float D = 2.0f * (far * near * r_depth); + + memset(&data, 0, sizeof(data)); + mType = kTypeUnknown; + + data[kScaleX] = x; + data[kScaleY] = y; + data[8] = A; + data[9] = B; + data[kScaleZ] = C; + data[kTranslateZ] = D; + data[11] = -1.0f; +} + +// translated from android.opengl.Matrix#setLookAtM() +void Matrix4::loadLookAt(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) { + float fx = centerX - eyeX; + float fy = centerY - eyeY; + float fz = centerZ - eyeZ; + + // Normalize f + float rlf = 1.0f / sqrt(fx*fx + fy*fy + fz*fz); + fx *= rlf; + fy *= rlf; + fz *= rlf; + + // compute s = f x up (x means "cross product") + float sx = fy * upZ - fz * upY; + float sy = fz * upX - fx * upZ; + float sz = fx * upY - fy * upX; + + // and normalize s + float rls = 1.0f / sqrt(sx*sx + sy*sy + sz*sz); + sx *= rls; + sy *= rls; + sz *= rls; + + // compute u = s x f + float ux = sy * fz - sz * fy; + float uy = sz * fx - sx * fz; + float uz = sx * fy - sy * fx; + + mType = kTypeUnknown; + data[0] = sx; + data[1] = ux; + data[2] = -fx; + data[3] = 0.0f; + + data[4] = sy; + data[5] = uy; + data[6] = -fy; + data[7] = 0.0f; + + data[8] = sz; + data[9] = uz; + data[10] = -fz; + data[11] = 0.0f; + + data[12] = 0.0f; + data[13] = 0.0f; + data[14] = 0.0f; + data[15] = 1.0f; + + translate(-eyeX, -eyeY, -eyeZ); +} + void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) { loadIdentity(); @@ -382,6 +463,14 @@ void Matrix4::loadOrtho(float left, float right, float bottom, float top, float mType = kTypeTranslate | kTypeScale | kTypeRectToRect; } +void Matrix4::mapPoint3d(Vector3& vec) const { + //TODO: optimize simple case + Vector3 orig(vec); + vec.x = orig.x * data[kScaleX] + orig.y * data[kSkewX] + orig.z * data[8] + data[kTranslateX]; + vec.y = orig.x * data[kSkewY] + orig.y * data[kScaleY] + orig.z * data[9] + data[kTranslateY]; + vec.z = orig.x * data[2] + orig.y * data[6] + orig.z * data[kScaleZ] + data[kTranslateZ]; +} + #define MUL_ADD_STORE(a, b, c) a = (a) * (b) + (c) void Matrix4::mapPoint(float& x, float& y) const { diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index b861ba4..00ca050 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -121,6 +121,10 @@ public: void loadRotate(float angle); void loadRotate(float angle, float x, float y, float z); void loadMultiply(const Matrix4& u, const Matrix4& v); + void loadFrustum(float left, float top, float right, float bottom, float near, float far); + void loadLookAt(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ); void loadOrtho(float left, float right, float bottom, float top, float near, float far); @@ -134,17 +138,18 @@ public: void multiply(float v); - void translate(float x, float y) { + void translate(float x, float y, float z = 0) { if ((getType() & sGeometryMask) <= kTypeTranslate) { data[kTranslateX] += x; data[kTranslateY] += y; + data[kTranslateZ] += z; } else { // Doing a translation will only affect the translate bit of the type // Save the type uint8_t type = mType; Matrix4 u; - u.loadTranslate(x, y, 0.0f); + u.loadTranslate(x, y, z); multiply(u); // Restore the type and fix the translate bit @@ -190,8 +195,9 @@ public: void copyTo(float* v) const; void copyTo(SkMatrix& v) const; - void mapRect(Rect& r) const; - void mapPoint(float& x, float& y) const; + void mapPoint3d(Vector3& vec) const; + void mapPoint(float& x, float& y) const; // 2d only + void mapRect(Rect& r) const; // 2d only float getTranslateX() const; float getTranslateY() const; diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 214d0b1..578a251 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -36,7 +36,9 @@ #include "Fence.h" #include "PathTessellator.h" #include "Properties.h" +#include "ShadowTessellator.h" #include "Vector.h" +#include "VertexBuffer.h" namespace android { namespace uirenderer { @@ -180,7 +182,21 @@ void OpenGLRenderer::setViewport(int width, int height) { } void OpenGLRenderer::initViewport(int width, int height) { - mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); + float dist = std::max(width, height) * 1.5; + + if (DEBUG_ENABLE_3D) { + // TODO: make view proj app configurable + Matrix4 projection; + projection.loadFrustum(-width / 2, -height / 2, width / 2, height / 2, dist, 0); + Matrix4 view; + view.loadLookAt(0, 0, dist, + 0, 0, 0, + 0, 1, 0); + mViewProjMatrix.loadMultiply(projection, view); + mViewProjMatrix.translate(-width/2, -height/2); + } else { + mViewProjMatrix.loadOrtho(0, width, height, 0, -1, 1); + } mWidth = width; mHeight = height; @@ -753,7 +769,7 @@ bool OpenGLRenderer::restoreSnapshot() { if (restoreOrtho) { Rect& r = previous->viewport; glViewport(r.left, r.top, r.right, r.bottom); - mOrthoMatrix.load(current->orthoMatrix); + mViewProjMatrix.load(current->orthoMatrix); } mSaveCount--; @@ -984,7 +1000,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); mSnapshot->height = bounds.getHeight(); - mSnapshot->orthoMatrix.load(mOrthoMatrix); + mSnapshot->orthoMatrix.load(mViewProjMatrix); endTiling(); debugOverdraw(false, false); @@ -1013,7 +1029,9 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { // Change the ortho projection glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); - mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); + + // TODO: determine best way to support 3d drawing within HW layers + mViewProjMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); return true; } @@ -1539,10 +1557,8 @@ void OpenGLRenderer::getMatrix(SkMatrix* matrix) { } void OpenGLRenderer::concatMatrix(SkMatrix* matrix) { - SkMatrix transform; - currentTransform().copyTo(transform); - transform.preConcat(*matrix); - currentTransform().load(transform); + mat4 transform(*matrix); + currentTransform().multiply(transform); } /////////////////////////////////////////////////////////////////////////////// @@ -1927,10 +1943,10 @@ void OpenGLRenderer::setupDrawModelView(ModelViewMode mode, bool offset, bool dirty = right - left > 0.0f && bottom - top > 0.0f; if (!ignoreTransform) { - mCaches.currentProgram->set(mOrthoMatrix, mModelView, currentTransform(), offset); + mCaches.currentProgram->set(mViewProjMatrix, mModelView, currentTransform(), offset); if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, currentTransform()); } else { - mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity(), offset); + mCaches.currentProgram->set(mViewProjMatrix, mModelView, mat4::identity(), offset); if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom); } } @@ -2064,9 +2080,12 @@ void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) { status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t replayFlags) { status_t status; + // All the usual checks and setup operations (quickReject, setupDraw, etc.) // will be performed by the display list itself if (displayList && displayList->isRenderable()) { + // compute 3d ordering + displayList->computeOrdering(); if (CC_UNLIKELY(mCaches.drawDeferDisabled)) { status = startFrame(); ReplayStateStruct replayStruct(*this, dirty, replayFlags); @@ -2082,7 +2101,7 @@ status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, flushLayers(); status = startFrame(); - return status | deferredList.flush(*this, dirty); + return deferredList.flush(*this, dirty) | status; } return DrawGlInfo::kStatusDone; @@ -2562,7 +2581,7 @@ status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPa setupDrawColorFilterUniforms(); setupDrawShaderUniforms(); - void* vertices = vertexBuffer.getBuffer(); + const void* vertices = vertexBuffer.getBuffer(); bool force = mCaches.unbindMeshBuffer(); mCaches.bindPositionVertexPointer(true, vertices, isAA ? gAlphaVertexStride : gVertexStride); mCaches.resetTexCoordsVertexPointer(); @@ -3366,6 +3385,25 @@ status_t OpenGLRenderer::drawRects(const float* rects, int count, SkPaint* paint return drawColorRects(rects, count, color, mode); } +status_t OpenGLRenderer::drawShadow(const mat4& casterTransform, float casterAlpha, + float width, float height) { + if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone; + + // For now, always and scissor + // TODO: use quickReject + mCaches.enableScissor(); + + SkPaint paint; + paint.setColor(0x3f000000); + // Force the draw to use alpha values. + paint.setAntiAlias(true); + + VertexBuffer shadowVertexBuffer; + ShadowTessellator::tessellateAmbientShadow(width, height, casterTransform, + shadowVertexBuffer); + return drawVertexBuffer(shadowVertexBuffer, &paint); +} + status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color, SkXfermode::Mode mode, bool ignoreTransform, bool dirty, bool clip) { if (count == 0) { diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index ff37e18..6046531 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -265,6 +265,12 @@ public: ANDROID_API void getMatrix(SkMatrix* matrix); virtual void setMatrix(SkMatrix* matrix); virtual void concatMatrix(SkMatrix* matrix); + virtual void concatMatrix(Matrix4& matrix) { + currentTransform().multiply(matrix); + } + void translateZ(float z) { + currentTransform().translate(0,0,z); + } ANDROID_API const Rect& getClipBounds(); @@ -314,6 +320,9 @@ public: DrawOpMode drawOpMode = kDrawOpMode_Immediate); virtual status_t drawRects(const float* rects, int count, SkPaint* paint); + status_t drawShadow(const mat4& casterTransform, float casterAlpha, + float width, float height); + virtual void resetShader(); virtual void setupShader(SkiaShader* shader); @@ -1069,8 +1078,8 @@ private: // Dimensions of the drawing surface int mWidth, mHeight; - // Matrix used for ortho projection in shaders - mat4 mOrthoMatrix; + // Matrix used for view/projection in shaders + mat4 mViewProjMatrix; /** * Model-view matrix used to position/size objects diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h index e0044e8..236658d 100644 --- a/libs/hwui/PathTessellator.h +++ b/libs/hwui/PathTessellator.h @@ -22,84 +22,11 @@ #include "Matrix.h" #include "Rect.h" #include "Vertex.h" +#include "VertexBuffer.h" namespace android { namespace uirenderer { -class VertexBuffer { -public: - VertexBuffer(): - mBuffer(0), - mVertexCount(0), - mCleanupMethod(NULL) - {} - - ~VertexBuffer() { - if (mCleanupMethod) mCleanupMethod(mBuffer); - } - - /** - This should be the only method used by the PathTessellator. 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() - */ - template <class TYPE> - TYPE* alloc(int vertexCount) { - if (mVertexCount) { - TYPE* reallocBuffer = (TYPE*)mReallocBuffer; - // already have allocated the buffer, re-allocate space within - if (mReallocBuffer != mBuffer) { - // not first re-allocation, leave space for degenerate triangles to separate strips - reallocBuffer += 2; - } - mReallocBuffer = reallocBuffer + vertexCount; - return reallocBuffer; - } - mVertexCount = vertexCount; - mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount]; - mCleanupMethod = &(cleanup<TYPE>); - - return (TYPE*)mBuffer; - } - - template <class TYPE> - void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) { - int verticesToCopy = srcBuffer.getVertexCount(); - - TYPE* dst = alloc<TYPE>(verticesToCopy); - TYPE* src = (TYPE*)srcBuffer.getBuffer(); - - for (int i = 0; i < verticesToCopy; i++) { - TYPE::copyWithOffset(&dst[i], src[i], xOffset, yOffset); - } - } - - void* getBuffer() const { return mBuffer; } // shouldn't be const, since not a const ptr? - unsigned int getVertexCount() const { return mVertexCount; } - - template <class TYPE> - void createDegenerateSeparators(int allocSize) { - TYPE* end = (TYPE*)mBuffer + mVertexCount; - for (TYPE* degen = (TYPE*)mBuffer + allocSize; degen < end; degen += 2 + allocSize) { - memcpy(degen, degen - 1, sizeof(TYPE)); - memcpy(degen + 1, degen + 2, sizeof(TYPE)); - } - } - -private: - template <class TYPE> - static void cleanup(void* buffer) { - delete[] (TYPE*)buffer; - } - - void* mBuffer; - unsigned int mVertexCount; - - void* mReallocBuffer; // used for multi-allocation - - void (*mCleanupMethod)(void*); -}; - class PathTessellator { public: static void expandBoundsForStroke(SkRect& bounds, const SkPaint* paint); diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp new file mode 100644 index 0000000..49a3d2c --- /dev/null +++ b/libs/hwui/ShadowTessellator.cpp @@ -0,0 +1,73 @@ +/* + * 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" + +#include <math.h> +#include <utils/Log.h> + +#include "AmbientShadow.h" +#include "ShadowTessellator.h" + +namespace android { +namespace uirenderer { + +// TODO: Support path as the input of the polygon instead of the rect's width +// and height. +void ShadowTessellator::tessellateAmbientShadow(float width, float height, + const mat4& casterTransform, VertexBuffer& shadowVertexBuffer) { + + Vector3 pivot(width / 2, height / 2, 0.0f); + casterTransform.mapPoint3d(pivot); + + // TODO: The zScaleFactor need to be mapped to the screen. + float zScaleFactor = 0.5; + Rect blockRect(pivot.x - width * zScaleFactor, pivot.y - height * zScaleFactor, + pivot.x + width * zScaleFactor, pivot.y + height * zScaleFactor); + + // Generate the caster's polygon from the rect. + // TODO: support arbitrary polygon, and the z value need to be computed + // according to the transformation for each vertex. + const int vertexCount = 4; + Vector3 polygon[vertexCount]; + polygon[0].x = blockRect.left; + polygon[0].y = blockRect.top; + polygon[0].z = pivot.z; + polygon[1].x = blockRect.right; + polygon[1].y = blockRect.top; + polygon[1].z = pivot.z; + polygon[2].x = blockRect.right; + polygon[2].y = blockRect.bottom; + polygon[2].z = pivot.z; + polygon[3].x = blockRect.left; + polygon[3].y = blockRect.bottom; + polygon[3].z = pivot.z; + + // A bunch of parameters to tweak the shadow. + // TODO: Allow some of these changable by debug settings or APIs. + const int rays = 120; + const int layers = 2; + const float strength = 0.5; + const float heightFactor = 120; + const float geomFactor = 60; + + AmbientShadow::createAmbientShadow(polygon, vertexCount, rays, layers, strength, + heightFactor, geomFactor, shadowVertexBuffer); + +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h new file mode 100644 index 0000000..05cb00c --- /dev/null +++ b/libs/hwui/ShadowTessellator.h @@ -0,0 +1,37 @@ + +/* + * 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_SHADOW_TESSELLATOR_H +#define ANDROID_HWUI_SHADOW_TESSELLATOR_H + +#include "Debug.h" +#include "Matrix.h" + +namespace android { +namespace uirenderer { + +class ShadowTessellator { +public: + static void tessellateAmbientShadow(float width, float height, + const mat4& casterTransform, VertexBuffer& shadowVertexBuffer); + +}; // ShadowTessellator + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_SHADOW_TESSELLATOR_H diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h index 497924e..5110272 100644 --- a/libs/hwui/Vector.h +++ b/libs/hwui/Vector.h @@ -107,6 +107,21 @@ struct Vector2 { } }; // class Vector2 +class Vector3 { +public: + float x; + float y; + float z; + + Vector3() : + x(0.0f), y(0.0f), z(0.0f) { + } + + Vector3(float px, float py, float pz) : + x(px), y(py), z(pz) { + } +}; + /////////////////////////////////////////////////////////////////////////////// // Types /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h new file mode 100644 index 0000000..8b6872e --- /dev/null +++ b/libs/hwui/VertexBuffer.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2012 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_VERTEX_BUFFER_H +#define ANDROID_HWUI_VERTEX_BUFFER_H + + +namespace android { +namespace uirenderer { + +class VertexBuffer { +public: + VertexBuffer(): + mBuffer(0), + mVertexCount(0), + mCleanupMethod(NULL) + {} + + ~VertexBuffer() { + if (mCleanupMethod) mCleanupMethod(mBuffer); + } + + /** + 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()) + */ + template <class TYPE> + TYPE* alloc(int vertexCount) { + if (mVertexCount) { + TYPE* reallocBuffer = (TYPE*)mReallocBuffer; + // already have allocated the buffer, re-allocate space within + if (mReallocBuffer != mBuffer) { + // not first re-allocation, leave space for degenerate triangles to separate strips + reallocBuffer += 2; + } + mReallocBuffer = reallocBuffer + vertexCount; + return reallocBuffer; + } + mVertexCount = vertexCount; + mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount]; + mCleanupMethod = &(cleanup<TYPE>); + + return (TYPE*)mBuffer; + } + + template <class TYPE> + void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) { + int verticesToCopy = srcBuffer.getVertexCount(); + + TYPE* dst = alloc<TYPE>(verticesToCopy); + TYPE* src = (TYPE*)srcBuffer.getBuffer(); + + for (int i = 0; i < verticesToCopy; i++) { + TYPE::copyWithOffset(&dst[i], src[i], xOffset, yOffset); + } + } + + const void* getBuffer() const { return mBuffer; } + unsigned int getVertexCount() const { return mVertexCount; } + + template <class TYPE> + void createDegenerateSeparators(int allocSize) { + TYPE* end = (TYPE*)mBuffer + mVertexCount; + for (TYPE* degen = (TYPE*)mBuffer + allocSize; degen < end; degen += 2 + allocSize) { + memcpy(degen, degen - 1, sizeof(TYPE)); + memcpy(degen + 1, degen + 2, sizeof(TYPE)); + } + } + +private: + template <class TYPE> + static void cleanup(void* buffer) { + delete[] (TYPE*)buffer; + } + + void* mBuffer; + unsigned int mVertexCount; + + void* mReallocBuffer; // used for multi-allocation + + void (*mCleanupMethod)(void*); +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_VERTEX_BUFFER_H |