| File: Rendering\RenderBatchBuilder.cs | Web Access |
| Project: src\aspnetcore\src\Components\Components\src\Microsoft.AspNetCore.Components.csproj (Microsoft.AspNetCore.Components) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Components.RenderTree; namespace Microsoft.AspNetCore.Components.Rendering; /// <summary> /// Collects the data produced by the rendering system during the course /// of rendering a single batch. This tracks both the final output data /// and the intermediate states (such as the queue of components still to /// be rendered). /// </summary> internal sealed class RenderBatchBuilder : IDisposable { // A value that, if changed, causes expiry of all ParameterView instances issued // for this RenderBatchBuilder. This is to prevent invalid reads from arrays that // may have been returned to the shared pool. private int _parameterViewValidityStamp; // Primary result data public ArrayBuilder<RenderTreeDiff> UpdatedComponentDiffs { get; } = new ArrayBuilder<RenderTreeDiff>(); public ArrayBuilder<int> DisposedComponentIds { get; } = new ArrayBuilder<int>(); public ArrayBuilder<ulong> DisposedEventHandlerIds { get; } = new ArrayBuilder<ulong>(); public ArrayBuilder<NamedEventChange>? NamedEventChanges; // Buffers referenced by UpdatedComponentDiffs public ArrayBuilder<RenderTreeEdit> EditsBuffer { get; } = new ArrayBuilder<RenderTreeEdit>(64); public ArrayBuilder<RenderTreeFrame> ReferenceFramesBuffer { get; } = new ArrayBuilder<RenderTreeFrame>(64); // State of render pipeline public Queue<RenderQueueEntry> ComponentRenderQueue { get; } = new Queue<RenderQueueEntry>(); public Queue<int> ComponentDisposalQueue { get; } = new Queue<int>(); // Scratch data structure for understanding attribute diffs. public Dictionary<string, int> AttributeDiffSet { get; } = new Dictionary<string, int>(); public int ParameterViewValidityStamp => _parameterViewValidityStamp; internal StackObjectPool<Dictionary<object, KeyedItemInfo>> KeyedItemInfoDictionaryPool { get; } = new StackObjectPool<Dictionary<object, KeyedItemInfo>>(maxPreservedItems: 10, () => new Dictionary<object, KeyedItemInfo>()); public void ClearStateForCurrentBatch() { // This method is used to reset the builder back to a default state so it can // begin building the next batch. That means clearing all the tracked state, but // *not* clearing ComponentRenderQueue because that may hold information about // the next batch we want to build. We shouldn't ever need to clear // ComponentRenderQueue explicitly, because it gets cleared as an aspect of // processing the render queue. EditsBuffer.Clear(); ReferenceFramesBuffer.Clear(); UpdatedComponentDiffs.Clear(); DisposedComponentIds.Clear(); DisposedEventHandlerIds.Clear(); AttributeDiffSet.Clear(); NamedEventChanges?.Clear(); } public RenderBatch ToBatch() => new RenderBatch( UpdatedComponentDiffs.ToRange(), ReferenceFramesBuffer.ToRange(), DisposedComponentIds.ToRange(), DisposedEventHandlerIds.ToRange(), NamedEventChanges?.ToRange()); public void InvalidateParameterViews() { // Wrapping is fine because all that matters is whether a snapshotted value matches // the current one. There's no plausible case where it wraps around and happens to // increment all the way back to a previously-snapshotted value on the exact same // call that's checking the value. if (_parameterViewValidityStamp == int.MaxValue) { _parameterViewValidityStamp = int.MinValue; } else { _parameterViewValidityStamp++; } } public void AddNamedEvent(int componentId, int frameIndex, ref RenderTreeFrame frame) { NamedEventChanges ??= new(); NamedEventChanges.Append(new NamedEventChange(NamedEventChangeType.Added, componentId, frameIndex, frame.NamedEventType, frame.NamedEventAssignedName)); } public void RemoveNamedEvent(int componentId, int frameIndex, ref RenderTreeFrame frame) { NamedEventChanges ??= new(); NamedEventChanges.Append(new NamedEventChange(NamedEventChangeType.Removed, componentId, frameIndex, frame.NamedEventType, frame.NamedEventAssignedName)); } public void Dispose() { EditsBuffer.Dispose(); ReferenceFramesBuffer.Dispose(); UpdatedComponentDiffs.Dispose(); DisposedComponentIds.Dispose(); DisposedEventHandlerIds.Dispose(); NamedEventChanges?.Dispose(); } }