@bldrs-ai/conway 1.536.1507 → 1.543.1513
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiled/examples/browser-bundled.cjs +39 -5
- package/compiled/examples/cli-bundled.cjs +197 -22
- package/compiled/examples/cli-step-bundled.cjs +39 -5
- package/compiled/examples/validator-bundled.cjs +39 -5
- package/compiled/src/compat/web-ifc/geometry_shard_coordination.test.d.ts +2 -0
- package/compiled/src/compat/web-ifc/geometry_shard_coordination.test.d.ts.map +1 -0
- package/compiled/src/compat/web-ifc/geometry_shard_coordination.test.js +236 -0
- package/compiled/src/compat/web-ifc/ifc_api.d.ts +26 -0
- package/compiled/src/compat/web-ifc/ifc_api.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api.js +33 -0
- package/compiled/src/compat/web-ifc/ifc_api_model_passthrough.d.ts +6 -0
- package/compiled/src/compat/web-ifc/ifc_api_model_passthrough.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api_preview_channel.test.js +264 -1
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts +116 -5
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/ifc_api_proxy_ifc.js +256 -85
- package/compiled/src/compat/web-ifc/store_preview_channel.d.ts +79 -0
- package/compiled/src/compat/web-ifc/store_preview_channel.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/store_preview_channel.js +237 -14
- package/compiled/src/compat/web-ifc/store_preview_channel.test.js +270 -1
- package/compiled/src/compat/web-ifc/streamed_preview_channel.d.ts +106 -1
- package/compiled/src/compat/web-ifc/streamed_preview_channel.d.ts.map +1 -1
- package/compiled/src/compat/web-ifc/streamed_preview_channel.js +269 -17
- package/compiled/src/core/progress_log.d.ts +75 -2
- package/compiled/src/core/progress_log.d.ts.map +1 -1
- package/compiled/src/core/progress_log.js +59 -2
- package/compiled/src/core/progress_log.test.js +75 -1
- package/compiled/src/ifc/dangling_placement_error.d.ts +35 -0
- package/compiled/src/ifc/dangling_placement_error.d.ts.map +1 -0
- package/compiled/src/ifc/dangling_placement_error.js +38 -0
- package/compiled/src/ifc/geometry_dispatch.d.ts +74 -3
- package/compiled/src/ifc/geometry_dispatch.d.ts.map +1 -1
- package/compiled/src/ifc/geometry_dispatch.js +261 -5
- package/compiled/src/ifc/geometry_dispatch.test.js +147 -1
- package/compiled/src/ifc/ifc_geometry_extraction.d.ts +54 -0
- package/compiled/src/ifc/ifc_geometry_extraction.d.ts.map +1 -1
- package/compiled/src/ifc/ifc_geometry_extraction.js +153 -20
- package/compiled/src/index.d.ts +1 -1
- package/compiled/src/index.d.ts.map +1 -1
- package/compiled/src/index.js +1 -1
- package/compiled/src/step/dangling_reference_error.d.ts +34 -0
- package/compiled/src/step/dangling_reference_error.d.ts.map +1 -0
- package/compiled/src/step/dangling_reference_error.js +36 -0
- package/compiled/src/step/step_entity_base.d.ts +17 -0
- package/compiled/src/step/step_entity_base.d.ts.map +1 -1
- package/compiled/src/step/step_entity_base.js +28 -4
- package/compiled/src/version/version.js +1 -1
- package/compiled/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ConwayGeometry, } from "../../index.js";
|
|
2
|
-
import { geometryDispatchKey, shardOfDispatchKey } from "../../ifc/geometry_dispatch.js";
|
|
2
|
+
import { computeDispatchKeys, computeRelatingLocalIDs, geometryDispatchKey, relatingLocalIDOf, shardOfDispatchKey, } from "../../ifc/geometry_dispatch.js";
|
|
3
3
|
import IfcStepModel from "../../ifc/ifc_step_model.js";
|
|
4
4
|
import { WindowedStepBufferProvider } from "../../step/step_buffer_provider.js";
|
|
5
5
|
import * as glmatrix from "gl-matrix";
|
|
@@ -186,6 +186,18 @@ export class IfcApiProxyIfc {
|
|
|
186
186
|
* large-coordinate budget.
|
|
187
187
|
*/
|
|
188
188
|
this.demandCoordinationFromPreview_ = false;
|
|
189
|
+
/**
|
|
190
|
+
* True when the coordination frame was HANDED to this instance rather than
|
|
191
|
+
* derived from its own first geometry (see setCoordinationFrame).
|
|
192
|
+
*
|
|
193
|
+
* The distinction is what makes a recentred model shardable. A derived
|
|
194
|
+
* frame is anchored on whichever product this instance happened to extract
|
|
195
|
+
* first, so N workers derive N frames and their outputs merge shifted by
|
|
196
|
+
* whole recentre cells; a supplied one is the same for every worker by
|
|
197
|
+
* construction, which is the only property the refusals in
|
|
198
|
+
* checkShardPreconditions_ were ever protecting.
|
|
199
|
+
*/
|
|
200
|
+
this.coordinationSupplied_ = false;
|
|
189
201
|
this._isCoordinated = false;
|
|
190
202
|
this.linearScalingFactor = 1;
|
|
191
203
|
this.identity = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
@@ -1483,7 +1495,7 @@ export class IfcApiProxyIfc {
|
|
|
1483
1495
|
return { extracted: 0, remaining: 0 };
|
|
1484
1496
|
}
|
|
1485
1497
|
this.checkShardPreconditions_();
|
|
1486
|
-
this.
|
|
1498
|
+
await this.ensureDemandWorklistsAsync_();
|
|
1487
1499
|
if (!this.model[0].isSourceExternal) {
|
|
1488
1500
|
return this.pumpGeometryBatch_(batchSize, meshCallback);
|
|
1489
1501
|
}
|
|
@@ -1607,6 +1619,97 @@ export class IfcApiProxyIfc {
|
|
|
1607
1619
|
if (this.demandProducts_ !== void 0) {
|
|
1608
1620
|
return;
|
|
1609
1621
|
}
|
|
1622
|
+
const { products, aggregates } = this.collectDemandCandidates_();
|
|
1623
|
+
if (this.shard_ === void 0) {
|
|
1624
|
+
this.adoptDemandWorklists_(products, aggregates);
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
// Reachable only on a resident source: the sync pump refuses a windowed
|
|
1628
|
+
// one outright, and the async pump routes a sharded model through
|
|
1629
|
+
// ensureDemandWorklistsAsync_. Asserted rather than assumed, because the
|
|
1630
|
+
// failure it guards against is a silently wrong partition — a walk that
|
|
1631
|
+
// hit a non-resident record here would throw out of the load instead
|
|
1632
|
+
// (see geometryDispatchKey), but the diagnostic would name a record
|
|
1633
|
+
// rather than the missing await.
|
|
1634
|
+
if (this.model[0].isSourceExternal) {
|
|
1635
|
+
throw new Error("a sharded windowed model needs the async worklist build, which " +
|
|
1636
|
+
"pages each product's dispatch closure \u2014 reached the synchronous " +
|
|
1637
|
+
"one instead");
|
|
1638
|
+
}
|
|
1639
|
+
const productKeys = new Uint32Array(products.length);
|
|
1640
|
+
for (let where = 0; where < products.length; ++where) {
|
|
1641
|
+
productKeys[where] = geometryDispatchKey(this.model[0], products[where]);
|
|
1642
|
+
}
|
|
1643
|
+
const aggregateKeys = new Uint32Array(aggregates.length);
|
|
1644
|
+
for (let where = 0; where < aggregates.length; ++where) {
|
|
1645
|
+
aggregateKeys[where] = geometryDispatchKey(this.model[0], relatingLocalIDOf(this.model[0], aggregates[where].localID));
|
|
1646
|
+
}
|
|
1647
|
+
this.adoptShardedWorklists_(products, aggregates, productKeys, aggregateKeys);
|
|
1648
|
+
}
|
|
1649
|
+
/**
|
|
1650
|
+
* Worklist build for a windowed source — the same partition, with the
|
|
1651
|
+
* dispatch walk's records paged before they are read.
|
|
1652
|
+
*
|
|
1653
|
+
* Sharding a windowed model rests on every worker computing the SAME key
|
|
1654
|
+
* for a product, and an inline walk cannot promise that: whether a record
|
|
1655
|
+
* read resolves depends on which chunks THIS worker holds, so workers
|
|
1656
|
+
* disagree and both moduli select a product (extracted twice) or neither
|
|
1657
|
+
* (dropped). computeDispatchKeys pages each hop of the walk first, which
|
|
1658
|
+
* is what makes the keys a function of the file rather than of paging —
|
|
1659
|
+
* and is why the pool can finally serve a store-backed open, the only kind
|
|
1660
|
+
* Share performs.
|
|
1661
|
+
*
|
|
1662
|
+
* Resident sources take the same route with the paging short-circuited, so
|
|
1663
|
+
* there is one partition rather than a windowed one and a resident one.
|
|
1664
|
+
*/
|
|
1665
|
+
async ensureDemandWorklistsAsync_() {
|
|
1666
|
+
if (this.demandProducts_ !== void 0) {
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1669
|
+
// BEFORE collecting: the aggregate-target set is read from the
|
|
1670
|
+
// IfcRelAggregates records themselves, and on a windowed source those
|
|
1671
|
+
// pages are long gone by the first pump. The sync walk swallows the
|
|
1672
|
+
// non-resident read and caches the incomplete set for the model's life,
|
|
1673
|
+
// so a child it missed is extracted BOTH by the per-product worklist
|
|
1674
|
+
// (with the uncut content that deferral exists to suppress) and again by
|
|
1675
|
+
// the aggregates pass. Measured at 0 targets against a resident open's 1
|
|
1676
|
+
// on aggregate_master_voids.ifc behind 512-byte windows.
|
|
1677
|
+
//
|
|
1678
|
+
// Not a sharding bug — it costs an unsharded windowed load the same
|
|
1679
|
+
// duplicate — but this is the pump entry every windowed model reaches,
|
|
1680
|
+
// so it is the place that can page first.
|
|
1681
|
+
await this.conwayGeometry_.ensureAggregateTargetLocalIDs();
|
|
1682
|
+
const { products, aggregates } = this.collectDemandCandidates_();
|
|
1683
|
+
if (this.shard_ === void 0) {
|
|
1684
|
+
this.adoptDemandWorklists_(products, aggregates);
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1687
|
+
const productKeys = await computeDispatchKeys(this.model[0], products);
|
|
1688
|
+
const aggregateKeys = await computeDispatchKeys(this.model[0], await computeRelatingLocalIDs(this.model[0], aggregates.map((aggregate) => aggregate.localID)));
|
|
1689
|
+
// Re-checked after the awaits: a concurrent pump could have built the
|
|
1690
|
+
// worklists while this one was paging, and adopting a second set would
|
|
1691
|
+
// reset the cursors under it, re-extracting everything already pumped.
|
|
1692
|
+
if (this.demandProducts_ !== void 0) {
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
this.adoptShardedWorklists_(products, aggregates, productKeys, aggregateKeys);
|
|
1696
|
+
}
|
|
1697
|
+
/**
|
|
1698
|
+
* Everything the pump could extract, before any shard narrows it.
|
|
1699
|
+
*
|
|
1700
|
+
* Unchanged from what the pump enumerated before sharding existed, and
|
|
1701
|
+
* deliberately shared by both key computations so they start from the same
|
|
1702
|
+
* lists in the same order — that order is what makes the keys align to the
|
|
1703
|
+
* worklists.
|
|
1704
|
+
*
|
|
1705
|
+
* The product walk itself reads no record bytes (the type index and the
|
|
1706
|
+
* local-ID column answer it). `aggregateTargetLocalIDs` does scan the
|
|
1707
|
+
* IfcRelAggregates records, so a windowed caller must have paged them —
|
|
1708
|
+
* `ensureDemandWorklistsAsync_` does, and carries the reasoning.
|
|
1709
|
+
*
|
|
1710
|
+
* @return {object} The unsharded product and rel-aggregates worklists.
|
|
1711
|
+
*/
|
|
1712
|
+
collectDemandCandidates_() {
|
|
1610
1713
|
// Aggregate-target products are extracted ONLY by the
|
|
1611
1714
|
// rel-aggregates pass below (with the relating object's master
|
|
1612
1715
|
// rel-voids), never by the per-product pass: extracting them here
|
|
@@ -1627,28 +1730,51 @@ export class IfcApiProxyIfc {
|
|
|
1627
1730
|
for (const relAggregate of this.model[0].types(IfcRelAggregates)) {
|
|
1628
1731
|
aggregates.push(relAggregate);
|
|
1629
1732
|
}
|
|
1733
|
+
return { products, aggregates };
|
|
1734
|
+
}
|
|
1735
|
+
/**
|
|
1736
|
+
* Install the whole model's worklists — the unsharded case.
|
|
1737
|
+
*
|
|
1738
|
+
* @param products Every extractable product, in index order.
|
|
1739
|
+
* @param aggregates Every rel-aggregates entry, in index order.
|
|
1740
|
+
*/
|
|
1741
|
+
adoptDemandWorklists_(products, aggregates) {
|
|
1742
|
+
this.demandProducts_ = products;
|
|
1743
|
+
this.demandAggregates_ = aggregates;
|
|
1744
|
+
this.demandCursor_ = 0;
|
|
1745
|
+
this.demandAggregatesCursor_ = 0;
|
|
1746
|
+
}
|
|
1747
|
+
/**
|
|
1748
|
+
* Install the worklists narrowed to this instance's shard.
|
|
1749
|
+
*
|
|
1750
|
+
* Keys are passed in rather than computed here because where they come
|
|
1751
|
+
* from is the whole difference between a resident and a windowed source —
|
|
1752
|
+
* one walks the attributes directly, the other pages the walk's closure
|
|
1753
|
+
* first — while the narrowing itself is identical. Separate from
|
|
1754
|
+
* {@link adoptDemandWorklists_} so the keys are required by the signature
|
|
1755
|
+
* rather than by a comment.
|
|
1756
|
+
*
|
|
1757
|
+
* @param products Every extractable product, in index order.
|
|
1758
|
+
* @param aggregates Every rel-aggregates entry, in index order.
|
|
1759
|
+
* @param productKeys Placement keys aligned to `products`.
|
|
1760
|
+
* @param aggregateKeys Placement keys aligned to `aggregates`.
|
|
1761
|
+
*/
|
|
1762
|
+
adoptShardedWorklists_(products, aggregates, productKeys, aggregateKeys) {
|
|
1763
|
+
const shard = this.shard_;
|
|
1630
1764
|
let placed = 0;
|
|
1631
1765
|
let positional = 0;
|
|
1632
|
-
this.demandProducts_ =
|
|
1633
|
-
|
|
1634
|
-
const key = geometryDispatchKey(this.model[0], localID);
|
|
1766
|
+
this.demandProducts_ = products.filter((localID, where) => {
|
|
1767
|
+
const key = productKeys[where];
|
|
1635
1768
|
// A key equal to the product's own local ID means the walk found
|
|
1636
1769
|
// nothing to place by — a product with no representation. That
|
|
1637
1770
|
// costs affinity, not correctness: the key is still a pure
|
|
1638
1771
|
// function of the product, so every worker computes the same one
|
|
1639
1772
|
// and the partition holds; it just stops avoiding duplication.
|
|
1640
|
-
//
|
|
1641
|
-
// Correctness would only break if the SAME product keyed
|
|
1642
|
-
// differently in different workers, which is why windowed sources
|
|
1643
|
-
// are refused outright (checkShardPreconditions_) rather than
|
|
1644
|
-
// merely warned about — an earlier version of this comment had
|
|
1645
|
-
// that wrong.
|
|
1646
1773
|
key === localID ? ++positional : ++placed;
|
|
1647
|
-
return shardOfDispatchKey(key,
|
|
1648
|
-
this.shard_.index;
|
|
1774
|
+
return shardOfDispatchKey(key, shard.count) === shard.index;
|
|
1649
1775
|
});
|
|
1650
|
-
if (
|
|
1651
|
-
Logger.warning(`[shard ${
|
|
1776
|
+
if (positional > placed) {
|
|
1777
|
+
Logger.warning(`[shard ${shard.index}/${shard.count}] ` +
|
|
1652
1778
|
`${positional} of ${positional + placed} products have no ` +
|
|
1653
1779
|
"placement key, so sharding is mostly positional and shared " +
|
|
1654
1780
|
"geometry will be rebuilt per shard.");
|
|
@@ -1664,25 +1790,94 @@ export class IfcApiProxyIfc {
|
|
|
1664
1790
|
// geometry, so getting it wrong leaves placement with almost nothing
|
|
1665
1791
|
// to control (measured on D3D, where that mistake made every strategy
|
|
1666
1792
|
// look identical).
|
|
1667
|
-
this.demandAggregates_ =
|
|
1668
|
-
aggregates : aggregates.filter((relAggregate) => {
|
|
1669
|
-
// Guarded even though sharding now refuses windowed sources:
|
|
1670
|
-
// this getter reads a record synchronously, and geometryDispatchKey's
|
|
1671
|
-
// own try/catch is INSIDE the call, so a throw here would abort
|
|
1672
|
-
// worklist construction for the whole model rather than place one
|
|
1673
|
-
// aggregate by fallback. Depth-in-depth for a total function.
|
|
1674
|
-
let relating;
|
|
1675
|
-
try {
|
|
1676
|
-
relating = relAggregate.RelatingObject?.localID;
|
|
1677
|
-
}
|
|
1678
|
-
catch {
|
|
1679
|
-
relating = void 0;
|
|
1680
|
-
}
|
|
1681
|
-
return shardOfDispatchKey(geometryDispatchKey(this.model[0], relating ?? relAggregate.localID), this.shard_.count) === this.shard_.index;
|
|
1682
|
-
});
|
|
1793
|
+
this.demandAggregates_ = aggregates.filter((relAggregate, where) => shardOfDispatchKey(aggregateKeys[where], shard.count) === shard.index);
|
|
1683
1794
|
this.demandCursor_ = 0;
|
|
1684
1795
|
this.demandAggregatesCursor_ = 0;
|
|
1685
1796
|
}
|
|
1797
|
+
/**
|
|
1798
|
+
* Hand this model the recentre frame to apply, instead of letting it
|
|
1799
|
+
* derive one — the other half of what a worker pool needs.
|
|
1800
|
+
*
|
|
1801
|
+
* `COORDINATE_TO_ORIGIN` normally recentres a model onto an anchor taken
|
|
1802
|
+
* from the FIRST geometry the instance captures. That is fine for one
|
|
1803
|
+
* instance and fatal for N: each worker starts on a different product, so
|
|
1804
|
+
* each derives a different frame, and a model spanning more than one
|
|
1805
|
+
* recentre cell reassembles with its shards offset by whole cells. Nothing
|
|
1806
|
+
* downstream can detect it — the placements are individually plausible.
|
|
1807
|
+
*
|
|
1808
|
+
* So the frame becomes an input. A coordinator derives it once (Share's
|
|
1809
|
+
* parse-time preview channel already does, and
|
|
1810
|
+
* {@link getAppliedCoordination} reports what any instance applied), hands
|
|
1811
|
+
* the same matrix to every worker, and the shards agree by construction
|
|
1812
|
+
* rather than by luck. This is what lifts the COORDINATE_TO_ORIGIN refusal
|
|
1813
|
+
* in {@link setGeometryShard}.
|
|
1814
|
+
*
|
|
1815
|
+
* A supplied frame is FINAL. The adopted-preview path re-derives when the
|
|
1816
|
+
* first durable placement lands outside the large-coordinate budget
|
|
1817
|
+
* (Share#1634); that check is disabled here, because a worker that
|
|
1818
|
+
* re-derived would silently leave the frame its siblings are still using.
|
|
1819
|
+
* A coordinator that wants that validation runs it on its own instance,
|
|
1820
|
+
* before it hands the frame out.
|
|
1821
|
+
*
|
|
1822
|
+
* Deferred models only, like sharding itself: a classic open has already
|
|
1823
|
+
* placed everything by the time this could be called.
|
|
1824
|
+
*
|
|
1825
|
+
* Supply it BEFORE claiming a shard: {@link setGeometryShard} refuses a
|
|
1826
|
+
* COORDINATE_TO_ORIGIN model that has no frame yet, so the reverse order
|
|
1827
|
+
* fails at the claim rather than at the pump.
|
|
1828
|
+
*
|
|
1829
|
+
* @param matrix Column-major mat4 of 16 finite numbers, or undefined to
|
|
1830
|
+
* drop a previously supplied frame and go back to deriving.
|
|
1831
|
+
*/
|
|
1832
|
+
setCoordinationFrame(matrix) {
|
|
1833
|
+
// Same "before the first pump" rule as setGeometryShard, and for a
|
|
1834
|
+
// sharper reason: placements already emitted carry the old frame, and
|
|
1835
|
+
// nothing re-places them, so a late call would leave one model in two
|
|
1836
|
+
// coordinate systems.
|
|
1837
|
+
if (this.demandProducts_ !== void 0) {
|
|
1838
|
+
throw new Error("SetCoordinationFrame must be called before the first " +
|
|
1839
|
+
"ExtractGeometryBatch: placements already emitted carry the frame " +
|
|
1840
|
+
"that was in force when they were captured, and are not re-placed.");
|
|
1841
|
+
}
|
|
1842
|
+
if (!this.deferredMode_) {
|
|
1843
|
+
throw new Error("SetCoordinationFrame requires DEFER_GEOMETRY: a classic open has " +
|
|
1844
|
+
"already placed every product, so a frame supplied now would be " +
|
|
1845
|
+
"accepted and then ignored.");
|
|
1846
|
+
}
|
|
1847
|
+
if (matrix === void 0) {
|
|
1848
|
+
if (this.coordinationSupplied_) {
|
|
1849
|
+
this.demandCoordination_ = void 0;
|
|
1850
|
+
this.coordinationSupplied_ = false;
|
|
1851
|
+
this._isCoordinated = false;
|
|
1852
|
+
}
|
|
1853
|
+
return;
|
|
1854
|
+
}
|
|
1855
|
+
if (matrix.length !== IDENTITY_MAT4.length ||
|
|
1856
|
+
!matrix.every((element) => Number.isFinite(element))) {
|
|
1857
|
+
throw new Error(`invalid coordination frame: expected ${IDENTITY_MAT4.length} ` +
|
|
1858
|
+
`finite numbers (column-major mat4), got ${matrix.length}`);
|
|
1859
|
+
}
|
|
1860
|
+
// A frame this instance DERIVED (or adopted from its own preview) is not
|
|
1861
|
+
// one a coordinator can reconcile with — geometry has already been placed
|
|
1862
|
+
// in it. Overwriting would leave that geometry in the old frame and
|
|
1863
|
+
// everything after it in the new one.
|
|
1864
|
+
if (this.demandCoordination_ !== void 0 && !this.coordinationSupplied_) {
|
|
1865
|
+
throw new Error("this model has already adopted a coordination frame of its own " +
|
|
1866
|
+
"(a preview channel, or a first durable capture), so it cannot be " +
|
|
1867
|
+
"given another: geometry placed in the old frame is not re-placed.");
|
|
1868
|
+
}
|
|
1869
|
+
// Copied, not retained: a coordinator handing one array to N instances
|
|
1870
|
+
// and mutating it between calls would otherwise leave every instance
|
|
1871
|
+
// pointing at the last frame written. Same aliasing class as the shard
|
|
1872
|
+
// descriptor below.
|
|
1873
|
+
this.demandCoordination_ = [...matrix];
|
|
1874
|
+
this.coordinationSupplied_ = true;
|
|
1875
|
+
// Stops the capture path from deriving over the top of it (see
|
|
1876
|
+
// streamNewMeshes_), and stops the adopted-preview revalidation from
|
|
1877
|
+
// replacing it.
|
|
1878
|
+
this._isCoordinated = true;
|
|
1879
|
+
this.demandCoordinationFromPreview_ = false;
|
|
1880
|
+
}
|
|
1686
1881
|
/**
|
|
1687
1882
|
* Extract only one shard of this model's geometry — the across-product
|
|
1688
1883
|
* parallelism seam.
|
|
@@ -1782,29 +1977,22 @@ export class IfcApiProxyIfc {
|
|
|
1782
1977
|
"preview payloads.");
|
|
1783
1978
|
}
|
|
1784
1979
|
// Already-adopted frame, not just the current flag — see
|
|
1785
|
-
// checkShardPreconditions_, which carries the reasoning.
|
|
1786
|
-
|
|
1980
|
+
// checkShardPreconditions_, which carries the reasoning. A SUPPLIED
|
|
1981
|
+
// frame is the exception both refusals exist to allow: it is the same
|
|
1982
|
+
// matrix in every worker, so there is nothing left to disagree about.
|
|
1983
|
+
if (this.demandCoordination_ !== void 0 && !this.coordinationSupplied_) {
|
|
1787
1984
|
throw new Error("SetGeometryShard cannot be used on a model that has already " +
|
|
1788
1985
|
"adopted a recentre frame (for example via ON_PREVIEW_MESH): the " +
|
|
1789
1986
|
"frame is derived from whichever geometry this instance saw " +
|
|
1790
1987
|
"first, so workers would not share it.");
|
|
1791
1988
|
}
|
|
1792
|
-
if (this.settings?.COORDINATE_TO_ORIGIN === true) {
|
|
1793
|
-
throw new Error("SetGeometryShard cannot be combined with COORDINATE_TO_ORIGIN
|
|
1794
|
-
"
|
|
1795
|
-
"
|
|
1796
|
-
"
|
|
1797
|
-
"
|
|
1798
|
-
|
|
1799
|
-
// Refused at claim time as well as at every pump (see
|
|
1800
|
-
// checkShardPreconditions_, which carries the reasoning): whether the
|
|
1801
|
-
// dispatch key resolves depends on which pages are resident in this
|
|
1802
|
-
// worker, so workers disagree and the partition stops being a partition.
|
|
1803
|
-
if (this.model[0].isSourceExternal) {
|
|
1804
|
-
throw new Error("SetGeometryShard cannot be used with a windowed external " +
|
|
1805
|
-
"source: the dispatch key walks attribute records, whose " +
|
|
1806
|
-
"residency differs per worker, so workers would disagree about " +
|
|
1807
|
-
"which products they own.");
|
|
1989
|
+
if (this.settings?.COORDINATE_TO_ORIGIN === true && !this.coordinationSupplied_) {
|
|
1990
|
+
throw new Error("SetGeometryShard cannot be combined with COORDINATE_TO_ORIGIN " +
|
|
1991
|
+
"unless a coordination frame was supplied: each shard would " +
|
|
1992
|
+
"otherwise derive its own recentre anchor from whichever product " +
|
|
1993
|
+
"it happens to extract first, so merged output can be shifted " +
|
|
1994
|
+
"between shards. Call SetCoordinationFrame with one frame for the " +
|
|
1995
|
+
"whole pool, or open without COORDINATE_TO_ORIGIN.");
|
|
1808
1996
|
}
|
|
1809
1997
|
// Snapshotted, not retained. A coordinator configuring several engine
|
|
1810
1998
|
// instances from one descriptor object — mutating `index` between calls
|
|
@@ -1851,12 +2039,19 @@ export class IfcApiProxyIfc {
|
|
|
1851
2039
|
* it is the third such window found in this review; patching each one as
|
|
1852
2040
|
* it turns up is losing strategy.
|
|
1853
2041
|
*
|
|
1854
|
-
* So a sharded model never
|
|
1855
|
-
* instant
|
|
1856
|
-
*
|
|
1857
|
-
*
|
|
2042
|
+
* So a sharded model never DERIVES a frame, whatever the flag says at the
|
|
2043
|
+
* instant one would be derived. The refusals still fire first in every
|
|
2044
|
+
* observable case, which is what a caller should see; this is what makes a
|
|
2045
|
+
* missed one merely refused-late rather than silently wrong.
|
|
1858
2046
|
*
|
|
1859
|
-
*
|
|
2047
|
+
* Deriving is all this governs. A frame handed in by
|
|
2048
|
+
* {@link setCoordinationFrame} is applied by the capture path
|
|
2049
|
+
* unconditionally — that is the point of supplying one — and this staying
|
|
2050
|
+
* false for a sharded model is exactly what keeps a worker from deriving
|
|
2051
|
+
* over the top of it.
|
|
2052
|
+
*
|
|
2053
|
+
* @return {boolean} Whether to derive a recentre frame from this
|
|
2054
|
+
* instance's first geometry.
|
|
1860
2055
|
*/
|
|
1861
2056
|
recentreEnabled_() {
|
|
1862
2057
|
return this.shard_ === void 0 && this.settings?.COORDINATE_TO_ORIGIN === true;
|
|
@@ -1881,42 +2076,18 @@ export class IfcApiProxyIfc {
|
|
|
1881
2076
|
"be sharded: the preview runs during open, before a shard " +
|
|
1882
2077
|
"exists, so its payloads are not partitioned.");
|
|
1883
2078
|
}
|
|
1884
|
-
if (this.demandCoordination_ !== void 0) {
|
|
2079
|
+
if (this.demandCoordination_ !== void 0 && !this.coordinationSupplied_) {
|
|
1885
2080
|
throw new Error("a recentre frame has already been adopted on this model, so it " +
|
|
1886
2081
|
"cannot be sharded: the frame came from whichever geometry this " +
|
|
1887
2082
|
"instance saw first, and other workers will have adopted their " +
|
|
1888
2083
|
"own.");
|
|
1889
2084
|
}
|
|
1890
|
-
if (this.settings?.COORDINATE_TO_ORIGIN === true) {
|
|
1891
|
-
throw new Error("COORDINATE_TO_ORIGIN was enabled on a sharded model
|
|
1892
|
-
"
|
|
1893
|
-
"
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
// that every worker computes the SAME key for a product. geometryDispatchKey
|
|
1897
|
-
// walks attribute records, and on a windowed source whether that walk
|
|
1898
|
-
// succeeds depends on which pages happen to be resident in THIS worker —
|
|
1899
|
-
// so one worker returns the mapped-source key while another catches the
|
|
1900
|
-
// non-resident read and falls back to the product's local ID.
|
|
1901
|
-
//
|
|
1902
|
-
// That is not weaker affinity, it is a wrong partition. Two workers
|
|
1903
|
-
// disagreeing about a product's key can have both modulo results select
|
|
1904
|
-
// it (extracted twice) or neither (dropped silently). The union check in
|
|
1905
|
-
// m3_worker_pool.mjs cannot see it either, because it runs on resident
|
|
1906
|
-
// buffers where the walk always succeeds.
|
|
1907
|
-
//
|
|
1908
|
-
// Making this work needs a key that does not depend on residency —
|
|
1909
|
-
// derived from the reference columns, or from a closure paged
|
|
1910
|
-
// deterministically before the split. Until then it is refused: workers
|
|
1911
|
-
// on a windowed store were already out of scope for this change (they
|
|
1912
|
-
// need the OPFS-backed source, not a resident buffer per worker), so
|
|
1913
|
-
// this closes a path that has no caller rather than removing a feature.
|
|
1914
|
-
if (this.model[0].isSourceExternal) {
|
|
1915
|
-
throw new Error("a geometry shard cannot be claimed on a windowed external " +
|
|
1916
|
-
"source: the dispatch key walks attribute records, so whether it " +
|
|
1917
|
-
"resolves depends on which pages are resident in each worker, and " +
|
|
1918
|
-
"workers that disagree about a product would extract it twice or " +
|
|
1919
|
-
"not at all.");
|
|
2085
|
+
if (this.settings?.COORDINATE_TO_ORIGIN === true && !this.coordinationSupplied_) {
|
|
2086
|
+
throw new Error("COORDINATE_TO_ORIGIN was enabled on a sharded model with no " +
|
|
2087
|
+
"supplied coordination frame: each shard would derive its own " +
|
|
2088
|
+
"recentre anchor from whichever product it extracts first, so " +
|
|
2089
|
+
"merged output can be shifted between shards. Supply one frame " +
|
|
2090
|
+
"for the pool with SetCoordinationFrame.");
|
|
1920
2091
|
}
|
|
1921
2092
|
}
|
|
1922
2093
|
pumpGeometryBatch_(batchSize, meshCallback) {
|
|
@@ -28,6 +28,40 @@ export declare class StorePreviewChannel {
|
|
|
28
28
|
coordinationMatrix?: number[];
|
|
29
29
|
private stopped_;
|
|
30
30
|
private emittedUnits_;
|
|
31
|
+
/**
|
|
32
|
+
* Preview meshes handed to `onMesh_`. Tracked apart from
|
|
33
|
+
* {@link emittedUnits_} because the two genuinely diverge: a product can
|
|
34
|
+
* place several meshes, and a product with no Representation extracts
|
|
35
|
+
* cleanly while placing none — `extractProductGeometry` returns early on
|
|
36
|
+
* a null Representation and `extractProductGeometryByLocalID` still
|
|
37
|
+
* answers true. Reporting only units let the load log say "no mesh, 20
|
|
38
|
+
* emitted" about one load (codex round 2 on #543).
|
|
39
|
+
*/
|
|
40
|
+
private meshesEmitted_;
|
|
41
|
+
private deferredUnits_;
|
|
42
|
+
private deferredOnPlacement_;
|
|
43
|
+
/**
|
|
44
|
+
* Products that deferred under the CURRENT generation, promoted to
|
|
45
|
+
* {@link retryQueue_} when the next one is built. Held separately so a
|
|
46
|
+
* retry always runs against a longer index than the attempt that failed —
|
|
47
|
+
* re-running one inside its own generation would fail identically and
|
|
48
|
+
* spin.
|
|
49
|
+
*/
|
|
50
|
+
private deferredForRetry_;
|
|
51
|
+
private retryQueue_;
|
|
52
|
+
private retryCursor_;
|
|
53
|
+
private retriedUnits_;
|
|
54
|
+
/**
|
|
55
|
+
* Ms from channel construction to the first PLACED PRODUCT payload —
|
|
56
|
+
* time-to-first-pixel, the number conway#542 exists to move. The spatial
|
|
57
|
+
* plates are deliberately not counted: they are the building's wireframe
|
|
58
|
+
* skeleton, they go out on a different schedule (see
|
|
59
|
+
* {@link maybeEmitEarlySpatialPlates_}), and counting them would report
|
|
60
|
+
* first pixels for a load whose products are all still deferring.
|
|
61
|
+
* {@link earlyPlateCount} is where those are visible.
|
|
62
|
+
*/
|
|
63
|
+
private firstMeshMs_?;
|
|
64
|
+
private readonly startedMs_;
|
|
31
65
|
private emittedBytes_;
|
|
32
66
|
private unitOrdinal_;
|
|
33
67
|
private lastInlineTick_;
|
|
@@ -61,6 +95,35 @@ export declare class StorePreviewChannel {
|
|
|
61
95
|
constructor(store_: StepExternalByteStore, sink_: ColumnarIndexSink<number>, conwaywasm_: ConwayGeometry, coordinateToOrigin_: boolean, onMesh_: (mesh: PreviewMeshPayload) => void, maxUnits?: number, maxBytes?: number, firstGenerationMinRecords?: number);
|
|
62
96
|
get capped(): boolean;
|
|
63
97
|
get emittedUnits(): number;
|
|
98
|
+
/**
|
|
99
|
+
* What the preview delivered, and how fast.
|
|
100
|
+
*
|
|
101
|
+
* `firstMeshMs` is time-to-first-pixel measured from channel
|
|
102
|
+
* construction, undefined when no placed product was ever emitted.
|
|
103
|
+
* `meshes` counts what actually reached the consumer, `emitted` counts
|
|
104
|
+
* the products extraction accepted — they are not the same number in
|
|
105
|
+
* either direction, so the line reports both.
|
|
106
|
+
* `deferred` counts products a tick attempted and could not extract;
|
|
107
|
+
* `deferredOnPlacement` is the subset still waiting on a placement chain
|
|
108
|
+
* the prefix does not hold; `retried` counts attempts that were second
|
|
109
|
+
* looks at an earlier deferral rather than first attempts, so a channel
|
|
110
|
+
* whose retry path has silently stopped firing is visible rather than
|
|
111
|
+
* merely slower. A first load that shows nothing is a very
|
|
112
|
+
* different problem depending on that ratio — near 1.0 is the file-layout
|
|
113
|
+
* case a sharded parse attacks, anything else is not — and inferring it
|
|
114
|
+
* from a code comment is what conway#542 exists to stop.
|
|
115
|
+
*
|
|
116
|
+
* @return {object} `{firstMeshMs, meshes, emitted, deferred,
|
|
117
|
+
* deferredOnPlacement, retried}`
|
|
118
|
+
*/
|
|
119
|
+
get previewYield(): {
|
|
120
|
+
firstMeshMs?: number;
|
|
121
|
+
meshes: number;
|
|
122
|
+
emitted: number;
|
|
123
|
+
deferred: number;
|
|
124
|
+
deferredOnPlacement: number;
|
|
125
|
+
retried: number;
|
|
126
|
+
};
|
|
64
127
|
/** Products the current generation would run (test seam). */
|
|
65
128
|
get productCount(): number;
|
|
66
129
|
/**
|
|
@@ -120,6 +183,20 @@ export declare class StorePreviewChannel {
|
|
|
120
183
|
* @return {Promise<boolean>} True when a product was attempted.
|
|
121
184
|
*/
|
|
122
185
|
private tickOnce_;
|
|
186
|
+
/**
|
|
187
|
+
* Whether either queue still has a product to attempt.
|
|
188
|
+
*
|
|
189
|
+
* Static and fully parameterised so the two callers — the tick and the
|
|
190
|
+
* generation gate — cannot drift apart on what "pending" means, which is
|
|
191
|
+
* the bug that would silently strand the retry queue.
|
|
192
|
+
*
|
|
193
|
+
* @param retryCursor How far into the retry queue this generation is.
|
|
194
|
+
* @param retryLength The retry queue's length.
|
|
195
|
+
* @param ordinal The forward cursor into the generation's products.
|
|
196
|
+
* @param productCount That generation's product count.
|
|
197
|
+
* @return {boolean} True when something is left to attempt.
|
|
198
|
+
*/
|
|
199
|
+
private static hasPending_;
|
|
123
200
|
/**
|
|
124
201
|
* @return {Promise<boolean>} True when a generation with pending units exists.
|
|
125
202
|
*/
|
|
@@ -181,6 +258,8 @@ export declare class StorePreviewChannel {
|
|
|
181
258
|
/**
|
|
182
259
|
* Copy newly extracted instances out of wasm — same placement math
|
|
183
260
|
* as {@link StreamedPreviewChannel}.
|
|
261
|
+
*
|
|
262
|
+
* @return {number} Meshes emitted by this pass.
|
|
184
263
|
*/
|
|
185
264
|
private captureNewInstances_;
|
|
186
265
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store_preview_channel.d.ts","sourceRoot":"","sources":["../../../../src/compat/web-ifc/store_preview_channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAY5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAA;AAGrE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAA;
|
|
1
|
+
{"version":3,"file":"store_preview_channel.d.ts","sourceRoot":"","sources":["../../../../src/compat/web-ifc/store_preview_channel.ts"],"names":[],"mappings":"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"}
|