2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <utils/JenkinsHash.h>
18 #include <utils/Trace.h>
21 #include "OpenGLRenderer.h"
22 #include "PathTessellator.h"
23 #include "ShadowTessellator.h"
24 #include "TessellationCache.h"
26 #include "thread/Signal.h"
27 #include "thread/Task.h"
28 #include "thread/TaskProcessor.h"
31 namespace uirenderer {
33 ///////////////////////////////////////////////////////////////////////////////
35 ///////////////////////////////////////////////////////////////////////////////
37 TessellationCache::Description::Description()
42 , cap(SkPaint::kDefault_Cap)
43 , style(SkPaint::kFill_Style)
45 memset(&shape, 0, sizeof(Shape));
48 TessellationCache::Description::Description(Type type, const Matrix4& transform, const SkPaint& paint)
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));
58 hash_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);
70 void 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);
78 TessellationCache::ShadowDescription::ShadowDescription()
80 memset(&matrixData, 0, 16 * sizeof(float));
83 TessellationCache::ShadowDescription::ShadowDescription(const void* nodeKey, const Matrix4* drawTransform)
85 memcpy(&matrixData, drawTransform->data, 16 * sizeof(float));
88 hash_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);
94 ///////////////////////////////////////////////////////////////////////////////
95 // General purpose tessellation task processing
96 ///////////////////////////////////////////////////////////////////////////////
98 class TessellationCache::TessellationTask : public Task<VertexBuffer*> {
100 TessellationTask(Tessellator tessellator, const Description& description)
101 : tessellator(tessellator)
102 , description(description) {
105 ~TessellationTask() {}
107 Tessellator tessellator;
108 Description description;
111 class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> {
113 TessellationProcessor(Caches& caches)
114 : TaskProcessor<VertexBuffer*>(&caches.tasks) {}
115 ~TessellationProcessor() {}
117 virtual void onProcess(const sp<Task<VertexBuffer*> >& task) override {
118 TessellationTask* t = static_cast<TessellationTask*>(task.get());
119 ATRACE_NAME("shape tessellation");
120 VertexBuffer* buffer = t->tessellator(t->description);
121 t->setResult(buffer);
125 class TessellationCache::Buffer {
127 Buffer(const sp<Task<VertexBuffer*> >& task)
137 unsigned int getSize() {
139 return mBuffer->getSize();
142 const VertexBuffer* getVertexBuffer() {
148 void blockOnPrecache() {
149 if (mTask != nullptr) {
150 mBuffer = mTask->getResult();
151 LOG_ALWAYS_FATAL_IF(mBuffer == nullptr, "Failed to precache");
155 sp<Task<VertexBuffer*> > mTask;
156 VertexBuffer* mBuffer;
159 ///////////////////////////////////////////////////////////////////////////////
160 // Shadow tessellation task processing
161 ///////////////////////////////////////////////////////////////////////////////
163 class ShadowTask : public Task<TessellationCache::vertexBuffer_pair_t*> {
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)
171 , casterPerimeter(*casterPerimeter)
172 , transformXY(*transformXY)
173 , transformZ(*transformZ)
174 , lightCenter(lightCenter)
175 , lightRadius(lightRadius) {
179 TessellationCache::vertexBuffer_pair_t* bufferPair = getResult();
180 delete bufferPair->getFirst();
181 delete bufferPair->getSecond();
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.
189 * These deep copies could be avoided, long term, by cancelling or flushing outstanding tasks
190 * before tearning down single-frame LinearAllocators.
192 const Matrix4 drawTransform;
193 const Rect localClip;
195 const SkPath casterPerimeter;
196 const Matrix4 transformXY;
197 const Matrix4 transformZ;
198 const Vector3 lightCenter;
199 const float lightRadius;
202 static void mapPointFakeZ(Vector3& point, const mat4* transformXY, const mat4* transformZ) {
203 // map z coordinate with true 3d matrix
204 point.z = transformZ->mapZ(point);
206 // map x,y coordinates with draw/Skia matrix
207 transformXY->mapPoint(point.x, point.y);
210 static void reverseVertexArray(Vertex* polygon, int len) {
212 for (int i = 0; i < n; i++) {
213 Vertex tmp = polygon[i];
215 polygon[i] = polygon[k];
220 static void tessellateShadows(
221 const Matrix4* drawTransform, const Rect* localClip,
222 bool isCasterOpaque, const SkPath* casterPerimeter,
223 const Matrix4* casterTransformXY, const Matrix4* casterTransformZ,
224 const Vector3& lightCenter, float lightRadius,
225 VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer) {
227 // tessellate caster outline into a 2d polygon
228 Vector<Vertex> casterVertices2d;
229 const float casterRefinementThreshold = 2.0f;
230 PathTessellator::approximatePathOutlineVertices(*casterPerimeter,
231 casterRefinementThreshold, casterVertices2d);
233 // Shadow requires CCW for now. TODO: remove potential double-reverse
234 reverseVertexArray(casterVertices2d.editArray(), casterVertices2d.size());
236 if (casterVertices2d.size() == 0) return;
238 // map 2d caster poly into 3d
239 const int casterVertexCount = casterVertices2d.size();
240 Vector3 casterPolygon[casterVertexCount];
241 float minZ = FLT_MAX;
242 float maxZ = -FLT_MAX;
243 for (int i = 0; i < casterVertexCount; i++) {
244 const Vertex& point2d = casterVertices2d[i];
245 casterPolygon[i] = (Vector3){point2d.x, point2d.y, 0};
246 mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
247 minZ = std::min(minZ, casterPolygon[i].z);
248 maxZ = std::max(maxZ, casterPolygon[i].z);
251 // map the centroid of the caster into 3d
252 Vector2 centroid = ShadowTessellator::centroid2d(
253 reinterpret_cast<const Vector2*>(casterVertices2d.array()),
255 Vector3 centroid3d = {centroid.x, centroid.y, 0};
256 mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ);
258 // if the caster intersects the z=0 plane, lift it in Z so it doesn't
259 if (minZ < SHADOW_MIN_CASTER_Z) {
260 float casterLift = SHADOW_MIN_CASTER_Z - minZ;
261 for (int i = 0; i < casterVertexCount; i++) {
262 casterPolygon[i].z += casterLift;
264 centroid3d.z += casterLift;
267 // Check whether we want to draw the shadow at all by checking the caster's bounds against clip.
268 // We only have ortho projection, so we can just ignore the Z in caster for
269 // simple rejection calculation.
270 Rect casterBounds(casterPerimeter->getBounds());
271 casterTransformXY->mapRect(casterBounds);
273 // actual tessellation of both shadows
274 ShadowTessellator::tessellateAmbientShadow(
275 isCasterOpaque, casterPolygon, casterVertexCount, centroid3d,
276 casterBounds, *localClip, maxZ, ambientBuffer);
278 ShadowTessellator::tessellateSpotShadow(
279 isCasterOpaque, casterPolygon, casterVertexCount, centroid3d,
280 *drawTransform, lightCenter, lightRadius, casterBounds, *localClip,
284 class ShadowProcessor : public TaskProcessor<TessellationCache::vertexBuffer_pair_t*> {
286 ShadowProcessor(Caches& caches)
287 : TaskProcessor<TessellationCache::vertexBuffer_pair_t*>(&caches.tasks) {}
288 ~ShadowProcessor() {}
290 virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t*> >& task) override {
291 ShadowTask* t = static_cast<ShadowTask*>(task.get());
292 ATRACE_NAME("shadow tessellation");
294 VertexBuffer* ambientBuffer = new VertexBuffer;
295 VertexBuffer* spotBuffer = new VertexBuffer;
296 tessellateShadows(&t->drawTransform, &t->localClip, t->opaque, &t->casterPerimeter,
297 &t->transformXY, &t->transformZ, t->lightCenter, t->lightRadius,
298 *ambientBuffer, *spotBuffer);
300 t->setResult(new TessellationCache::vertexBuffer_pair_t(ambientBuffer, spotBuffer));
304 ///////////////////////////////////////////////////////////////////////////////
305 // Cache constructor/destructor
306 ///////////////////////////////////////////////////////////////////////////////
308 TessellationCache::TessellationCache()
310 , mMaxSize(MB(DEFAULT_VERTEX_CACHE_SIZE))
311 , mCache(LruCache<Description, Buffer*>::kUnlimitedCapacity)
312 , mShadowCache(LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) {
313 char property[PROPERTY_VALUE_MAX];
314 if (property_get(PROPERTY_VERTEX_CACHE_SIZE, property, nullptr) > 0) {
315 INIT_LOGD(" Setting %s cache size to %sMB", name, property);
316 setMaxSize(MB(atof(property)));
318 INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_VERTEX_CACHE_SIZE);
321 mCache.setOnEntryRemovedListener(&mBufferRemovedListener);
322 mShadowCache.setOnEntryRemovedListener(&mBufferPairRemovedListener);
323 mDebugEnabled = Properties::debugLevel & kDebugCaches;
326 TessellationCache::~TessellationCache() {
330 ///////////////////////////////////////////////////////////////////////////////
332 ///////////////////////////////////////////////////////////////////////////////
334 uint32_t TessellationCache::getSize() {
335 LruCache<Description, Buffer*>::Iterator iter(mCache);
337 while (iter.next()) {
338 size += iter.value()->getSize();
343 uint32_t TessellationCache::getMaxSize() {
347 void TessellationCache::setMaxSize(uint32_t maxSize) {
349 while (mSize > mMaxSize) {
350 mCache.removeOldest();
354 ///////////////////////////////////////////////////////////////////////////////
356 ///////////////////////////////////////////////////////////////////////////////
359 void TessellationCache::trim() {
360 uint32_t size = getSize();
361 while (size > mMaxSize) {
362 size -= mCache.peekOldestValue()->getSize();
363 mCache.removeOldest();
365 mShadowCache.clear();
368 void TessellationCache::clear() {
370 mShadowCache.clear();
373 ///////////////////////////////////////////////////////////////////////////////
375 ///////////////////////////////////////////////////////////////////////////////
377 void TessellationCache::BufferRemovedListener::operator()(Description& description,
382 ///////////////////////////////////////////////////////////////////////////////
384 ///////////////////////////////////////////////////////////////////////////////
386 void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
387 bool opaque, const SkPath* casterPerimeter,
388 const Matrix4* transformXY, const Matrix4* transformZ,
389 const Vector3& lightCenter, float lightRadius) {
390 ShadowDescription key(casterPerimeter, drawTransform);
392 if (mShadowCache.get(key)) return;
393 sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque,
394 casterPerimeter, transformXY, transformZ, lightCenter, lightRadius);
395 if (mShadowProcessor == nullptr) {
396 mShadowProcessor = new ShadowProcessor(Caches::getInstance());
398 mShadowProcessor->add(task);
399 task->incStrong(nullptr); // not using sp<>s, so manually ref while in the cache
400 mShadowCache.put(key, task.get());
403 void TessellationCache::getShadowBuffers(const Matrix4* drawTransform, const Rect& localClip,
404 bool opaque, const SkPath* casterPerimeter,
405 const Matrix4* transformXY, const Matrix4* transformZ,
406 const Vector3& lightCenter, float lightRadius, vertexBuffer_pair_t& outBuffers) {
407 ShadowDescription key(casterPerimeter, drawTransform);
408 ShadowTask* task = static_cast<ShadowTask*>(mShadowCache.get(key));
410 precacheShadows(drawTransform, localClip, opaque, casterPerimeter,
411 transformXY, transformZ, lightCenter, lightRadius);
412 task = static_cast<ShadowTask*>(mShadowCache.get(key));
414 LOG_ALWAYS_FATAL_IF(task == nullptr, "shadow not precached");
415 outBuffers = *(task->getResult());
418 ///////////////////////////////////////////////////////////////////////////////
419 // Tessellation precaching
420 ///////////////////////////////////////////////////////////////////////////////
422 TessellationCache::Buffer* TessellationCache::getOrCreateBuffer(
423 const Description& entry, Tessellator tessellator) {
424 Buffer* buffer = mCache.get(entry);
426 // not cached, enqueue a task to fill the buffer
427 sp<TessellationTask> task = new TessellationTask(tessellator, entry);
428 buffer = new Buffer(task);
430 if (mProcessor == nullptr) {
431 mProcessor = new TessellationProcessor(Caches::getInstance());
433 mProcessor->add(task);
434 mCache.put(entry, buffer);
439 static VertexBuffer* tessellatePath(const TessellationCache::Description& description,
440 const SkPath& path) {
443 description.setupMatrixAndPaint(&matrix, &paint);
444 VertexBuffer* buffer = new VertexBuffer();
445 PathTessellator::tessellatePath(path, &paint, matrix, *buffer);
449 ///////////////////////////////////////////////////////////////////////////////
451 ///////////////////////////////////////////////////////////////////////////////
453 static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description) {
454 SkRect rect = SkRect::MakeWH(description.shape.roundRect.width,
455 description.shape.roundRect.height);
456 float rx = description.shape.roundRect.rx;
457 float ry = description.shape.roundRect.ry;
458 if (description.style == SkPaint::kStrokeAndFill_Style) {
459 float outset = description.strokeWidth / 2;
460 rect.outset(outset, outset);
465 path.addRoundRect(rect, rx, ry);
466 return tessellatePath(description, path);
469 TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(
470 const Matrix4& transform, const SkPaint& paint,
471 float width, float height, float rx, float ry) {
472 Description entry(Description::kRoundRect, transform, paint);
473 entry.shape.roundRect.width = width;
474 entry.shape.roundRect.height = height;
475 entry.shape.roundRect.rx = rx;
476 entry.shape.roundRect.ry = ry;
477 return getOrCreateBuffer(entry, &tessellateRoundRect);
479 const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, const SkPaint& paint,
480 float width, float height, float rx, float ry) {
481 return getRoundRectBuffer(transform, paint, width, height, rx, ry)->getVertexBuffer();
484 }; // namespace uirenderer
485 }; // namespace android