In RenderTreeFrame, use explicit [FieldOffset] to shrink the struct from 40 bytes to 28 bytes (in 32-bit mode) or 32 bytes (in 64-bit mode)
This commit is contained in:
parent
f1332919bc
commit
aae72c8136
|
|
@ -1,6 +1,6 @@
|
||||||
import { System_String, System_Array, Pointer } from '../Platform/Platform';
|
import { System_String, System_Array, Pointer } from '../Platform/Platform';
|
||||||
import { platform } from '../Environment';
|
import { platform } from '../Environment';
|
||||||
const renderTreeFrameStructLength = 40;
|
const renderTreeFrameStructLength = 28;
|
||||||
|
|
||||||
// To minimise GC pressure, instead of instantiating a JS object to represent each tree frame,
|
// To minimise GC pressure, instead of instantiating a JS object to represent each tree frame,
|
||||||
// we work in terms of pointers to the structs on the .NET heap, and use static functions that
|
// we work in terms of pointers to the structs on the .NET heap, and use static functions that
|
||||||
|
|
@ -13,12 +13,12 @@ export function getTreeFramePtr(renderTreeEntries: System_Array<RenderTreeFrameP
|
||||||
export const renderTreeFrame = {
|
export const renderTreeFrame = {
|
||||||
// The properties and memory layout must be kept in sync with the .NET equivalent in RenderTreeFrame.cs
|
// The properties and memory layout must be kept in sync with the .NET equivalent in RenderTreeFrame.cs
|
||||||
frameType: (frame: RenderTreeFramePointer) => platform.readInt32Field(frame, 4) as FrameType,
|
frameType: (frame: RenderTreeFramePointer) => platform.readInt32Field(frame, 4) as FrameType,
|
||||||
elementName: (frame: RenderTreeFramePointer) => platform.readStringField(frame, 8),
|
descendantsEndIndex: (frame: RenderTreeFramePointer) => platform.readInt32Field(frame, 8) as FrameType,
|
||||||
descendantsEndIndex: (frame: RenderTreeFramePointer) => platform.readInt32Field(frame, 12) as FrameType,
|
componentId: (frame: RenderTreeFramePointer) => platform.readInt32Field(frame, 12),
|
||||||
|
elementName: (frame: RenderTreeFramePointer) => platform.readStringField(frame, 16),
|
||||||
textContent: (frame: RenderTreeFramePointer) => platform.readStringField(frame, 16),
|
textContent: (frame: RenderTreeFramePointer) => platform.readStringField(frame, 16),
|
||||||
attributeName: (frame: RenderTreeFramePointer) => platform.readStringField(frame, 20),
|
attributeName: (frame: RenderTreeFramePointer) => platform.readStringField(frame, 16),
|
||||||
attributeValue: (frame: RenderTreeFramePointer) => platform.readStringField(frame, 24),
|
attributeValue: (frame: RenderTreeFramePointer) => platform.readStringField(frame, 24),
|
||||||
componentId: (frame: RenderTreeFramePointer) => platform.readInt32Field(frame, 32),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum FrameType {
|
export enum FrameType {
|
||||||
|
|
|
||||||
|
|
@ -3,133 +3,163 @@
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Blazor.Components;
|
using Microsoft.AspNetCore.Blazor.Components;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Blazor.RenderTree
|
namespace Microsoft.AspNetCore.Blazor.RenderTree
|
||||||
{
|
{
|
||||||
// TODO: Consider coalescing properties of compatible types that don't need to be
|
|
||||||
// used simultaneously. For example, 'ElementName' and 'AttributeName' could be replaced
|
|
||||||
// by a single 'Name' property.
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an entry in a tree of user interface (UI) items.
|
/// Represents an entry in a tree of user interface (UI) items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
public struct RenderTreeFrame
|
public struct RenderTreeFrame
|
||||||
{
|
{
|
||||||
|
// Note that the struct layout has to be valid in both 32-bit and 64-bit runtime platforms,
|
||||||
|
// which means that all reference-type fields need to take up 8 bytes (except for the last
|
||||||
|
// one, which will be sized as either 4 or 8 bytes depending on the runtime platform).
|
||||||
|
// This is not optimal for the Mono-WebAssembly case because that's always 32-bit so the
|
||||||
|
// reference-type fields could be reduced to 4 bytes each. We could use ifdefs to have
|
||||||
|
// different fields offsets for the 32 and 64 bit compile targets, but then we'd have the
|
||||||
|
// complexity of needing different binaries when loaded into Mono-WASM vs desktop.
|
||||||
|
// Eventually we might stop using this shared memory interop altogether (and would have to
|
||||||
|
// if running as a web worker) so for now to keep things simple, treat reference types as
|
||||||
|
// 8 bytes here.
|
||||||
|
|
||||||
|
// Common
|
||||||
|
[FieldOffset(0)] int _sequence;
|
||||||
|
[FieldOffset(4)] RenderTreeFrameType _frameType;
|
||||||
|
|
||||||
|
// RenderTreeFrameType.Element
|
||||||
|
[FieldOffset(8)] private int _elementDescendantsEndIndex;
|
||||||
|
[FieldOffset(16)] string _elementName;
|
||||||
|
|
||||||
|
// RenderTreeFrameType.Text
|
||||||
|
[FieldOffset(16)] private string _textContent;
|
||||||
|
|
||||||
|
// RenderTreeFrameType.Attribute
|
||||||
|
[FieldOffset(16)] private string _attributeName;
|
||||||
|
[FieldOffset(24)] private object _attributeValue;
|
||||||
|
|
||||||
|
// RenderTreeFrameType.Component
|
||||||
|
[FieldOffset(8)] private int _componentDescendantsEndIndex;
|
||||||
|
[FieldOffset(12)] private int _componentId;
|
||||||
|
[FieldOffset(16)] private Type _componentType;
|
||||||
|
[FieldOffset(24)] private IComponent _component;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the sequence number of the frame. Sequence numbers indicate the relative source
|
/// Gets the sequence number of the frame. Sequence numbers indicate the relative source
|
||||||
/// positions of the instructions that inserted the frames. Sequence numbers are only
|
/// positions of the instructions that inserted the frames. Sequence numbers are only
|
||||||
/// comparable within the same sequence (typically, the same source method).
|
/// comparable within the same sequence (typically, the same source method).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Sequence { get; private set; }
|
public int Sequence => _sequence;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Describes the type of this frame.
|
/// Describes the type of this frame.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public RenderTreeFrameType FrameType { get; private set; }
|
public RenderTreeFrameType FrameType => _frameType;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>,
|
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>,
|
||||||
/// gets a name representing the type of the element. Otherwise, the value is <see langword="null"/>.
|
/// gets a name representing the type of the element. Otherwise, the value is <see langword="null"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ElementName { get; private set; }
|
public string ElementName => _elementName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>,
|
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>,
|
||||||
/// gets the index of the final descendant frame in the tree. The value is
|
/// gets the index of the final descendant frame in the tree. The value is
|
||||||
/// zero if the frame is of a different type, or if it has not yet been closed.
|
/// zero if the frame is of a different type, or if it has not yet been closed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int ElementDescendantsEndIndex { get; private set; }
|
public int ElementDescendantsEndIndex => _elementDescendantsEndIndex;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Text"/>,
|
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Text"/>,
|
||||||
/// gets the content of the text frame. Otherwise, the value is <see langword="null"/>.
|
/// gets the content of the text frame. Otherwise, the value is <see langword="null"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string TextContent { get; private set; }
|
public string TextContent => _textContent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
|
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
|
||||||
/// gets the attribute name. Otherwise, the value is <see langword="null"/>.
|
/// gets the attribute name. Otherwise, the value is <see langword="null"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string AttributeName { get; private set; }
|
public string AttributeName => _attributeName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
|
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
|
||||||
/// gets the attribute value. Otherwise, the value is <see langword="null"/>.
|
/// gets the attribute value. Otherwise, the value is <see langword="null"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object AttributeValue { get; private set; }
|
public object AttributeValue => _attributeValue;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
|
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
|
||||||
/// gets the type of the child component.
|
/// gets the type of the child component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Type ComponentType { get; private set; }
|
public Type ComponentType => _componentType;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
|
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
|
||||||
/// gets the child component instance identifier.
|
/// gets the child component instance identifier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int ComponentId { get; private set; }
|
public int ComponentId => _componentId;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
|
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
|
||||||
/// gets the child component instance. Otherwise, the value is <see langword="null"/>.
|
/// gets the child component instance. Otherwise, the value is <see langword="null"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IComponent Component { get; private set; }
|
public IComponent Component => _component;
|
||||||
|
|
||||||
internal static RenderTreeFrame Element(int sequence, string elementName) => new RenderTreeFrame
|
internal static RenderTreeFrame Element(int sequence, string elementName) => new RenderTreeFrame
|
||||||
{
|
{
|
||||||
Sequence = sequence,
|
_sequence = sequence,
|
||||||
FrameType = RenderTreeFrameType.Element,
|
_frameType = RenderTreeFrameType.Element,
|
||||||
ElementName = elementName,
|
_elementName = elementName,
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static RenderTreeFrame Text(int sequence, string textContent) => new RenderTreeFrame
|
internal static RenderTreeFrame Text(int sequence, string textContent) => new RenderTreeFrame
|
||||||
{
|
{
|
||||||
Sequence = sequence,
|
_sequence = sequence,
|
||||||
FrameType = RenderTreeFrameType.Text,
|
_frameType = RenderTreeFrameType.Text,
|
||||||
TextContent = textContent ?? string.Empty,
|
_textContent = textContent ?? string.Empty,
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static RenderTreeFrame Attribute(int sequence, string name, string value) => new RenderTreeFrame
|
internal static RenderTreeFrame Attribute(int sequence, string name, string value) => new RenderTreeFrame
|
||||||
{
|
{
|
||||||
Sequence = sequence,
|
_sequence = sequence,
|
||||||
FrameType = RenderTreeFrameType.Attribute,
|
_frameType = RenderTreeFrameType.Attribute,
|
||||||
AttributeName = name,
|
_attributeName = name,
|
||||||
AttributeValue = value
|
_attributeValue = value
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static RenderTreeFrame Attribute(int sequence, string name, UIEventHandler value) => new RenderTreeFrame
|
internal static RenderTreeFrame Attribute(int sequence, string name, UIEventHandler value) => new RenderTreeFrame
|
||||||
{
|
{
|
||||||
Sequence = sequence,
|
_sequence = sequence,
|
||||||
FrameType = RenderTreeFrameType.Attribute,
|
_frameType = RenderTreeFrameType.Attribute,
|
||||||
AttributeName = name,
|
_attributeName = name,
|
||||||
AttributeValue = value
|
_attributeValue = value
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static RenderTreeFrame Attribute(int sequence, string name, object value) => new RenderTreeFrame
|
internal static RenderTreeFrame Attribute(int sequence, string name, object value) => new RenderTreeFrame
|
||||||
{
|
{
|
||||||
Sequence = sequence,
|
_sequence = sequence,
|
||||||
FrameType = RenderTreeFrameType.Attribute,
|
_frameType = RenderTreeFrameType.Attribute,
|
||||||
AttributeName = name,
|
_attributeName = name,
|
||||||
AttributeValue = value
|
_attributeValue = value
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static RenderTreeFrame ChildComponent<T>(int sequence) where T: IComponent => new RenderTreeFrame
|
internal static RenderTreeFrame ChildComponent<T>(int sequence) where T : IComponent => new RenderTreeFrame
|
||||||
{
|
{
|
||||||
Sequence = sequence,
|
_sequence = sequence,
|
||||||
FrameType = RenderTreeFrameType.Component,
|
_frameType = RenderTreeFrameType.Component,
|
||||||
ComponentType = typeof(T)
|
_componentType = typeof(T)
|
||||||
};
|
};
|
||||||
|
|
||||||
internal void CloseElement(int descendantsEndIndex)
|
internal void CloseElement(int descendantsEndIndex)
|
||||||
{
|
{
|
||||||
ElementDescendantsEndIndex = descendantsEndIndex;
|
_elementDescendantsEndIndex = descendantsEndIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SetChildComponentInstance(int componentId, IComponent component)
|
internal void SetChildComponentInstance(int componentId, IComponent component)
|
||||||
{
|
{
|
||||||
ComponentId = componentId;
|
_componentId = componentId;
|
||||||
Component = component;
|
_component = component;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SetSequence(int sequence)
|
internal void SetSequence(int sequence)
|
||||||
|
|
@ -137,7 +167,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
|
||||||
// This is only used when appending attribute frames, because helpers such as @onclick
|
// This is only used when appending attribute frames, because helpers such as @onclick
|
||||||
// need to construct the attribute frame in a context where they don't know the sequence
|
// need to construct the attribute frame in a context where they don't know the sequence
|
||||||
// number, so we assign it later
|
// number, so we assign it later
|
||||||
Sequence = sequence;
|
_sequence = sequence;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue