1 /* 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 17 #include "LayerBuilder.h" 18 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 26 namespace android { 27 namespace uirenderer { 28 29 class BatchBase { 30 public: 31 BatchBase(batchid_t batchId, BakedOpState* op, bool merging) 32 : mBatchId(batchId), mMerging(merging) { 33 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 { 54 ALOGD(" Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING, this, mBatchId, 55 mMerging, (int)mOps.size(), RECT_ARGS(mBounds)); 56 } 57 58 protected: 59 batchid_t mBatchId; 60 Rect mBounds; 61 std::vector<BakedOpState*> mOps; 62 bool mMerging; 63 }; 64 65 class OpBatch : public BatchBase { 66 public: 67 OpBatch(batchid_t batchId, BakedOpState* op) : BatchBase(batchId, op, false) {} 68 69 void batchOp(BakedOpState* op) { 70 mBounds.unionWith(op->computedState.clippedBounds); 71 mOps.push_back(op); 72 } 73 }; 74 75 class MergingOpBatch : public BatchBase { 76 public: 77 MergingOpBatch(batchid_t batchId, BakedOpState* op) 78 : BatchBase(batchId, op, true), mClipSideFlags(op->computedState.clipSideFlags) {} 79 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, 85 float boundsDelta) { 86 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) { 99 return paint.getAlpha() == 255 && paint.getColorFilter() == nullptr && 100 paint.getShader() == nullptr; 101 } 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 105 return a.getAlpha() == b.getAlpha() && a.getColorFilter() == b.getColorFilter() && 106 a.getShader() == b.getShader(); 107 } 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 { 120 bool isTextBatch = 121 getBatchId() == OpBatchType::Text || getBatchId() == OpBatchType::ColorText; 122 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; 137 138 // Local masks prevent merge, since they're potentially in different coordinate spaces 139 if (lhs->computedState.localProjectionPathMask || 140 rhs->computedState.localProjectionPathMask) 141 return false; 142 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; 153 if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Left, boundsDelta)) 154 return false; 155 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; 160 if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Right, boundsDelta)) 161 return false; 162 boundsDelta = opBounds.bottom - mBounds.bottom; 163 if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Bottom, boundsDelta)) 164 return false; 165 } 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 194 private: 195 int mClipSideFlags; 196 }; 197 198 LayerBuilder::LayerBuilder(uint32_t width, uint32_t height, const Rect& repaintRect, 199 const BeginLayerOp* beginLayerOp, RenderNode* renderNode) 200 : width(width) 201 , height(height) 202 , repaintRect(repaintRect) 203 , repaintClip(repaintRect) 204 , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr) 205 , beginLayerOp(beginLayerOp) 206 , renderNode(renderNode) {} 207 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 210 void LayerBuilder::locateInsertIndex(int batchId, const Rect& clippedBounds, 211 BatchBase** targetBatch, size_t* insertBatchIndex) const { 212 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; 220 if (!*targetBatch) break; // found insert position, quit 221 } 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 233 void LayerBuilder::deferLayerClear(const Rect& rect) { 234 mClearRects.push_back(rect); 235 } 236 237 void 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 243 if (CC_UNLIKELY(activeUnclippedSaveLayers.empty() && 244 bakedState->computedState.opaqueOverClippedBounds && 245 bakedState->computedState.clippedBounds.contains(repaintRect) && 246 !Properties::debugOverdraw)) { 247 // discard all deferred drawing ops, since new one will occlude them 248 clear(); 249 } 250 } 251 } 252 253 void LayerBuilder::flushLayerClears(LinearAllocator& allocator) { 254 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 259 Vertex* const verts = (Vertex*)allocator.create_trivial_array<Vertex>(vertCount); 260 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 } 270 mClearRects.clear(); // discard rects before drawing so this method isn't reentrant 271 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>(); 275 paint->setBlendMode(SkBlendMode::kClear); 276 SimpleRectsOp* op = allocator.create_trivial<SimpleRectsOp>( 277 bounds, Matrix4::identity(), nullptr, paint, verts, vertCount); 278 BakedOpState* bakedState = 279 BakedOpState::directConstruct(allocator, &repaintClip, bounds, *op); 280 deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices); 281 } 282 } 283 284 void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, 285 batchid_t batchId) { 286 onDeferOp(allocator, op); 287 OpBatch* targetBatch = mBatchLookup[batchId]; 288 289 size_t insertBatchIndex = mBatches.size(); 290 if (targetBatch) { 291 locateInsertIndex(batchId, op->computedState.clippedBounds, (BatchBase**)(&targetBatch), 292 &insertBatchIndex); 293 } 294 295 if (targetBatch) { 296 targetBatch->batchOp(op); 297 } else { 298 // new non-merging batch 299 targetBatch = allocator.create<OpBatch>(batchId, op); 300 mBatchLookup[batchId] = targetBatch; 301 mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch); 302 } 303 } 304 305 void LayerBuilder::deferMergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId, 306 mergeid_t mergeId) { 307 onDeferOp(allocator, op); 308 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(); 320 locateInsertIndex(batchId, op->computedState.clippedBounds, (BatchBase**)(&targetBatch), 321 &insertBatchIndex); 322 323 if (targetBatch) { 324 targetBatch->mergeOp(op); 325 } else { 326 // new merging batch 327 targetBatch = allocator.create<MergingOpBatch>(batchId, op); 328 mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch)); 329 330 mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch); 331 } 332 } 333 334 void LayerBuilder::replayBakedOpsImpl(void* arg, BakedOpReceiver* unmergedReceivers, 335 MergedOpReceiver* mergedReceivers) const { 336 if (renderNode) { 337 ATRACE_FORMAT_BEGIN("Issue HW Layer DisplayList %s %ux%u", renderNode->getName(), width, 338 height); 339 } else { 340 ATRACE_BEGIN("flush drawing commands"); 341 } 342 343 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); 348 MergedBakedOpList data = {batch->getOps().data(), size, 349 mergingBatch->getClipSideFlags(), 350 mergingBatch->getClipRect()}; 351 mergedReceivers[opId](arg, data); 352 } else { 353 for (const BakedOpState* op : batch->getOps()) { 354 unmergedReceivers[op->op->opId](arg, *op); 355 } 356 } 357 } 358 ATRACE_END(); 359 } 360 361 void 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 369 void LayerBuilder::dump() const { 370 ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p (%s)", this, width, height, 371 offscreenBuffer, beginLayerOp, renderNode, renderNode ? renderNode->getName() : "-"); 372 for (const BatchBase* batch : mBatches) { 373 batch->dump(); 374 } 375 } 376 377 } // namespace uirenderer 378 } // namespace android 379