diff options
Diffstat (limited to 'libs/hwui/TessellationCache.cpp')
-rw-r--r-- | libs/hwui/TessellationCache.cpp | 444 |
1 files changed, 0 insertions, 444 deletions
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp deleted file mode 100644 index c7d93da718e7..000000000000 --- a/libs/hwui/TessellationCache.cpp +++ /dev/null @@ -1,444 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <utils/JenkinsHash.h> -#include <utils/Trace.h> - -#include "Caches.h" -#include "PathTessellator.h" -#include "ShadowTessellator.h" -#include "TessellationCache.h" - -#include "thread/Signal.h" -#include "thread/Task.h" -#include "thread/TaskProcessor.h" - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Cache entries -/////////////////////////////////////////////////////////////////////////////// - -TessellationCache::Description::Description() - : type(Type::None) - , scaleX(1.0f) - , scaleY(1.0f) - , aa(false) - , cap(SkPaint::kDefault_Cap) - , style(SkPaint::kFill_Style) - , strokeWidth(1.0f) { - // Shape bits should be set to zeroes, because they are used for hash calculation. - memset(&shape, 0, sizeof(Shape)); -} - -TessellationCache::Description::Description(Type type, const Matrix4& transform, - const SkPaint& paint) - : type(type) - , aa(paint.isAntiAlias()) - , cap(paint.getStrokeCap()) - , style(paint.getStyle()) - , strokeWidth(paint.getStrokeWidth()) { - PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY); - // Shape bits should be set to zeroes, because they are used for hash calculation. - memset(&shape, 0, sizeof(Shape)); -} - -bool TessellationCache::Description::operator==(const TessellationCache::Description& rhs) const { - if (type != rhs.type) return false; - if (scaleX != rhs.scaleX) return false; - if (scaleY != rhs.scaleY) return false; - if (aa != rhs.aa) return false; - if (cap != rhs.cap) return false; - if (style != rhs.style) return false; - if (strokeWidth != rhs.strokeWidth) return false; - if (type == Type::None) return true; - const Shape::RoundRect& lRect = shape.roundRect; - const Shape::RoundRect& rRect = rhs.shape.roundRect; - - if (lRect.width != rRect.width) return false; - if (lRect.height != rRect.height) return false; - if (lRect.rx != rRect.rx) return false; - return lRect.ry == rRect.ry; -} - -hash_t TessellationCache::Description::hash() const { - uint32_t hash = JenkinsHashMix(0, static_cast<int>(type)); - hash = JenkinsHashMix(hash, aa); - hash = JenkinsHashMix(hash, cap); - hash = JenkinsHashMix(hash, style); - hash = JenkinsHashMix(hash, android::hash_type(strokeWidth)); - hash = JenkinsHashMix(hash, android::hash_type(scaleX)); - hash = JenkinsHashMix(hash, android::hash_type(scaleY)); - hash = JenkinsHashMixBytes(hash, (uint8_t*)&shape, sizeof(Shape)); - return JenkinsHashWhiten(hash); -} - -void TessellationCache::Description::setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const { - matrix->loadScale(scaleX, scaleY, 1.0f); - paint->setAntiAlias(aa); - paint->setStrokeCap(cap); - paint->setStyle(style); - paint->setStrokeWidth(strokeWidth); -} - -TessellationCache::ShadowDescription::ShadowDescription() : nodeKey(nullptr) { - memset(&matrixData, 0, sizeof(matrixData)); -} - -TessellationCache::ShadowDescription::ShadowDescription(const SkPath* nodeKey, - const Matrix4* drawTransform) - : nodeKey(nodeKey) { - memcpy(&matrixData, drawTransform->data, sizeof(matrixData)); -} - -bool TessellationCache::ShadowDescription::operator==( - const TessellationCache::ShadowDescription& rhs) const { - return nodeKey == rhs.nodeKey && memcmp(&matrixData, &rhs.matrixData, sizeof(matrixData)) == 0; -} - -hash_t TessellationCache::ShadowDescription::hash() const { - uint32_t hash = JenkinsHashMixBytes(0, (uint8_t*)&nodeKey, sizeof(const void*)); - hash = JenkinsHashMixBytes(hash, (uint8_t*)&matrixData, sizeof(matrixData)); - return JenkinsHashWhiten(hash); -} - -/////////////////////////////////////////////////////////////////////////////// -// General purpose tessellation task processing -/////////////////////////////////////////////////////////////////////////////// - -class TessellationCache::TessellationTask : public Task<VertexBuffer*> { -public: - TessellationTask(Tessellator tessellator, const Description& description) - : tessellator(tessellator), description(description) {} - - ~TessellationTask() {} - - Tessellator tessellator; - Description description; -}; - -class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> { -public: - explicit TessellationProcessor(Caches& caches) : TaskProcessor<VertexBuffer*>(&caches.tasks) {} - ~TessellationProcessor() {} - - virtual void onProcess(const sp<Task<VertexBuffer*> >& task) override { - TessellationTask* t = static_cast<TessellationTask*>(task.get()); - ATRACE_NAME("shape tessellation"); - VertexBuffer* buffer = t->tessellator(t->description); - t->setResult(buffer); - } -}; - -class TessellationCache::Buffer { -public: - explicit Buffer(const sp<Task<VertexBuffer*> >& task) : mTask(task), mBuffer(nullptr) {} - - ~Buffer() { - mTask.clear(); - delete mBuffer; - } - - unsigned int getSize() { - blockOnPrecache(); - return mBuffer->getSize(); - } - - const VertexBuffer* getVertexBuffer() { - blockOnPrecache(); - return mBuffer; - } - -private: - void blockOnPrecache() { - if (mTask != nullptr) { - mBuffer = mTask->getResult(); - LOG_ALWAYS_FATAL_IF(mBuffer == nullptr, "Failed to precache"); - mTask.clear(); - } - } - sp<Task<VertexBuffer*> > mTask; - VertexBuffer* mBuffer; -}; - -/////////////////////////////////////////////////////////////////////////////// -// Shadow tessellation task processing -/////////////////////////////////////////////////////////////////////////////// - -static void mapPointFakeZ(Vector3& point, const mat4* transformXY, const mat4* transformZ) { - // map z coordinate with true 3d matrix - point.z = transformZ->mapZ(point); - - // map x,y coordinates with draw/Skia matrix - transformXY->mapPoint(point.x, point.y); -} - -static void reverseVertexArray(Vertex* polygon, int len) { - int n = len / 2; - for (int i = 0; i < n; i++) { - Vertex tmp = polygon[i]; - int k = len - 1 - i; - polygon[i] = polygon[k]; - polygon[k] = tmp; - } -} - -void tessellateShadows(const Matrix4* drawTransform, const Rect* localClip, bool isCasterOpaque, - const SkPath* casterPerimeter, const Matrix4* casterTransformXY, - const Matrix4* casterTransformZ, const Vector3& lightCenter, - float lightRadius, VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer) { - // tessellate caster outline into a 2d polygon - std::vector<Vertex> casterVertices2d; - const float casterRefinementThreshold = 2.0f; - PathTessellator::approximatePathOutlineVertices(*casterPerimeter, casterRefinementThreshold, - casterVertices2d); - - // Shadow requires CCW for now. TODO: remove potential double-reverse - reverseVertexArray(&casterVertices2d.front(), casterVertices2d.size()); - - if (casterVertices2d.size() == 0) return; - - // map 2d caster poly into 3d - const int casterVertexCount = casterVertices2d.size(); - Vector3 casterPolygon[casterVertexCount]; - float minZ = FLT_MAX; - float maxZ = -FLT_MAX; - for (int i = 0; i < casterVertexCount; i++) { - const Vertex& point2d = casterVertices2d[i]; - casterPolygon[i] = (Vector3){point2d.x, point2d.y, 0}; - mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ); - minZ = std::min(minZ, casterPolygon[i].z); - maxZ = std::max(maxZ, casterPolygon[i].z); - } - - // map the centroid of the caster into 3d - Vector2 centroid = ShadowTessellator::centroid2d( - reinterpret_cast<const Vector2*>(&casterVertices2d.front()), casterVertexCount); - Vector3 centroid3d = {centroid.x, centroid.y, 0}; - mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ); - - // if the caster intersects the z=0 plane, lift it in Z so it doesn't - if (minZ < SHADOW_MIN_CASTER_Z) { - float casterLift = SHADOW_MIN_CASTER_Z - minZ; - for (int i = 0; i < casterVertexCount; i++) { - casterPolygon[i].z += casterLift; - } - centroid3d.z += casterLift; - } - - // Check whether we want to draw the shadow at all by checking the caster's bounds against clip. - // We only have ortho projection, so we can just ignore the Z in caster for - // simple rejection calculation. - Rect casterBounds(casterPerimeter->getBounds()); - casterTransformXY->mapRect(casterBounds); - - // actual tessellation of both shadows - ShadowTessellator::tessellateAmbientShadow(isCasterOpaque, casterPolygon, casterVertexCount, - centroid3d, casterBounds, *localClip, maxZ, - ambientBuffer); - - ShadowTessellator::tessellateSpotShadow(isCasterOpaque, casterPolygon, casterVertexCount, - centroid3d, *drawTransform, lightCenter, lightRadius, - casterBounds, *localClip, spotBuffer); -} - -class ShadowProcessor : public TaskProcessor<TessellationCache::vertexBuffer_pair_t> { -public: - explicit ShadowProcessor(Caches& caches) - : TaskProcessor<TessellationCache::vertexBuffer_pair_t>(&caches.tasks) {} - ~ShadowProcessor() {} - - virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t> >& task) override { - TessellationCache::ShadowTask* t = static_cast<TessellationCache::ShadowTask*>(task.get()); - ATRACE_NAME("shadow tessellation"); - - tessellateShadows(&t->drawTransform, &t->localClip, t->opaque, &t->casterPerimeter, - &t->transformXY, &t->transformZ, t->lightCenter, t->lightRadius, - t->ambientBuffer, t->spotBuffer); - - t->setResult(TessellationCache::vertexBuffer_pair_t(&t->ambientBuffer, &t->spotBuffer)); - } -}; - -/////////////////////////////////////////////////////////////////////////////// -// Cache constructor/destructor -/////////////////////////////////////////////////////////////////////////////// - -TessellationCache::TessellationCache() - : mMaxSize(MB(1)) - , mCache(LruCache<Description, Buffer*>::kUnlimitedCapacity) - , mShadowCache( - LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) { - mCache.setOnEntryRemovedListener(&mBufferRemovedListener); - mShadowCache.setOnEntryRemovedListener(&mBufferPairRemovedListener); - mDebugEnabled = Properties::debugLevel & kDebugCaches; -} - -TessellationCache::~TessellationCache() { - mCache.clear(); -} - -/////////////////////////////////////////////////////////////////////////////// -// Size management -/////////////////////////////////////////////////////////////////////////////// - -uint32_t TessellationCache::getSize() { - LruCache<Description, Buffer*>::Iterator iter(mCache); - uint32_t size = 0; - while (iter.next()) { - size += iter.value()->getSize(); - } - return size; -} - -uint32_t TessellationCache::getMaxSize() { - return mMaxSize; -} - -/////////////////////////////////////////////////////////////////////////////// -// Caching -/////////////////////////////////////////////////////////////////////////////// - -void TessellationCache::trim() { - uint32_t size = getSize(); - while (size > mMaxSize) { - size -= mCache.peekOldestValue()->getSize(); - mCache.removeOldest(); - } - mShadowCache.clear(); -} - -void TessellationCache::clear() { - mCache.clear(); - mShadowCache.clear(); -} - -/////////////////////////////////////////////////////////////////////////////// -// Callbacks -/////////////////////////////////////////////////////////////////////////////// - -void TessellationCache::BufferRemovedListener::operator()(Description& description, - Buffer*& buffer) { - delete buffer; -} - -/////////////////////////////////////////////////////////////////////////////// -// Shadows -/////////////////////////////////////////////////////////////////////////////// - -void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect& localClip, - bool opaque, const SkPath* casterPerimeter, - const Matrix4* transformXY, const Matrix4* transformZ, - const Vector3& lightCenter, float lightRadius) { - ShadowDescription key(casterPerimeter, drawTransform); - - if (mShadowCache.get(key)) return; - sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque, casterPerimeter, - transformXY, transformZ, lightCenter, lightRadius); - if (mShadowProcessor == nullptr) { - mShadowProcessor = new ShadowProcessor(Caches::getInstance()); - } - mShadowProcessor->add(task); - task->incStrong(nullptr); // not using sp<>s, so manually ref while in the cache - mShadowCache.put(key, task.get()); -} - -sp<TessellationCache::ShadowTask> TessellationCache::getShadowTask( - const Matrix4* drawTransform, const Rect& localClip, bool opaque, - const SkPath* casterPerimeter, const Matrix4* transformXY, const Matrix4* transformZ, - const Vector3& lightCenter, float lightRadius) { - ShadowDescription key(casterPerimeter, drawTransform); - ShadowTask* task = static_cast<ShadowTask*>(mShadowCache.get(key)); - if (!task) { - precacheShadows(drawTransform, localClip, opaque, casterPerimeter, transformXY, transformZ, - lightCenter, lightRadius); - task = static_cast<ShadowTask*>(mShadowCache.get(key)); - } - LOG_ALWAYS_FATAL_IF(task == nullptr, "shadow not precached"); - return task; -} - -/////////////////////////////////////////////////////////////////////////////// -// Tessellation precaching -/////////////////////////////////////////////////////////////////////////////// - -TessellationCache::Buffer* TessellationCache::getOrCreateBuffer(const Description& entry, - Tessellator tessellator) { - Buffer* buffer = mCache.get(entry); - if (!buffer) { - // not cached, enqueue a task to fill the buffer - sp<TessellationTask> task = new TessellationTask(tessellator, entry); - buffer = new Buffer(task); - - if (mProcessor == nullptr) { - mProcessor = new TessellationProcessor(Caches::getInstance()); - } - mProcessor->add(task); - bool inserted = mCache.put(entry, buffer); - // Note to the static analyzer that this insert should always succeed. - LOG_ALWAYS_FATAL_IF(!inserted, "buffers shouldn't spontaneously appear in the cache"); - } - return buffer; -} - -static VertexBuffer* tessellatePath(const TessellationCache::Description& description, - const SkPath& path) { - Matrix4 matrix; - SkPaint paint; - description.setupMatrixAndPaint(&matrix, &paint); - VertexBuffer* buffer = new VertexBuffer(); - PathTessellator::tessellatePath(path, &paint, matrix, *buffer); - return buffer; -} - -/////////////////////////////////////////////////////////////////////////////// -// RoundRect -/////////////////////////////////////////////////////////////////////////////// - -static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description) { - SkRect rect = - SkRect::MakeWH(description.shape.roundRect.width, description.shape.roundRect.height); - float rx = description.shape.roundRect.rx; - float ry = description.shape.roundRect.ry; - if (description.style == SkPaint::kStrokeAndFill_Style) { - float outset = description.strokeWidth / 2; - rect.outset(outset, outset); - rx += outset; - ry += outset; - } - SkPath path; - path.addRoundRect(rect, rx, ry); - return tessellatePath(description, path); -} - -TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(const Matrix4& transform, - const SkPaint& paint, float width, - float height, float rx, float ry) { - Description entry(Description::Type::RoundRect, transform, paint); - entry.shape.roundRect.width = width; - entry.shape.roundRect.height = height; - entry.shape.roundRect.rx = rx; - entry.shape.roundRect.ry = ry; - return getOrCreateBuffer(entry, &tessellateRoundRect); -} -const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, const SkPaint& paint, - float width, float height, float rx, float ry) { - return getRoundRectBuffer(transform, paint, width, height, rx, ry)->getVertexBuffer(); -} - -}; // namespace uirenderer -}; // namespace android |