static bool UpdateTextCoreSettings(RenderChain renderChain, VisualElement ve) { if (ve == null || !TextUtilities.IsFontAssigned(ve)) { return(false); } bool allocatesID = RenderChainVEData.AllocatesID(ve.renderChainData.textCoreSettingsID); var settings = TextUtilities.GetTextCoreSettingsForElement(ve); // If we aren't using a color ID (the DynamicColor flag), the text color will be stored in the vertex data, // so there's no need for a color match with the default TextCore settings. bool useDefaultColor = !NeedsColorID(ve) || settings.faceColor == Color.white; if (useDefaultColor && !NeedsTextCoreSettings(ve) && !allocatesID) { // Use default TextCore settings ve.renderChainData.textCoreSettingsID = UIRVEShaderInfoAllocator.defaultTextCoreSettings; return(true); } if (!allocatesID) { ve.renderChainData.textCoreSettingsID = renderChain.shaderInfoAllocator.AllocTextCoreSettings(settings); } if (RenderChainVEData.AllocatesID(ve.renderChainData.textCoreSettingsID)) { renderChain.shaderInfoAllocator.SetTextCoreSettingValue(ve.renderChainData.textCoreSettingsID, settings); } return(true); }
static Vector4 GetClipRectIDClipInfo(VisualElement ve) { Rect rect; Debug.Assert(RenderChainVEData.AllocatesID(ve.renderChainData.clipRectID)); if (ve.renderChainData.groupTransformAncestor == null) { rect = ve.worldClip; } else { rect = ve.worldClipMinusGroup; // Subtract the transform of the group transform ancestor VisualElement.TransformAlignedRect(ref ve.renderChainData.groupTransformAncestor.worldTransformInverse, ref rect); } // See ComputeRelativeClipRectCoords in the shader for details on this computation Vector2 min = rect.min; Vector2 max = rect.max; Vector2 diff = max - min; Vector2 mul = new Vector2(1 / (diff.x + 0.0001f), 1 / (diff.y + 0.0001f)); Vector2 a = 2 * mul; Vector2 b = -(min + max) * mul; return(new Vector4(a.x, a.y, b.x, b.y)); }
static void DepthFirstOnVisualsChanged(RenderChain renderChain, VisualElement ve, uint dirtyID, bool parentHierarchyHidden, bool hierarchical, ref ChainBuilderStats stats) { if (dirtyID == ve.renderChainData.dirtyID) { return; } ve.renderChainData.dirtyID = dirtyID; // Prevent reprocessing of the same element in the same pass if (hierarchical) { stats.recursiveVisualUpdatesExpanded++; } bool wasHierarchyHidden = ve.renderChainData.isHierarchyHidden; ve.renderChainData.isHierarchyHidden = parentHierarchyHidden || IsElementHierarchyHidden(ve); if (wasHierarchyHidden != ve.renderChainData.isHierarchyHidden) { hierarchical = true; } if (!hierarchical && (ve.renderChainData.dirtiedValues & RenderDataDirtyTypes.AllVisuals) == RenderDataDirtyTypes.VisualsOpacityId) { stats.opacityIdUpdates++; CommandGenerator.UpdateOpacityId(ve, renderChain); return; } UpdateWorldFlipsWinding(ve); Debug.Assert(ve.renderChainData.clipMethod != ClipMethod.Undetermined); Debug.Assert(RenderChainVEData.AllocatesID(ve.renderChainData.transformID) || ve.hierarchy.parent == null || ve.renderChainData.transformID.Equals(ve.hierarchy.parent.renderChainData.transformID) || (ve.renderHints & RenderHints.GroupTransform) != 0); if (ve is TextElement) { RenderEvents.UpdateTextCoreSettings(renderChain, ve); } UIRStylePainter.ClosingInfo closingInfo = CommandGenerator.PaintElement(renderChain, ve, ref stats); if (hierarchical) { // Recurse on children int childrenCount = ve.hierarchy.childCount; for (int i = 0; i < childrenCount; i++) { DepthFirstOnVisualsChanged(renderChain, ve.hierarchy[i], dirtyID, ve.renderChainData.isHierarchyHidden, true, ref stats); } } // By closing the element after its children, we can ensure closing data is allocated // at a time that would maintain continuity in the index buffer if (closingInfo.needsClosing) { CommandGenerator.ClosePaintElement(ve, closingInfo, renderChain, ref stats); } }
static Vector4 GetClipRectIDClipInfo(VisualElement ve) { Debug.Assert(RenderChainVEData.AllocatesID(ve.renderChainData.clipRectID)); if (ve.renderChainData.groupTransformAncestor == null) { return(UIRUtility.ToVector4(ve.worldClip)); } Rect rect = ve.worldClipMinusGroup; // Subtract the transform of the group transform ancestor VisualElement.TransformAlignedRect(ref ve.renderChainData.groupTransformAncestor.worldTransformInverse, ref rect); return(UIRUtility.ToVector4(rect)); }
static Matrix4x4 GetTransformIDTransformInfo(VisualElement ve) { Debug.Assert(RenderChainVEData.AllocatesID(ve.renderChainData.transformID) || (ve.renderHints & (RenderHints.GroupTransform)) != 0); Matrix4x4 transform; if (ve.renderChainData.groupTransformAncestor != null) { VisualElement.MultiplyMatrix34(ref ve.renderChainData.groupTransformAncestor.worldTransformInverse, ref ve.worldTransformRef, out transform); } else { transform = ve.worldTransform; } transform.m22 = 1.0f; // Once world-space mode is introduced, this should become conditional return(transform); }
static void GetVerticesTransformInfo(VisualElement ve, out Matrix4x4 transform) { if (RenderChainVEData.AllocatesID(ve.renderChainData.transformID) || (ve.renderHints & (RenderHints.GroupTransform)) != 0) { transform = Matrix4x4.identity; } else if (ve.renderChainData.boneTransformAncestor != null) { VisualElement.MultiplyMatrix34(ref ve.renderChainData.boneTransformAncestor.worldTransformInverse, ref ve.worldTransformRef, out transform); } else if (ve.renderChainData.groupTransformAncestor != null) { VisualElement.MultiplyMatrix34(ref ve.renderChainData.groupTransformAncestor.worldTransformInverse, ref ve.worldTransformRef, out transform); } else { transform = ve.worldTransform; } transform.m22 = 1.0f; // Once world-space mode is introduced, this should become conditional }
static void DepthFirstOnTransformOrSizeChanged(RenderChain renderChain, VisualElement parent, VisualElement ve, uint dirtyID, UIRenderDevice device, bool isAncestorOfChangeSkinned, bool transformChanged, ref ChainBuilderStats stats) { if (dirtyID == ve.renderChainData.dirtyID) { return; } stats.recursiveTransformUpdatesExpanded++; transformChanged |= (ve.renderChainData.dirtiedValues & RenderDataDirtyTypes.Transform) != 0; if (RenderChainVEData.AllocatesID(ve.renderChainData.clipRectID)) { renderChain.shaderInfoAllocator.SetClipRectValue(ve.renderChainData.clipRectID, GetClipRectIDClipInfo(ve)); } if (transformChanged && UpdateLocalFlipsWinding(ve)) { renderChain.UIEOnVisualsChanged(ve, true); } bool dirtyHasBeenResolved = true; if (RenderChainVEData.AllocatesID(ve.renderChainData.transformID)) { renderChain.shaderInfoAllocator.SetTransformValue(ve.renderChainData.transformID, GetTransformIDTransformInfo(ve)); isAncestorOfChangeSkinned = true; stats.boneTransformed++; } else if (!transformChanged) { // Only the clip info had to be updated, we can skip the other cases which are for transform changes only. } else if ((ve.renderHints & RenderHints.GroupTransform) != 0) { stats.groupTransformElementsChanged++; } else if (isAncestorOfChangeSkinned) { // Children of a bone element inherit the transform data change automatically when the root updates that data, no need to do anything for children Debug.Assert(RenderChainVEData.InheritsID(ve.renderChainData.transformID)); // The element MUST have a transformID that has been inherited from an ancestor dirtyHasBeenResolved = false; // We just skipped processing, if another later transform change is queued on this element this pass then we should still process it stats.skipTransformed++; } else if ((ve.renderChainData.dirtiedValues & (RenderDataDirtyTypes.Visuals | RenderDataDirtyTypes.VisualsHierarchy)) == 0 && (ve.renderChainData.data != null)) { // If a visual update will happen, then skip work here as the visual update will incorporate the transformed vertices if (!ve.renderChainData.disableNudging && CommandGenerator.NudgeVerticesToNewSpace(ve, renderChain, device)) { stats.nudgeTransformed++; } else { renderChain.UIEOnVisualsChanged(ve, false); // Nudging not allowed, so do a full visual repaint stats.visualUpdateTransformed++; } } if (dirtyHasBeenResolved) { ve.renderChainData.dirtyID = dirtyID; // Prevent reprocessing of the same element in the same pass } // Make sure to pre-evaluate world transform and clip now so we don't do it at render time if (renderChain.drawInCameras) { ve.EnsureWorldTransformAndClipUpToDate(); } if ((ve.renderHints & RenderHints.GroupTransform) == 0) { // Recurse on children int childrenCount = ve.hierarchy.childCount; for (int i = 0; i < childrenCount; i++) { DepthFirstOnTransformOrSizeChanged(renderChain, ve, ve.hierarchy[i], dirtyID, device, isAncestorOfChangeSkinned, transformChanged, ref stats); } } }
static void DepthFirstOnClippingChanged(RenderChain renderChain, VisualElement parent, VisualElement ve, uint dirtyID, bool hierarchical, bool isRootOfChange, // MUST be true on the root call. bool isPendingHierarchicalRepaint, // MUST be false on the root call. bool inheritedClipRectIDChanged, // MUST be false on the root call. bool inheritedMaskingChanged, // MUST be false on the root call. UIRenderDevice device, ref ChainBuilderStats stats) { bool upToDate = dirtyID == ve.renderChainData.dirtyID; if (upToDate && !inheritedClipRectIDChanged && !inheritedMaskingChanged) { return; } ve.renderChainData.dirtyID = dirtyID; // Prevent reprocessing of the same element in the same pass if (!isRootOfChange) { stats.recursiveClipUpdatesExpanded++; } isPendingHierarchicalRepaint |= (ve.renderChainData.dirtiedValues & RenderDataDirtyTypes.VisualsHierarchy) != 0; // Internal operations (done in this call) to do: bool mustUpdateClipRectID = hierarchical || isRootOfChange || inheritedClipRectIDChanged; bool mustUpdateClippingMethod = hierarchical || isRootOfChange; bool mustUpdateChildrenMasking = hierarchical || isRootOfChange || inheritedMaskingChanged; // External operations (done by recursion or postponed) to do: bool mustRepaintThis = false; bool mustRepaintHierarchy = false; bool mustProcessSizeChange = false; // mustRecurse implies recursing on all children, but doesn't force anything beyond them. // hierarchical implies recursing on all descendants // As a result, hierarchical implies mustRecurse bool mustRecurse = hierarchical; ClipMethod oldClippingMethod = ve.renderChainData.clipMethod; ClipMethod newClippingMethod = mustUpdateClippingMethod ? DetermineSelfClipMethod(renderChain, ve) : oldClippingMethod; // Shader discard support bool clipRectIDChanged = false; if (mustUpdateClipRectID) { BMPAlloc newClipRectID = ve.renderChainData.clipRectID; if (newClippingMethod == ClipMethod.ShaderDiscard) { if (!RenderChainVEData.AllocatesID(ve.renderChainData.clipRectID)) { newClipRectID = renderChain.shaderInfoAllocator.AllocClipRect(); if (!newClipRectID.IsValid()) { newClippingMethod = ClipMethod.Scissor; // Fallback to scissor since we couldn't allocate a clipRectID // Both shader discard and scisorring work with world-clip rectangles, so no need // to inherit any clipRectIDs for such elements, our own scissor rect clips up correctly newClipRectID = UIRVEShaderInfoAllocator.infiniteClipRect; } } } else { if (RenderChainVEData.AllocatesID(ve.renderChainData.clipRectID)) { renderChain.shaderInfoAllocator.FreeClipRect(ve.renderChainData.clipRectID); } // Inherit parent's clipRectID if possible. // Group transforms shouldn't inherit the clipRectID since they have a new frame of reference, // they provide a new baseline with the _PixelClipRect instead. if ((ve.renderHints & RenderHints.GroupTransform) == 0) { newClipRectID = ((newClippingMethod != ClipMethod.Scissor) && (parent != null)) ? parent.renderChainData.clipRectID : UIRVEShaderInfoAllocator.infiniteClipRect; newClipRectID.ownedState = OwnedState.Inherited; } } clipRectIDChanged = !ve.renderChainData.clipRectID.Equals(newClipRectID); Debug.Assert((ve.renderHints & RenderHints.GroupTransform) == 0 || !clipRectIDChanged); ve.renderChainData.clipRectID = newClipRectID; } bool maskingChanged = false; if (oldClippingMethod != newClippingMethod) { ve.renderChainData.clipMethod = newClippingMethod; if (oldClippingMethod == ClipMethod.Stencil || newClippingMethod == ClipMethod.Stencil) { maskingChanged = true; mustUpdateChildrenMasking = true; } if (oldClippingMethod == ClipMethod.Scissor || newClippingMethod == ClipMethod.Scissor) { // We need to add/remove scissor push/pop commands mustRepaintThis = true; } if (newClippingMethod == ClipMethod.ShaderDiscard || oldClippingMethod == ClipMethod.ShaderDiscard && RenderChainVEData.AllocatesID(ve.renderChainData.clipRectID)) { // We must update the clipping rects. mustProcessSizeChange = true; } } if (clipRectIDChanged) { // Our children MUST update their render data clipRectIDs mustRecurse = true; // Our children MUST update their vertex clipRectIDs mustRepaintHierarchy = true; } if (mustUpdateChildrenMasking) { int newChildrenMaskDepth = 0; int newChildrenStencilRef = 0; if (parent != null) { newChildrenMaskDepth = parent.renderChainData.childrenMaskDepth; newChildrenStencilRef = parent.renderChainData.childrenStencilRef; if (newClippingMethod == ClipMethod.Stencil) { if (newChildrenMaskDepth > newChildrenStencilRef) { ++newChildrenStencilRef; } ++newChildrenMaskDepth; } // When applying the MaskContainer hint, we skip because the last depth level because even though we // could technically increase the reference value, it would be useless since there won't be more // deeply nested masks that could benefit from it. if ((ve.renderHints & RenderHints.MaskContainer) == RenderHints.MaskContainer && newChildrenMaskDepth < UIRUtility.k_MaxMaskDepth) { newChildrenStencilRef = newChildrenMaskDepth; } } if (ve.renderChainData.childrenMaskDepth != newChildrenMaskDepth || ve.renderChainData.childrenStencilRef != newChildrenStencilRef) { maskingChanged = true; } ve.renderChainData.childrenMaskDepth = newChildrenMaskDepth; ve.renderChainData.childrenStencilRef = newChildrenStencilRef; } if (maskingChanged) { mustRecurse = true; // Our children must update their inherited state. // These optimizations would allow to skip repainting the hierarchy: // a) We could update the stencilRef in the commands without repainting // b) The winding order could be reversed without repainting (when required) // In the meantime, we have no other choice but to request a hierarchical repaint. mustRepaintHierarchy = true; } if ((mustRepaintThis || mustRepaintHierarchy) && !isPendingHierarchicalRepaint) { renderChain.UIEOnVisualsChanged(ve, mustRepaintHierarchy); isPendingHierarchicalRepaint = true; } if (mustProcessSizeChange) { renderChain.UIEOnTransformOrSizeChanged(ve, false, true); } if (mustRecurse) { int childrenCount = ve.hierarchy.childCount; for (int i = 0; i < childrenCount; i++) { DepthFirstOnClippingChanged( renderChain, ve, ve.hierarchy[i], dirtyID, // Having to recurse doesn't mean that we need to process ALL descendants. For example, the // propagation of the transformId may stop if a group or a bone is encountered. hierarchical, false, isPendingHierarchicalRepaint, clipRectIDChanged, maskingChanged, device, ref stats); } } }
internal static uint DepthFirstOnChildRemoving(RenderChain renderChain, VisualElement ve) { // Recurse on children int childrenCount = ve.hierarchy.childCount - 1; uint deepCount = 0; while (childrenCount >= 0) { deepCount += DepthFirstOnChildRemoving(renderChain, ve.hierarchy[childrenCount--]); } if (ve.renderChainData.isInChain) { renderChain.ChildWillBeRemoved(ve); CommandGenerator.ResetCommands(renderChain, ve); renderChain.ResetTextures(ve); ve.renderChainData.isInChain = false; ve.renderChainData.clipMethod = ClipMethod.Undetermined; if (ve.renderChainData.next != null) { ve.renderChainData.next.renderChainData.prev = ve.renderChainData.prev; } if (ve.renderChainData.prev != null) { ve.renderChainData.prev.renderChainData.next = ve.renderChainData.next; } if (RenderChainVEData.AllocatesID(ve.renderChainData.textCoreSettingsID)) { renderChain.shaderInfoAllocator.FreeTextCoreSettings(ve.renderChainData.textCoreSettingsID); ve.renderChainData.textCoreSettingsID = UIRVEShaderInfoAllocator.defaultTextCoreSettings; } if (RenderChainVEData.AllocatesID(ve.renderChainData.opacityID)) { renderChain.shaderInfoAllocator.FreeOpacity(ve.renderChainData.opacityID); ve.renderChainData.opacityID = UIRVEShaderInfoAllocator.fullOpacity; } if (RenderChainVEData.AllocatesID(ve.renderChainData.colorID)) { renderChain.shaderInfoAllocator.FreeColor(ve.renderChainData.colorID); ve.renderChainData.colorID = BMPAlloc.Invalid; } if (RenderChainVEData.AllocatesID(ve.renderChainData.backgroundColorID)) { renderChain.shaderInfoAllocator.FreeColor(ve.renderChainData.backgroundColorID); ve.renderChainData.backgroundColorID = BMPAlloc.Invalid; } if (RenderChainVEData.AllocatesID(ve.renderChainData.borderLeftColorID)) { renderChain.shaderInfoAllocator.FreeColor(ve.renderChainData.borderLeftColorID); ve.renderChainData.borderLeftColorID = BMPAlloc.Invalid; } if (RenderChainVEData.AllocatesID(ve.renderChainData.borderTopColorID)) { renderChain.shaderInfoAllocator.FreeColor(ve.renderChainData.borderTopColorID); ve.renderChainData.borderTopColorID = BMPAlloc.Invalid; } if (RenderChainVEData.AllocatesID(ve.renderChainData.borderRightColorID)) { renderChain.shaderInfoAllocator.FreeColor(ve.renderChainData.borderRightColorID); ve.renderChainData.borderRightColorID = BMPAlloc.Invalid; } if (RenderChainVEData.AllocatesID(ve.renderChainData.borderBottomColorID)) { renderChain.shaderInfoAllocator.FreeColor(ve.renderChainData.borderBottomColorID); ve.renderChainData.borderBottomColorID = BMPAlloc.Invalid; } if (RenderChainVEData.AllocatesID(ve.renderChainData.tintColorID)) { renderChain.shaderInfoAllocator.FreeColor(ve.renderChainData.tintColorID); ve.renderChainData.tintColorID = BMPAlloc.Invalid; } if (RenderChainVEData.AllocatesID(ve.renderChainData.clipRectID)) { renderChain.shaderInfoAllocator.FreeClipRect(ve.renderChainData.clipRectID); ve.renderChainData.clipRectID = UIRVEShaderInfoAllocator.infiniteClipRect; } if (RenderChainVEData.AllocatesID(ve.renderChainData.transformID)) { renderChain.shaderInfoAllocator.FreeTransform(ve.renderChainData.transformID); ve.renderChainData.transformID = UIRVEShaderInfoAllocator.identityTransform; } ve.renderChainData.boneTransformAncestor = ve.renderChainData.groupTransformAncestor = null; if (ve.renderChainData.closingData != null) { renderChain.device.Free(ve.renderChainData.closingData); ve.renderChainData.closingData = null; } if (ve.renderChainData.data != null) { renderChain.device.Free(ve.renderChainData.data); ve.renderChainData.data = null; } } return(deepCount + 1); }
internal static uint DepthFirstOnChildAdded(RenderChain renderChain, VisualElement parent, VisualElement ve, int index, bool resetState) { Debug.Assert(ve.panel != null); if (ve.renderChainData.isInChain) { return(0); // Already added, redundant call } if (resetState) { ve.renderChainData = new RenderChainVEData(); } ve.renderChainData.isInChain = true; ve.renderChainData.verticesSpace = Matrix4x4.identity; ve.renderChainData.transformID = UIRVEShaderInfoAllocator.identityTransform; ve.renderChainData.clipRectID = UIRVEShaderInfoAllocator.infiniteClipRect; ve.renderChainData.opacityID = UIRVEShaderInfoAllocator.fullOpacity; ve.renderChainData.colorID = BMPAlloc.Invalid; // Rely on vertex tint color by default ve.renderChainData.backgroundColorID = BMPAlloc.Invalid; ve.renderChainData.borderLeftColorID = BMPAlloc.Invalid; ve.renderChainData.borderTopColorID = BMPAlloc.Invalid; ve.renderChainData.borderRightColorID = BMPAlloc.Invalid; ve.renderChainData.borderBottomColorID = BMPAlloc.Invalid; ve.renderChainData.tintColorID = BMPAlloc.Invalid; ve.renderChainData.textCoreSettingsID = UIRVEShaderInfoAllocator.defaultTextCoreSettings; ve.renderChainData.compositeOpacity = float.MaxValue; // Any unreasonable value will do to trip the opacity composer to work UpdateLocalFlipsWinding(ve); if (parent != null) { if ((parent.renderHints & (RenderHints.GroupTransform)) != 0) { ve.renderChainData.groupTransformAncestor = parent; } else { ve.renderChainData.groupTransformAncestor = parent.renderChainData.groupTransformAncestor; } ve.renderChainData.hierarchyDepth = parent.renderChainData.hierarchyDepth + 1; } else { ve.renderChainData.groupTransformAncestor = null; ve.renderChainData.hierarchyDepth = 0; } renderChain.EnsureFitsDepth(ve.renderChainData.hierarchyDepth); if (index > 0) { Debug.Assert(parent != null); ve.renderChainData.prev = GetLastDeepestChild(parent.hierarchy[index - 1]); } else { ve.renderChainData.prev = parent; } ve.renderChainData.next = ve.renderChainData.prev != null ? ve.renderChainData.prev.renderChainData.next : null; if (ve.renderChainData.prev != null) { ve.renderChainData.prev.renderChainData.next = ve; } if (ve.renderChainData.next != null) { ve.renderChainData.next.renderChainData.prev = ve; } // TransformID // Since transform type is controlled by render hints which are locked on the VE by now, we can // go ahead and prep transform data now and never check on it again under regular circumstances Debug.Assert(!RenderChainVEData.AllocatesID(ve.renderChainData.transformID)); if (NeedsTransformID(ve)) { ve.renderChainData.transformID = renderChain.shaderInfoAllocator.AllocTransform(); // May fail, that's ok } else { ve.renderChainData.transformID = BMPAlloc.Invalid; } ve.renderChainData.boneTransformAncestor = null; if (NeedsColorID(ve)) { InitColorIDs(renderChain, ve); SetColorValues(renderChain, ve); } if (!RenderChainVEData.AllocatesID(ve.renderChainData.transformID)) { if (parent != null && (ve.renderHints & RenderHints.GroupTransform) == 0) { if (RenderChainVEData.AllocatesID(parent.renderChainData.transformID)) { ve.renderChainData.boneTransformAncestor = parent; } else { ve.renderChainData.boneTransformAncestor = parent.renderChainData.boneTransformAncestor; } ve.renderChainData.transformID = parent.renderChainData.transformID; ve.renderChainData.transformID.ownedState = OwnedState.Inherited; // Mark this allocation as not owned by us (inherited) } else { ve.renderChainData.transformID = UIRVEShaderInfoAllocator.identityTransform; } } else { renderChain.shaderInfoAllocator.SetTransformValue(ve.renderChainData.transformID, GetTransformIDTransformInfo(ve)); } // Recurse on children int childrenCount = ve.hierarchy.childCount; uint deepCount = 0; for (int i = 0; i < childrenCount; i++) { deepCount += DepthFirstOnChildAdded(renderChain, ve, ve.hierarchy[i], i, resetState); } return(1 + deepCount); }