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