FrameBuilder.cpp revision d645640180c25c2711e99aa82ec629155f8e91ba
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 "FrameBuilder.h" 18 19#include "LayerUpdateQueue.h" 20#include "RenderNode.h" 21#include "VectorDrawable.h" 22#include "renderstate/OffscreenBufferPool.h" 23#include "hwui/Canvas.h" 24#include "utils/FatVector.h" 25#include "utils/PaintUtils.h" 26#include "utils/TraceUtils.h" 27 28#include <SkPathOps.h> 29#include <utils/TypeHelpers.h> 30 31namespace android { 32namespace uirenderer { 33 34FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, 35 uint32_t viewportWidth, uint32_t viewportHeight, 36 const std::vector< sp<RenderNode> >& nodes, 37 const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches& caches) 38 : mCanvasState(*this) 39 , mCaches(caches) 40 , mLightRadius(lightGeometry.radius) 41 , mDrawFbo0(!nodes.empty()) { 42 ATRACE_NAME("prepare drawing commands"); 43 44 mLayerBuilders.reserve(layers.entries().size()); 45 mLayerStack.reserve(layers.entries().size()); 46 47 // Prepare to defer Fbo0 48 auto fbo0 = mAllocator.create<LayerBuilder>(viewportWidth, viewportHeight, Rect(clip)); 49 mLayerBuilders.push_back(fbo0); 50 mLayerStack.push_back(0); 51 mCanvasState.initializeSaveStack(viewportWidth, viewportHeight, 52 clip.fLeft, clip.fTop, clip.fRight, clip.fBottom, 53 lightGeometry.center); 54 55 // Render all layers to be updated, in order. Defer in reverse order, so that they'll be 56 // updated in the order they're passed in (mLayerBuilders are issued to Renderer in reverse) 57 for (int i = layers.entries().size() - 1; i >= 0; i--) { 58 RenderNode* layerNode = layers.entries()[i].renderNode; 59 // only schedule repaint if node still on layer - possible it may have been 60 // removed during a dropped frame, but layers may still remain scheduled so 61 // as not to lose info on what portion is damaged 62 if (CC_LIKELY(layerNode->getLayer() != nullptr)) { 63 const Rect& layerDamage = layers.entries()[i].damage; 64 layerNode->computeOrdering(); 65 66 // map current light center into RenderNode's coordinate space 67 Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter(); 68 layerNode->getLayer()->inverseTransformInWindow.mapPoint3d(lightCenter); 69 70 saveForLayer(layerNode->getWidth(), layerNode->getHeight(), 0, 0, 71 layerDamage, lightCenter, nullptr, layerNode); 72 73 if (layerNode->getDisplayList()) { 74 deferNodeOps(*layerNode); 75 } 76 restoreForLayer(); 77 } 78 } 79 80 // It there are multiple render nodes, they are laid out as follows: 81 // #0 - backdrop (content + caption) 82 // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds) 83 // #2 - additional overlay nodes 84 // Usually the backdrop cannot be seen since it will be entirely covered by the content. While 85 // resizing however it might become partially visible. The following render loop will crop the 86 // backdrop against the content and draw the remaining part of it. It will then draw the content 87 // cropped to the backdrop (since that indicates a shrinking of the window). 88 // 89 // Additional nodes will be drawn on top with no particular clipping semantics. 90 91 // The bounds of the backdrop against which the content should be clipped. 92 Rect backdropBounds = contentDrawBounds; 93 // Usually the contents bounds should be mContentDrawBounds - however - we will 94 // move it towards the fixed edge to give it a more stable appearance (for the moment). 95 // If there is no content bounds we ignore the layering as stated above and start with 2. 96 int layer = (contentDrawBounds.isEmpty() || nodes.size() == 1) ? 2 : 0; 97 98 for (const sp<RenderNode>& node : nodes) { 99 if (node->nothingToDraw()) continue; 100 node->computeOrdering(); 101 int count = mCanvasState.save(SaveFlags::MatrixClip); 102 103 if (layer == 0) { 104 const RenderProperties& properties = node->properties(); 105 Rect targetBounds(properties.getLeft(), properties.getTop(), 106 properties.getRight(), properties.getBottom()); 107 // Move the content bounds towards the fixed corner of the backdrop. 108 const int x = targetBounds.left; 109 const int y = targetBounds.top; 110 // Remember the intersection of the target bounds and the intersection bounds against 111 // which we have to crop the content. 112 backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight()); 113 backdropBounds.doIntersect(targetBounds); 114 } else if (layer == 1) { 115 // We shift and clip the content to match its final location in the window. 116 const float left = contentDrawBounds.left; 117 const float top = contentDrawBounds.top; 118 const float dx = backdropBounds.left - left; 119 const float dy = backdropBounds.top - top; 120 const float width = backdropBounds.getWidth(); 121 const float height = backdropBounds.getHeight(); 122 mCanvasState.translate(dx, dy); 123 // It gets cropped against the bounds of the backdrop to stay inside. 124 mCanvasState.clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op); 125 } 126 127 deferNodePropsAndOps(*node); 128 mCanvasState.restoreToCount(count); 129 layer++; 130 } 131} 132 133void FrameBuilder::onViewportInitialized() {} 134 135void FrameBuilder::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {} 136 137void FrameBuilder::deferNodePropsAndOps(RenderNode& node) { 138 const RenderProperties& properties = node.properties(); 139 const Outline& outline = properties.getOutline(); 140 if (properties.getAlpha() <= 0 141 || (outline.getShouldClip() && outline.isEmpty()) 142 || properties.getScaleX() == 0 143 || properties.getScaleY() == 0) { 144 return; // rejected 145 } 146 147 if (properties.getLeft() != 0 || properties.getTop() != 0) { 148 mCanvasState.translate(properties.getLeft(), properties.getTop()); 149 } 150 if (properties.getStaticMatrix()) { 151 mCanvasState.concatMatrix(*properties.getStaticMatrix()); 152 } else if (properties.getAnimationMatrix()) { 153 mCanvasState.concatMatrix(*properties.getAnimationMatrix()); 154 } 155 if (properties.hasTransformMatrix()) { 156 if (properties.isTransformTranslateOnly()) { 157 mCanvasState.translate(properties.getTranslationX(), properties.getTranslationY()); 158 } else { 159 mCanvasState.concatMatrix(*properties.getTransformMatrix()); 160 } 161 } 162 163 const int width = properties.getWidth(); 164 const int height = properties.getHeight(); 165 166 Rect saveLayerBounds; // will be set to non-empty if saveLayer needed 167 const bool isLayer = properties.effectiveLayerType() != LayerType::None; 168 int clipFlags = properties.getClippingFlags(); 169 if (properties.getAlpha() < 1) { 170 if (isLayer) { 171 clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer 172 } 173 if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) { 174 // simply scale rendering content's alpha 175 mCanvasState.scaleAlpha(properties.getAlpha()); 176 } else { 177 // schedule saveLayer by initializing saveLayerBounds 178 saveLayerBounds.set(0, 0, width, height); 179 if (clipFlags) { 180 properties.getClippingRectForFlags(clipFlags, &saveLayerBounds); 181 clipFlags = 0; // all clipping done by savelayer 182 } 183 } 184 185 if (CC_UNLIKELY(ATRACE_ENABLED() && properties.promotedToLayer())) { 186 // pretend alpha always causes savelayer to warn about 187 // performance problem affecting old versions 188 ATRACE_FORMAT("%s alpha caused saveLayer %dx%d", node.getName(), width, height); 189 } 190 } 191 if (clipFlags) { 192 Rect clipRect; 193 properties.getClippingRectForFlags(clipFlags, &clipRect); 194 mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, 195 SkRegion::kIntersect_Op); 196 } 197 198 if (properties.getRevealClip().willClip()) { 199 Rect bounds; 200 properties.getRevealClip().getBounds(&bounds); 201 mCanvasState.setClippingRoundRect(mAllocator, 202 bounds, properties.getRevealClip().getRadius()); 203 } else if (properties.getOutline().willClip()) { 204 mCanvasState.setClippingOutline(mAllocator, &(properties.getOutline())); 205 } 206 207 bool quickRejected = mCanvasState.currentSnapshot()->getRenderTargetClip().isEmpty() 208 || (properties.getClipToBounds() 209 && mCanvasState.quickRejectConservative(0, 0, width, height)); 210 if (!quickRejected) { 211 // not rejected, so defer render as either Layer, or direct (possibly wrapped in saveLayer) 212 if (node.getLayer()) { 213 // HW layer 214 LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>(node); 215 BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp); 216 if (bakedOpState) { 217 // Node's layer already deferred, schedule it to render into parent layer 218 currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap); 219 } 220 } else if (CC_UNLIKELY(!saveLayerBounds.isEmpty())) { 221 // draw DisplayList contents within temporary, since persisted layer could not be used. 222 // (temp layers are clipped to viewport, since they don't persist offscreen content) 223 SkPaint saveLayerPaint; 224 saveLayerPaint.setAlpha(properties.getAlpha()); 225 deferBeginLayerOp(*mAllocator.create_trivial<BeginLayerOp>( 226 saveLayerBounds, 227 Matrix4::identity(), 228 nullptr, // no record-time clip - need only respect defer-time one 229 &saveLayerPaint)); 230 deferNodeOps(node); 231 deferEndLayerOp(*mAllocator.create_trivial<EndLayerOp>()); 232 } else { 233 deferNodeOps(node); 234 } 235 } 236} 237 238typedef key_value_pair_t<float, const RenderNodeOp*> ZRenderNodeOpPair; 239 240template <typename V> 241static void buildZSortedChildList(V* zTranslatedNodes, 242 const DisplayList& displayList, const DisplayList::Chunk& chunk) { 243 if (chunk.beginChildIndex == chunk.endChildIndex) return; 244 245 for (size_t i = chunk.beginChildIndex; i < chunk.endChildIndex; i++) { 246 RenderNodeOp* childOp = displayList.getChildren()[i]; 247 RenderNode* child = childOp->renderNode; 248 float childZ = child->properties().getZ(); 249 250 if (!MathUtils::isZero(childZ) && chunk.reorderChildren) { 251 zTranslatedNodes->push_back(ZRenderNodeOpPair(childZ, childOp)); 252 childOp->skipInOrderDraw = true; 253 } else if (!child->properties().getProjectBackwards()) { 254 // regular, in order drawing DisplayList 255 childOp->skipInOrderDraw = false; 256 } 257 } 258 259 // Z sort any 3d children (stable-ness makes z compare fall back to standard drawing order) 260 std::stable_sort(zTranslatedNodes->begin(), zTranslatedNodes->end()); 261} 262 263template <typename V> 264static size_t findNonNegativeIndex(const V& zTranslatedNodes) { 265 for (size_t i = 0; i < zTranslatedNodes.size(); i++) { 266 if (zTranslatedNodes[i].key >= 0.0f) return i; 267 } 268 return zTranslatedNodes.size(); 269} 270 271template <typename V> 272void FrameBuilder::defer3dChildren(const ClipBase* reorderClip, ChildrenSelectMode mode, 273 const V& zTranslatedNodes) { 274 const int size = zTranslatedNodes.size(); 275 if (size == 0 276 || (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f) 277 || (mode == ChildrenSelectMode::Positive && zTranslatedNodes[size - 1].key < 0.0f)) { 278 // no 3d children to draw 279 return; 280 } 281 282 /** 283 * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters 284 * with very similar Z heights to draw together. 285 * 286 * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are 287 * underneath both, and neither's shadow is drawn on top of the other. 288 */ 289 const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes); 290 size_t drawIndex, shadowIndex, endIndex; 291 if (mode == ChildrenSelectMode::Negative) { 292 drawIndex = 0; 293 endIndex = nonNegativeIndex; 294 shadowIndex = endIndex; // draw no shadows 295 } else { 296 drawIndex = nonNegativeIndex; 297 endIndex = size; 298 shadowIndex = drawIndex; // potentially draw shadow for each pos Z child 299 } 300 301 float lastCasterZ = 0.0f; 302 while (shadowIndex < endIndex || drawIndex < endIndex) { 303 if (shadowIndex < endIndex) { 304 const RenderNodeOp* casterNodeOp = zTranslatedNodes[shadowIndex].value; 305 const float casterZ = zTranslatedNodes[shadowIndex].key; 306 // attempt to render the shadow if the caster about to be drawn is its caster, 307 // OR if its caster's Z value is similar to the previous potential caster 308 if (shadowIndex == drawIndex || casterZ - lastCasterZ < 0.1f) { 309 deferShadow(reorderClip, *casterNodeOp); 310 311 lastCasterZ = casterZ; // must do this even if current caster not casting a shadow 312 shadowIndex++; 313 continue; 314 } 315 } 316 317 const RenderNodeOp* childOp = zTranslatedNodes[drawIndex].value; 318 deferRenderNodeOpImpl(*childOp); 319 drawIndex++; 320 } 321} 322 323void FrameBuilder::deferShadow(const ClipBase* reorderClip, const RenderNodeOp& casterNodeOp) { 324 auto& node = *casterNodeOp.renderNode; 325 auto& properties = node.properties(); 326 327 if (properties.getAlpha() <= 0.0f 328 || properties.getOutline().getAlpha() <= 0.0f 329 || !properties.getOutline().getPath() 330 || properties.getScaleX() == 0 331 || properties.getScaleY() == 0) { 332 // no shadow to draw 333 return; 334 } 335 336 const SkPath* casterOutlinePath = properties.getOutline().getPath(); 337 const SkPath* revealClipPath = properties.getRevealClip().getPath(); 338 if (revealClipPath && revealClipPath->isEmpty()) return; 339 340 float casterAlpha = properties.getAlpha() * properties.getOutline().getAlpha(); 341 342 // holds temporary SkPath to store the result of intersections 343 SkPath* frameAllocatedPath = nullptr; 344 const SkPath* casterPath = casterOutlinePath; 345 346 // intersect the shadow-casting path with the reveal, if present 347 if (revealClipPath) { 348 frameAllocatedPath = createFrameAllocatedPath(); 349 350 Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, frameAllocatedPath); 351 casterPath = frameAllocatedPath; 352 } 353 354 // intersect the shadow-casting path with the clipBounds, if present 355 if (properties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS) { 356 if (!frameAllocatedPath) { 357 frameAllocatedPath = createFrameAllocatedPath(); 358 } 359 Rect clipBounds; 360 properties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds); 361 SkPath clipBoundsPath; 362 clipBoundsPath.addRect(clipBounds.left, clipBounds.top, 363 clipBounds.right, clipBounds.bottom); 364 365 Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, frameAllocatedPath); 366 casterPath = frameAllocatedPath; 367 } 368 369 // apply reorder clip to shadow, so it respects clip at beginning of reorderable chunk 370 int restoreTo = mCanvasState.save(SaveFlags::MatrixClip); 371 mCanvasState.writableSnapshot()->applyClip(reorderClip, 372 *mCanvasState.currentSnapshot()->transform); 373 if (CC_LIKELY(!mCanvasState.getRenderTargetClipBounds().isEmpty())) { 374 Matrix4 shadowMatrixXY(casterNodeOp.localMatrix); 375 Matrix4 shadowMatrixZ(casterNodeOp.localMatrix); 376 node.applyViewPropertyTransforms(shadowMatrixXY, false); 377 node.applyViewPropertyTransforms(shadowMatrixZ, true); 378 379 sp<TessellationCache::ShadowTask> task = mCaches.tessellationCache.getShadowTask( 380 mCanvasState.currentTransform(), 381 mCanvasState.getLocalClipBounds(), 382 casterAlpha >= 1.0f, 383 casterPath, 384 &shadowMatrixXY, &shadowMatrixZ, 385 mCanvasState.currentSnapshot()->getRelativeLightCenter(), 386 mLightRadius); 387 ShadowOp* shadowOp = mAllocator.create<ShadowOp>(task, casterAlpha); 388 BakedOpState* bakedOpState = BakedOpState::tryShadowOpConstruct( 389 mAllocator, *mCanvasState.writableSnapshot(), shadowOp); 390 if (CC_LIKELY(bakedOpState)) { 391 currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Shadow); 392 } 393 } 394 mCanvasState.restoreToCount(restoreTo); 395} 396 397void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) { 398 int count = mCanvasState.save(SaveFlags::MatrixClip); 399 const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath(); 400 401 SkPath transformedMaskPath; // on stack, since BakedOpState makes a deep copy 402 if (projectionReceiverOutline) { 403 // transform the mask for this projector into render target space 404 // TODO: consider combining both transforms by stashing transform instead of applying 405 SkMatrix skCurrentTransform; 406 mCanvasState.currentTransform()->copyTo(skCurrentTransform); 407 projectionReceiverOutline->transform( 408 skCurrentTransform, 409 &transformedMaskPath); 410 mCanvasState.setProjectionPathMask(mAllocator, &transformedMaskPath); 411 } 412 413 for (size_t i = 0; i < renderNode.mProjectedNodes.size(); i++) { 414 RenderNodeOp* childOp = renderNode.mProjectedNodes[i]; 415 RenderNode& childNode = *childOp->renderNode; 416 417 // Draw child if it has content, but ignore state in childOp - matrix already applied to 418 // transformFromCompositingAncestor, and record-time clip is ignored when projecting 419 if (!childNode.nothingToDraw()) { 420 int restoreTo = mCanvasState.save(SaveFlags::MatrixClip); 421 422 // Apply transform between ancestor and projected descendant 423 mCanvasState.concatMatrix(childOp->transformFromCompositingAncestor); 424 425 deferNodePropsAndOps(childNode); 426 427 mCanvasState.restoreToCount(restoreTo); 428 } 429 } 430 mCanvasState.restoreToCount(count); 431} 432 433/** 434 * Used to define a list of lambdas referencing private FrameBuilder::onXX::defer() methods. 435 * 436 * This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas. 437 * E.g. a BitmapOp op then would be dispatched to FrameBuilder::onBitmapOp(const BitmapOp&) 438 */ 439#define OP_RECEIVER(Type) \ 440 [](FrameBuilder& frameBuilder, const RecordedOp& op) { frameBuilder.defer##Type(static_cast<const Type&>(op)); }, 441void FrameBuilder::deferNodeOps(const RenderNode& renderNode) { 442 typedef void (*OpDispatcher) (FrameBuilder& frameBuilder, const RecordedOp& op); 443 static OpDispatcher receivers[] = BUILD_DEFERRABLE_OP_LUT(OP_RECEIVER); 444 445 // can't be null, since DL=null node rejection happens before deferNodePropsAndOps 446 const DisplayList& displayList = *(renderNode.getDisplayList()); 447 for (auto& chunk : displayList.getChunks()) { 448 FatVector<ZRenderNodeOpPair, 16> zTranslatedNodes; 449 buildZSortedChildList(&zTranslatedNodes, displayList, chunk); 450 451 defer3dChildren(chunk.reorderClip, ChildrenSelectMode::Negative, zTranslatedNodes); 452 for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) { 453 const RecordedOp* op = displayList.getOps()[opIndex]; 454 receivers[op->opId](*this, *op); 455 456 if (CC_UNLIKELY(!renderNode.mProjectedNodes.empty() 457 && displayList.projectionReceiveIndex >= 0 458 && static_cast<int>(opIndex) == displayList.projectionReceiveIndex)) { 459 deferProjectedChildren(renderNode); 460 } 461 } 462 defer3dChildren(chunk.reorderClip, ChildrenSelectMode::Positive, zTranslatedNodes); 463 } 464} 465 466void FrameBuilder::deferRenderNodeOpImpl(const RenderNodeOp& op) { 467 if (op.renderNode->nothingToDraw()) return; 468 int count = mCanvasState.save(SaveFlags::MatrixClip); 469 470 // apply state from RecordedOp (clip first, since op's clip is transformed by current matrix) 471 mCanvasState.writableSnapshot()->applyClip(op.localClip, 472 *mCanvasState.currentSnapshot()->transform); 473 mCanvasState.concatMatrix(op.localMatrix); 474 475 // then apply state from node properties, and defer ops 476 deferNodePropsAndOps(*op.renderNode); 477 478 mCanvasState.restoreToCount(count); 479} 480 481void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) { 482 if (!op.skipInOrderDraw) { 483 deferRenderNodeOpImpl(op); 484 } 485} 486 487/** 488 * Defers an unmergeable, strokeable op, accounting correctly 489 * for paint's style on the bounds being computed. 490 */ 491BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId, 492 BakedOpState::StrokeBehavior strokeBehavior) { 493 // Note: here we account for stroke when baking the op 494 BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct( 495 mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior); 496 if (!bakedState) return nullptr; // quick rejected 497 498 if (op.opId == RecordedOpId::RectOp && op.paint->getStyle() != SkPaint::kStroke_Style) { 499 bakedState->setupOpacity(op.paint); 500 } 501 502 currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId); 503 return bakedState; 504} 505 506/** 507 * Returns batch id for tessellatable shapes, based on paint. Checks to see if path effect/AA will 508 * be used, since they trigger significantly different rendering paths. 509 * 510 * Note: not used for lines/points, since they don't currently support path effects. 511 */ 512static batchid_t tessBatchId(const RecordedOp& op) { 513 const SkPaint& paint = *(op.paint); 514 return paint.getPathEffect() 515 ? OpBatchType::AlphaMaskTexture 516 : (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices); 517} 518 519void FrameBuilder::deferArcOp(const ArcOp& op) { 520 deferStrokeableOp(op, tessBatchId(op)); 521} 522 523static bool hasMergeableClip(const BakedOpState& state) { 524 return state.computedState.clipState 525 || state.computedState.clipState->mode == ClipMode::Rectangle; 526} 527 528void FrameBuilder::deferBitmapOp(const BitmapOp& op) { 529 BakedOpState* bakedState = tryBakeOpState(op); 530 if (!bakedState) return; // quick rejected 531 532 if (op.bitmap->isOpaque()) { 533 bakedState->setupOpacity(op.paint); 534 } 535 536 // Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation 537 // Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in 538 // MergingDrawBatch::canMergeWith() 539 if (bakedState->computedState.transform.isSimple() 540 && bakedState->computedState.transform.positiveScale() 541 && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode 542 && op.bitmap->colorType() != kAlpha_8_SkColorType 543 && hasMergeableClip(*bakedState)) { 544 mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID()); 545 // TODO: AssetAtlas in mergeId 546 currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::Bitmap, mergeId); 547 } else { 548 currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); 549 } 550} 551 552void FrameBuilder::deferBitmapMeshOp(const BitmapMeshOp& op) { 553 BakedOpState* bakedState = tryBakeOpState(op); 554 if (!bakedState) return; // quick rejected 555 currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); 556} 557 558void FrameBuilder::deferBitmapRectOp(const BitmapRectOp& op) { 559 BakedOpState* bakedState = tryBakeOpState(op); 560 if (!bakedState) return; // quick rejected 561 currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); 562} 563 564void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) { 565 const SkBitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty(); 566 SkPaint* paint = op.vectorDrawable->getPaint(); 567 const BitmapRectOp* resolvedOp = mAllocator.create_trivial<BitmapRectOp>(op.unmappedBounds, 568 op.localMatrix, 569 op.localClip, 570 paint, 571 &bitmap, 572 Rect(bitmap.width(), bitmap.height())); 573 deferBitmapRectOp(*resolvedOp); 574} 575 576void FrameBuilder::deferCirclePropsOp(const CirclePropsOp& op) { 577 // allocate a temporary oval op (with mAllocator, so it persists until render), so the 578 // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple. 579 float x = *(op.x); 580 float y = *(op.y); 581 float radius = *(op.radius); 582 Rect unmappedBounds(x - radius, y - radius, x + radius, y + radius); 583 const OvalOp* resolvedOp = mAllocator.create_trivial<OvalOp>( 584 unmappedBounds, 585 op.localMatrix, 586 op.localClip, 587 op.paint); 588 deferOvalOp(*resolvedOp); 589} 590 591void FrameBuilder::deferColorOp(const ColorOp& op) { 592 BakedOpState* bakedState = tryBakeUnboundedOpState(op); 593 if (!bakedState) return; // quick rejected 594 currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices); 595} 596 597void FrameBuilder::deferFunctorOp(const FunctorOp& op) { 598 BakedOpState* bakedState = tryBakeUnboundedOpState(op); 599 if (!bakedState) return; // quick rejected 600 currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Functor); 601} 602 603void FrameBuilder::deferLinesOp(const LinesOp& op) { 604 batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices; 605 deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced); 606} 607 608void FrameBuilder::deferOvalOp(const OvalOp& op) { 609 deferStrokeableOp(op, tessBatchId(op)); 610} 611 612void FrameBuilder::deferPatchOp(const PatchOp& op) { 613 BakedOpState* bakedState = tryBakeOpState(op); 614 if (!bakedState) return; // quick rejected 615 616 if (bakedState->computedState.transform.isPureTranslate() 617 && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode 618 && hasMergeableClip(*bakedState)) { 619 mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.bitmap->getGenerationID()); 620 // TODO: AssetAtlas in mergeId 621 622 // Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together 623 currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::MergedPatch, mergeId); 624 } else { 625 // Use Bitmap batchId since Bitmap+Patch use same shader 626 currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap); 627 } 628} 629 630void FrameBuilder::deferPathOp(const PathOp& op) { 631 auto state = deferStrokeableOp(op, OpBatchType::AlphaMaskTexture); 632 if (CC_LIKELY(state)) { 633 mCaches.pathCache.precache(op.path, op.paint); 634 } 635} 636 637void FrameBuilder::deferPointsOp(const PointsOp& op) { 638 batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices; 639 deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced); 640} 641 642void FrameBuilder::deferRectOp(const RectOp& op) { 643 deferStrokeableOp(op, tessBatchId(op)); 644} 645 646void FrameBuilder::deferRoundRectOp(const RoundRectOp& op) { 647 auto state = deferStrokeableOp(op, tessBatchId(op)); 648 if (CC_LIKELY(state && !op.paint->getPathEffect())) { 649 // TODO: consider storing tessellation task in BakedOpState 650 mCaches.tessellationCache.precacheRoundRect(state->computedState.transform, *(op.paint), 651 op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry); 652 } 653} 654 655void FrameBuilder::deferRoundRectPropsOp(const RoundRectPropsOp& op) { 656 // allocate a temporary round rect op (with mAllocator, so it persists until render), so the 657 // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple. 658 const RoundRectOp* resolvedOp = mAllocator.create_trivial<RoundRectOp>( 659 Rect(*(op.left), *(op.top), *(op.right), *(op.bottom)), 660 op.localMatrix, 661 op.localClip, 662 op.paint, *op.rx, *op.ry); 663 deferRoundRectOp(*resolvedOp); 664} 665 666void FrameBuilder::deferSimpleRectsOp(const SimpleRectsOp& op) { 667 BakedOpState* bakedState = tryBakeOpState(op); 668 if (!bakedState) return; // quick rejected 669 currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices); 670} 671 672static batchid_t textBatchId(const SkPaint& paint) { 673 // TODO: better handling of shader (since we won't care about color then) 674 return paint.getColor() == SK_ColorBLACK ? OpBatchType::Text : OpBatchType::ColorText; 675} 676 677void FrameBuilder::deferTextOp(const TextOp& op) { 678 BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct( 679 mAllocator, *mCanvasState.writableSnapshot(), op, 680 BakedOpState::StrokeBehavior::StyleDefined); 681 if (!bakedState) return; // quick rejected 682 683 batchid_t batchId = textBatchId(*(op.paint)); 684 if (bakedState->computedState.transform.isPureTranslate() 685 && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode 686 && hasMergeableClip(*bakedState)) { 687 mergeid_t mergeId = reinterpret_cast<mergeid_t>(op.paint->getColor()); 688 currentLayer().deferMergeableOp(mAllocator, bakedState, batchId, mergeId); 689 } else { 690 currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId); 691 } 692 693 FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(); 694 auto& totalTransform = bakedState->computedState.transform; 695 if (totalTransform.isPureTranslate() || totalTransform.isPerspective()) { 696 fontRenderer.precache(op.paint, op.glyphs, op.glyphCount, SkMatrix::I()); 697 } else { 698 // Partial transform case, see BakedOpDispatcher::renderTextOp 699 float sx, sy; 700 totalTransform.decomposeScale(sx, sy); 701 fontRenderer.precache(op.paint, op.glyphs, op.glyphCount, SkMatrix::MakeScale( 702 roundf(std::max(1.0f, sx)), 703 roundf(std::max(1.0f, sy)))); 704 } 705} 706 707void FrameBuilder::deferTextOnPathOp(const TextOnPathOp& op) { 708 BakedOpState* bakedState = tryBakeUnboundedOpState(op); 709 if (!bakedState) return; // quick rejected 710 currentLayer().deferUnmergeableOp(mAllocator, bakedState, textBatchId(*(op.paint))); 711 712 mCaches.fontRenderer.getFontRenderer().precache( 713 op.paint, op.glyphs, op.glyphCount, SkMatrix::I()); 714} 715 716void FrameBuilder::deferTextureLayerOp(const TextureLayerOp& op) { 717 if (CC_UNLIKELY(!op.layer->isRenderable())) return; 718 719 const TextureLayerOp* textureLayerOp = &op; 720 // Now safe to access transform (which was potentially unready at record time) 721 if (!op.layer->getTransform().isIdentity()) { 722 // non-identity transform present, so 'inject it' into op by copying + replacing matrix 723 Matrix4 combinedMatrix(op.localMatrix); 724 combinedMatrix.multiply(op.layer->getTransform()); 725 textureLayerOp = mAllocator.create<TextureLayerOp>(op, combinedMatrix); 726 } 727 BakedOpState* bakedState = tryBakeOpState(*textureLayerOp); 728 729 if (!bakedState) return; // quick rejected 730 currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::TextureLayer); 731} 732 733void FrameBuilder::saveForLayer(uint32_t layerWidth, uint32_t layerHeight, 734 float contentTranslateX, float contentTranslateY, 735 const Rect& repaintRect, 736 const Vector3& lightCenter, 737 const BeginLayerOp* beginLayerOp, RenderNode* renderNode) { 738 mCanvasState.save(SaveFlags::MatrixClip); 739 mCanvasState.writableSnapshot()->initializeViewport(layerWidth, layerHeight); 740 mCanvasState.writableSnapshot()->roundRectClipState = nullptr; 741 mCanvasState.writableSnapshot()->setRelativeLightCenter(lightCenter); 742 mCanvasState.writableSnapshot()->transform->loadTranslate( 743 contentTranslateX, contentTranslateY, 0); 744 mCanvasState.writableSnapshot()->setClip( 745 repaintRect.left, repaintRect.top, repaintRect.right, repaintRect.bottom); 746 747 // create a new layer repaint, and push its index on the stack 748 mLayerStack.push_back(mLayerBuilders.size()); 749 auto newFbo = mAllocator.create<LayerBuilder>(layerWidth, layerHeight, 750 repaintRect, beginLayerOp, renderNode); 751 mLayerBuilders.push_back(newFbo); 752} 753 754void FrameBuilder::restoreForLayer() { 755 // restore canvas, and pop finished layer off of the stack 756 mCanvasState.restore(); 757 mLayerStack.pop_back(); 758} 759 760// TODO: defer time rejection (when bounds become empty) + tests 761// Option - just skip layers with no bounds at playback + defer? 762void FrameBuilder::deferBeginLayerOp(const BeginLayerOp& op) { 763 uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth(); 764 uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight(); 765 766 auto previous = mCanvasState.currentSnapshot(); 767 Vector3 lightCenter = previous->getRelativeLightCenter(); 768 769 // Combine all transforms used to present saveLayer content: 770 // parent content transform * canvas transform * bounds offset 771 Matrix4 contentTransform(*(previous->transform)); 772 contentTransform.multiply(op.localMatrix); 773 contentTransform.translate(op.unmappedBounds.left, op.unmappedBounds.top); 774 775 Matrix4 inverseContentTransform; 776 inverseContentTransform.loadInverse(contentTransform); 777 778 // map the light center into layer-relative space 779 inverseContentTransform.mapPoint3d(lightCenter); 780 781 // Clip bounds of temporary layer to parent's clip rect, so: 782 Rect saveLayerBounds(layerWidth, layerHeight); 783 // 1) transform Rect(width, height) into parent's space 784 // note: left/top offsets put in contentTransform above 785 contentTransform.mapRect(saveLayerBounds); 786 // 2) intersect with parent's clip 787 saveLayerBounds.doIntersect(previous->getRenderTargetClip()); 788 // 3) and transform back 789 inverseContentTransform.mapRect(saveLayerBounds); 790 saveLayerBounds.doIntersect(Rect(layerWidth, layerHeight)); 791 saveLayerBounds.roundOut(); 792 793 // if bounds are reduced, will clip the layer's area by reducing required bounds... 794 layerWidth = saveLayerBounds.getWidth(); 795 layerHeight = saveLayerBounds.getHeight(); 796 // ...and shifting drawing content to account for left/top side clipping 797 float contentTranslateX = -saveLayerBounds.left; 798 float contentTranslateY = -saveLayerBounds.top; 799 800 saveForLayer(layerWidth, layerHeight, 801 contentTranslateX, contentTranslateY, 802 Rect(layerWidth, layerHeight), 803 lightCenter, 804 &op, nullptr); 805} 806 807void FrameBuilder::deferEndLayerOp(const EndLayerOp& /* ignored */) { 808 const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp; 809 int finishedLayerIndex = mLayerStack.back(); 810 811 restoreForLayer(); 812 813 // record the draw operation into the previous layer's list of draw commands 814 // uses state from the associated beginLayerOp, since it has all the state needed for drawing 815 LayerOp* drawLayerOp = mAllocator.create_trivial<LayerOp>( 816 beginLayerOp.unmappedBounds, 817 beginLayerOp.localMatrix, 818 beginLayerOp.localClip, 819 beginLayerOp.paint, 820 &(mLayerBuilders[finishedLayerIndex]->offscreenBuffer)); 821 BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp); 822 823 if (bakedOpState) { 824 // Layer will be drawn into parent layer (which is now current, since we popped mLayerStack) 825 currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Bitmap); 826 } else { 827 // Layer won't be drawn - delete its drawing batches to prevent it from doing any work 828 // TODO: need to prevent any render work from being done 829 // - create layerop earlier for reject purposes? 830 mLayerBuilders[finishedLayerIndex]->clear(); 831 return; 832 } 833} 834 835void FrameBuilder::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) { 836 Matrix4 boundsTransform(*(mCanvasState.currentSnapshot()->transform)); 837 boundsTransform.multiply(op.localMatrix); 838 839 Rect dstRect(op.unmappedBounds); 840 boundsTransform.mapRect(dstRect); 841 dstRect.doIntersect(mCanvasState.currentSnapshot()->getRenderTargetClip()); 842 843 if (dstRect.isEmpty()) { 844 // Unclipped layer rejected - push a null op, so next EndUnclippedLayerOp is ignored 845 currentLayer().activeUnclippedSaveLayers.push_back(nullptr); 846 } else { 847 // Allocate a holding position for the layer object (copyTo will produce, copyFrom will consume) 848 OffscreenBuffer** layerHandle = mAllocator.create<OffscreenBuffer*>(nullptr); 849 850 /** 851 * First, defer an operation to copy out the content from the rendertarget into a layer. 852 */ 853 auto copyToOp = mAllocator.create_trivial<CopyToLayerOp>(op, layerHandle); 854 BakedOpState* bakedState = BakedOpState::directConstruct(mAllocator, 855 &(currentLayer().repaintClip), dstRect, *copyToOp); 856 currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::CopyToLayer); 857 858 /** 859 * Defer a clear rect, so that clears from multiple unclipped layers can be drawn 860 * both 1) simultaneously, and 2) as long after the copyToLayer executes as possible 861 */ 862 currentLayer().deferLayerClear(dstRect); 863 864 /** 865 * And stash an operation to copy that layer back under the rendertarget until 866 * a balanced EndUnclippedLayerOp is seen 867 */ 868 auto copyFromOp = mAllocator.create_trivial<CopyFromLayerOp>(op, layerHandle); 869 bakedState = BakedOpState::directConstruct(mAllocator, 870 &(currentLayer().repaintClip), dstRect, *copyFromOp); 871 currentLayer().activeUnclippedSaveLayers.push_back(bakedState); 872 } 873} 874 875void FrameBuilder::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) { 876 LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!"); 877 878 BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back(); 879 currentLayer().activeUnclippedSaveLayers.pop_back(); 880 if (copyFromLayerOp) { 881 currentLayer().deferUnmergeableOp(mAllocator, copyFromLayerOp, OpBatchType::CopyFromLayer); 882 } 883} 884 885void FrameBuilder::finishDefer() { 886 mCaches.fontRenderer.endPrecaching(); 887} 888 889} // namespace uirenderer 890} // namespace android 891