diff --git a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Interop/InternalRegisteredFunction.ts b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Interop/InternalRegisteredFunction.ts
index fa1a98f69d..fe7a4cd82b 100644
--- a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Interop/InternalRegisteredFunction.ts
+++ b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Interop/InternalRegisteredFunction.ts
@@ -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,
};
diff --git a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Platform/Mono/MonoPlatform.ts b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Platform/Mono/MonoPlatform.ts
index c710e606c1..6f24cd086f 100644
--- a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Platform/Mono/MonoPlatform.ts
+++ b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Platform/Mono/MonoPlatform.ts
@@ -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
diff --git a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Platform/Platform.ts b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Platform/Platform.ts
index 032defcdd9..ddefea35da 100644
--- a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Platform/Platform.ts
+++ b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Platform/Platform.ts
@@ -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
diff --git a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/RenderBatch.ts b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/RenderBatch.ts
new file mode 100644
index 0000000000..8d99186863
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/RenderBatch.ts
@@ -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 }
diff --git a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/RenderComponentArgs.ts b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/RenderComponentArgs.ts
deleted file mode 100644
index 7ecbfc6aff..0000000000
--- a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/RenderComponentArgs.ts
+++ /dev/null
@@ -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 }
diff --git a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/Renderer.ts b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/Renderer.ts
index 081b54292f..740bfdb726 100644
--- a/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/Renderer.ts
+++ b/src/Microsoft.AspNetCore.Blazor.Browser.JS/src/Rendering/Renderer.ts
@@ -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) {
diff --git a/src/Microsoft.AspNetCore.Blazor.Browser/Rendering/BrowserRenderer.cs b/src/Microsoft.AspNetCore.Blazor.Browser/Rendering/BrowserRenderer.cs
index 96b188f653..49d5091672 100644
--- a/src/Microsoft.AspNetCore.Blazor.Browser/Rendering/BrowserRenderer.cs
+++ b/src/Microsoft.AspNetCore.Blazor.Browser/Rendering/BrowserRenderer.cs
@@ -68,33 +68,10 @@ namespace Microsoft.AspNetCore.Blazor.Browser.Rendering
///
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(
- "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(
+ "renderBatch",
+ _browserRendererId,
+ batch);
}
}
}