Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions packages/exojs-tilemap/src/webgl2/WebGl2TileChunkRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,15 +408,15 @@ export class WebGl2TileChunkRenderer extends AbstractWebGl2Renderer<TileChunkNod
* dispatching here and bumps the stats from the instruction descriptor.
* @internal
*/
public replayRetainedBatch(payload: WebGl2RetainedBatchPayload): void {
public replayRetainedBatch(payload: WebGl2RetainedBatchPayload): boolean {
const backend = this.getBackendOrNull();
const vao = payload.vao;
const transformTexture = payload.bundle.transformTexture;

if (backend === null || vao === null || transformTexture === null) {
// Defensive: a bundle in this state never validates (generation), so a
// spliced replay cannot reach here; skip rather than crash mid-frame.
return;
return false;
}

backend.setBlendMode(payload.blendMode);
Expand All @@ -440,6 +440,8 @@ export class WebGl2TileChunkRenderer extends AbstractWebGl2Renderer<TileChunkNod
this._shader.sync();
backend.bindVertexArrayObject(vao);
vao.drawInstanced(4, 0, payload.instanceCount, RenderingPrimitives.TriangleStrip);

return true;
}

protected onConnect(backend: WebGl2Backend): void {
Expand Down
5 changes: 1 addition & 4 deletions site/src/lib/bench-profiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -674,10 +674,7 @@ const ARCHETYPE_TITLES: Readonly<Record<string, string>> = {
* removing a losing card and quietly keeping a winning one is exactly what a
* fixed headline set exists to prevent.
*/
const WITHHELD_SCENARIOS: Readonly<Record<string, string>> = {
joints:
'On the published profiles the chains had settled and every arm that sleeps was idle: Nape-JS took less than the clock resolved, and the other figures set a solver at work against one at rest. The scene now keeps the chains moving, and the comparison returns with the next reference measurement.',
};
const WITHHELD_SCENARIOS: Readonly<Record<string, string>> = {};

/** Why a scenario publishes no cross-arm comparison, or `undefined` where it publishes one. */
export const withheldScenario = (archetype: string): string | undefined => WITHHELD_SCENARIOS[archetype];
Expand Down
4 changes: 2 additions & 2 deletions src/rendering/plan/DerivedRootProduct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,12 +327,12 @@ export class DerivedRootProduct {
// A table sized only now has no previous assignment to diff against, so
// every admitted item enters - which is what the backend's empty stores
// require anyway.
this.slots.update(scopes[0]!, this._current, null);
this.slots.update(scopes[0]!, this._current, null, this._queried);

return;
}

this.slots.update(scopes[0]!, this._current, hadPrevious ? this._previous : null);
this.slots.update(scopes[0]!, this._current, hadPrevious ? this._previous : null, this._queried);
}
}

Expand Down
18 changes: 9 additions & 9 deletions src/rendering/plan/DerivedSelectionState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ export class DerivedSelectionState {
* ordinal; pass `null` when there is none, in which case every admitted item
* is treated as entering.
*/
public update(rootScope: SourceScope, current: readonly MembershipBits[], previous: readonly MembershipBits[] | null): void {
public update(rootScope: SourceScope, current: readonly MembershipBits[], previous: readonly MembershipBits[] | null, queried?: Uint8Array): void {
resetSlotStats(this.stats);
this._enteredCount = 0;
this._orderCount = 0;
Expand All @@ -288,7 +288,7 @@ export class DerivedSelectionState {
}

this._admitTree(rootScope, current, previous);
this._walkScope(rootScope, current);
this._walkScope(rootScope, current, queried);
this.stats.orderEntries = this._orderCount;
this.stats.slotCapacity = this._slotCount;
}
Expand Down Expand Up @@ -448,7 +448,7 @@ export class DerivedSelectionState {
* contributes nothing to append. A live entry is never culled here either:
* its own collect applies whatever cull its node has.
*/
private _walkScope(scope: SourceScope, current: readonly MembershipBits[]): void {
private _walkScope(scope: SourceScope, current: readonly MembershipBits[], queried: Uint8Array | undefined): void {
const bits = current[scope.ordinal]!;
const words = bits.words;
const wordCount = bits.wordCount;
Expand Down Expand Up @@ -480,11 +480,11 @@ export class DerivedSelectionState {

other++;

if (nested.kind === RenderEntryKind.Group) {
if (nested.kind === RenderEntryKind.Group && queried?.[nested.ordinal] !== 0) {
this._orderCount = cursor;
this._walkScope(nested, current);
this._walkScope(nested, current, queried);
cursor = this._orderCount;
} else {
} else if (nested.kind !== RenderEntryKind.Group) {
this._mark(cursor, nested);
}
}
Expand All @@ -500,9 +500,9 @@ export class DerivedSelectionState {

other++;

if (nested.kind === RenderEntryKind.Group) {
this._walkScope(nested, current);
} else {
if (nested.kind === RenderEntryKind.Group && queried?.[nested.ordinal] !== 0) {
this._walkScope(nested, current, queried);
} else if (nested.kind !== RenderEntryKind.Group) {
this._mark(this._orderCount, nested);
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/rendering/webgl2/WebGl2Backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2317,7 +2317,10 @@ export class WebGl2Backend implements RenderBackend {
}

this._bindRenderTarget(this._renderTarget);
payload.replayer.replayRetainedBatch(payload);
if (!payload.replayer.replayRetainedBatch(payload)) {
return;
}

this._stats.batches++;
this._stats.drawCalls += batch.drawCalls;
// Nodes, not instances: a batch whose renderer expands one node into many
Expand Down
6 changes: 4 additions & 2 deletions src/rendering/webgl2/WebGl2MeshRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ export class WebGl2MeshRenderer extends AbstractWebGl2Renderer<Mesh> implements
* (`drawElementsInstanced`), unlike the sprite path's `drawArraysInstanced`.
* @internal
*/
public replayRetainedBatch(payload: WebGl2RetainedBatchPayload): void {
public replayRetainedBatch(payload: WebGl2RetainedBatchPayload): boolean {
const backend = this.getBackendOrNull();
const vao = payload.vao;
const geometry = payload.geometry;
Expand All @@ -779,7 +779,7 @@ export class WebGl2MeshRenderer extends AbstractWebGl2Renderer<Mesh> implements
if (backend === null || vao === null || geometry === null || geometry === undefined || transformTexture === null || tintTexture === null) {
// Defensive: a bundle in this state never validates (generation), so a
// spliced replay cannot reach here; skip rather than crash mid-frame.
return;
return false;
}

const shader = this._defaultShader;
Expand Down Expand Up @@ -819,6 +819,8 @@ export class WebGl2MeshRenderer extends AbstractWebGl2Renderer<Mesh> implements
shader.sync();
backend.bindVertexArrayObject(vao);
vao.drawInstanced(geometry.indexCount, 0, payload.instanceCount, RenderingPrimitives.Triangles);

return true;
}

private _canBatchStatic(draw: PendingMeshDraw): boolean {
Expand Down
6 changes: 4 additions & 2 deletions src/rendering/webgl2/WebGl2NineSliceSpriteRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,15 +367,15 @@ export class WebGl2NineSliceSpriteRenderer extends AbstractWebGl2Renderer<NineSl
* stats from the instruction descriptor.
* @internal
*/
public replayRetainedBatch(payload: WebGl2RetainedBatchPayload): void {
public replayRetainedBatch(payload: WebGl2RetainedBatchPayload): boolean {
const backend = this.getBackendOrNull();
const vao = payload.vao;
const transformTexture = payload.bundle.transformTexture;

if (backend === null || vao === null || transformTexture === null) {
// Defensive: a bundle in this state never validates (generation), so a
// spliced replay cannot reach here; skip rather than crash mid-frame.
return;
return false;
}

backend.setBlendMode(payload.blendMode);
Expand All @@ -399,6 +399,8 @@ export class WebGl2NineSliceSpriteRenderer extends AbstractWebGl2Renderer<NineSl
this._shader.sync();
backend.bindVertexArrayObject(vao);
vao.drawInstanced(4, 0, payload.instanceCount, RenderingPrimitives.TriangleStrip);

return true;
}

protected onConnect(backend: WebGl2Backend): void {
Expand Down
6 changes: 4 additions & 2 deletions src/rendering/webgl2/WebGl2RepeatingSpriteRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -552,15 +552,15 @@ export class WebGl2RepeatingSpriteRenderer extends AbstractWebGl2Renderer<Repeat
* transform unit). Mirrors {@link WebGl2SpriteRenderer.replayRetainedBatch}.
* @internal
*/
public replayRetainedBatch(payload: WebGl2RetainedBatchPayload): void {
public replayRetainedBatch(payload: WebGl2RetainedBatchPayload): boolean {
const backend = this.getBackendOrNull();
const vao = payload.vao;
const transformTexture = payload.bundle.transformTexture;

if (backend === null || vao === null || transformTexture === null) {
// Defensive: a bundle in this state never validates (generation), so a
// spliced replay cannot reach here; skip rather than crash mid-frame.
return;
return false;
}

if (payload.blendMode !== this._currentBlendMode) {
Expand All @@ -584,6 +584,8 @@ export class WebGl2RepeatingSpriteRenderer extends AbstractWebGl2Renderer<Repeat

backend.bindVertexArrayObject(vao);
vao.drawInstanced(4, 0, payload.instanceCount, RenderingPrimitives.TriangleStrip);

return true;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/rendering/webgl2/WebGl2RetainedGroupResources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ export interface WebGl2RetainedBatchReplayer {
configureRetainedVao(payload: WebGl2RetainedBatchPayload): void;
/** Preflight structural live state before any instruction in the set draws. */
validateRetainedBatch?(payload: WebGl2RetainedBatchPayload): boolean;
/** Replay the batch: live state (blend, uniforms, textures), cached data (bytes, transforms). */
replayRetainedBatch(payload: WebGl2RetainedBatchPayload): void;
/** Replay the batch and report whether it issued a draw. */
replayRetainedBatch(payload: WebGl2RetainedBatchPayload): boolean;
}

/**
Expand Down
6 changes: 4 additions & 2 deletions src/rendering/webgl2/WebGl2SpriteRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ export class WebGl2SpriteRenderer extends AbstractWebGl2Renderer<Sprite> impleme
* dispatching here and bumps the stats from the instruction descriptor.
* @internal
*/
public replayRetainedBatch(payload: WebGl2RetainedBatchPayload): void {
public replayRetainedBatch(payload: WebGl2RetainedBatchPayload): boolean {
const backend = this.getBackendOrNull();
const vao = payload.vao;
const transformTexture = payload.bundle.transformTexture;
Expand All @@ -558,7 +558,7 @@ export class WebGl2SpriteRenderer extends AbstractWebGl2Renderer<Sprite> impleme
if (backend === null || vao === null || transformTexture === null || tintTexture === null) {
// Defensive: a bundle in this state never validates (generation), so a
// spliced replay cannot reach here; skip rather than crash mid-frame.
return;
return false;
}

backend.setBlendMode(payload.blendMode);
Expand Down Expand Up @@ -614,6 +614,8 @@ export class WebGl2SpriteRenderer extends AbstractWebGl2Renderer<Sprite> impleme
this._bindBaseTextureSamplers(backend, material, textures.length);
vao.drawInstanced(4, 0, payload.instanceCount, RenderingPrimitives.TriangleStrip);
this._unbindBaseTextureSamplers(backend, material, textures.length);

return true;
}

private _bindBaseTextureSamplers(backend: WebGl2Backend, material: AnySpriteMaterial | null, slotCount: number): void {
Expand Down
8 changes: 5 additions & 3 deletions src/rendering/webgl2/WebGl2TextRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -726,19 +726,19 @@ export class WebGl2TextRenderer extends AbstractWebGl2Renderer<Text | BitmapText
* static quad-index pattern, and the group-owned per-node style texture.
* @internal
*/
public replayRetainedBatch(payload: WebGl2RetainedBatchPayload): void {
public replayRetainedBatch(payload: WebGl2RetainedBatchPayload): boolean {
const backend = this.getBackendOrNull();
const vao = payload.vao;
const data = payload.rendererData as TextRetainedRendererData | null;

if (backend === null || vao === null || data === null || !(payload.bundle instanceof WebGl2RetainedGroupResources)) {
return;
return false;
}

const state = payload.bundle.rendererReplayState;

if (!(state instanceof TextRetainedReplayState) || state.nodeDataTexture === null) {
return;
return false;
}

const shader = this._shaderFor(data.shaderType);
Expand Down Expand Up @@ -773,6 +773,8 @@ export class WebGl2TextRenderer extends AbstractWebGl2Renderer<Text | BitmapText
shader.sync();
backend.bindVertexArrayObject(vao);
vao.draw(state.quadCount * 6, 0, RenderingPrimitives.Triangles);

return true;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,9 @@ describe('WebGL2 renderer matrix: Text retained instruction-set replay cells', (
// F3 static frame: the fast/instruction-replay tier is the only path that
// can draw the group's glyphs. A neutered replay that never issues its
// drawElements leaves the canvas empty where the record frame had ink.
WebGl2TextRenderer.prototype.replayRetainedBatch = function (): void {};
WebGl2TextRenderer.prototype.replayRetainedBatch = function (): boolean {
return true;
};

render(backend, scene.root);

Expand Down
15 changes: 14 additions & 1 deletion test/site/bench-invalid-values.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@
import { describe, expect, it } from 'vitest';

import { openingLoad, physicsCards, renderingCards } from '../../site/src/lib/bench-cards';
import { benchProfiles, formatMs, isQuantitative, outcomeOf, type ProfileCell, publishedMs, type TimerCheck } from '../../site/src/lib/bench-profiles';
import {
benchProfiles,
formatMs,
isQuantitative,
outcomeOf,
type ProfileCell,
publishedMs,
type TimerCheck,
withheldScenario,
} from '../../site/src/lib/bench-profiles';

/** A cell whose competitor arm reported `ms` under the given timer verdict. */
const cellOf = (ms: number, timer: TimerCheck, comparable = true): ProfileCell => ({
Expand Down Expand Up @@ -153,4 +162,8 @@ describe('every published profile', () => {
expect(openingLoad(card)).toBeDefined();
}
});

it('publishes the aligned joint-chain workload as a normal comparison', () => {
expect(withheldScenario('joints')).toBeUndefined();
});
});
Loading