@galacean/engine-ui 2.0.0-alpha.32 → 2.0.0-alpha.34
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/dist/browser.js +194 -32
- package/dist/browser.js.map +1 -1
- package/dist/browser.min.js +1 -1
- package/dist/browser.min.js.map +1 -1
- package/dist/main.js +194 -32
- package/dist/main.js.map +1 -1
- package/dist/module.js +195 -33
- package/dist/module.js.map +1 -1
- package/package.json +3 -3
- package/types/component/UIBatchSorter.d.ts +1 -0
- package/types/component/UICanvas.d.ts +1 -0
- package/types/component/UIRenderer.d.ts +3 -3
package/dist/browser.js
CHANGED
|
@@ -419,6 +419,155 @@
|
|
|
419
419
|
return Utils;
|
|
420
420
|
}();
|
|
421
421
|
|
|
422
|
+
/**
|
|
423
|
+
* @internal
|
|
424
|
+
* Visual-layering driven UI batching: solve visual correctness first, batching falls out for free.
|
|
425
|
+
*
|
|
426
|
+
* Think of it as stacking elements onto numbered shelves (depths):
|
|
427
|
+
* - Non-overlapping elements share shelf 0 — free to cluster regardless of material.
|
|
428
|
+
* - Overlapping + batch-compatible — share the same shelf, still cluster.
|
|
429
|
+
* - Overlapping + batch-incompatible — bumped one shelf higher (drawn on top).
|
|
430
|
+
*
|
|
431
|
+
* `depth` is a global tag, not a spatial group: distant elements at the same visual layer
|
|
432
|
+
* share the same depth and get clustered together. Once depth is assigned, sort by
|
|
433
|
+
* (depth, material, texture, hierarchyIndex) — visual order is locked, batching is the
|
|
434
|
+
* by-product of materials clustering within each depth band.
|
|
435
|
+
*
|
|
436
|
+
* Overlap detection is accelerated by a 10×10 spatial grid. Grid origin and cell size
|
|
437
|
+
* are derived from the union AABB of input elements (the bounds transform itself is the
|
|
438
|
+
* work the loop must do anyway, so the union accumulation just piggybacks). This stays
|
|
439
|
+
* correct across SS/WS canvases, any pivot, and any element distribution — no dependency
|
|
440
|
+
* on canvas configuration.
|
|
441
|
+
*/ var UIBatchSorter = /*#__PURE__*/ function() {
|
|
442
|
+
function UIBatchSorter() {}
|
|
443
|
+
/**
|
|
444
|
+
* Reorders `elements` in place for optimal batching.
|
|
445
|
+
* @param elements - in hierarchy order (depth-first traversal); mutated in place
|
|
446
|
+
* @param canvasWorldMatrix - reduces world bounds to canvas-local for precise overlap
|
|
447
|
+
*/ UIBatchSorter.sort = function sort(elements, canvasWorldMatrix) {
|
|
448
|
+
var count = elements.length;
|
|
449
|
+
if (count <= 1) return;
|
|
450
|
+
var gridDim = this._gridDim;
|
|
451
|
+
var entries = this._entries;
|
|
452
|
+
var grid = this._grid;
|
|
453
|
+
var dirtyCells = this._dirtyCells;
|
|
454
|
+
var localBoundsPool = this._localBoundsPool;
|
|
455
|
+
var worldToLocal = this._worldToLocal;
|
|
456
|
+
engine.Matrix.invert(canvasWorldMatrix, worldToLocal);
|
|
457
|
+
while(entries.length < count)entries.push(new SortEntry());
|
|
458
|
+
// Allocate grid once, reuse forever (each cell must be a real [], not a sparse hole)
|
|
459
|
+
if (grid.length < gridDim) {
|
|
460
|
+
while(grid.length < gridDim)grid.push([]);
|
|
461
|
+
for(var i = 0; i < gridDim; i++){
|
|
462
|
+
var row = grid[i];
|
|
463
|
+
while(row.length < gridDim)row.push([]);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
// Clear only previously-used cells (no full sweep)
|
|
467
|
+
for(var k = 0, n = dirtyCells.length; k < n; k += 2){
|
|
468
|
+
grid[dirtyCells[k]][dirtyCells[k + 1]].length = 0;
|
|
469
|
+
}
|
|
470
|
+
dirtyCells.length = 0;
|
|
471
|
+
// Pass 1: transform world bounds to canvas-local + accumulate the union AABB.
|
|
472
|
+
// Transform is already required to do precise overlap, so the union accumulation
|
|
473
|
+
// is essentially free — just a few minmax compares riding on the existing pass
|
|
474
|
+
var unionMinX = Infinity;
|
|
475
|
+
var unionMinY = Infinity;
|
|
476
|
+
var unionMaxX = -Infinity;
|
|
477
|
+
var unionMaxY = -Infinity;
|
|
478
|
+
for(var i1 = 0; i1 < count; i1++){
|
|
479
|
+
var _localBoundsPool, _i;
|
|
480
|
+
var localBounds = (_localBoundsPool = localBoundsPool)[_i = i1] || (_localBoundsPool[_i] = new engine.BoundingBox());
|
|
481
|
+
engine.BoundingBox.transform(elements[i1].component.bounds, worldToLocal, localBounds);
|
|
482
|
+
var min = localBounds.min, max = localBounds.max;
|
|
483
|
+
if (min.x < unionMinX) unionMinX = min.x;
|
|
484
|
+
if (min.y < unionMinY) unionMinY = min.y;
|
|
485
|
+
if (max.x > unionMaxX) unionMaxX = max.x;
|
|
486
|
+
if (max.y > unionMaxY) unionMaxY = max.y;
|
|
487
|
+
}
|
|
488
|
+
// Guard against a degenerate union (all elements collapsed onto a single point /
|
|
489
|
+
// axis); the grid then collapses to cell 0 which is semantically correct
|
|
490
|
+
var cellSizeX = Math.max(1e-6, unionMaxX - unionMinX) / gridDim;
|
|
491
|
+
var cellSizeY = Math.max(1e-6, unionMaxY - unionMinY) / gridDim;
|
|
492
|
+
var maxCell = gridDim - 1;
|
|
493
|
+
// Pass 2: register each element into grid cells and compute its depth
|
|
494
|
+
for(var i2 = 0; i2 < count; i2++){
|
|
495
|
+
var element = elements[i2];
|
|
496
|
+
var localBounds1 = localBoundsPool[i2];
|
|
497
|
+
var materialId = element.material.instanceId;
|
|
498
|
+
var textureId = element.texture ? element.texture.instanceId : 0;
|
|
499
|
+
// Floor + clamp guards floating-point edge cases (a `max` exactly on the union
|
|
500
|
+
// boundary would otherwise produce `gridDim`)
|
|
501
|
+
var minCellX = Math.min(maxCell, Math.max(0, Math.floor((localBounds1.min.x - unionMinX) / cellSizeX)));
|
|
502
|
+
var maxCellX = Math.min(maxCell, Math.max(0, Math.floor((localBounds1.max.x - unionMinX) / cellSizeX)));
|
|
503
|
+
var minCellY = Math.min(maxCell, Math.max(0, Math.floor((localBounds1.min.y - unionMinY) / cellSizeY)));
|
|
504
|
+
var maxCellY = Math.min(maxCell, Math.max(0, Math.floor((localBounds1.max.y - unionMinY) / cellSizeY)));
|
|
505
|
+
// Find the deepest overlapping prior, and whether that shelf has any incompatible occupant
|
|
506
|
+
var maxDepth = -1;
|
|
507
|
+
var maxDepthIncompatible = false;
|
|
508
|
+
for(var cellY = minCellY; cellY <= maxCellY; cellY++){
|
|
509
|
+
for(var cellX = minCellX; cellX <= maxCellX; cellX++){
|
|
510
|
+
var cell = grid[cellX][cellY];
|
|
511
|
+
for(var j = 0, m = cell.length; j < m; j++){
|
|
512
|
+
var other = cell[j];
|
|
513
|
+
if (!UIBatchSorter._rectOverlap(localBounds1, other.bounds)) continue;
|
|
514
|
+
var otherDepth = other.depth;
|
|
515
|
+
var otherIncompatible = other.materialId !== materialId || other.textureId !== textureId;
|
|
516
|
+
if (otherDepth > maxDepth) {
|
|
517
|
+
maxDepth = otherDepth;
|
|
518
|
+
maxDepthIncompatible = otherIncompatible;
|
|
519
|
+
} else if (otherDepth === maxDepth && otherIncompatible) {
|
|
520
|
+
maxDepthIncompatible = true;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
var entry = entries[i2];
|
|
526
|
+
entry.element = element;
|
|
527
|
+
entry.hierarchyIndex = i2;
|
|
528
|
+
// Share the deepest shelf if compatible; bump up one if blocked by an incompatible occupant
|
|
529
|
+
entry.depth = maxDepth < 0 ? 0 : maxDepth + (maxDepthIncompatible ? 1 : 0);
|
|
530
|
+
entry.materialId = materialId;
|
|
531
|
+
entry.textureId = textureId;
|
|
532
|
+
entry.bounds = localBounds1;
|
|
533
|
+
for(var cellY1 = minCellY; cellY1 <= maxCellY; cellY1++){
|
|
534
|
+
for(var cellX1 = minCellX; cellX1 <= maxCellX; cellX1++){
|
|
535
|
+
var cell1 = grid[cellX1][cellY1];
|
|
536
|
+
if (cell1.length === 0) dirtyCells.push(cellX1, cellY1);
|
|
537
|
+
cell1.push(entry);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
// @ts-ignore — Utils._quickSort is @internal
|
|
542
|
+
engine.Utils._quickSort(entries, 0, count, UIBatchSorter._compareEntries);
|
|
543
|
+
for(var i3 = 0; i3 < count; i3++)elements[i3] = entries[i3].element;
|
|
544
|
+
};
|
|
545
|
+
UIBatchSorter._rectOverlap = function _rectOverlap(a, b) {
|
|
546
|
+
return a.min.x < b.max.x && a.max.x > b.min.x && a.min.y < b.max.y && a.max.y > b.min.y;
|
|
547
|
+
};
|
|
548
|
+
UIBatchSorter._compareEntries = function _compareEntries(a, b) {
|
|
549
|
+
return a.depth - b.depth || a.materialId - b.materialId || a.textureId - b.textureId || a.hierarchyIndex - b.hierarchyIndex;
|
|
550
|
+
};
|
|
551
|
+
return UIBatchSorter;
|
|
552
|
+
}();
|
|
553
|
+
// Spatial-hash is fastest when cell size ≈ typical element size (one element per cell).
|
|
554
|
+
// UI elements typically span ~10% of the canvas → 10×10 grid.
|
|
555
|
+
UIBatchSorter._gridDim = 10;
|
|
556
|
+
UIBatchSorter._entries = new Array();
|
|
557
|
+
UIBatchSorter._grid = [];
|
|
558
|
+
// Interleaved (x, y) cell coords touched in the previous frame
|
|
559
|
+
UIBatchSorter._dirtyCells = new Array();
|
|
560
|
+
UIBatchSorter._localBoundsPool = new Array();
|
|
561
|
+
UIBatchSorter._worldToLocal = new engine.Matrix();
|
|
562
|
+
// materialId/textureId/bounds are flattened from RenderElement to avoid
|
|
563
|
+
// multi-hop property access in the hot inner loop and the sort comparator.
|
|
564
|
+
var SortEntry = function SortEntry() {
|
|
565
|
+
this.hierarchyIndex = 0;
|
|
566
|
+
this.depth = 0;
|
|
567
|
+
this.materialId = 0;
|
|
568
|
+
this.textureId = 0;
|
|
569
|
+
};
|
|
570
|
+
|
|
422
571
|
/**
|
|
423
572
|
* Render mode for ui canvas.
|
|
424
573
|
*/ var CanvasRenderMode = /*#__PURE__*/ function(CanvasRenderMode) {
|
|
@@ -897,17 +1046,17 @@
|
|
|
897
1046
|
}
|
|
898
1047
|
var _proto = UIRenderer.prototype;
|
|
899
1048
|
// @ts-ignore
|
|
900
|
-
_proto._canBatch = function _canBatch(
|
|
901
|
-
return engine.
|
|
1049
|
+
_proto._canBatch = function _canBatch(preElement, curElement) {
|
|
1050
|
+
return engine.VertexMergeBatcher.canBatchSprite(preElement, curElement);
|
|
902
1051
|
};
|
|
903
1052
|
// @ts-ignore
|
|
904
|
-
_proto._batch = function _batch(
|
|
905
|
-
engine.
|
|
1053
|
+
_proto._batch = function _batch(preElement, curElement) {
|
|
1054
|
+
engine.VertexMergeBatcher.batch(preElement, curElement);
|
|
906
1055
|
};
|
|
907
1056
|
// @ts-ignore
|
|
908
|
-
_proto._updateTransformShaderData = function _updateTransformShaderData(context, onlyMVP
|
|
1057
|
+
_proto._updateTransformShaderData = function _updateTransformShaderData(context, onlyMVP) {
|
|
909
1058
|
// @ts-ignore
|
|
910
|
-
|
|
1059
|
+
this._updateWorldSpaceTransformShaderData(context, onlyMVP);
|
|
911
1060
|
};
|
|
912
1061
|
// @ts-ignore
|
|
913
1062
|
_proto._prepareRender = function _prepareRender(context) {
|
|
@@ -1366,7 +1515,7 @@
|
|
|
1366
1515
|
_inherits(UICanvas, Component);
|
|
1367
1516
|
function UICanvas(entity) {
|
|
1368
1517
|
var _this;
|
|
1369
|
-
_this = Component.call(this, entity) || this, /** @internal */ _this._canvasIndex = -1, /** @internal */ _this._indexInRootCanvas = -1, /** @internal */ _this._isRootCanvasDirty = false, /** @internal */ _this._rootCanvasListeningEntities = [], /** @internal */ _this._isRootCanvas = false, /** @internal */ _this._sortDistance = 0, /** @internal */ _this._orderedRenderers = [], /** @internal */ _this._realRenderMode = 4, /** @internal */ _this._disorderedElements = new engine.DisorderedArray(), _this._renderMode = CanvasRenderMode.WorldSpace, _this._resolutionAdaptationMode = ResolutionAdaptationMode.HeightAdaptation, _this._sortOrder = 0, _this._distance = 10, _this._referenceResolution = new engine.Vector2(800, 600), _this._referenceResolutionPerUnit = 100, _this._hierarchyVersion = -1, _this._center = new engine.Vector3();
|
|
1518
|
+
_this = Component.call(this, entity) || this, /** @internal */ _this._canvasIndex = -1, /** @internal */ _this._indexInRootCanvas = -1, /** @internal */ _this._isRootCanvasDirty = false, /** @internal */ _this._rootCanvasListeningEntities = [], /** @internal */ _this._isRootCanvas = false, /** @internal */ _this._renderElements = [], /** @internal */ _this._batchedRenderElements = [], /** @internal */ _this._sortDistance = 0, /** @internal */ _this._orderedRenderers = [], /** @internal */ _this._realRenderMode = 4, /** @internal */ _this._disorderedElements = new engine.DisorderedArray(), _this._renderMode = CanvasRenderMode.WorldSpace, _this._resolutionAdaptationMode = ResolutionAdaptationMode.HeightAdaptation, _this._sortOrder = 0, _this._distance = 10, _this._referenceResolution = new engine.Vector2(800, 600), _this._referenceResolutionPerUnit = 100, _this._hierarchyVersion = -1, _this._center = new engine.Vector3();
|
|
1370
1519
|
_this._onCanvasSizeListener = _this._onCanvasSizeListener.bind(_this);
|
|
1371
1520
|
_this._onCameraModifyListener = _this._onCameraModifyListener.bind(_this);
|
|
1372
1521
|
_this._onCameraTransformListener = _this._onCameraTransformListener.bind(_this);
|
|
@@ -1423,11 +1572,10 @@
|
|
|
1423
1572
|
var _this = this, engine = _this.engine, mode = _this._realRenderMode;
|
|
1424
1573
|
var _context_camera = context.camera, enableFrustumCulling = _context_camera.enableFrustumCulling, cullingMask = _context_camera.cullingMask, frustum = _context_camera._frustum;
|
|
1425
1574
|
var frameCount = engine.time.frameCount;
|
|
1426
|
-
|
|
1427
|
-
|
|
1575
|
+
var renderElements = this._renderElements;
|
|
1576
|
+
renderElements.length = 0;
|
|
1428
1577
|
var virtualCamera = context.virtualCamera;
|
|
1429
1578
|
this._updateSortDistance(virtualCamera.isOrthographic, virtualCamera.position, virtualCamera.forward);
|
|
1430
|
-
renderElement.set(this.sortOrder, this._sortDistance);
|
|
1431
1579
|
var _engine_canvas = engine.canvas, width = _engine_canvas.width, height = _engine_canvas.height;
|
|
1432
1580
|
var renderers = this._getRenderers();
|
|
1433
1581
|
for(var i = 0, n = renderers.length; i < n; i++){
|
|
@@ -1456,6 +1604,13 @@
|
|
|
1456
1604
|
renderer._prepareRender(context);
|
|
1457
1605
|
renderer._renderFrameCount = frameCount;
|
|
1458
1606
|
}
|
|
1607
|
+
var batchedRenderElements = this._batchedRenderElements;
|
|
1608
|
+
batchedRenderElements.length = 0;
|
|
1609
|
+
UIBatchSorter.sort(renderElements, this.entity.transform.worldMatrix);
|
|
1610
|
+
engine._batcherManager.batch(renderElements, batchedRenderElements);
|
|
1611
|
+
for(var i1 = 0, n1 = batchedRenderElements.length; i1 < n1; i1++){
|
|
1612
|
+
batchedRenderElements[i1].subDistancePriority = i1;
|
|
1613
|
+
}
|
|
1459
1614
|
};
|
|
1460
1615
|
/**
|
|
1461
1616
|
* @internal
|
|
@@ -1493,6 +1648,11 @@
|
|
|
1493
1648
|
this._setIsRootCanvas(false);
|
|
1494
1649
|
Utils.cleanRootCanvas(this);
|
|
1495
1650
|
};
|
|
1651
|
+
// @ts-ignore
|
|
1652
|
+
_proto._onDisable = function _onDisable() {
|
|
1653
|
+
this._renderElements.length = 0;
|
|
1654
|
+
this._batchedRenderElements.length = 0;
|
|
1655
|
+
};
|
|
1496
1656
|
/**
|
|
1497
1657
|
* @internal
|
|
1498
1658
|
*/ _proto._rootCanvasListener = function _rootCanvasListener(flag, param) {
|
|
@@ -1933,7 +2093,10 @@
|
|
|
1933
2093
|
], exports.UICanvas.prototype, "_isRootCanvas", void 0);
|
|
1934
2094
|
__decorate([
|
|
1935
2095
|
engine.ignoreClone
|
|
1936
|
-
], exports.UICanvas.prototype, "
|
|
2096
|
+
], exports.UICanvas.prototype, "_renderElements", void 0);
|
|
2097
|
+
__decorate([
|
|
2098
|
+
engine.ignoreClone
|
|
2099
|
+
], exports.UICanvas.prototype, "_batchedRenderElements", void 0);
|
|
1937
2100
|
__decorate([
|
|
1938
2101
|
engine.ignoreClone
|
|
1939
2102
|
], exports.UICanvas.prototype, "_sortDistance", void 0);
|
|
@@ -2124,16 +2287,14 @@
|
|
|
2124
2287
|
dirtyUpdateFlag &= ~UIRendererUpdateFlags.Color;
|
|
2125
2288
|
}
|
|
2126
2289
|
this._dirtyUpdateFlag = dirtyUpdateFlag;
|
|
2127
|
-
// Init sub render element.
|
|
2128
2290
|
var engine$1 = context.camera.engine;
|
|
2129
|
-
var
|
|
2291
|
+
var renderElement = engine$1._renderElementPool.get();
|
|
2130
2292
|
var subChunk = this._subChunk;
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
canvas._renderElement.addSubRenderElement(subRenderElement);
|
|
2293
|
+
renderElement.set(this, material, subChunk.chunk.primitive, subChunk.subMesh, this.sprite.texture, subChunk);
|
|
2294
|
+
renderElement.subShader = material.shader.subShaders[0];
|
|
2295
|
+
renderElement.priority = canvas.sortOrder;
|
|
2296
|
+
renderElement.distanceForSort = canvas._sortDistance;
|
|
2297
|
+
canvas._renderElements.push(renderElement);
|
|
2137
2298
|
};
|
|
2138
2299
|
_proto._onTransformChanged = function _onTransformChanged(type) {
|
|
2139
2300
|
if (type & UITransformModifyFlags.Size && this._drawMode === engine.SpriteDrawMode.Tiled) {
|
|
@@ -2396,24 +2557,25 @@
|
|
|
2396
2557
|
this._setDirtyFlagFalse(UIRendererUpdateFlags.Color);
|
|
2397
2558
|
}
|
|
2398
2559
|
var engine$1 = context.camera.engine;
|
|
2399
|
-
var
|
|
2560
|
+
var textRenderElementPool = engine$1._textRenderElementPool;
|
|
2400
2561
|
var material = this.getMaterial();
|
|
2401
|
-
var
|
|
2562
|
+
var renderElements = canvas._renderElements;
|
|
2563
|
+
var priority = canvas.sortOrder;
|
|
2564
|
+
var distanceForSort = canvas._sortDistance;
|
|
2402
2565
|
var textChunks = this._textChunks;
|
|
2403
|
-
var
|
|
2566
|
+
var subShader = material.shader.subShaders[0];
|
|
2404
2567
|
for(var i = 0, n = textChunks.length; i < n; ++i){
|
|
2405
2568
|
var // @ts-ignore
|
|
2406
|
-
|
|
2569
|
+
_renderElement;
|
|
2407
2570
|
var _textChunks_i = textChunks[i], subChunk = _textChunks_i.subChunk, texture = _textChunks_i.texture;
|
|
2408
|
-
var
|
|
2409
|
-
|
|
2410
|
-
(
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
renderElement.addSubRenderElement(subRenderElement);
|
|
2571
|
+
var renderElement = textRenderElementPool.get();
|
|
2572
|
+
renderElement.set(this, material, subChunk.chunk.primitive, subChunk.subMesh, texture, subChunk);
|
|
2573
|
+
(_renderElement = renderElement).shaderData || (_renderElement.shaderData = new engine.ShaderData(engine.ShaderDataGroup.RenderElement));
|
|
2574
|
+
renderElement.shaderData.setTexture(Text._textTextureProperty, texture);
|
|
2575
|
+
renderElement.subShader = subShader;
|
|
2576
|
+
renderElement.priority = priority;
|
|
2577
|
+
renderElement.distanceForSort = distanceForSort;
|
|
2578
|
+
renderElements.push(renderElement);
|
|
2417
2579
|
}
|
|
2418
2580
|
};
|
|
2419
2581
|
_proto._resetSubFont = function _resetSubFont() {
|