Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve performance of SVGRenderer #11359

Merged
merged 9 commits into from
May 20, 2017
119 changes: 62 additions & 57 deletions examples/js/renderers/SVGRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ THREE.SVGRenderer = function () {
_viewMatrix = new THREE.Matrix4(),
_viewProjectionMatrix = new THREE.Matrix4(),

_svgPathPool = [], _svgLinePool = [], _svgRectPool = [],
_svgNode, _pathCount = 0, _lineCount = 0, _rectCount = 0,
_quality = 1;
_svgPathPool = [],
_svgNode, _pathCount = 0, _currPath, _currStyle,
_quality = 1, _precision = null;

this.domElement = _svg;

Expand Down Expand Up @@ -104,11 +104,15 @@ THREE.SVGRenderer = function () {

};

this.setPrecision = function ( precision ) {

_precision = precision;

};

function removeChildNodes() {

_pathCount = 0;
_lineCount = 0;
_rectCount = 0;

while ( _svg.childNodes.length > 0 ) {

Expand All @@ -118,10 +122,26 @@ THREE.SVGRenderer = function () {

}

function getSvgColor ( color, opacity ) {

var arg = Math.floor( color.r * 255 ) + ',' + Math.floor( color.g * 255 ) + ',' + Math.floor( color.b * 255 );

if ( opacity === undefined || opacity === 1 ) return 'rgb(' + arg + ')';

return 'rgba(' + arg + ',' + opacity + ')';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, similar approach can be used THREE.Color.getStyle().
One could add opacity parameter to the function and return rgba() string if opacity defined and not equal to 1

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you done benchmarks on this? Maybe the browser is already doing the same check internally?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reduces size of SVG string - when written into the file.
I do not think that it has any significant impact on performance


}

function convert ( c ) {

return _precision !== null ? c.toFixed(_precision) : c;

}

this.clear = function () {

removeChildNodes();
_svg.style.backgroundColor = 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearAlpha + ')';
_svg.style.backgroundColor = getSvgColor( _clearColor, _clearAlpha );

};

Expand All @@ -139,7 +159,7 @@ THREE.SVGRenderer = function () {
if ( background && background.isColor ) {

removeChildNodes();
_svg.style.backgroundColor = 'rgb(' + Math.floor( background.r * 255 ) + ',' + Math.floor( background.g * 255 ) + ',' + Math.floor( background.b * 255 ) + ')';
_svg.style.backgroundColor = getSvgColor( background );

} else if ( this.autoClear === true ) {

Expand All @@ -160,6 +180,7 @@ THREE.SVGRenderer = function () {
_normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse );

calculateLights( _lights );
_currPath = _currStyle = ""; // reset accumulated path

for ( var e = 0, el = _elements.length; e < el; e ++ ) {

Expand Down Expand Up @@ -220,6 +241,8 @@ THREE.SVGRenderer = function () {

}

flushPath(); // just to flush last svg:path

scene.traverseVisible( function ( object ) {

if ( object instanceof THREE.SVGObject ) {
Expand Down Expand Up @@ -331,37 +354,28 @@ THREE.SVGRenderer = function () {
scaleY *= material.size;
}

_svgNode = getRectNode( _rectCount ++ );

_svgNode.setAttribute( 'x', v1.x - ( scaleX * 0.5 ) );
_svgNode.setAttribute( 'y', v1.y - ( scaleY * 0.5 ) );
_svgNode.setAttribute( 'width', scaleX );
_svgNode.setAttribute( 'height', scaleY );
var path = 'M' + convert( v1.x - scaleX * 0.5 ) + ',' + convert( v1.y - scaleY * 0.5 ) + 'h' + convert( scaleX ) + 'v' + convert( scaleY ) + 'h' + convert(-scaleX) + 'z';
var style = "";

if ( material.isSpriteMaterial || material.isPointsMaterial ) {

_svgNode.setAttribute( 'style', 'fill: ' + material.color.getStyle() );
style = 'fill:' + getSvgColor( material.color, material.opacity );

}

_svg.appendChild( _svgNode );
addPath( style, path );

}

function renderLine( v1, v2, element, material ) {

_svgNode = getLineNode( _lineCount ++ );

_svgNode.setAttribute( 'x1', v1.positionScreen.x );
_svgNode.setAttribute( 'y1', v1.positionScreen.y );
_svgNode.setAttribute( 'x2', v2.positionScreen.x );
_svgNode.setAttribute( 'y2', v2.positionScreen.y );
var path = 'M' + convert( v1.positionScreen.x ) + ',' + convert( v1.positionScreen.y ) + 'L' + convert( v2.positionScreen.x ) + ',' + convert( v2.positionScreen.y );

if ( material instanceof THREE.LineBasicMaterial ) {

_svgNode.setAttribute( 'style', 'fill: none; stroke: ' + material.color.getStyle() + '; stroke-width: ' + material.linewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.linecap + '; stroke-linejoin: ' + material.linejoin );
var style = 'fill:none;stroke:' + getSvgColor( material.color, material.opacity ) + ';stroke-width:' + material.linewidth + ';stroke-linecap:' + material.linecap + ';stroke-linejoin:' + material.linejoin;

_svg.appendChild( _svgNode );
addPath( style, path );

}

Expand All @@ -372,8 +386,8 @@ THREE.SVGRenderer = function () {
_this.info.render.vertices += 3;
_this.info.render.faces ++;

_svgNode = getPathNode( _pathCount ++ );
_svgNode.setAttribute( 'd', 'M ' + v1.positionScreen.x + ' ' + v1.positionScreen.y + ' L ' + v2.positionScreen.x + ' ' + v2.positionScreen.y + ' L ' + v3.positionScreen.x + ',' + v3.positionScreen.y + 'z' );
var path = 'M' + convert( v1.positionScreen.x ) + ',' + convert( v1.positionScreen.y ) + 'L' + convert( v2.positionScreen.x ) + ',' + convert( v2.positionScreen.y ) + 'L' + convert( v3.positionScreen.x ) + ',' + convert( v3.positionScreen.y ) + 'z';
var style = '';

if ( material instanceof THREE.MeshBasicMaterial ) {

Expand Down Expand Up @@ -413,75 +427,66 @@ THREE.SVGRenderer = function () {

if ( material.wireframe ) {

_svgNode.setAttribute( 'style', 'fill: none; stroke: ' + _color.getStyle() + '; stroke-width: ' + material.wireframeLinewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.wireframeLinecap + '; stroke-linejoin: ' + material.wireframeLinejoin );
style = 'fill:none;stroke:' + getSvgColor( _color, material.opacity ) + ';stroke-width:' + material.wireframeLinewidth + ';stroke-linecap:' + material.wireframeLinecap + ';stroke-linejoin:' + material.wireframeLinejoin;

} else {

_svgNode.setAttribute( 'style', 'fill: ' + _color.getStyle() + '; fill-opacity: ' + material.opacity );
style = 'fill:' + getSvgColor( _color, material.opacity );

}

_svg.appendChild( _svgNode );
addPath( style, path );

}

function getLineNode( id ) {

if ( _svgLinePool[ id ] == null ) {
function addPath ( style, path ) {

_svgLinePool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'line' );
if ( _currStyle == style ) {

if ( _quality == 0 ) {
_currPath += path

_svgLinePool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed
} else {

}
flushPath();

return _svgLinePool[ id ];
_currStyle = style;
_currPath = path;

}

return _svgLinePool[ id ];

}

function getPathNode( id ) {

if ( _svgPathPool[ id ] == null ) {

_svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );
function flushPath() {

if ( _quality == 0 ) {

_svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed

}
if ( _currPath ) {

return _svgPathPool[ id ];
_svgNode = getPathNode( _pathCount ++ );
_svgNode.setAttribute( 'd', _currPath );
_svgNode.setAttribute( 'style', _currStyle );
_svg.appendChild( _svgNode );

}

return _svgPathPool[ id ];

_currPath = _currStyle = "";
}

function getRectNode( id ) {
function getPathNode( id ) {

if ( _svgRectPool[ id ] == null ) {
if ( _svgPathPool[ id ] == null ) {

_svgRectPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'rect' );
_svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );

if ( _quality == 0 ) {

_svgRectPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed
_svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed

}

return _svgRectPool[ id ];
return _svgPathPool[ id ];

}

return _svgRectPool[ id ];
return _svgPathPool[ id ];

}

Expand Down