Skip to content

Commit aed29ca

Browse files
authored
SVGRenderer: Fix performance regression. (#33220)
1 parent f01bb08 commit aed29ca

2 files changed

Lines changed: 24 additions & 63 deletions

File tree

examples/jsm/renderers/Projector.js

Lines changed: 18 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -310,18 +310,18 @@ class Projector {
310310
const v2 = _vertexPool[ b ];
311311
const v3 = _vertexPool[ c ];
312312

313-
// Get homogeneous clip space positions (before perspective divide)
314-
_clipPos1.copy( v1.positionWorld ).applyMatrix4( _viewProjectionMatrix );
315-
_clipPos2.copy( v2.positionWorld ).applyMatrix4( _viewProjectionMatrix );
316-
_clipPos3.copy( v3.positionWorld ).applyMatrix4( _viewProjectionMatrix );
317-
318-
// Check if triangle needs clipping
319-
const nearDist1 = _clipPos1.z + _clipPos1.w;
320-
const nearDist2 = _clipPos2.z + _clipPos2.w;
321-
const nearDist3 = _clipPos3.z + _clipPos3.w;
322-
const farDist1 = - _clipPos1.z + _clipPos1.w;
323-
const farDist2 = - _clipPos2.z + _clipPos2.w;
324-
const farDist3 = - _clipPos3.z + _clipPos3.w;
313+
// Derive near/far clip distances from NDC z and stored clip-space w
314+
// (projectVertex already computed positionScreen = clipPos / w, with w preserved)
315+
const w1 = v1.positionScreen.w;
316+
const w2 = v2.positionScreen.w;
317+
const w3 = v3.positionScreen.w;
318+
319+
const nearDist1 = w1 * ( v1.positionScreen.z + 1 );
320+
const nearDist2 = w2 * ( v2.positionScreen.z + 1 );
321+
const nearDist3 = w3 * ( v3.positionScreen.z + 1 );
322+
const farDist1 = w1 * ( 1 - v1.positionScreen.z );
323+
const farDist2 = w2 * ( 1 - v2.positionScreen.z );
324+
const farDist3 = w3 * ( 1 - v3.positionScreen.z );
325325

326326
// Check if completely outside
327327
if ( ( nearDist1 < 0 && nearDist2 < 0 && nearDist3 < 0 ) ||
@@ -385,7 +385,10 @@ class Projector {
385385

386386
}
387387

388-
// Triangle needs clipping
388+
// Triangle needs clipping - reconstruct clip-space positions from NDC + w
389+
_clipPos1.set( v1.positionScreen.x * w1, v1.positionScreen.y * w1, v1.positionScreen.z * w1, w1 );
390+
_clipPos2.set( v2.positionScreen.x * w2, v2.positionScreen.y * w2, v2.positionScreen.z * w2, w2 );
391+
_clipPos3.set( v3.positionScreen.x * w3, v3.positionScreen.y * w3, v3.positionScreen.z * w3, w3 );
389392
_clipInputVertices[ 0 ] = _clipPos1;
390393
_clipInputVertices[ 1 ] = _clipPos2;
391394
_clipInputVertices[ 2 ] = _clipPos3;
@@ -577,7 +580,7 @@ class Projector {
577580

578581
if ( sortObjects === true ) {
579582

580-
painterSortStable( _renderData.objects, 0, _renderData.objects.length );
583+
_renderData.objects.sort( painterSort );
581584

582585
}
583586

@@ -843,7 +846,7 @@ class Projector {
843846

844847
if ( sortElements === true ) {
845848

846-
painterSortStable( _renderData.elements, 0, _renderData.elements.length );
849+
_renderData.elements.sort( painterSort );
847850

848851
}
849852

@@ -987,29 +990,6 @@ class Projector {
987990

988991
}
989992

990-
function painterSortStable( array, start, length ) {
991-
992-
// A stable insertion sort for sorting render items
993-
// This avoids the GC overhead of Array.prototype.sort()
994-
995-
for ( let i = start + 1; i < start + length; i ++ ) {
996-
997-
const item = array[ i ];
998-
let j = i - 1;
999-
1000-
while ( j >= start && painterSort( array[ j ], item ) > 0 ) {
1001-
1002-
array[ j + 1 ] = array[ j ];
1003-
j --;
1004-
1005-
}
1006-
1007-
array[ j + 1 ] = item;
1008-
1009-
}
1010-
1011-
}
1012-
1013993
// Sutherland-Hodgman triangle clipping in homogeneous clip space
1014994
// Returns count of vertices in clipped polygon (0 if completely clipped, 3+ if partially clipped)
1015995
// Result vertices are in _clipInput array

examples/jsm/renderers/SVGRenderer.js

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -300,29 +300,6 @@ class SVGRenderer {
300300

301301
}
302302

303-
function arraySortStable( array, start, length ) {
304-
305-
// A stable insertion sort for sorting the render list
306-
// This avoids the GC overhead of Array.prototype.sort()
307-
308-
for ( let i = start + 1; i < start + length; i ++ ) {
309-
310-
const item = array[ i ];
311-
let j = i - 1;
312-
313-
while ( j >= start && renderSort( array[ j ], item ) > 0 ) {
314-
315-
array[ j + 1 ] = array[ j ];
316-
j --;
317-
318-
}
319-
320-
array[ j + 1 ] = item;
321-
322-
}
323-
324-
}
325-
326303
/**
327304
* Performs a manual clear with the defined clear color.
328305
*/
@@ -416,9 +393,13 @@ class SVGRenderer {
416393

417394
} );
418395

419-
if ( this.sortElements ) {
396+
_renderListPool.length = _renderListCount;
397+
398+
if ( this.sortElements && _svgObjectCount > 0 ) {
420399

421-
arraySortStable( _renderListPool, 0, _renderListCount );
400+
// Elements are already sorted by the Projector.
401+
// Only re-sort when SVGObjects need depth-interleaving.
402+
_renderListPool.sort( renderSort );
422403

423404
}
424405

0 commit comments

Comments
 (0)