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