static void InjectClosingCommandInBetween(RenderChain renderChain, RenderChainCommand cmd, ref RenderChainCommand prev, ref RenderChainCommand next) { Debug.Assert(cmd.closing); if (prev != null) { cmd.prev = prev; prev.next = cmd; } if (next != null) { cmd.next = next; next.prev = cmd; } VisualElement ve = cmd.owner; ve.renderChainData.lastClosingCommand = cmd; if (ve.renderChainData.firstClosingCommand == null) { ve.renderChainData.firstClosingCommand = cmd; } renderChain.OnRenderCommandAdded(cmd); // Adjust the pointers as a facility for later injections prev = cmd; next = cmd.next; }
public void ApplyVisualElementClipping() { bool flag = this.currentElement.renderChainData.clipMethod == ClipMethod.Scissor; if (flag) { RenderChainCommand renderChainCommand = this.m_Owner.AllocCommand(); renderChainCommand.type = CommandType.PushScissor; renderChainCommand.owner = this.currentElement; this.m_Entries.Add(new UIRStylePainter.Entry { customCommand = renderChainCommand }); this.m_ClosingInfo.needsClosing = (this.m_ClosingInfo.popScissorClip = true); } else { bool flag2 = this.currentElement.renderChainData.clipMethod == ClipMethod.Stencil; if (flag2) { bool flag3 = UIRUtility.IsVectorImageBackground(this.currentElement); if (flag3) { this.GenerateStencilClipEntryForSVGBackground(); } else { this.GenerateStencilClipEntryForRoundedRectBackground(); } } } this.m_ClipRectID = this.currentElement.renderChainData.clipRectID; }
public void Begin(VisualElement ve) { this.currentElement = ve; this.m_NextMeshWriteDataPoolItem = 0; this.m_SVGBackgroundEntryIndex = -1; this.currentElement.renderChainData.usesLegacyText = (this.currentElement.renderChainData.usesAtlas = (this.currentElement.renderChainData.disableNudging = false)); this.currentElement.renderChainData.displacementUVStart = (this.currentElement.renderChainData.displacementUVEnd = 0); bool flag = (this.currentElement.renderHints & RenderHints.GroupTransform) > RenderHints.None; bool flag2 = flag; if (flag2) { RenderChainCommand renderChainCommand = this.m_Owner.AllocCommand(); renderChainCommand.owner = this.currentElement; renderChainCommand.type = CommandType.PushView; this.m_Entries.Add(new UIRStylePainter.Entry { customCommand = renderChainCommand }); this.m_ClosingInfo.needsClosing = (this.m_ClosingInfo.popViewMatrix = true); } bool flag3 = this.currentElement.hierarchy.parent != null; if (flag3) { this.m_StencilClip = this.currentElement.hierarchy.parent.renderChainData.isStencilClipped; this.m_ClipRectID = (flag ? UIRVEShaderInfoAllocator.infiniteClipRect : this.currentElement.hierarchy.parent.renderChainData.clipRectID); } else { this.m_StencilClip = false; this.m_ClipRectID = UIRVEShaderInfoAllocator.infiniteClipRect; } }
static void FindClosingCommandInsertionPoint(VisualElement ve, out RenderChainCommand prev, out RenderChainCommand next) { // Closing commands for a visual element come after the closing commands of the shallowest child // If not found, then after the last command of the last deepest child // If not found, then after the last command of self VisualElement nextDrawingElem = ve.renderChainData.next; // Depth first search for the first VE that has a command (i.e. non empty element). // This can be potentially O(n) of VE count // It is ok to check against lastCommand to mean the presence of closingCommand too, as we // require that closing commands only exist if a startup command exists too while (nextDrawingElem != null && nextDrawingElem.renderChainData.firstCommand == null) { nextDrawingElem = nextDrawingElem.renderChainData.next; } if (nextDrawingElem != null && nextDrawingElem.renderChainData.firstCommand != null) { // A next drawing element can be: // A) A next sibling of ve (O(1) check time) // B) A child/grand-child of self (O(n) of tree depth check time - meh) // C) A next sibling of a parent/ancestor (lengthy check time, so it is left as the only choice remaining after the first two) if (nextDrawingElem.hierarchy.parent == ve.hierarchy.parent) // Case A { next = nextDrawingElem.renderChainData.firstCommand; prev = next.prev; } else if (ve.IsParentOrAncestorOf(nextDrawingElem)) // Case B { // Enclose the last deepest drawing child by our closing command for (;;) { prev = nextDrawingElem.renderChainData.lastClosingOrLastCommand; nextDrawingElem = prev.next?.owner; if (nextDrawingElem == null || !ve.IsParentOrAncestorOf(nextDrawingElem)) { break; } } next = prev.next; } else { // Case C, just wrap ourselves prev = ve.renderChainData.lastCommand; next = prev.next; } } else { prev = ve.renderChainData.lastCommand; next = prev.next; // prev should not be null since we don't support closing commands without opening commands too } }
public void DrawImmediate(Action callback, bool cullingEnabled) { RenderChainCommand renderChainCommand = this.m_Owner.AllocCommand(); renderChainCommand.type = (cullingEnabled ? CommandType.ImmediateCull : CommandType.Immediate); renderChainCommand.owner = this.currentElement; renderChainCommand.callback = callback; this.m_Entries.Add(new UIRStylePainter.Entry { customCommand = renderChainCommand }); }
public void LandClipUnregisterMeshDrawCommand(RenderChainCommand cmd) { Debug.Assert(m_ClosingInfo.needsClosing); m_ClosingInfo.clipUnregisterDrawCommand = cmd; }
static void FindCommandInsertionPoint(VisualElement ve, out RenderChainCommand prev, out RenderChainCommand next) { VisualElement prevDrawingElem = ve.renderChainData.prev; // This can be potentially O(n) of VE count // It is ok to check against lastCommand to mean the presence of closingCommand too, as we // require that closing commands only exist if a startup command exists too while (prevDrawingElem != null && prevDrawingElem.renderChainData.lastCommand == null) { prevDrawingElem = prevDrawingElem.renderChainData.prev; } if (prevDrawingElem != null && prevDrawingElem.renderChainData.lastCommand != null) { // A previous drawing element can be: // A) A previous sibling (O(1) check time) // B) A parent/ancestor (O(n) of tree depth check time - meh) // C) A child/grand-child of a previous sibling to an ancestor (lengthy check time, so it is left as the only choice remaining after the first two) if (prevDrawingElem.hierarchy.parent == ve.hierarchy.parent) // Case A { prev = prevDrawingElem.renderChainData.lastClosingOrLastCommand; } else if (prevDrawingElem.IsParentOrAncestorOf(ve)) // Case B { prev = prevDrawingElem.renderChainData.lastCommand; } else { // Case C, get the last command that isn't owned by us, this is to skip potential // closing commands wrapped after the previous drawing element var lastCommand = prevDrawingElem.renderChainData.lastClosingOrLastCommand; for (;;) { prev = lastCommand; lastCommand = lastCommand.next; if (lastCommand == null || (lastCommand.owner == ve) || !lastCommand.closing) // Once again, we assume closing commands cannot exist without opening commands on the element { break; } if (lastCommand.owner.IsParentOrAncestorOf(ve)) { break; } } } next = prev.next; } else { VisualElement nextDrawingElem = ve.renderChainData.next; // This can be potentially O(n) of VE count, very bad.. must adjust while (nextDrawingElem != null && nextDrawingElem.renderChainData.firstCommand == null) { nextDrawingElem = nextDrawingElem.renderChainData.next; } next = nextDrawingElem?.renderChainData.firstCommand; prev = null; Debug.Assert((next == null) || (next.prev == null)); } }
static RenderChainCommand InjectClosingMeshDrawCommand(RenderChain renderChain, VisualElement ve, ref RenderChainCommand cmdPrev, ref RenderChainCommand cmdNext, MeshHandle mesh, int indexCount, int indexOffset, Material material, TextureId texture, int stencilRef) { var cmd = renderChain.AllocCommand(); cmd.type = CommandType.Draw; cmd.closing = true; cmd.state = new State { material = material, texture = texture, stencilRef = stencilRef }; cmd.mesh = mesh; cmd.indexOffset = indexOffset; cmd.indexCount = indexCount; cmd.owner = ve; InjectClosingCommandInBetween(renderChain, cmd, ref cmdPrev, ref cmdNext); return(cmd); }
public unsafe static UIRStylePainter.ClosingInfo PaintElement(RenderChain renderChain, VisualElement ve, ref ChainBuilderStats stats) { var device = renderChain.device; var isClippingWithStencil = ve.renderChainData.clipMethod == ClipMethod.Stencil; var isClippingWithScissors = ve.renderChainData.clipMethod == ClipMethod.Scissor; var isGroup = (ve.renderHints & RenderHints.GroupTransform) != 0; // Groups need to push view and scissors if ((UIRUtility.IsElementSelfHidden(ve) && !isClippingWithStencil && !isClippingWithScissors && !isGroup) || ve.renderChainData.isHierarchyHidden) { if (ve.renderChainData.data != null) { device.Free(ve.renderChainData.data); ve.renderChainData.data = null; } if (ve.renderChainData.firstCommand != null) { ResetCommands(renderChain, ve); } renderChain.ResetTextures(ve); return(new UIRStylePainter.ClosingInfo()); } // Retain our command insertion points if possible, to avoid paying the cost of finding them again RenderChainCommand oldCmdPrev = ve.renderChainData.firstCommand?.prev; RenderChainCommand oldCmdNext = ve.renderChainData.lastCommand?.next; RenderChainCommand oldClosingCmdPrev, oldClosingCmdNext; bool commandsAndClosingCommandsWereConsecutive = (ve.renderChainData.firstClosingCommand != null) && (oldCmdNext == ve.renderChainData.firstClosingCommand); if (commandsAndClosingCommandsWereConsecutive) { oldCmdNext = ve.renderChainData.lastClosingCommand.next; oldClosingCmdPrev = oldClosingCmdNext = null; } else { oldClosingCmdPrev = ve.renderChainData.firstClosingCommand?.prev; oldClosingCmdNext = ve.renderChainData.lastClosingCommand?.next; } Debug.Assert(oldCmdPrev?.owner != ve); Debug.Assert(oldCmdNext?.owner != ve); Debug.Assert(oldClosingCmdPrev?.owner != ve); Debug.Assert(oldClosingCmdNext?.owner != ve); ResetCommands(renderChain, ve); renderChain.ResetTextures(ve); k_GenerateEntries.Begin(); var painter = renderChain.painter; painter.Begin(ve); if (ve.visible) { painter.DrawVisualElementBackground(); painter.DrawVisualElementBorder(); painter.ApplyVisualElementClipping(); InvokeGenerateVisualContent(ve, painter.meshGenerationContext); } else { // Even though the element hidden, we still have to push the stencil shape or setup the scissors in case any children are visible. if (isClippingWithScissors || isClippingWithStencil) { painter.ApplyVisualElementClipping(); } } k_GenerateEntries.End(); MeshHandle data = ve.renderChainData.data; if (painter.totalVertices > device.maxVerticesPerPage) { Debug.LogError($"A {nameof(VisualElement)} must not allocate more than {device.maxVerticesPerPage } vertices."); if (data != null) { device.Free(data); data = null; } renderChain.ResetTextures(ve); // Restart without drawing anything. painter.Reset(); painter.Begin(ve); } // Convert entries to commands. var entries = painter.entries; if (entries.Count > 0) { NativeSlice <Vertex> verts = new NativeSlice <Vertex>(); NativeSlice <UInt16> indices = new NativeSlice <UInt16>(); UInt16 indexOffset = 0; if (painter.totalVertices > 0) { UpdateOrAllocate(ref data, painter.totalVertices, painter.totalIndices, device, out verts, out indices, out indexOffset, ref stats); } int vertsFilled = 0, indicesFilled = 0; RenderChainCommand cmdPrev = oldCmdPrev, cmdNext = oldCmdNext; if (oldCmdPrev == null && oldCmdNext == null) { FindCommandInsertionPoint(ve, out cmdPrev, out cmdNext); } // Vertex data, lazily computed bool vertexDataComputed = false; Matrix4x4 transform = Matrix4x4.identity; Color32 xformClipPages = new Color32(0, 0, 0, 0); Color32 ids = new Color32(0, 0, 0, 0); Color32 addFlags = new Color32(0, 0, 0, 0); Color32 opacityPage = new Color32(0, 0, 0, 0); Color32 textCoreSettingsPage = new Color32(0, 0, 0, 0); k_ConvertEntriesToCommandsMarker.Begin(); int firstDisplacementUV = -1, lastDisplacementUVPlus1 = -1; foreach (var entry in painter.entries) { if (entry.vertices.Length > 0 && entry.indices.Length > 0) { if (!vertexDataComputed) { vertexDataComputed = true; GetVerticesTransformInfo(ve, out transform); ve.renderChainData.verticesSpace = transform; // This is the space for the generated vertices below } Color32 transformData = renderChain.shaderInfoAllocator.TransformAllocToVertexData(ve.renderChainData.transformID); Color32 opacityData = renderChain.shaderInfoAllocator.OpacityAllocToVertexData(ve.renderChainData.opacityID); Color32 textCoreSettingsData = renderChain.shaderInfoAllocator.TextCoreSettingsToVertexData(ve.renderChainData.textCoreSettingsID); xformClipPages.r = transformData.r; xformClipPages.g = transformData.g; ids.r = transformData.b; opacityPage.r = opacityData.r; opacityPage.g = opacityData.g; ids.b = opacityData.b; if (entry.isTextEntry) { // It's important to avoid writing these values when the vertices aren't for text, // as these settings are shared with the vector graphics gradients. // The same applies to the CopyTransformVertsPos* methods below. textCoreSettingsPage.r = textCoreSettingsData.r; textCoreSettingsPage.g = textCoreSettingsData.g; ids.a = textCoreSettingsData.b; } Color32 clipRectData = renderChain.shaderInfoAllocator.ClipRectAllocToVertexData(entry.clipRectID); xformClipPages.b = clipRectData.r; xformClipPages.a = clipRectData.g; ids.g = clipRectData.b; addFlags.r = (byte)entry.addFlags; float textureId = entry.texture.ConvertToGpu(); // Copy vertices, transforming them as necessary var targetVerticesSlice = verts.Slice(vertsFilled, entry.vertices.Length); if (entry.uvIsDisplacement) { if (firstDisplacementUV < 0) { firstDisplacementUV = vertsFilled; lastDisplacementUVPlus1 = vertsFilled + entry.vertices.Length; } else if (lastDisplacementUVPlus1 == vertsFilled) { lastDisplacementUVPlus1 += entry.vertices.Length; } else { ve.renderChainData.disableNudging = true; // Disjoint displacement UV entries, we can't keep track of them, so disable nudging optimization altogether } } int entryIndexCount = entry.indices.Length; int entryIndexOffset = vertsFilled + indexOffset; var targetIndicesSlice = indices.Slice(indicesFilled, entryIndexCount); bool shapeWindingIsClockwise = UIRUtility.ShapeWindingIsClockwise(entry.maskDepth, entry.stencilRef); bool transformFlipsWinding = ve.renderChainData.worldFlipsWinding; var job = new ConvertMeshJobData { vertSrc = (IntPtr)entry.vertices.GetUnsafePtr(), vertDst = (IntPtr)targetVerticesSlice.GetUnsafePtr(), vertCount = targetVerticesSlice.Length, transform = transform, transformUVs = entry.uvIsDisplacement ? 1 : 0, xformClipPages = xformClipPages, ids = ids, addFlags = addFlags, opacityPage = opacityPage, textCoreSettingsPage = textCoreSettingsPage, isText = entry.isTextEntry ? 1 : 0, textureId = textureId, indexSrc = (IntPtr)entry.indices.GetUnsafePtr(), indexDst = (IntPtr)targetIndicesSlice.GetUnsafePtr(), indexCount = targetIndicesSlice.Length, indexOffset = entryIndexOffset, flipIndices = shapeWindingIsClockwise == transformFlipsWinding ? 1 : 0 }; renderChain.jobManager.Add(ref job); if (entry.isClipRegisterEntry) { painter.LandClipRegisterMesh(targetVerticesSlice, targetIndicesSlice, entryIndexOffset); } var cmd = InjectMeshDrawCommand(renderChain, ve, ref cmdPrev, ref cmdNext, data, entryIndexCount, indicesFilled, entry.material, entry.texture, entry.stencilRef); if (entry.isTextEntry) { // Set font atlas texture gradient scale cmd.state.sdfScale = entry.fontTexSDFScale; } vertsFilled += entry.vertices.Length; indicesFilled += entryIndexCount; } else if (entry.customCommand != null) { InjectCommandInBetween(renderChain, entry.customCommand, ref cmdPrev, ref cmdNext); } else { Debug.Assert(false); // Unable to determine what kind of command to generate here } } if (!ve.renderChainData.disableNudging && (firstDisplacementUV >= 0)) { ve.renderChainData.displacementUVStart = firstDisplacementUV; ve.renderChainData.displacementUVEnd = lastDisplacementUVPlus1; } k_ConvertEntriesToCommandsMarker.End(); } else if (data != null) { device.Free(data); data = null; } ve.renderChainData.data = data; if (painter.closingInfo.clipperRegisterIndices.Length == 0 && ve.renderChainData.closingData != null) { // No more closing data needed, so free it now device.Free(ve.renderChainData.closingData); ve.renderChainData.closingData = null; } if (painter.closingInfo.needsClosing) { k_GenerateClosingCommandsMarker.Begin(); RenderChainCommand cmdPrev = oldClosingCmdPrev, cmdNext = oldClosingCmdNext; if (commandsAndClosingCommandsWereConsecutive) { cmdPrev = ve.renderChainData.lastCommand; cmdNext = cmdPrev.next; } else if (cmdPrev == null && cmdNext == null) { FindClosingCommandInsertionPoint(ve, out cmdPrev, out cmdNext); } if (painter.closingInfo.PopDefaultMaterial) { var cmd = renderChain.AllocCommand(); cmd.type = CommandType.PopDefaultMaterial; cmd.closing = true; cmd.owner = ve; InjectClosingCommandInBetween(renderChain, cmd, ref cmdPrev, ref cmdNext); } if (painter.closingInfo.blitAndPopRenderTexture) { { var cmd = renderChain.AllocCommand(); cmd.type = CommandType.BlitToPreviousRT; cmd.closing = true; cmd.owner = ve; cmd.state.material = GetBlitMaterial(ve.subRenderTargetMode); Debug.Assert(cmd.state.material != null); InjectClosingCommandInBetween(renderChain, cmd, ref cmdPrev, ref cmdNext); } { var cmd = renderChain.AllocCommand(); cmd.type = CommandType.PopRenderTexture; cmd.closing = true; cmd.owner = ve; InjectClosingCommandInBetween(renderChain, cmd, ref cmdPrev, ref cmdNext); } } if (painter.closingInfo.clipperRegisterIndices.Length > 0) { var cmd = InjectClosingMeshDrawCommand(renderChain, ve, ref cmdPrev, ref cmdNext, null, 0, 0, null, TextureId.invalid, painter.closingInfo.maskStencilRef); painter.LandClipUnregisterMeshDrawCommand(cmd); // Placeholder command that will be filled actually later } if (painter.closingInfo.popViewMatrix) { var cmd = renderChain.AllocCommand(); cmd.type = CommandType.PopView; cmd.closing = true; cmd.owner = ve; InjectClosingCommandInBetween(renderChain, cmd, ref cmdPrev, ref cmdNext); } if (painter.closingInfo.popScissorClip) { var cmd = renderChain.AllocCommand(); cmd.type = CommandType.PopScissor; cmd.closing = true; cmd.owner = ve; InjectClosingCommandInBetween(renderChain, cmd, ref cmdPrev, ref cmdNext); } k_GenerateClosingCommandsMarker.End(); } // When we have a closing mesh, we must have an opening mesh. At least we assumed where we decide // whether we must nudge or not: we only test whether the opening mesh is non-null. Debug.Assert(ve.renderChainData.closingData == null || ve.renderChainData.data != null); var closingInfo = painter.closingInfo; painter.Reset(); return(closingInfo); }