blob: 15ede4ca148a5138bef8ae3b5c67a10b6d31b99b [file] [log] [blame]
Chris Craik5ea17242016-01-11 14:07:59 -08001/*
2 * Copyright (C) 2016 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
Chris Craikf158b492016-01-12 14:45:08 -080017#include "LayerBuilder.h"
Chris Craik5ea17242016-01-11 14:07:59 -080018
19#include "BakedOpState.h"
20#include "RenderNode.h"
21#include "utils/PaintUtils.h"
22#include "utils/TraceUtils.h"
23
24#include <utils/TypeHelpers.h>
25
26namespace android {
27namespace uirenderer {
28
29class BatchBase {
30public:
31 BatchBase(batchid_t batchId, BakedOpState* op, bool merging)
John Reck1bcacfd2017-11-03 10:12:19 -070032 : mBatchId(batchId), mMerging(merging) {
Chris Craik5ea17242016-01-11 14:07:59 -080033 mBounds = op->computedState.clippedBounds;
34 mOps.push_back(op);
35 }
36
37 bool intersects(const Rect& rect) const {
38 if (!rect.intersects(mBounds)) return false;
39
40 for (const BakedOpState* op : mOps) {
41 if (rect.intersects(op->computedState.clippedBounds)) {
42 return true;
43 }
44 }
45 return false;
46 }
47
48 batchid_t getBatchId() const { return mBatchId; }
49 bool isMerging() const { return mMerging; }
50
51 const std::vector<BakedOpState*>& getOps() const { return mOps; }
52
53 void dump() const {
John Reck1bcacfd2017-11-03 10:12:19 -070054 ALOGD(" Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING, this, mBatchId,
55 mMerging, (int)mOps.size(), RECT_ARGS(mBounds));
Chris Craik5ea17242016-01-11 14:07:59 -080056 }
John Reck1bcacfd2017-11-03 10:12:19 -070057
Chris Craik5ea17242016-01-11 14:07:59 -080058protected:
59 batchid_t mBatchId;
60 Rect mBounds;
61 std::vector<BakedOpState*> mOps;
62 bool mMerging;
63};
64
65class OpBatch : public BatchBase {
66public:
John Reck1bcacfd2017-11-03 10:12:19 -070067 OpBatch(batchid_t batchId, BakedOpState* op) : BatchBase(batchId, op, false) {}
Chris Craik5ea17242016-01-11 14:07:59 -080068
69 void batchOp(BakedOpState* op) {
70 mBounds.unionWith(op->computedState.clippedBounds);
71 mOps.push_back(op);
72 }
73};
74
75class MergingOpBatch : public BatchBase {
76public:
Chris Craik5ea17242016-01-11 14:07:59 -080077 MergingOpBatch(batchid_t batchId, BakedOpState* op)
John Reck1bcacfd2017-11-03 10:12:19 -070078 : BatchBase(batchId, op, true), mClipSideFlags(op->computedState.clipSideFlags) {}
Chris Craik5ea17242016-01-11 14:07:59 -080079
80 /*
81 * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds
82 * and clip side flags. Positive bounds delta means new bounds fit in old.
83 */
84 static inline bool checkSide(const int currentFlags, const int newFlags, const int side,
John Reck1bcacfd2017-11-03 10:12:19 -070085 float boundsDelta) {
Chris Craik5ea17242016-01-11 14:07:59 -080086 bool currentClipExists = currentFlags & side;
87 bool newClipExists = newFlags & side;
88
89 // if current is clipped, we must be able to fit new bounds in current
90 if (boundsDelta > 0 && currentClipExists) return false;
91
92 // if new is clipped, we must be able to fit current bounds in new
93 if (boundsDelta < 0 && newClipExists) return false;
94
95 return true;
96 }
97
98 static bool paintIsDefault(const SkPaint& paint) {
John Reck1bcacfd2017-11-03 10:12:19 -070099 return paint.getAlpha() == 255 && paint.getColorFilter() == nullptr &&
100 paint.getShader() == nullptr;
Chris Craik5ea17242016-01-11 14:07:59 -0800101 }
102
103 static bool paintsAreEquivalent(const SkPaint& a, const SkPaint& b) {
104 // Note: don't check color, since all currently mergeable ops can merge across colors
John Reck1bcacfd2017-11-03 10:12:19 -0700105 return a.getAlpha() == b.getAlpha() && a.getColorFilter() == b.getColorFilter() &&
106 a.getShader() == b.getShader();
Chris Craik5ea17242016-01-11 14:07:59 -0800107 }
108
109 /*
110 * Checks if a (mergeable) op can be merged into this batch
111 *
112 * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
113 * important to consider all paint attributes used in the draw calls in deciding both a) if an
114 * op tries to merge at all, and b) if the op can merge with another set of ops
115 *
116 * False positives can lead to information from the paints of subsequent merged operations being
117 * dropped, so we make simplifying qualifications on the ops that can merge, per op type.
118 */
119 bool canMergeWith(BakedOpState* op) const {
John Reck1bcacfd2017-11-03 10:12:19 -0700120 bool isTextBatch =
121 getBatchId() == OpBatchType::Text || getBatchId() == OpBatchType::ColorText;
Chris Craik5ea17242016-01-11 14:07:59 -0800122
123 // Overlapping other operations is only allowed for text without shadow. For other ops,
124 // multiDraw isn't guaranteed to overdraw correctly
125 if (!isTextBatch || PaintUtils::hasTextShadow(op->op->paint)) {
126 if (intersects(op->computedState.clippedBounds)) return false;
127 }
128
129 const BakedOpState* lhs = op;
130 const BakedOpState* rhs = mOps[0];
131
132 if (!MathUtils::areEqual(lhs->alpha, rhs->alpha)) return false;
133
134 // Identical round rect clip state means both ops will clip in the same way, or not at all.
135 // As the state objects are const, we can compare their pointers to determine mergeability
136 if (lhs->roundRectClipState != rhs->roundRectClipState) return false;
Chris Craik678ff812016-03-01 13:27:54 -0800137
138 // Local masks prevent merge, since they're potentially in different coordinate spaces
John Reck1bcacfd2017-11-03 10:12:19 -0700139 if (lhs->computedState.localProjectionPathMask ||
140 rhs->computedState.localProjectionPathMask)
141 return false;
Chris Craik5ea17242016-01-11 14:07:59 -0800142
143 /* Clipping compatibility check
144 *
145 * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its
146 * clip for that side.
147 */
148 const int currentFlags = mClipSideFlags;
149 const int newFlags = op->computedState.clipSideFlags;
150 if (currentFlags != OpClipSideFlags::None || newFlags != OpClipSideFlags::None) {
151 const Rect& opBounds = op->computedState.clippedBounds;
152 float boundsDelta = mBounds.left - opBounds.left;
John Reck1bcacfd2017-11-03 10:12:19 -0700153 if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Left, boundsDelta))
154 return false;
Chris Craik5ea17242016-01-11 14:07:59 -0800155 boundsDelta = mBounds.top - opBounds.top;
156 if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Top, boundsDelta)) return false;
157
158 // right and bottom delta calculation reversed to account for direction
159 boundsDelta = opBounds.right - mBounds.right;
John Reck1bcacfd2017-11-03 10:12:19 -0700160 if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Right, boundsDelta))
161 return false;
Chris Craik5ea17242016-01-11 14:07:59 -0800162 boundsDelta = opBounds.bottom - mBounds.bottom;
John Reck1bcacfd2017-11-03 10:12:19 -0700163 if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Bottom, boundsDelta))
164 return false;
Chris Craik5ea17242016-01-11 14:07:59 -0800165 }
166
167 const SkPaint* newPaint = op->op->paint;
168 const SkPaint* oldPaint = mOps[0]->op->paint;
169
170 if (newPaint == oldPaint) {
171 // if paints are equal, then modifiers + paint attribs don't need to be compared
172 return true;
173 } else if (newPaint && !oldPaint) {
174 return paintIsDefault(*newPaint);
175 } else if (!newPaint && oldPaint) {
176 return paintIsDefault(*oldPaint);
177 }
178 return paintsAreEquivalent(*newPaint, *oldPaint);
179 }
180
181 void mergeOp(BakedOpState* op) {
182 mBounds.unionWith(op->computedState.clippedBounds);
183 mOps.push_back(op);
184
185 // Because a new op must have passed canMergeWith(), we know it's passed the clipping compat
186 // check, and doesn't extend past a side of the clip that's in use by the merged batch.
187 // Therefore it's safe to simply always merge flags, and use the bounds as the clip rect.
188 mClipSideFlags |= op->computedState.clipSideFlags;
189 }
190
191 int getClipSideFlags() const { return mClipSideFlags; }
192 const Rect& getClipRect() const { return mBounds; }
193
194private:
195 int mClipSideFlags;
196};
197
John Reck1bcacfd2017-11-03 10:12:19 -0700198LayerBuilder::LayerBuilder(uint32_t width, uint32_t height, const Rect& repaintRect,
199 const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
Chris Craik5ea17242016-01-11 14:07:59 -0800200 : width(width)
201 , height(height)
202 , repaintRect(repaintRect)
Chris Craik4876de12016-02-25 16:54:08 -0800203 , repaintClip(repaintRect)
Chris Craik5ea17242016-01-11 14:07:59 -0800204 , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
205 , beginLayerOp(beginLayerOp)
Chris Craik4876de12016-02-25 16:54:08 -0800206 , renderNode(renderNode) {}
Chris Craik5ea17242016-01-11 14:07:59 -0800207
208// iterate back toward target to see if anything drawn since should overlap the new op
209// if no target, merging ops still iterate to find similar batch to insert after
Chris Craikf158b492016-01-12 14:45:08 -0800210void LayerBuilder::locateInsertIndex(int batchId, const Rect& clippedBounds,
John Reck1bcacfd2017-11-03 10:12:19 -0700211 BatchBase** targetBatch, size_t* insertBatchIndex) const {
Chris Craik5ea17242016-01-11 14:07:59 -0800212 for (int i = mBatches.size() - 1; i >= 0; i--) {
213 BatchBase* overBatch = mBatches[i];
214
215 if (overBatch == *targetBatch) break;
216
217 // TODO: also consider shader shared between batch types
218 if (batchId == overBatch->getBatchId()) {
219 *insertBatchIndex = i + 1;
John Reck1bcacfd2017-11-03 10:12:19 -0700220 if (!*targetBatch) break; // found insert position, quit
Chris Craik5ea17242016-01-11 14:07:59 -0800221 }
222
223 if (overBatch->intersects(clippedBounds)) {
224 // NOTE: it may be possible to optimize for special cases where two operations
225 // of the same batch/paint could swap order, such as with a non-mergeable
226 // (clipped) and a mergeable text operation
227 *targetBatch = nullptr;
228 break;
229 }
230 }
231}
232
Chris Craikf158b492016-01-12 14:45:08 -0800233void LayerBuilder::deferLayerClear(const Rect& rect) {
Chris Craik5ea17242016-01-11 14:07:59 -0800234 mClearRects.push_back(rect);
235}
236
Chris Craik80d2ade2016-03-28 12:54:07 -0700237void LayerBuilder::onDeferOp(LinearAllocator& allocator, const BakedOpState* bakedState) {
238 if (bakedState->op->opId != RecordedOpId::CopyToLayerOp) {
239 // First non-CopyToLayer, so stop stashing up layer clears for unclipped save layers,
240 // and issue them together in one draw.
241 flushLayerClears(allocator);
242
John Reck1bcacfd2017-11-03 10:12:19 -0700243 if (CC_UNLIKELY(activeUnclippedSaveLayers.empty() &&
244 bakedState->computedState.opaqueOverClippedBounds &&
245 bakedState->computedState.clippedBounds.contains(repaintRect) &&
246 !Properties::debugOverdraw)) {
Chris Craik80d2ade2016-03-28 12:54:07 -0700247 // discard all deferred drawing ops, since new one will occlude them
248 clear();
249 }
250 }
251}
252
Chris Craikf158b492016-01-12 14:45:08 -0800253void LayerBuilder::flushLayerClears(LinearAllocator& allocator) {
Chris Craik5ea17242016-01-11 14:07:59 -0800254 if (CC_UNLIKELY(!mClearRects.empty())) {
255 const int vertCount = mClearRects.size() * 4;
256 // put the verts in the frame allocator, since
257 // 1) SimpleRectsOps needs verts, not rects
258 // 2) even if mClearRects stored verts, std::vectors will move their contents
John Reck1bcacfd2017-11-03 10:12:19 -0700259 Vertex* const verts = (Vertex*)allocator.create_trivial_array<Vertex>(vertCount);
Chris Craik5ea17242016-01-11 14:07:59 -0800260
261 Vertex* currentVert = verts;
262 Rect bounds = mClearRects[0];
263 for (auto&& rect : mClearRects) {
264 bounds.unionWith(rect);
265 Vertex::set(currentVert++, rect.left, rect.top);
266 Vertex::set(currentVert++, rect.right, rect.top);
267 Vertex::set(currentVert++, rect.left, rect.bottom);
268 Vertex::set(currentVert++, rect.right, rect.bottom);
269 }
John Reck1bcacfd2017-11-03 10:12:19 -0700270 mClearRects.clear(); // discard rects before drawing so this method isn't reentrant
Chris Craik5ea17242016-01-11 14:07:59 -0800271
272 // One or more unclipped saveLayers have been enqueued, with deferred clears.
273 // Flush all of these clears with a single draw
274 SkPaint* paint = allocator.create<SkPaint>();
Mike Reed260ab722016-10-07 15:59:20 -0400275 paint->setBlendMode(SkBlendMode::kClear);
John Reck1bcacfd2017-11-03 10:12:19 -0700276 SimpleRectsOp* op = allocator.create_trivial<SimpleRectsOp>(
277 bounds, Matrix4::identity(), nullptr, paint, verts, vertCount);
278 BakedOpState* bakedState =
279 BakedOpState::directConstruct(allocator, &repaintClip, bounds, *op);
Chris Craik5ea17242016-01-11 14:07:59 -0800280 deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices);
281 }
282}
283
John Reck1bcacfd2017-11-03 10:12:19 -0700284void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op,
285 batchid_t batchId) {
Chris Craik80d2ade2016-03-28 12:54:07 -0700286 onDeferOp(allocator, op);
Chris Craik5ea17242016-01-11 14:07:59 -0800287 OpBatch* targetBatch = mBatchLookup[batchId];
288
289 size_t insertBatchIndex = mBatches.size();
290 if (targetBatch) {
John Reck1bcacfd2017-11-03 10:12:19 -0700291 locateInsertIndex(batchId, op->computedState.clippedBounds, (BatchBase**)(&targetBatch),
292 &insertBatchIndex);
Chris Craik5ea17242016-01-11 14:07:59 -0800293 }
294
295 if (targetBatch) {
296 targetBatch->batchOp(op);
John Reck1bcacfd2017-11-03 10:12:19 -0700297 } else {
Chris Craik5ea17242016-01-11 14:07:59 -0800298 // new non-merging batch
John Reck7df9ff22016-02-10 16:08:08 -0800299 targetBatch = allocator.create<OpBatch>(batchId, op);
Chris Craik5ea17242016-01-11 14:07:59 -0800300 mBatchLookup[batchId] = targetBatch;
301 mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
302 }
303}
304
John Reck1bcacfd2017-11-03 10:12:19 -0700305void LayerBuilder::deferMergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId,
306 mergeid_t mergeId) {
Chris Craik80d2ade2016-03-28 12:54:07 -0700307 onDeferOp(allocator, op);
Chris Craik5ea17242016-01-11 14:07:59 -0800308 MergingOpBatch* targetBatch = nullptr;
309
310 // Try to merge with any existing batch with same mergeId
311 auto getResult = mMergingBatchLookup[batchId].find(mergeId);
312 if (getResult != mMergingBatchLookup[batchId].end()) {
313 targetBatch = getResult->second;
314 if (!targetBatch->canMergeWith(op)) {
315 targetBatch = nullptr;
316 }
317 }
318
319 size_t insertBatchIndex = mBatches.size();
John Reck1bcacfd2017-11-03 10:12:19 -0700320 locateInsertIndex(batchId, op->computedState.clippedBounds, (BatchBase**)(&targetBatch),
321 &insertBatchIndex);
Chris Craik5ea17242016-01-11 14:07:59 -0800322
323 if (targetBatch) {
324 targetBatch->mergeOp(op);
John Reck1bcacfd2017-11-03 10:12:19 -0700325 } else {
Chris Craik5ea17242016-01-11 14:07:59 -0800326 // new merging batch
John Reck7df9ff22016-02-10 16:08:08 -0800327 targetBatch = allocator.create<MergingOpBatch>(batchId, op);
Chris Craik5ea17242016-01-11 14:07:59 -0800328 mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch));
329
330 mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
331 }
332}
333
John Reck1bcacfd2017-11-03 10:12:19 -0700334void LayerBuilder::replayBakedOpsImpl(void* arg, BakedOpReceiver* unmergedReceivers,
335 MergedOpReceiver* mergedReceivers) const {
Chris Craikaff230f2016-05-04 16:27:28 -0700336 if (renderNode) {
John Reck1bcacfd2017-11-03 10:12:19 -0700337 ATRACE_FORMAT_BEGIN("Issue HW Layer DisplayList %s %ux%u", renderNode->getName(), width,
338 height);
Chris Craikaff230f2016-05-04 16:27:28 -0700339 } else {
340 ATRACE_BEGIN("flush drawing commands");
341 }
342
Chris Craik5ea17242016-01-11 14:07:59 -0800343 for (const BatchBase* batch : mBatches) {
344 size_t size = batch->getOps().size();
345 if (size > 1 && batch->isMerging()) {
346 int opId = batch->getOps()[0]->op->opId;
347 const MergingOpBatch* mergingBatch = static_cast<const MergingOpBatch*>(batch);
John Reck1bcacfd2017-11-03 10:12:19 -0700348 MergedBakedOpList data = {batch->getOps().data(), size,
349 mergingBatch->getClipSideFlags(),
350 mergingBatch->getClipRect()};
Chris Craik5ea17242016-01-11 14:07:59 -0800351 mergedReceivers[opId](arg, data);
352 } else {
353 for (const BakedOpState* op : batch->getOps()) {
354 unmergedReceivers[op->op->opId](arg, *op);
355 }
356 }
357 }
Chris Craikaff230f2016-05-04 16:27:28 -0700358 ATRACE_END();
Chris Craik5ea17242016-01-11 14:07:59 -0800359}
360
Chris Craik80d2ade2016-03-28 12:54:07 -0700361void LayerBuilder::clear() {
362 mBatches.clear();
363 for (int i = 0; i < OpBatchType::Count; i++) {
364 mBatchLookup[i] = nullptr;
365 mMergingBatchLookup[i].clear();
366 }
367}
368
Chris Craikf158b492016-01-12 14:45:08 -0800369void LayerBuilder::dump() const {
John Reck1bcacfd2017-11-03 10:12:19 -0700370 ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p (%s)", this, width, height,
371 offscreenBuffer, beginLayerOp, renderNode, renderNode ? renderNode->getName() : "-");
Chris Craik5ea17242016-01-11 14:07:59 -0800372 for (const BatchBase* batch : mBatches) {
373 batch->dump();
374 }
375}
376
John Reck1bcacfd2017-11-03 10:12:19 -0700377} // namespace uirenderer
378} // namespace android