void TextureMapperLayer::paintSelf(const TextureMapperPaintOptions& options) { if (!m_state.visible || !m_state.contentsVisible) return; // We apply the following transform to compensate for painting into a surface, and then apply the offset so that the painting fits in the target rect. TransformationMatrix transform; transform.translate(options.offset.width(), options.offset.height()); transform.multiply(options.transform); transform.multiply(m_transform.combined()); float opacity = options.opacity; RefPtr<BitmapTexture> mask = options.mask; if (m_backingStore) { ASSERT(m_state.drawsContent && m_state.contentsVisible && !m_size.isEmpty()); ASSERT(!layerRect().isEmpty()); m_backingStore->paintToTextureMapper(options.textureMapper, layerRect(), transform, opacity, mask.get()); } if (m_contentsLayer) { ASSERT(!layerRect().isEmpty()); m_contentsLayer->paintToTextureMapper(options.textureMapper, m_state.contentsRect, transform, opacity, mask.get()); } }
IntRect TextureMapperLayer::intermediateSurfaceRect(const TransformationMatrix& matrix) { IntRect rect; TransformationMatrix localTransform = TransformationMatrix(matrix).multiply(m_transform.combined()); rect = enclosingIntRect(localTransform.mapRect(layerRect())); if (!m_state.masksToBounds && !m_state.maskLayer) { for (size_t i = 0; i < m_children.size(); ++i) rect.unite(m_children[i]->intermediateSurfaceRect(matrix)); } #if ENABLE(CSS_FILTERS) if (m_filters.hasOutsets()) { int leftOutset; int topOutset; int bottomOutset; int rightOutset; m_filters.getOutsets(topOutset, rightOutset, bottomOutset, leftOutset); IntRect unfilteredTargetRect(rect); rect.move(std::max(0, -leftOutset), std::max(0, -topOutset)); rect.expand(leftOutset + rightOutset, topOutset + bottomOutset); rect.unite(unfilteredTargetRect); } #endif if (m_state.replicaLayer) rect.unite(m_state.replicaLayer->intermediateSurfaceRect(matrix)); return rect; }
void TextureMapperLayer::paintSelf(const TextureMapperPaintOptions& options) { if (!m_state.visible || !m_state.contentsVisible) return; // We apply the following transform to compensate for painting into a surface, and then apply the offset so that the painting fits in the target rect. TransformationMatrix transform; transform.translate(options.offset.width(), options.offset.height()); transform.multiply(options.transform); transform.multiply(m_currentTransform.combined()); if (m_state.solidColor.isValid() && !m_state.contentsRect.isEmpty() && m_state.solidColor.alpha()) { options.textureMapper->drawSolidColor(m_state.contentsRect, transform, blendWithOpacity(m_state.solidColor, options.opacity)); if (m_state.showDebugBorders) options.textureMapper->drawBorder(m_state.debugBorderColor, m_state.debugBorderWidth, layerRect(), transform); return; } options.textureMapper->setWrapMode(TextureMapper::StretchWrap); options.textureMapper->setPatternTransform(TransformationMatrix()); if (m_backingStore) { FloatRect targetRect = layerRect(); ASSERT(!targetRect.isEmpty()); m_backingStore->paintToTextureMapper(options.textureMapper, targetRect, transform, options.opacity); if (m_state.showDebugBorders) m_backingStore->drawBorder(options.textureMapper, m_state.debugBorderColor, m_state.debugBorderWidth, targetRect, transform); // Only draw repaint count for the main backing store. if (m_state.showRepaintCounter) m_backingStore->drawRepaintCounter(options.textureMapper, m_state.repaintCount, m_state.debugBorderColor, targetRect, transform); } if (!m_contentsLayer) return; if (!m_state.contentsTileSize.isEmpty()) { computePatternTransformIfNeeded(); options.textureMapper->setWrapMode(TextureMapper::RepeatWrap); options.textureMapper->setPatternTransform(m_patternTransform); } ASSERT(!layerRect().isEmpty()); m_contentsLayer->paintToTextureMapper(options.textureMapper, m_state.contentsRect, transform, options.opacity); if (m_state.showDebugBorders) m_contentsLayer->drawBorder(options.textureMapper, m_state.debugBorderColor, m_state.debugBorderWidth, m_state.contentsRect, transform); }
const IntRect CCLayerImpl::getDrawRect() const { // Form the matrix used by the shader to map the corners of the layer's // bounds into the view space. FloatRect layerRect(-0.5 * bounds().width(), -0.5 * bounds().height(), bounds().width(), bounds().height()); IntRect mappedRect = enclosingIntRect(drawTransform().mapRect(layerRect)); return mappedRect; }
void CCLayerImpl::appendDebugBorderQuad(CCQuadCuller& quadList, const CCSharedQuadState* sharedQuadState) const { if (!hasDebugBorders()) return; IntRect layerRect(IntPoint(), contentBounds()); quadList.append(CCDebugBorderDrawQuad::create(sharedQuadState, layerRect, debugBorderColor(), debugBorderWidth())); }
void TextureMapperLayer::computeOverlapRegions(Region& overlapRegion, Region& nonOverlapRegion, ResolveSelfOverlapMode mode) { if (!m_state.visible || !m_state.contentsVisible) return; FloatRect boundingRect; if (m_backingStore || m_state.masksToBounds || m_state.maskLayer || hasFilters()) boundingRect = layerRect(); else if (m_contentsLayer || m_state.solidColor.alpha()) boundingRect = m_state.contentsRect; if (m_currentFilters.hasOutsets()) { FilterOutsets outsets = m_currentFilters.outsets(); IntRect unfilteredTargetRect(boundingRect); boundingRect.move(std::max(0, -outsets.left()), std::max(0, -outsets.top())); boundingRect.expand(outsets.left() + outsets.right(), outsets.top() + outsets.bottom()); boundingRect.unite(unfilteredTargetRect); } TransformationMatrix replicaMatrix; if (m_state.replicaLayer) { replicaMatrix = replicaTransform(); boundingRect.unite(replicaMatrix.mapRect(boundingRect)); } boundingRect = m_currentTransform.combined().mapRect(boundingRect); // Count all masks and filters as overlap layers. if (hasFilters() || m_state.maskLayer || (m_state.replicaLayer && m_state.replicaLayer->m_state.maskLayer)) { Region newOverlapRegion(enclosingIntRect(boundingRect)); nonOverlapRegion.subtract(newOverlapRegion); overlapRegion.unite(newOverlapRegion); return; } Region newOverlapRegion; Region newNonOverlapRegion(enclosingIntRect(boundingRect)); if (!m_state.masksToBounds) { for (auto* child : m_children) child->computeOverlapRegions(newOverlapRegion, newNonOverlapRegion, ResolveSelfOverlapIfNeeded); } if (m_state.replicaLayer) { newOverlapRegion.unite(replicaMatrix.mapRect(newOverlapRegion.bounds())); Region replicaRegion(replicaMatrix.mapRect(newNonOverlapRegion.bounds())); resolveOverlaps(replicaRegion, newOverlapRegion, newNonOverlapRegion); } if ((mode != ResolveSelfOverlapAlways) && shouldBlend()) { newNonOverlapRegion.unite(newOverlapRegion); newOverlapRegion = Region(); } overlapRegion.unite(newOverlapRegion); resolveOverlaps(newNonOverlapRegion, overlapRegion, nonOverlapRegion); }
void LayerRendererChromium::drawLayer(CCLayerImpl* layer, CCRenderSurface* targetSurface) { if (layer->renderSurface() && layer->renderSurface() != targetSurface) { layer->renderSurface()->draw(this, layer->getDrawRect()); layer->renderSurface()->releaseContentsTexture(); return; } if (!layer->drawsContent()) return; if (!layer->opacity()) return; if (layer->bounds().isEmpty()) return; IntRect targetSurfaceRect = layer->targetRenderSurface() ? layer->targetRenderSurface()->contentRect() : m_defaultRenderSurface->contentRect(); if (layer->usesLayerScissor()) { IntRect scissorRect = layer->scissorRect(); targetSurfaceRect.intersect(scissorRect); if (targetSurfaceRect.isEmpty()) return; setScissorToRect(scissorRect); } else GLC(m_context.get(), m_context->disable(GraphicsContext3D::SCISSOR_TEST)); IntRect visibleLayerRect = CCLayerTreeHostCommon::calculateVisibleLayerRect(targetSurfaceRect, layer->bounds(), layer->contentBounds(), layer->drawTransform()); visibleLayerRect.move(toSize(layer->scrollPosition())); layer->setVisibleLayerRect(visibleLayerRect); // The layer should not be drawn if (1) it is not double-sided and (2) the back of the layer is facing the screen. // This second condition is checked by computing the transformed normal of the layer. if (!layer->doubleSided()) { FloatRect layerRect(FloatPoint(0, 0), FloatSize(layer->bounds())); FloatQuad mappedLayer = layer->screenSpaceTransform().mapQuad(FloatQuad(layerRect)); FloatSize horizontalDir = mappedLayer.p2() - mappedLayer.p1(); FloatSize verticalDir = mappedLayer.p4() - mappedLayer.p1(); FloatPoint3D xAxis(horizontalDir.width(), horizontalDir.height(), 0); FloatPoint3D yAxis(verticalDir.width(), verticalDir.height(), 0); FloatPoint3D zAxis = xAxis.cross(yAxis); if (zAxis.z() < 0) return; } layer->draw(this); // Draw the debug border if there is one. layer->drawDebugBorder(this); }
// Returns true if any part of the layer falls within the visibleRect bool LayerRendererChromium::isLayerVisible(LayerChromium* layer, const TransformationMatrix& matrix, const IntRect& visibleRect) { // Form the matrix used by the shader to map the corners of the layer's // bounds into clip space. TransformationMatrix renderMatrix = matrix; renderMatrix.scale3d(layer->bounds().width(), layer->bounds().height(), 1); renderMatrix.multiply(m_projectionMatrix); FloatRect layerRect(-0.5, -0.5, 1, 1); FloatRect mappedRect = renderMatrix.mapRect(layerRect); // The layer is visible if it intersects any part of a rectangle whose origin // is at (-1, -1) and size is 2x2. return mappedRect.intersects(FloatRect(-1, -1, 2, 2)); }
IntRect TextureMapperLayer::intermediateSurfaceRect(const TransformationMatrix& matrix) { IntRect rect; TransformationMatrix localTransform = TransformationMatrix(matrix).multiply(m_transform.combined()); rect = enclosingIntRect(localTransform.mapRect(layerRect())); if (!m_state.masksToBounds && !m_state.maskLayer) { for (size_t i = 0; i < m_children.size(); ++i) rect.unite(m_children[i]->intermediateSurfaceRect(matrix)); } if (m_state.replicaLayer) rect.unite(m_state.replicaLayer->intermediateSurfaceRect(matrix)); return rect; }
void TextureMapperLayer::paintSelf(const TextureMapperPaintOptions& options) { // We apply the following transform to compensate for painting into a surface, and then apply the offset so that the painting fits in the target rect. TransformationMatrix transform = TransformationMatrix(options.transform) .multiply(m_transform.combined()) .translate(options.offset.width(), options.offset.height()); float opacity = options.opacity; RefPtr<BitmapTexture> mask = options.mask; if (m_backingStore) m_backingStore->paintToTextureMapper(options.textureMapper, layerRect(), transform, opacity, mask.get()); if (m_contentsLayer) m_contentsLayer->paintToTextureMapper(options.textureMapper, m_state.contentsRect, transform, opacity, mask.get()); }
void TextureMapperLayer::updateBackingStore(TextureMapper* textureMapper, GraphicsLayer* layer) { if (!layer || !textureMapper) return; if (!m_shouldUpdateBackingStoreFromLayer) return; if (!m_state.drawsContent || m_size.isEmpty()) { m_backingStore.clear(); return; } IntRect dirtyRect = enclosingIntRect(m_state.needsDisplay ? layerRect() : m_state.needsDisplayRect); if (dirtyRect.isEmpty()) return; if (!m_backingStore) m_backingStore = TextureMapperTiledBackingStore::create(); #if PLATFORM(QT) ASSERT(dynamic_cast<TextureMapperTiledBackingStore*>(m_backingStore.get())); #endif // Paint the entire dirty rect into an image buffer. This ensures we only paint once. OwnPtr<ImageBuffer> imageBuffer = ImageBuffer::create(dirtyRect.size()); GraphicsContext* context = imageBuffer->context(); context->setImageInterpolationQuality(textureMapper->imageInterpolationQuality()); context->setTextDrawingMode(textureMapper->textDrawingMode()); context->translate(-dirtyRect.x(), -dirtyRect.y()); layer->paintGraphicsLayerContents(*context, dirtyRect); RefPtr<Image> image; #if PLATFORM(QT) image = imageBuffer->copyImage(DontCopyBackingStore); #else // FIXME: support DontCopyBackingStore in non-Qt ports that use TextureMapper. image = imageBuffer->copyImage(CopyBackingStore); #endif static_cast<TextureMapperTiledBackingStore*>(m_backingStore.get())->updateContents(textureMapper, image.get(), m_size, dirtyRect); m_state.needsDisplay = false; m_state.needsDisplayRect = IntRect(); }
void TextureMapperLayer::paintSelfAndChildren(const TextureMapperPaintOptions& options) { paintSelf(options); if (m_children.isEmpty()) return; bool shouldClip = m_state.masksToBounds && !m_state.preserves3D; if (shouldClip) { TransformationMatrix clipTransform; clipTransform.translate(options.offset.width(), options.offset.height()); clipTransform.multiply(options.transform); clipTransform.multiply(m_currentTransform.combined()); options.textureMapper->beginClip(clipTransform, layerRect()); } for (auto* child : m_children) child->paintRecursive(options); if (shouldClip) options.textureMapper->endClip(); }
void TextureMapperLayer::paintSelfAndChildren(const TextureMapperPaintOptions& options) { paintSelf(options); if (m_children.isEmpty()) return; bool shouldClip = m_state.masksToBounds && !m_state.preserves3D; if (shouldClip) options.textureMapper->beginClip(TransformationMatrix(options.transform).multiply(m_transform.combined()), layerRect()); for (size_t i = 0; i < m_children.size(); ++i) m_children[i]->paintRecursive(options); if (shouldClip) options.textureMapper->endClip(); }
// Recursively walks the layer tree starting at the given node and computes all the // necessary transformations, scissor rectangles, render surfaces, etc. void LayerRendererChromium::updatePropertiesAndRenderSurfaces(CCLayerImpl* layer, const TransformationMatrix& parentMatrix, LayerList& renderSurfaceLayerList, LayerList& layerList) { // Compute the new matrix transformation that will be applied to this layer and // all its children. It's important to remember that the layer's position // is the position of the layer's anchor point. Also, the coordinate system used // assumes that the origin is at the lower left even though the coordinates the browser // gives us for the layers are for the upper left corner. The Y flip happens via // the orthographic projection applied at render time. // The transformation chain for the layer is (using the Matrix x Vector order): // M = M[p] * Tr[l] * M[l] * Tr[c] // Where M[p] is the parent matrix passed down to the function // Tr[l] is the translation matrix locating the layer's anchor point // Tr[c] is the translation offset between the anchor point and the center of the layer // M[l] is the layer's matrix (applied at the anchor point) // This transform creates a coordinate system whose origin is the center of the layer. // Note that the final matrix used by the shader for the layer is P * M * S . This final product // is computed in drawTexturedQuad(). // Where: P is the projection matrix // M is the layer's matrix computed above // S is the scale adjustment (to scale up to the layer size) IntSize bounds = layer->bounds(); FloatPoint anchorPoint = layer->anchorPoint(); FloatPoint position = layer->position(); // Offset between anchor point and the center of the quad. float centerOffsetX = (0.5 - anchorPoint.x()) * bounds.width(); float centerOffsetY = (0.5 - anchorPoint.y()) * bounds.height(); TransformationMatrix layerLocalTransform; // LT = Tr[l] layerLocalTransform.translate3d(position.x(), position.y(), layer->anchorPointZ()); // LT = Tr[l] * M[l] layerLocalTransform.multiply(layer->transform()); // LT = Tr[l] * M[l] * Tr[c] layerLocalTransform.translate3d(centerOffsetX, centerOffsetY, -layer->anchorPointZ()); TransformationMatrix combinedTransform = parentMatrix; combinedTransform = combinedTransform.multiply(layerLocalTransform); FloatRect layerRect(-0.5 * layer->bounds().width(), -0.5 * layer->bounds().height(), layer->bounds().width(), layer->bounds().height()); IntRect transformedLayerRect; // The layer and its descendants render on a new RenderSurface if any of // these conditions hold: // 1. The layer clips its descendants and its transform is not a simple translation. // 2. If the layer has opacity != 1 and does not have a preserves-3d transform style. // 3. The layer uses a mask // 4. The layer has a replica (used for reflections) // 5. The layer doesn't preserve-3d but is the child of a layer which does. // If a layer preserves-3d then we don't create a RenderSurface for it to avoid flattening // out its children. The opacity value of the children layers is multiplied by the opacity // of their parent. bool useSurfaceForClipping = layer->masksToBounds() && !isScaleOrTranslation(combinedTransform); bool useSurfaceForOpacity = layer->opacity() != 1 && !layer->preserves3D(); bool useSurfaceForMasking = layer->maskLayer(); bool useSurfaceForReflection = layer->replicaLayer(); bool useSurfaceForFlatDescendants = layer->parent() && layer->parent()->preserves3D() && !layer->preserves3D() && layer->descendantsDrawsContent(); if (useSurfaceForMasking || useSurfaceForReflection || useSurfaceForFlatDescendants || ((useSurfaceForClipping || useSurfaceForOpacity) && layer->descendantsDrawsContent())) { RenderSurfaceChromium* renderSurface = layer->renderSurface(); if (!renderSurface) renderSurface = layer->createRenderSurface(); // The origin of the new surface is the upper left corner of the layer. TransformationMatrix drawTransform; drawTransform.translate3d(0.5 * bounds.width(), 0.5 * bounds.height(), 0); layer->setDrawTransform(drawTransform); transformedLayerRect = IntRect(0, 0, bounds.width(), bounds.height()); // Layer's opacity will be applied when drawing the render surface. renderSurface->m_drawOpacity = layer->opacity(); if (layer->parent() && layer->parent()->preserves3D()) renderSurface->m_drawOpacity *= layer->parent()->drawOpacity(); layer->setDrawOpacity(1); TransformationMatrix layerOriginTransform = combinedTransform; layerOriginTransform.translate3d(-0.5 * bounds.width(), -0.5 * bounds.height(), 0); renderSurface->m_originTransform = layerOriginTransform; layer->setScissorRect(IntRect()); // The render surface scissor rect is the scissor rect that needs to // be applied before drawing the render surface onto its containing // surface and is therefore expressed in the parent's coordinate system. renderSurface->m_scissorRect = layer->parent() ? layer->parent()->scissorRect() : layer->scissorRect(); renderSurface->m_layerList.clear(); if (layer->maskLayer()) { renderSurface->m_maskLayer = layer->maskLayer(); layer->maskLayer()->setTargetRenderSurface(renderSurface); } else renderSurface->m_maskLayer = 0; if (layer->replicaLayer() && layer->replicaLayer()->maskLayer()) layer->replicaLayer()->maskLayer()->setTargetRenderSurface(renderSurface); renderSurfaceLayerList.append(layer); } else { // DT = M[p] * LT layer->setDrawTransform(combinedTransform); transformedLayerRect = enclosingIntRect(layer->drawTransform().mapRect(layerRect)); layer->setDrawOpacity(layer->opacity()); if (layer->parent()) { if (layer->parent()->preserves3D()) layer->setDrawOpacity(layer->drawOpacity() * layer->parent()->drawOpacity()); // Layers inherit the scissor rect from their parent. layer->setScissorRect(layer->parent()->scissorRect()); layer->setTargetRenderSurface(layer->parent()->targetRenderSurface()); } if (layer != m_rootCCLayerImpl.get()) layer->clearRenderSurface(); if (layer->masksToBounds()) { IntRect scissor = transformedLayerRect; if (!layer->scissorRect().isEmpty()) scissor.intersect(layer->scissorRect()); layer->setScissorRect(scissor); } } if (layer->renderSurface()) layer->setTargetRenderSurface(layer->renderSurface()); else { ASSERT(layer->parent()); layer->setTargetRenderSurface(layer->parent()->targetRenderSurface()); } // drawableContentRect() is always stored in the coordinate system of the // RenderSurface the layer draws into. if (layer->drawsContent()) layer->setDrawableContentRect(transformedLayerRect); else layer->setDrawableContentRect(IntRect()); TransformationMatrix sublayerMatrix = layer->drawTransform(); // Flatten to 2D if the layer doesn't preserve 3D. if (!layer->preserves3D()) { sublayerMatrix.setM13(0); sublayerMatrix.setM23(0); sublayerMatrix.setM31(0); sublayerMatrix.setM32(0); sublayerMatrix.setM33(1); sublayerMatrix.setM34(0); sublayerMatrix.setM43(0); } // Apply the sublayer transform at the center of the layer. sublayerMatrix.multiply(layer->sublayerTransform()); // The origin of the children is the top left corner of the layer, not the // center. The matrix passed down to the children is therefore: // M[s] = M * Tr[-center] sublayerMatrix.translate3d(-bounds.width() * 0.5, -bounds.height() * 0.5, 0); LayerList& descendants = (layer->renderSurface() ? layer->renderSurface()->m_layerList : layerList); descendants.append(layer); unsigned thisLayerIndex = descendants.size() - 1; for (size_t i = 0; i < layer->children().size(); ++i) { CCLayerImpl* child = layer->children()[i].get(); updatePropertiesAndRenderSurfaces(child, sublayerMatrix, renderSurfaceLayerList, descendants); if (child->renderSurface()) { RenderSurfaceChromium* childRenderSurface = child->renderSurface(); IntRect drawableContentRect = layer->drawableContentRect(); drawableContentRect.unite(enclosingIntRect(childRenderSurface->drawableContentRect())); layer->setDrawableContentRect(drawableContentRect); descendants.append(child); } else { IntRect drawableContentRect = layer->drawableContentRect(); drawableContentRect.unite(child->drawableContentRect()); layer->setDrawableContentRect(drawableContentRect); } } if (layer->masksToBounds() || useSurfaceForMasking) { IntRect drawableContentRect = layer->drawableContentRect(); drawableContentRect.intersect(transformedLayerRect); layer->setDrawableContentRect(drawableContentRect); } if (layer->renderSurface() && layer != m_rootCCLayerImpl.get()) { RenderSurfaceChromium* renderSurface = layer->renderSurface(); renderSurface->m_contentRect = layer->drawableContentRect(); FloatPoint surfaceCenter = renderSurface->contentRectCenter(); // Restrict the RenderSurface size to the portion that's visible. FloatSize centerOffsetDueToClipping; // Don't clip if the layer is reflected as the reflection shouldn't be // clipped. if (!layer->replicaLayer()) { if (!layer->scissorRect().isEmpty()) renderSurface->m_contentRect.intersect(layer->scissorRect()); FloatPoint clippedSurfaceCenter = renderSurface->contentRectCenter(); centerOffsetDueToClipping = clippedSurfaceCenter - surfaceCenter; } // The RenderSurface backing texture cannot exceed the maximum supported // texture size. renderSurface->m_contentRect.setWidth(std::min(renderSurface->m_contentRect.width(), m_maxTextureSize)); renderSurface->m_contentRect.setHeight(std::min(renderSurface->m_contentRect.height(), m_maxTextureSize)); if (renderSurface->m_contentRect.isEmpty()) renderSurface->m_layerList.clear(); // Since the layer starts a new render surface we need to adjust its // scissor rect to be expressed in the new surface's coordinate system. layer->setScissorRect(layer->drawableContentRect()); // Adjust the origin of the transform to be the center of the render surface. renderSurface->m_drawTransform = renderSurface->m_originTransform; renderSurface->m_drawTransform.translate3d(surfaceCenter.x() + centerOffsetDueToClipping.width(), surfaceCenter.y() + centerOffsetDueToClipping.height(), 0); // Compute the transformation matrix used to draw the replica of the render // surface. if (layer->replicaLayer()) { renderSurface->m_replicaDrawTransform = renderSurface->m_originTransform; renderSurface->m_replicaDrawTransform.translate3d(layer->replicaLayer()->position().x(), layer->replicaLayer()->position().y(), 0); renderSurface->m_replicaDrawTransform.multiply(layer->replicaLayer()->transform()); renderSurface->m_replicaDrawTransform.translate3d(surfaceCenter.x() - anchorPoint.x() * bounds.width(), surfaceCenter.y() - anchorPoint.y() * bounds.height(), 0); } } // If preserves-3d then sort all the descendants in 3D so that they can be // drawn from back to front. If the preserves-3d property is also set on the parent then // skip the sorting as the parent will sort all the descendants anyway. if (layer->preserves3D() && (!layer->parent() || !layer->parent()->preserves3D())) m_layerSorter.sort(&descendants.at(thisLayerIndex), descendants.end()); }