Home | History | Annotate | Download | only in hwui
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <utils/JenkinsHash.h>
     18 #include <utils/Trace.h>
     19 
     20 #include "Caches.h"
     21 #include "PathTessellator.h"
     22 #include "ShadowTessellator.h"
     23 #include "TessellationCache.h"
     24 
     25 #include "thread/Signal.h"
     26 #include "thread/Task.h"
     27 #include "thread/TaskProcessor.h"
     28 
     29 namespace android {
     30 namespace uirenderer {
     31 
     32 ///////////////////////////////////////////////////////////////////////////////
     33 // Cache entries
     34 ///////////////////////////////////////////////////////////////////////////////
     35 
     36 TessellationCache::Description::Description()
     37         : type(Type::None)
     38         , scaleX(1.0f)
     39         , scaleY(1.0f)
     40         , aa(false)
     41         , cap(SkPaint::kDefault_Cap)
     42         , style(SkPaint::kFill_Style)
     43         , strokeWidth(1.0f) {
     44     // Shape bits should be set to zeroes, because they are used for hash calculation.
     45     memset(&shape, 0, sizeof(Shape));
     46 }
     47 
     48 TessellationCache::Description::Description(Type type, const Matrix4& transform, const SkPaint& paint)
     49         : type(type)
     50         , aa(paint.isAntiAlias())
     51         , cap(paint.getStrokeCap())
     52         , style(paint.getStyle())
     53         , strokeWidth(paint.getStrokeWidth()) {
     54     PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
     55     // Shape bits should be set to zeroes, because they are used for hash calculation.
     56     memset(&shape, 0, sizeof(Shape));
     57 }
     58 
     59 bool TessellationCache::Description::operator==(const TessellationCache::Description& rhs) const {
     60     if (type != rhs.type) return false;
     61     if (scaleX != rhs.scaleX) return false;
     62     if (scaleY != rhs.scaleY) return false;
     63     if (aa != rhs.aa) return false;
     64     if (cap != rhs.cap) return false;
     65     if (style != rhs.style) return false;
     66     if (strokeWidth != rhs.strokeWidth) return false;
     67     if (type == Type::None) return true;
     68     const Shape::RoundRect& lRect = shape.roundRect;
     69     const Shape::RoundRect& rRect = rhs.shape.roundRect;
     70 
     71     if (lRect.width != rRect.width) return false;
     72     if (lRect.height != rRect.height) return false;
     73     if (lRect.rx != rRect.rx) return false;
     74     return lRect.ry == rRect.ry;
     75 }
     76 
     77 hash_t TessellationCache::Description::hash() const {
     78     uint32_t hash = JenkinsHashMix(0, static_cast<int>(type));
     79     hash = JenkinsHashMix(hash, aa);
     80     hash = JenkinsHashMix(hash, cap);
     81     hash = JenkinsHashMix(hash, style);
     82     hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
     83     hash = JenkinsHashMix(hash, android::hash_type(scaleX));
     84     hash = JenkinsHashMix(hash, android::hash_type(scaleY));
     85     hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape));
     86     return JenkinsHashWhiten(hash);
     87 }
     88 
     89 void TessellationCache::Description::setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const {
     90     matrix->loadScale(scaleX, scaleY, 1.0f);
     91     paint->setAntiAlias(aa);
     92     paint->setStrokeCap(cap);
     93     paint->setStyle(style);
     94     paint->setStrokeWidth(strokeWidth);
     95 }
     96 
     97 TessellationCache::ShadowDescription::ShadowDescription()
     98         : nodeKey(nullptr) {
     99     memset(&matrixData, 0, sizeof(matrixData));
    100 }
    101 
    102 TessellationCache::ShadowDescription::ShadowDescription(const SkPath* nodeKey, const Matrix4* drawTransform)
    103         : nodeKey(nodeKey) {
    104     memcpy(&matrixData, drawTransform->data, sizeof(matrixData));
    105 }
    106 
    107 bool TessellationCache::ShadowDescription::operator==(
    108         const TessellationCache::ShadowDescription& rhs) const {
    109     return nodeKey == rhs.nodeKey
    110             && memcmp(&matrixData, &rhs.matrixData, sizeof(matrixData)) == 0;
    111 }
    112 
    113 hash_t TessellationCache::ShadowDescription::hash() const {
    114     uint32_t hash = JenkinsHashMixBytes(0, (uint8_t*) &nodeKey, sizeof(const void*));
    115     hash = JenkinsHashMixBytes(hash, (uint8_t*) &matrixData, sizeof(matrixData));
    116     return JenkinsHashWhiten(hash);
    117 }
    118 
    119 ///////////////////////////////////////////////////////////////////////////////
    120 // General purpose tessellation task processing
    121 ///////////////////////////////////////////////////////////////////////////////
    122 
    123 class TessellationCache::TessellationTask : public Task<VertexBuffer*> {
    124 public:
    125     TessellationTask(Tessellator tessellator, const Description& description)
    126         : tessellator(tessellator)
    127         , description(description) {
    128     }
    129 
    130     ~TessellationTask() {}
    131 
    132     Tessellator tessellator;
    133     Description description;
    134 };
    135 
    136 class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> {
    137 public:
    138     explicit TessellationProcessor(Caches& caches)
    139             : TaskProcessor<VertexBuffer*>(&caches.tasks) {}
    140     ~TessellationProcessor() {}
    141 
    142     virtual void onProcess(const sp<Task<VertexBuffer*> >& task) override {
    143         TessellationTask* t = static_cast<TessellationTask*>(task.get());
    144         ATRACE_NAME("shape tessellation");
    145         VertexBuffer* buffer = t->tessellator(t->description);
    146         t->setResult(buffer);
    147     }
    148 };
    149 
    150 class TessellationCache::Buffer {
    151 public:
    152     explicit Buffer(const sp<Task<VertexBuffer*> >& task)
    153             : mTask(task)
    154             , mBuffer(nullptr) {
    155     }
    156 
    157     ~Buffer() {
    158         mTask.clear();
    159         delete mBuffer;
    160     }
    161 
    162     unsigned int getSize() {
    163         blockOnPrecache();
    164         return mBuffer->getSize();
    165     }
    166 
    167     const VertexBuffer* getVertexBuffer() {
    168         blockOnPrecache();
    169         return mBuffer;
    170     }
    171 
    172 private:
    173     void blockOnPrecache() {
    174         if (mTask != nullptr) {
    175             mBuffer = mTask->getResult();
    176             LOG_ALWAYS_FATAL_IF(mBuffer == nullptr, "Failed to precache");
    177             mTask.clear();
    178         }
    179     }
    180     sp<Task<VertexBuffer*> > mTask;
    181     VertexBuffer* mBuffer;
    182 };
    183 
    184 ///////////////////////////////////////////////////////////////////////////////
    185 // Shadow tessellation task processing
    186 ///////////////////////////////////////////////////////////////////////////////
    187 
    188 static void mapPointFakeZ(Vector3& point, const mat4* transformXY, const mat4* transformZ) {
    189     // map z coordinate with true 3d matrix
    190     point.z = transformZ->mapZ(point);
    191 
    192     // map x,y coordinates with draw/Skia matrix
    193     transformXY->mapPoint(point.x, point.y);
    194 }
    195 
    196 static void reverseVertexArray(Vertex* polygon, int len) {
    197     int n = len / 2;
    198     for (int i = 0; i < n; i++) {
    199         Vertex tmp = polygon[i];
    200         int k = len - 1 - i;
    201         polygon[i] = polygon[k];
    202         polygon[k] = tmp;
    203     }
    204 }
    205 
    206 void tessellateShadows(
    207         const Matrix4* drawTransform, const Rect* localClip,
    208         bool isCasterOpaque, const SkPath* casterPerimeter,
    209         const Matrix4* casterTransformXY, const Matrix4* casterTransformZ,
    210         const Vector3& lightCenter, float lightRadius,
    211         VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer) {
    212 
    213     // tessellate caster outline into a 2d polygon
    214     std::vector<Vertex> casterVertices2d;
    215     const float casterRefinementThreshold = 2.0f;
    216     PathTessellator::approximatePathOutlineVertices(*casterPerimeter,
    217             casterRefinementThreshold, casterVertices2d);
    218 
    219     // Shadow requires CCW for now. TODO: remove potential double-reverse
    220     reverseVertexArray(&casterVertices2d.front(), casterVertices2d.size());
    221 
    222     if (casterVertices2d.size() == 0) return;
    223 
    224     // map 2d caster poly into 3d
    225     const int casterVertexCount = casterVertices2d.size();
    226     Vector3 casterPolygon[casterVertexCount];
    227     float minZ = FLT_MAX;
    228     float maxZ = -FLT_MAX;
    229     for (int i = 0; i < casterVertexCount; i++) {
    230         const Vertex& point2d = casterVertices2d[i];
    231         casterPolygon[i] = (Vector3){point2d.x, point2d.y, 0};
    232         mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
    233         minZ = std::min(minZ, casterPolygon[i].z);
    234         maxZ = std::max(maxZ, casterPolygon[i].z);
    235     }
    236 
    237     // map the centroid of the caster into 3d
    238     Vector2 centroid =  ShadowTessellator::centroid2d(
    239             reinterpret_cast<const Vector2*>(&casterVertices2d.front()),
    240             casterVertexCount);
    241     Vector3 centroid3d = {centroid.x, centroid.y, 0};
    242     mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ);
    243 
    244     // if the caster intersects the z=0 plane, lift it in Z so it doesn't
    245     if (minZ < SHADOW_MIN_CASTER_Z) {
    246         float casterLift = SHADOW_MIN_CASTER_Z - minZ;
    247         for (int i = 0; i < casterVertexCount; i++) {
    248             casterPolygon[i].z += casterLift;
    249         }
    250         centroid3d.z += casterLift;
    251     }
    252 
    253     // Check whether we want to draw the shadow at all by checking the caster's bounds against clip.
    254     // We only have ortho projection, so we can just ignore the Z in caster for
    255     // simple rejection calculation.
    256     Rect casterBounds(casterPerimeter->getBounds());
    257     casterTransformXY->mapRect(casterBounds);
    258 
    259     // actual tessellation of both shadows
    260     ShadowTessellator::tessellateAmbientShadow(
    261             isCasterOpaque, casterPolygon, casterVertexCount, centroid3d,
    262             casterBounds, *localClip, maxZ, ambientBuffer);
    263 
    264     ShadowTessellator::tessellateSpotShadow(
    265             isCasterOpaque, casterPolygon, casterVertexCount, centroid3d,
    266             *drawTransform, lightCenter, lightRadius, casterBounds, *localClip,
    267             spotBuffer);
    268 }
    269 
    270 class ShadowProcessor : public TaskProcessor<TessellationCache::vertexBuffer_pair_t> {
    271 public:
    272     explicit ShadowProcessor(Caches& caches)
    273             : TaskProcessor<TessellationCache::vertexBuffer_pair_t>(&caches.tasks) {}
    274     ~ShadowProcessor() {}
    275 
    276     virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t> >& task) override {
    277         TessellationCache::ShadowTask* t = static_cast<TessellationCache::ShadowTask*>(task.get());
    278         ATRACE_NAME("shadow tessellation");
    279 
    280         tessellateShadows(&t->drawTransform, &t->localClip, t->opaque, &t->casterPerimeter,
    281                 &t->transformXY, &t->transformZ, t->lightCenter, t->lightRadius,
    282                 t->ambientBuffer, t->spotBuffer);
    283 
    284         t->setResult(TessellationCache::vertexBuffer_pair_t(&t->ambientBuffer, &t->spotBuffer));
    285     }
    286 };
    287 
    288 ///////////////////////////////////////////////////////////////////////////////
    289 // Cache constructor/destructor
    290 ///////////////////////////////////////////////////////////////////////////////
    291 
    292 TessellationCache::TessellationCache()
    293         : mMaxSize(MB(1))
    294         , mCache(LruCache<Description, Buffer*>::kUnlimitedCapacity)
    295         , mShadowCache(LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) {
    296     mCache.setOnEntryRemovedListener(&mBufferRemovedListener);
    297     mShadowCache.setOnEntryRemovedListener(&mBufferPairRemovedListener);
    298     mDebugEnabled = Properties::debugLevel & kDebugCaches;
    299 }
    300 
    301 TessellationCache::~TessellationCache() {
    302     mCache.clear();
    303 }
    304 
    305 ///////////////////////////////////////////////////////////////////////////////
    306 // Size management
    307 ///////////////////////////////////////////////////////////////////////////////
    308 
    309 uint32_t TessellationCache::getSize() {
    310     LruCache<Description, Buffer*>::Iterator iter(mCache);
    311     uint32_t size = 0;
    312     while (iter.next()) {
    313         size += iter.value()->getSize();
    314     }
    315     return size;
    316 }
    317 
    318 uint32_t TessellationCache::getMaxSize() {
    319     return mMaxSize;
    320 }
    321 
    322 ///////////////////////////////////////////////////////////////////////////////
    323 // Caching
    324 ///////////////////////////////////////////////////////////////////////////////
    325 
    326 
    327 void TessellationCache::trim() {
    328     uint32_t size = getSize();
    329     while (size > mMaxSize) {
    330         size -= mCache.peekOldestValue()->getSize();
    331         mCache.removeOldest();
    332     }
    333     mShadowCache.clear();
    334 }
    335 
    336 void TessellationCache::clear() {
    337     mCache.clear();
    338     mShadowCache.clear();
    339 }
    340 
    341 ///////////////////////////////////////////////////////////////////////////////
    342 // Callbacks
    343 ///////////////////////////////////////////////////////////////////////////////
    344 
    345 void TessellationCache::BufferRemovedListener::operator()(Description& description,
    346         Buffer*& buffer) {
    347     delete buffer;
    348 }
    349 
    350 ///////////////////////////////////////////////////////////////////////////////
    351 // Shadows
    352 ///////////////////////////////////////////////////////////////////////////////
    353 
    354 void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
    355         bool opaque, const SkPath* casterPerimeter,
    356         const Matrix4* transformXY, const Matrix4* transformZ,
    357         const Vector3& lightCenter, float lightRadius) {
    358     ShadowDescription key(casterPerimeter, drawTransform);
    359 
    360     if (mShadowCache.get(key)) return;
    361     sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque,
    362             casterPerimeter, transformXY, transformZ, lightCenter, lightRadius);
    363     if (mShadowProcessor == nullptr) {
    364         mShadowProcessor = new ShadowProcessor(Caches::getInstance());
    365     }
    366     mShadowProcessor->add(task);
    367     task->incStrong(nullptr); // not using sp<>s, so manually ref while in the cache
    368     mShadowCache.put(key, task.get());
    369 }
    370 
    371 sp<TessellationCache::ShadowTask> TessellationCache::getShadowTask(
    372         const Matrix4* drawTransform, const Rect& localClip,
    373         bool opaque, const SkPath* casterPerimeter,
    374         const Matrix4* transformXY, const Matrix4* transformZ,
    375         const Vector3& lightCenter, float lightRadius) {
    376     ShadowDescription key(casterPerimeter, drawTransform);
    377     ShadowTask* task = static_cast<ShadowTask*>(mShadowCache.get(key));
    378     if (!task) {
    379         precacheShadows(drawTransform, localClip, opaque, casterPerimeter,
    380                 transformXY, transformZ, lightCenter, lightRadius);
    381         task = static_cast<ShadowTask*>(mShadowCache.get(key));
    382     }
    383     LOG_ALWAYS_FATAL_IF(task == nullptr, "shadow not precached");
    384     return task;
    385 }
    386 
    387 ///////////////////////////////////////////////////////////////////////////////
    388 // Tessellation precaching
    389 ///////////////////////////////////////////////////////////////////////////////
    390 
    391 TessellationCache::Buffer* TessellationCache::getOrCreateBuffer(
    392         const Description& entry, Tessellator tessellator) {
    393     Buffer* buffer = mCache.get(entry);
    394     if (!buffer) {
    395         // not cached, enqueue a task to fill the buffer
    396         sp<TessellationTask> task = new TessellationTask(tessellator, entry);
    397         buffer = new Buffer(task);
    398 
    399         if (mProcessor == nullptr) {
    400             mProcessor = new TessellationProcessor(Caches::getInstance());
    401         }
    402         mProcessor->add(task);
    403         mCache.put(entry, buffer);
    404     }
    405     return buffer;
    406 }
    407 
    408 static VertexBuffer* tessellatePath(const TessellationCache::Description& description,
    409         const SkPath& path) {
    410     Matrix4 matrix;
    411     SkPaint paint;
    412     description.setupMatrixAndPaint(&matrix, &paint);
    413     VertexBuffer* buffer = new VertexBuffer();
    414     PathTessellator::tessellatePath(path, &paint, matrix, *buffer);
    415     return buffer;
    416 }
    417 
    418 ///////////////////////////////////////////////////////////////////////////////
    419 // RoundRect
    420 ///////////////////////////////////////////////////////////////////////////////
    421 
    422 static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description) {
    423     SkRect rect = SkRect::MakeWH(description.shape.roundRect.width,
    424             description.shape.roundRect.height);
    425     float rx = description.shape.roundRect.rx;
    426     float ry = description.shape.roundRect.ry;
    427     if (description.style == SkPaint::kStrokeAndFill_Style) {
    428         float outset = description.strokeWidth / 2;
    429         rect.outset(outset, outset);
    430         rx += outset;
    431         ry += outset;
    432     }
    433     SkPath path;
    434     path.addRoundRect(rect, rx, ry);
    435     return tessellatePath(description, path);
    436 }
    437 
    438 TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(
    439         const Matrix4& transform, const SkPaint& paint,
    440         float width, float height, float rx, float ry) {
    441     Description entry(Description::Type::RoundRect, transform, paint);
    442     entry.shape.roundRect.width = width;
    443     entry.shape.roundRect.height = height;
    444     entry.shape.roundRect.rx = rx;
    445     entry.shape.roundRect.ry = ry;
    446     return getOrCreateBuffer(entry, &tessellateRoundRect);
    447 }
    448 const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, const SkPaint& paint,
    449         float width, float height, float rx, float ry) {
    450     return getRoundRectBuffer(transform, paint, width, height, rx, ry)->getVertexBuffer();
    451 }
    452 
    453 }; // namespace uirenderer
    454 }; // namespace android
    455