blob: 41cc9d28c3f160a046a3598252c988879a47b703 [file] [log] [blame]
Chris Craik05f3d6e2014-06-02 16:27:04 -07001/*
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#define LOG_TAG "OpenGLRenderer"
18#define ATRACE_TAG ATRACE_TAG_VIEW
19
20#include <utils/JenkinsHash.h>
21#include <utils/Trace.h>
22
23#include "Caches.h"
24#include "OpenGLRenderer.h"
25#include "PathTessellator.h"
26#include "ShadowTessellator.h"
27#include "TessellationCache.h"
28
29#include "thread/Signal.h"
30#include "thread/Task.h"
31#include "thread/TaskProcessor.h"
32
33namespace android {
34namespace uirenderer {
35
36///////////////////////////////////////////////////////////////////////////////
37// Cache entries
38///////////////////////////////////////////////////////////////////////////////
39
40TessellationCache::Description::Description()
41 : type(kNone)
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)
49 : type(type)
50 , cap(SkPaint::kDefault_Cap)
51 , style(SkPaint::kFill_Style)
52 , strokeWidth(1.0f) {
53 memset(&shape, 0, sizeof(Shape));
54}
55
56TessellationCache::Description::Description(Type type, const SkPaint* paint)
57 : type(type)
58 , cap(paint->getStrokeCap())
59 , style(paint->getStyle())
60 , strokeWidth(paint->getStrokeWidth()) {
61 memset(&shape, 0, sizeof(Shape));
62}
63
64hash_t TessellationCache::Description::hash() const {
65 uint32_t hash = JenkinsHashMix(0, type);
66 hash = JenkinsHashMix(hash, cap);
67 hash = JenkinsHashMix(hash, style);
68 hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
69 hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape));
70 return JenkinsHashWhiten(hash);
71}
72
73TessellationCache::ShadowDescription::ShadowDescription()
74 : nodeKey(NULL) {
75 memset(&matrixData, 0, 16 * sizeof(float));
76}
77
78TessellationCache::ShadowDescription::ShadowDescription(const void* nodeKey, const Matrix4* drawTransform)
79 : nodeKey(nodeKey) {
80 memcpy(&matrixData, drawTransform->data, 16 * sizeof(float));
81}
82
83hash_t TessellationCache::ShadowDescription::hash() const {
84 uint32_t hash = JenkinsHashMixBytes(0, (uint8_t*) &nodeKey, sizeof(const void*));
85 hash = JenkinsHashMixBytes(hash, (uint8_t*) &matrixData, 16 * sizeof(float));
86 return JenkinsHashWhiten(hash);
87}
88
89///////////////////////////////////////////////////////////////////////////////
90// General purpose tessellation task processing
91///////////////////////////////////////////////////////////////////////////////
92
93class TessellationCache::TessellationTask : public Task<VertexBuffer*> {
94public:
95 TessellationTask(Tessellator tessellator, const Description& description,
96 const SkPaint* paint)
97 : tessellator(tessellator)
98 , description(description)
99 , paint(*paint) {
100 }
101
102 ~TessellationTask() {}
103
104 Tessellator tessellator;
105 Description description;
106
107 //copied, since input paint may not be immutable
108 const SkPaint paint;
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, t->paint);
121 t->setResult(buffer);
122 }
123};
124
125struct 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 - only the localClip is deep copied, since other pointers point at Allocator controlled
186 // objects, which are safe for the entire frame
187 const Matrix4* drawTransform;
188 const Rect localClip;
189 bool opaque;
190 const SkPath* casterPerimeter;
191 const Matrix4* transformXY;
192 const Matrix4* transformZ;
193 const Vector3 lightCenter;
194 const float lightRadius;
195};
196
197static void mapPointFakeZ(Vector3& point, const mat4* transformXY, const mat4* transformZ) {
198 // map z coordinate with true 3d matrix
199 point.z = transformZ->mapZ(point);
200
201 // map x,y coordinates with draw/Skia matrix
202 transformXY->mapPoint(point.x, point.y);
203}
204
205static void tessellateShadows(
206 const Matrix4* drawTransform, const Rect* localClip,
207 bool isCasterOpaque, const SkPath* casterPerimeter,
208 const Matrix4* casterTransformXY, const Matrix4* casterTransformZ,
209 const Vector3& lightCenter, float lightRadius,
210 VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer) {
211
212 // tessellate caster outline into a 2d polygon
213 Vector<Vertex> casterVertices2d;
214 const float casterRefinementThresholdSquared = 20.0f; // TODO: experiment with this value
215 PathTessellator::approximatePathOutlineVertices(*casterPerimeter,
216 casterRefinementThresholdSquared, casterVertices2d);
217 if (!ShadowTessellator::isClockwisePath(*casterPerimeter)) {
218 ShadowTessellator::reverseVertexArray(casterVertices2d.editArray(),
219 casterVertices2d.size());
220 }
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 = fmin(minZ, casterPolygon[i].z);
234 maxZ = fmax(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.array()),
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,
266 *drawTransform, lightCenter, lightRadius, casterBounds, *localClip,
267 spotBuffer);
268
269 // TODO: set ambientBuffer & spotBuffer's bounds for correct layer damage
270}
271
272class ShadowProcessor : public TaskProcessor<TessellationCache::vertexBuffer_pair_t*> {
273public:
274 ShadowProcessor(Caches& caches)
275 : TaskProcessor<TessellationCache::vertexBuffer_pair_t*>(&caches.tasks) {}
276 ~ShadowProcessor() {}
277
278 virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t*> >& task) {
279 ShadowTask* t = static_cast<ShadowTask*>(task.get());
280 ATRACE_NAME("shadow tessellation");
281
282 VertexBuffer* ambientBuffer = new VertexBuffer;
283 VertexBuffer* spotBuffer = new VertexBuffer;
284 tessellateShadows(t->drawTransform, &t->localClip, t->opaque, t->casterPerimeter,
285 t->transformXY, t->transformZ, t->lightCenter, t->lightRadius,
286 *ambientBuffer, *spotBuffer);
287
288 t->setResult(new TessellationCache::vertexBuffer_pair_t(ambientBuffer, spotBuffer));
289 }
290};
291
292///////////////////////////////////////////////////////////////////////////////
293// Cache constructor/destructor
294///////////////////////////////////////////////////////////////////////////////
295
296TessellationCache::TessellationCache()
297 : mSize(0)
298 , mMaxSize(MB(DEFAULT_VERTEX_CACHE_SIZE))
299 , mCache(LruCache<Description, Buffer*>::kUnlimitedCapacity)
300 , mShadowCache(LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) {
301 char property[PROPERTY_VALUE_MAX];
302 if (property_get(PROPERTY_VERTEX_CACHE_SIZE, property, NULL) > 0) {
303 INIT_LOGD(" Setting %s cache size to %sMB", name, property);
304 setMaxSize(MB(atof(property)));
305 } else {
306 INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_VERTEX_CACHE_SIZE);
307 }
308
309 mCache.setOnEntryRemovedListener(&mBufferRemovedListener);
310 mShadowCache.setOnEntryRemovedListener(&mBufferPairRemovedListener);
311 mDebugEnabled = readDebugLevel() & kDebugCaches;
312}
313
314TessellationCache::~TessellationCache() {
315 mCache.clear();
316}
317
318///////////////////////////////////////////////////////////////////////////////
319// Size management
320///////////////////////////////////////////////////////////////////////////////
321
322uint32_t TessellationCache::getSize() {
323 LruCache<Description, Buffer*>::Iterator iter(mCache);
324 uint32_t size = 0;
325 while (iter.next()) {
326 size += iter.value()->getSize();
327 }
328 return size;
329}
330
331uint32_t TessellationCache::getMaxSize() {
332 return mMaxSize;
333}
334
335void TessellationCache::setMaxSize(uint32_t maxSize) {
336 mMaxSize = maxSize;
337 while (mSize > mMaxSize) {
338 mCache.removeOldest();
339 }
340}
341
342///////////////////////////////////////////////////////////////////////////////
343// Caching
344///////////////////////////////////////////////////////////////////////////////
345
346
347void TessellationCache::trim() {
348 uint32_t size = getSize();
349 while (size > mMaxSize) {
350 size -= mCache.peekOldestValue()->getSize();
351 mCache.removeOldest();
352 }
353 mShadowCache.clear();
354}
355
356void TessellationCache::clear() {
357 mCache.clear();
358 mShadowCache.clear();
359}
360
361///////////////////////////////////////////////////////////////////////////////
362// Callbacks
363///////////////////////////////////////////////////////////////////////////////
364
365void TessellationCache::BufferRemovedListener::operator()(Description& description,
366 Buffer*& buffer) {
367 delete buffer;
368}
369
370///////////////////////////////////////////////////////////////////////////////
371// Shadows
372///////////////////////////////////////////////////////////////////////////////
373
374void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
375 bool opaque, const SkPath* casterPerimeter,
376 const Matrix4* transformXY, const Matrix4* transformZ,
377 const Vector3& lightCenter, float lightRadius) {
378 ShadowDescription key(casterPerimeter, drawTransform);
379
380 sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque,
381 casterPerimeter, transformXY, transformZ, lightCenter, lightRadius);
382 if (mShadowProcessor == NULL) {
383 mShadowProcessor = new ShadowProcessor(Caches::getInstance());
384 }
385 mShadowProcessor->add(task);
386
387 task->incStrong(NULL); // not using sp<>s, so manually ref while in the cache
388 mShadowCache.put(key, task.get());
389}
390
391void TessellationCache::getShadowBuffers(const Matrix4* drawTransform, const Rect& localClip,
392 bool opaque, const SkPath* casterPerimeter,
393 const Matrix4* transformXY, const Matrix4* transformZ,
394 const Vector3& lightCenter, float lightRadius, vertexBuffer_pair_t& outBuffers) {
395 ShadowDescription key(casterPerimeter, drawTransform);
396 ShadowTask* task = static_cast<ShadowTask*>(mShadowCache.get(key));
397 if (!task) {
398 precacheShadows(drawTransform, localClip, opaque, casterPerimeter,
399 transformXY, transformZ, lightCenter, lightRadius);
400 task = static_cast<ShadowTask*>(mShadowCache.get(key));
401 }
402 LOG_ALWAYS_FATAL_IF(task == NULL, "shadow not precached");
403 outBuffers = *(task->getResult());
404}
405
406///////////////////////////////////////////////////////////////////////////////
407// Tessellation precaching
408///////////////////////////////////////////////////////////////////////////////
409
410static VertexBuffer* tessellatePath(const SkPath& path, const SkPaint* paint,
411 float scaleX, float scaleY) {
412 VertexBuffer* buffer = new VertexBuffer();
413 Matrix4 matrix;
414 matrix.loadScale(scaleX, scaleY, 1);
415 PathTessellator::tessellatePath(path, paint, matrix, *buffer);
416 return buffer;
417}
418
419TessellationCache::Buffer* TessellationCache::getOrCreateBuffer(
420 const Description& entry, Tessellator tessellator, const SkPaint* paint) {
421 Buffer* buffer = mCache.get(entry);
422 if (!buffer) {
423 // not cached, enqueue a task to fill the buffer
424 sp<TessellationTask> task = new TessellationTask(tessellator, entry, paint);
425 buffer = new Buffer(task);
426
427 if (mProcessor == NULL) {
428 mProcessor = new TessellationProcessor(Caches::getInstance());
429 }
430 mProcessor->add(task);
431 mCache.put(entry, buffer);
432 }
433 return buffer;
434}
435
436///////////////////////////////////////////////////////////////////////////////
437// Rounded rects
438///////////////////////////////////////////////////////////////////////////////
439
440static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description,
441 const SkPaint& paint) {
442 SkRect rect = SkRect::MakeWH(description.shape.roundRect.mWidth,
443 description.shape.roundRect.mHeight);
444 float rx = description.shape.roundRect.mRx;
445 float ry = description.shape.roundRect.mRy;
446 if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) {
447 float outset = paint.getStrokeWidth() / 2;
448 rect.outset(outset, outset);
449 rx += outset;
450 ry += outset;
451 }
452 SkPath path;
453 path.addRoundRect(rect, rx, ry);
454 return tessellatePath(path, &paint,
455 description.shape.roundRect.mScaleX, description.shape.roundRect.mScaleY);
456}
457
458TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(const Matrix4& transform,
459 float width, float height, float rx, float ry, const SkPaint* paint) {
460 Description entry(Description::kRoundRect, paint);
461 entry.shape.roundRect.mWidth = width;
462 entry.shape.roundRect.mHeight = height;
463 entry.shape.roundRect.mRx = rx;
464 entry.shape.roundRect.mRy = ry;
465 PathTessellator::extractTessellationScales(transform,
466 &entry.shape.roundRect.mScaleX, &entry.shape.roundRect.mScaleY);
467
468 return getOrCreateBuffer(entry, &tessellateRoundRect, paint);
469}
470const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform,
471 float width, float height, float rx, float ry, const SkPaint* paint) {
472 return getRoundRectBuffer(transform, width, height, rx, ry, paint)->getVertexBuffer();
473}
474
475}; // namespace uirenderer
476}; // namespace android