Pass entire RenderBatch to JS side in one call

This commit is contained in:
Steve Sanderson 2018-01-29 21:43:49 +00:00
parent 55592f583b
commit c81d537ea3
7 changed files with 54 additions and 52 deletions

View File

@ -1,5 +1,5 @@
import { invokeWithJsonMarshalling } from './InvokeWithJsonMarshalling';
import { attachComponentToElement, renderRenderTree } from '../Rendering/Renderer';
import { attachComponentToElement, renderBatch } from '../Rendering/Renderer';
/**
* The definitive list of internal functions invokable from .NET code.
@ -8,5 +8,5 @@ import { attachComponentToElement, renderRenderTree } from '../Rendering/Rendere
export const internalRegisteredFunctions = {
attachComponentToElement,
invokeWithJsonMarshalling,
renderRenderTree,
renderBatch,
};

View File

@ -124,6 +124,10 @@ export const monoPlatform: Platform = {
const fieldValue = Module.getValue((baseAddress as any as number) + (fieldOffset || 0), 'i32');
return fieldValue === 0 ? null : monoPlatform.toJavaScriptString(fieldValue as any as System_String);
},
readStructField: function readStructField(baseAddress: Pointer, fieldOffset?: number): Pointer {
return ((baseAddress as any as number) + (fieldOffset || 0)) as any as Pointer;
},
};
// Bypass normal type checking to add this extra function. It's only intended to be called from

View File

@ -15,6 +15,7 @@
readInt32Field(baseAddress: Pointer, fieldOffset?: number): number;
readObjectField(baseAddress: Pointer, fieldOffset?: number): System_Object;
readStringField(baseAddress: Pointer, fieldOffset?: number): string | null;
readStructField(baseAddress: Pointer, fieldOffset?: number): Pointer;
}
// We don't actually instantiate any of these at runtime. For perf it's preferable to

View File

@ -0,0 +1,27 @@
import { Pointer, System_Array } from '../Platform/Platform';
import { platform } from '../Environment';
// Keep in sync with the structs in .NET code
export const renderBatch = {
updatedComponents: (obj: RenderBatchPointer) => platform.readStructField(obj, 0) as ArrayRangePointer,
};
const arrayRangeStructLength = 8;
export const arrayRange = {
array: (obj: ArrayRangePointer) => platform.readObjectField(obj, 0) as System_Array,
count: (obj: ArrayRangePointer) => platform.readInt32Field(obj, 4),
};
export const renderTreeDiffStructLength = 4 + 2 * arrayRangeStructLength;
export const renderTreeDiff = {
componentId: (obj: RenderTreeDiffPointer) => platform.readInt32Field(obj, 0),
edits: (obj: RenderTreeDiffPointer) => platform.readStructField(obj, 4) as ArrayRangePointer,
currentState: (obj: RenderTreeDiffPointer) => platform.readStructField(obj, 4 + arrayRangeStructLength) as ArrayRangePointer,
};
// Nominal types to ensure only valid pointers are passed to the functions above.
// At runtime the values are just numbers.
export interface RenderBatchPointer extends Pointer { RenderBatchPointer__DO_NOT_IMPLEMENT: any }
export interface ArrayRangePointer extends Pointer { ArrayRangePointer__DO_NOT_IMPLEMENT: any }
export interface RenderTreeDiffPointer extends Pointer { RenderTreeDiffPointer__DO_NOT_IMPLEMENT: any }

View File

@ -1,15 +0,0 @@
import { Pointer, System_Array } from '../Platform/Platform';
import { platform } from '../Environment';
// Keep in sync with the RenderComponentArgs struct in .NET code
export const renderComponentArgs = {
browserRendererId: (obj: RenderComponentArgsPointer) => platform.readInt32Field(obj, 0),
componentId: (obj: RenderComponentArgsPointer) => platform.readInt32Field(obj, 4),
renderTreeEdits: (obj: RenderComponentArgsPointer) => platform.readObjectField(obj, 8) as System_Array,
renderTreeEditsLength: (obj: RenderComponentArgsPointer) => platform.readInt32Field(obj, 12),
renderTree: (obj: RenderComponentArgsPointer) => platform.readObjectField(obj, 16) as System_Array
}
// Nominal type to ensure only valid pointers are passed to the renderComponentArgs functions.
// At runtime the values are just numbers.
export interface RenderComponentArgsPointer extends Pointer { RenderComponentArgsPointer__DO_NOT_IMPLEMENT: any }

View File

@ -2,7 +2,7 @@
import { platform } from '../Environment';
import { getTreeNodePtr, renderTreeNode, NodeType, RenderTreeNodePointer } from './RenderTreeNode';
import { RenderTreeEditPointer } from './RenderTreeEdit';
import { renderComponentArgs, RenderComponentArgsPointer } from './RenderComponentArgs';
import { renderBatch as renderBatchStruct, arrayRange, renderTreeDiffStructLength, renderTreeDiff, RenderBatchPointer, RenderTreeDiffPointer } from './RenderBatch';
import { BrowserRenderer } from './BrowserRenderer';
type BrowserRendererRegistry = { [browserRendererId: number]: BrowserRenderer };
@ -23,19 +23,27 @@ export function attachComponentToElement(browserRendererId: number, elementSelec
clearElement(element);
}
export function renderRenderTree(args: RenderComponentArgsPointer) {
const browserRendererId = renderComponentArgs.browserRendererId(args);
export function renderBatch(browserRendererId: number, batch: RenderBatchPointer) {
const browserRenderer = browserRenderers[browserRendererId];
if (!browserRenderer) {
throw new Error(`There is no browser renderer with ID ${browserRendererId}.`);
}
const updatedComponents = renderBatchStruct.updatedComponents(batch);
const updatedComponentsLength = arrayRange.count(updatedComponents);
const updatedComponentsArray = arrayRange.array(updatedComponents);
for (var i = 0; i < updatedComponentsLength; i++) {
const updatedComponentDiff = platform.getArrayEntryPtr(updatedComponentsArray, i, renderTreeDiffStructLength) as RenderTreeDiffPointer;
const componentId = renderTreeDiff.componentId(updatedComponentDiff);
const componentId = renderComponentArgs.componentId(args);
const edits = renderComponentArgs.renderTreeEdits(args);
const editsLength = renderComponentArgs.renderTreeEditsLength(args);
const tree = renderComponentArgs.renderTree(args);
const editsArrayRange = renderTreeDiff.edits(updatedComponentDiff);
const currentStateArrayRange = renderTreeDiff.currentState(updatedComponentDiff);
browserRenderer.updateComponent(componentId, edits, editsLength, tree);
const edits = arrayRange.array(editsArrayRange);
const editsLength = arrayRange.count(editsArrayRange);
const tree = arrayRange.array(currentStateArrayRange);
browserRenderer.updateComponent(componentId, edits, editsLength, tree);
}
}
function clearElement(element: Element) {

View File

@ -68,33 +68,10 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Rendering
/// <inheritdoc />
protected override void UpdateDisplay(RenderBatch batch)
{
// TODO: Pass to JS in a single call
for (var i = 0; i < batch.UpdatedComponents.Count; i++)
{
ref var diff = ref batch.UpdatedComponents.Array[i];
RegisteredFunction.InvokeUnmarshalled<RenderComponentArgs, object>(
"renderRenderTree",
new RenderComponentArgs
{
BrowserRendererId = _browserRendererId,
ComponentId = diff.ComponentId,
RenderTreeEdits = diff.Edits.Array,
RenderTreeEditsLength = diff.Edits.Count,
RenderTree = diff.CurrentState.Array
});
}
}
// Encapsulates the data we pass to the JS rendering function
private struct RenderComponentArgs
{
// Important: If you edit this struct, keep it in sync with RenderComponentArgs.ts
public int BrowserRendererId;
public int ComponentId;
public RenderTreeEdit[] RenderTreeEdits;
public int RenderTreeEditsLength;
public RenderTreeNode[] RenderTree;
RegisteredFunction.InvokeUnmarshalled<int, RenderBatch, object>(
"renderBatch",
_browserRendererId,
batch);
}
}
}