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 "OpenGLRenderer.h" 22#include "PathTessellator.h" 23#include "ShadowTessellator.h" 24#include "TessellationCache.h" 25 26#include "thread/Signal.h" 27#include "thread/Task.h" 28#include "thread/TaskProcessor.h" 29 30namespace android { 31namespace uirenderer { 32 33/////////////////////////////////////////////////////////////////////////////// 34// Cache entries 35/////////////////////////////////////////////////////////////////////////////// 36 37TessellationCache::Description::Description() 38 : type(kNone) 39 , scaleX(1.0f) 40 , scaleY(1.0f) 41 , aa(false) 42 , cap(SkPaint::kDefault_Cap) 43 , style(SkPaint::kFill_Style) 44 , strokeWidth(1.0f) { 45 memset(&shape, 0, sizeof(Shape)); 46} 47 48TessellationCache::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 memset(&shape, 0, sizeof(Shape)); 56} 57 58hash_t TessellationCache::Description::hash() const { 59 uint32_t hash = JenkinsHashMix(0, type); 60 hash = JenkinsHashMix(hash, aa); 61 hash = JenkinsHashMix(hash, cap); 62 hash = JenkinsHashMix(hash, style); 63 hash = JenkinsHashMix(hash, android::hash_type(strokeWidth)); 64 hash = JenkinsHashMix(hash, android::hash_type(scaleX)); 65 hash = JenkinsHashMix(hash, android::hash_type(scaleY)); 66 hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape)); 67 return JenkinsHashWhiten(hash); 68} 69 70void TessellationCache::Description::setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const { 71 matrix->loadScale(scaleX, scaleY, 1.0f); 72 paint->setAntiAlias(aa); 73 paint->setStrokeCap(cap); 74 paint->setStyle(style); 75 paint->setStrokeWidth(strokeWidth); 76} 77 78TessellationCache::ShadowDescription::ShadowDescription() 79 : nodeKey(NULL) { 80 memset(&matrixData, 0, 16 * sizeof(float)); 81} 82 83TessellationCache::ShadowDescription::ShadowDescription(const void* nodeKey, const Matrix4* drawTransform) 84 : nodeKey(nodeKey) { 85 memcpy(&matrixData, drawTransform->data, 16 * sizeof(float)); 86} 87 88hash_t TessellationCache::ShadowDescription::hash() const { 89 uint32_t hash = JenkinsHashMixBytes(0, (uint8_t*) &nodeKey, sizeof(const void*)); 90 hash = JenkinsHashMixBytes(hash, (uint8_t*) &matrixData, 16 * sizeof(float)); 91 return JenkinsHashWhiten(hash); 92} 93 94/////////////////////////////////////////////////////////////////////////////// 95// General purpose tessellation task processing 96/////////////////////////////////////////////////////////////////////////////// 97 98class TessellationCache::TessellationTask : public Task<VertexBuffer*> { 99public: 100 TessellationTask(Tessellator tessellator, const Description& description) 101 : tessellator(tessellator) 102 , description(description) { 103 } 104 105 ~TessellationTask() {} 106 107 Tessellator tessellator; 108 Description description; 109}; 110 111class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> { 112public: 113 TessellationProcessor(Caches& caches) 114 : TaskProcessor<VertexBuffer*>(&caches.tasks) {} 115 ~TessellationProcessor() {} 116 117 virtual void onProcess(const sp<Task<VertexBuffer*> >& task) { 118 TessellationTask* t = static_cast<TessellationTask*>(task.get()); 119 ATRACE_NAME("shape tessellation"); 120 VertexBuffer* buffer = t->tessellator(t->description); 121 t->setResult(buffer); 122 } 123}; 124 125class TessellationCache::Buffer { 126public: 127 Buffer(const sp<Task<VertexBuffer*> >& task) 128 : mTask(task) 129 , mBuffer(NULL) { 130 } 131 132 ~Buffer() { 133 mTask.clear(); 134 delete mBuffer; 135 } 136 137 unsigned int getSize() { 138 blockOnPrecache(); 139 return mBuffer->getSize(); 140 } 141 142 const VertexBuffer* getVertexBuffer() { 143 blockOnPrecache(); 144 return mBuffer; 145 } 146 147private: 148 void blockOnPrecache() { 149 if (mTask != NULL) { 150 mBuffer = mTask->getResult(); 151 LOG_ALWAYS_FATAL_IF(mBuffer == NULL, "Failed to precache"); 152 mTask.clear(); 153 } 154 } 155 sp<Task<VertexBuffer*> > mTask; 156 VertexBuffer* mBuffer; 157}; 158 159/////////////////////////////////////////////////////////////////////////////// 160// Shadow tessellation task processing 161/////////////////////////////////////////////////////////////////////////////// 162 163class ShadowTask : public Task<TessellationCache::vertexBuffer_pair_t*> { 164public: 165 ShadowTask(const Matrix4* drawTransform, const Rect& localClip, bool opaque, 166 const SkPath* casterPerimeter, const Matrix4* transformXY, const Matrix4* transformZ, 167 const Vector3& lightCenter, float lightRadius) 168 : drawTransform(*drawTransform) 169 , localClip(localClip) 170 , opaque(opaque) 171 , casterPerimeter(*casterPerimeter) 172 , transformXY(*transformXY) 173 , transformZ(*transformZ) 174 , lightCenter(lightCenter) 175 , lightRadius(lightRadius) { 176 } 177 178 ~ShadowTask() { 179 TessellationCache::vertexBuffer_pair_t* bufferPair = getResult(); 180 delete bufferPair->getFirst(); 181 delete bufferPair->getSecond(); 182 delete bufferPair; 183 } 184 185 /* Note - we deep copy all task parameters, because *even though* pointers into Allocator 186 * controlled objects (like the SkPath and Matrix4s) should be safe for the entire frame, 187 * certain Allocators are destroyed before trim() is called to flush incomplete tasks. 188 * 189 * These deep copies could be avoided, long term, by cancelling or flushing outstanding tasks 190 * before tearning down single-frame LinearAllocators. 191 */ 192 const Matrix4 drawTransform; 193 const Rect localClip; 194 bool opaque; 195 const SkPath casterPerimeter; 196 const Matrix4 transformXY; 197 const Matrix4 transformZ; 198 const Vector3 lightCenter; 199 const float lightRadius; 200}; 201 202static void mapPointFakeZ(Vector3& point, const mat4* transformXY, const mat4* transformZ) { 203 // map z coordinate with true 3d matrix 204 point.z = transformZ->mapZ(point); 205 206 // map x,y coordinates with draw/Skia matrix 207 transformXY->mapPoint(point.x, point.y); 208} 209 210static void tessellateShadows( 211 const Matrix4* drawTransform, const Rect* localClip, 212 bool isCasterOpaque, const SkPath* casterPerimeter, 213 const Matrix4* casterTransformXY, const Matrix4* casterTransformZ, 214 const Vector3& lightCenter, float lightRadius, 215 VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer) { 216 217 // tessellate caster outline into a 2d polygon 218 Vector<Vertex> casterVertices2d; 219 const float casterRefinementThresholdSquared = 4.0f; 220 PathTessellator::approximatePathOutlineVertices(*casterPerimeter, 221 casterRefinementThresholdSquared, casterVertices2d); 222 if (!ShadowTessellator::isClockwisePath(*casterPerimeter)) { 223 ShadowTessellator::reverseVertexArray(casterVertices2d.editArray(), 224 casterVertices2d.size()); 225 } 226 227 if (casterVertices2d.size() == 0) return; 228 229 // map 2d caster poly into 3d 230 const int casterVertexCount = casterVertices2d.size(); 231 Vector3 casterPolygon[casterVertexCount]; 232 float minZ = FLT_MAX; 233 float maxZ = -FLT_MAX; 234 for (int i = 0; i < casterVertexCount; i++) { 235 const Vertex& point2d = casterVertices2d[i]; 236 casterPolygon[i] = (Vector3){point2d.x, point2d.y, 0}; 237 mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ); 238 minZ = fmin(minZ, casterPolygon[i].z); 239 maxZ = fmax(maxZ, casterPolygon[i].z); 240 } 241 242 // map the centroid of the caster into 3d 243 Vector2 centroid = ShadowTessellator::centroid2d( 244 reinterpret_cast<const Vector2*>(casterVertices2d.array()), 245 casterVertexCount); 246 Vector3 centroid3d = {centroid.x, centroid.y, 0}; 247 mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ); 248 249 // if the caster intersects the z=0 plane, lift it in Z so it doesn't 250 if (minZ < SHADOW_MIN_CASTER_Z) { 251 float casterLift = SHADOW_MIN_CASTER_Z - minZ; 252 for (int i = 0; i < casterVertexCount; i++) { 253 casterPolygon[i].z += casterLift; 254 } 255 centroid3d.z += casterLift; 256 } 257 258 // Check whether we want to draw the shadow at all by checking the caster's bounds against clip. 259 // We only have ortho projection, so we can just ignore the Z in caster for 260 // simple rejection calculation. 261 Rect casterBounds(casterPerimeter->getBounds()); 262 casterTransformXY->mapRect(casterBounds); 263 264 // actual tessellation of both shadows 265 ShadowTessellator::tessellateAmbientShadow( 266 isCasterOpaque, casterPolygon, casterVertexCount, centroid3d, 267 casterBounds, *localClip, maxZ, ambientBuffer); 268 269 ShadowTessellator::tessellateSpotShadow( 270 isCasterOpaque, casterPolygon, casterVertexCount, centroid3d, 271 *drawTransform, lightCenter, lightRadius, casterBounds, *localClip, 272 spotBuffer); 273} 274 275class ShadowProcessor : public TaskProcessor<TessellationCache::vertexBuffer_pair_t*> { 276public: 277 ShadowProcessor(Caches& caches) 278 : TaskProcessor<TessellationCache::vertexBuffer_pair_t*>(&caches.tasks) {} 279 ~ShadowProcessor() {} 280 281 virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t*> >& task) { 282 ShadowTask* t = static_cast<ShadowTask*>(task.get()); 283 ATRACE_NAME("shadow tessellation"); 284 285 VertexBuffer* ambientBuffer = new VertexBuffer; 286 VertexBuffer* spotBuffer = new VertexBuffer; 287 tessellateShadows(&t->drawTransform, &t->localClip, t->opaque, &t->casterPerimeter, 288 &t->transformXY, &t->transformZ, t->lightCenter, t->lightRadius, 289 *ambientBuffer, *spotBuffer); 290 291 t->setResult(new TessellationCache::vertexBuffer_pair_t(ambientBuffer, spotBuffer)); 292 } 293}; 294 295/////////////////////////////////////////////////////////////////////////////// 296// Cache constructor/destructor 297/////////////////////////////////////////////////////////////////////////////// 298 299TessellationCache::TessellationCache() 300 : mSize(0) 301 , mMaxSize(MB(DEFAULT_VERTEX_CACHE_SIZE)) 302 , mCache(LruCache<Description, Buffer*>::kUnlimitedCapacity) 303 , mShadowCache(LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) { 304 char property[PROPERTY_VALUE_MAX]; 305 if (property_get(PROPERTY_VERTEX_CACHE_SIZE, property, NULL) > 0) { 306 INIT_LOGD(" Setting %s cache size to %sMB", name, property); 307 setMaxSize(MB(atof(property))); 308 } else { 309 INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_VERTEX_CACHE_SIZE); 310 } 311 312 mCache.setOnEntryRemovedListener(&mBufferRemovedListener); 313 mShadowCache.setOnEntryRemovedListener(&mBufferPairRemovedListener); 314 mDebugEnabled = readDebugLevel() & kDebugCaches; 315} 316 317TessellationCache::~TessellationCache() { 318 mCache.clear(); 319} 320 321/////////////////////////////////////////////////////////////////////////////// 322// Size management 323/////////////////////////////////////////////////////////////////////////////// 324 325uint32_t TessellationCache::getSize() { 326 LruCache<Description, Buffer*>::Iterator iter(mCache); 327 uint32_t size = 0; 328 while (iter.next()) { 329 size += iter.value()->getSize(); 330 } 331 return size; 332} 333 334uint32_t TessellationCache::getMaxSize() { 335 return mMaxSize; 336} 337 338void TessellationCache::setMaxSize(uint32_t maxSize) { 339 mMaxSize = maxSize; 340 while (mSize > mMaxSize) { 341 mCache.removeOldest(); 342 } 343} 344 345/////////////////////////////////////////////////////////////////////////////// 346// Caching 347/////////////////////////////////////////////////////////////////////////////// 348 349 350void TessellationCache::trim() { 351 uint32_t size = getSize(); 352 while (size > mMaxSize) { 353 size -= mCache.peekOldestValue()->getSize(); 354 mCache.removeOldest(); 355 } 356 mShadowCache.clear(); 357} 358 359void TessellationCache::clear() { 360 mCache.clear(); 361 mShadowCache.clear(); 362} 363 364/////////////////////////////////////////////////////////////////////////////// 365// Callbacks 366/////////////////////////////////////////////////////////////////////////////// 367 368void TessellationCache::BufferRemovedListener::operator()(Description& description, 369 Buffer*& buffer) { 370 delete buffer; 371} 372 373/////////////////////////////////////////////////////////////////////////////// 374// Shadows 375/////////////////////////////////////////////////////////////////////////////// 376 377void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect& localClip, 378 bool opaque, const SkPath* casterPerimeter, 379 const Matrix4* transformXY, const Matrix4* transformZ, 380 const Vector3& lightCenter, float lightRadius) { 381 ShadowDescription key(casterPerimeter, drawTransform); 382 383 sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque, 384 casterPerimeter, transformXY, transformZ, lightCenter, lightRadius); 385 if (mShadowProcessor == NULL) { 386 mShadowProcessor = new ShadowProcessor(Caches::getInstance()); 387 } 388 mShadowProcessor->add(task); 389 390 task->incStrong(NULL); // not using sp<>s, so manually ref while in the cache 391 mShadowCache.put(key, task.get()); 392} 393 394void TessellationCache::getShadowBuffers(const Matrix4* drawTransform, const Rect& localClip, 395 bool opaque, const SkPath* casterPerimeter, 396 const Matrix4* transformXY, const Matrix4* transformZ, 397 const Vector3& lightCenter, float lightRadius, vertexBuffer_pair_t& outBuffers) { 398 ShadowDescription key(casterPerimeter, drawTransform); 399 ShadowTask* task = static_cast<ShadowTask*>(mShadowCache.get(key)); 400 if (!task) { 401 precacheShadows(drawTransform, localClip, opaque, casterPerimeter, 402 transformXY, transformZ, lightCenter, lightRadius); 403 task = static_cast<ShadowTask*>(mShadowCache.get(key)); 404 } 405 LOG_ALWAYS_FATAL_IF(task == NULL, "shadow not precached"); 406 outBuffers = *(task->getResult()); 407} 408 409/////////////////////////////////////////////////////////////////////////////// 410// Tessellation precaching 411/////////////////////////////////////////////////////////////////////////////// 412 413TessellationCache::Buffer* TessellationCache::getOrCreateBuffer( 414 const Description& entry, Tessellator tessellator) { 415 Buffer* buffer = mCache.get(entry); 416 if (!buffer) { 417 // not cached, enqueue a task to fill the buffer 418 sp<TessellationTask> task = new TessellationTask(tessellator, entry); 419 buffer = new Buffer(task); 420 421 if (mProcessor == NULL) { 422 mProcessor = new TessellationProcessor(Caches::getInstance()); 423 } 424 mProcessor->add(task); 425 mCache.put(entry, buffer); 426 } 427 return buffer; 428} 429 430static VertexBuffer* tessellatePath(const TessellationCache::Description& description, 431 const SkPath& path) { 432 Matrix4 matrix; 433 SkPaint paint; 434 description.setupMatrixAndPaint(&matrix, &paint); 435 VertexBuffer* buffer = new VertexBuffer(); 436 PathTessellator::tessellatePath(path, &paint, matrix, *buffer); 437 return buffer; 438} 439 440/////////////////////////////////////////////////////////////////////////////// 441// RoundRect 442/////////////////////////////////////////////////////////////////////////////// 443 444static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description) { 445 SkRect rect = SkRect::MakeWH(description.shape.roundRect.width, 446 description.shape.roundRect.height); 447 float rx = description.shape.roundRect.rx; 448 float ry = description.shape.roundRect.ry; 449 if (description.style == SkPaint::kStrokeAndFill_Style) { 450 float outset = description.strokeWidth / 2; 451 rect.outset(outset, outset); 452 rx += outset; 453 ry += outset; 454 } 455 SkPath path; 456 path.addRoundRect(rect, rx, ry); 457 return tessellatePath(description, path); 458} 459 460TessellationCache::Buffer* TessellationCache::getRoundRectBuffer( 461 const Matrix4& transform, const SkPaint& paint, 462 float width, float height, float rx, float ry) { 463 Description entry(Description::kRoundRect, transform, paint); 464 entry.shape.roundRect.width = width; 465 entry.shape.roundRect.height = height; 466 entry.shape.roundRect.rx = rx; 467 entry.shape.roundRect.ry = ry; 468 return getOrCreateBuffer(entry, &tessellateRoundRect); 469} 470const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, const SkPaint& paint, 471 float width, float height, float rx, float ry) { 472 return getRoundRectBuffer(transform, paint, width, height, rx, ry)->getVertexBuffer(); 473} 474 475}; // namespace uirenderer 476}; // namespace android 477