summaryrefslogtreecommitdiff
path: root/libs/hwui/TessellationCache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui/TessellationCache.cpp')
-rw-r--r--libs/hwui/TessellationCache.cpp444
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