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