From c7a8eb101d355956ceeab4d9a7de5ddc979059d9 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 3 Nov 2023 21:02:05 +0900 Subject: [PATCH 1/2] Add support for multi draw --- examples/jsm/objects/BatchedMesh.js | 27 +++++++++++++++++++ src/renderers/WebGLRenderer.js | 6 ++++- src/renderers/webgl/WebGLBufferRenderer.js | 26 ++++++++++++++++++ .../webgl/WebGLIndexedBufferRenderer.js | 26 ++++++++++++++++++ 4 files changed, 84 insertions(+), 1 deletion(-) diff --git a/examples/jsm/objects/BatchedMesh.js b/examples/jsm/objects/BatchedMesh.js index cfe742d1034780..d582930c384e9d 100644 --- a/examples/jsm/objects/BatchedMesh.js +++ b/examples/jsm/objects/BatchedMesh.js @@ -113,6 +113,9 @@ class BatchedMesh extends Mesh { this._geometryInitialized = false; this._geometryCount = 0; + this._multiDrawCounts = null; + this._multiDrawStarts = null; + this._multiDrawCount = 0; // Local matrix per geometry by using data texture // @TODO: Support uniform parameter per geometry @@ -240,6 +243,8 @@ class BatchedMesh extends Mesh { geometry.setAttribute( ID_ATTR_NAME, new BufferAttribute( idArray, 1 ) ); this._geometryInitialized = true; + this._multiDrawCounts = new Int32Array( maxGeometryCount ); + this._multiDrawStarts = new Int32Array( maxGeometryCount ); } @@ -705,8 +710,30 @@ class BatchedMesh extends Mesh { material.defines.BATCHING = true; + const visible = this._visible; + const multiDrawCounts = this._multiDrawCounts; + const multiDrawStarts = this._multiDrawStarts; + const drawRanges = this._drawRanges; + let count = 0; + for ( let i = 0, l = visible.length; i < l; i ++ ) { + + if ( visible[ i ] ) { + + const range = drawRanges[ i ]; + multiDrawStarts[ count ] = range.start; + multiDrawCounts[ count ] = range.count; + count ++; + + } + + } + + this._multiDrawCount = count; + // @TODO: Implement frustum culling for each geometry + // @TODO: Implement geometry sorting for transparent and opaque materials + } onAfterRender( _renderer, _scene, _camera, _geometry, material/*, _group*/ ) { diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 15ebd1c6434e2c..8bcac7592035b2 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -896,7 +896,11 @@ class WebGLRenderer { } - if ( object.isInstancedMesh ) { + if ( object.isBatchedMesh ) { + + renderer.renderMultiDraw( object._multiDrawStarts, object._multiDrawCounts, object._multiDrawCount ) + + } else if ( object.isInstancedMesh ) { renderer.renderInstances( drawStart, drawCount, object.count ); diff --git a/src/renderers/webgl/WebGLBufferRenderer.js b/src/renderers/webgl/WebGLBufferRenderer.js index 484298f6db9023..da8d8cdc9a3551 100644 --- a/src/renderers/webgl/WebGLBufferRenderer.js +++ b/src/renderers/webgl/WebGLBufferRenderer.js @@ -49,11 +49,37 @@ function WebGLBufferRenderer( gl, extensions, info, capabilities ) { } + function renderMultiDraw( starts, counts, drawCount ) { + + if ( drawCount === 0 ) return; + + const extension = extensions.get( 'WEBGL_multi_draw' ); + if ( extension === null ) { + + console.error( 'THREE.WebGLBufferRenderer: using THREE.BatchedMesh but hardware does not support extension WEBGL_multi_draw.' ); + return; + + } + + extension.multiDrawArraysWEBGL( mode, starts, 0, counts, 0, drawCount ); + + let elementCount = 0; + for ( let i = 0; i < drawCount; i ++ ) { + + elementCount += counts[ i ]; + + } + + info.update( elementCount, mode, 1 ); + + } + // this.setMode = setMode; this.render = render; this.renderInstances = renderInstances; + this.renderMultiDraw = renderMultiDraw; } diff --git a/src/renderers/webgl/WebGLIndexedBufferRenderer.js b/src/renderers/webgl/WebGLIndexedBufferRenderer.js index 7186c6797b2e89..7c9e92717c72a4 100644 --- a/src/renderers/webgl/WebGLIndexedBufferRenderer.js +++ b/src/renderers/webgl/WebGLIndexedBufferRenderer.js @@ -58,12 +58,38 @@ function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { } + function renderMultiDraw( starts, counts, drawCount ) { + + if ( drawCount === 0 ) return; + + const extension = extensions.get( 'WEBGL_multi_draw' ); + if ( extension === null ) { + + console.error( 'THREE.WebGLBufferRenderer: using THREE.BatchedMesh but hardware does not support extension WEBGL_multi_draw.' ); + return; + + } + + extension.multiDrawElementsWEBGL( mode, counts, 0, type, starts, 0, drawCount ); + + let elementCount = 0; + for ( let i = 0; i < drawCount; i ++ ) { + + elementCount += counts[ i ]; + + } + + info.update( elementCount, mode, 1 ); + + } + // this.setMode = setMode; this.setIndex = setIndex; this.render = render; this.renderInstances = renderInstances; + this.renderMultiDraw = renderMultiDraw; } From c453f4b08a009dd562a3b80839b182fba3a82d85 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Fri, 3 Nov 2023 22:03:41 +0900 Subject: [PATCH 2/2] Fix multidraw --- examples/jsm/objects/BatchedMesh.js | 14 ++++++++++---- src/renderers/WebGLRenderer.js | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/examples/jsm/objects/BatchedMesh.js b/examples/jsm/objects/BatchedMesh.js index d582930c384e9d..4e80635577aa8a 100644 --- a/examples/jsm/objects/BatchedMesh.js +++ b/examples/jsm/objects/BatchedMesh.js @@ -441,7 +441,7 @@ class BatchedMesh extends Mesh { // add the reserved range and draw range objects reservedRanges.push( reservedRange ); drawRanges.push( { - start: hasIndex ? reservedRange.indexStart * 3 : reservedRange.vertexStart * 3, + start: hasIndex ? reservedRange.indexStart : reservedRange.vertexStart, count: - 1 } ); @@ -546,7 +546,7 @@ class BatchedMesh extends Mesh { // set drawRange count const drawRange = this._drawRanges[ id ]; const posAttr = geometry.getAttribute( 'position' ); - drawRange.count = hasIndex ? srcIndex.count * 3 : posAttr.count * 3; + drawRange.count = hasIndex ? srcIndex.count : posAttr.count; return id; @@ -710,17 +710,23 @@ class BatchedMesh extends Mesh { material.defines.BATCHING = true; + // the indexed version of the multi draw function requires specifying the start + // offset in bytes. + const index = _geometry.getIndex(); + const bytesPerElement = index === null ? 1 : index.array.BYTES_PER_ELEMENT; + const visible = this._visible; - const multiDrawCounts = this._multiDrawCounts; const multiDrawStarts = this._multiDrawStarts; + const multiDrawCounts = this._multiDrawCounts; const drawRanges = this._drawRanges; + let count = 0; for ( let i = 0, l = visible.length; i < l; i ++ ) { if ( visible[ i ] ) { const range = drawRanges[ i ]; - multiDrawStarts[ count ] = range.start; + multiDrawStarts[ count ] = range.start * bytesPerElement; multiDrawCounts[ count ] = range.count; count ++; diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 8bcac7592035b2..845e8c3fc61862 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -898,7 +898,7 @@ class WebGLRenderer { if ( object.isBatchedMesh ) { - renderer.renderMultiDraw( object._multiDrawStarts, object._multiDrawCounts, object._multiDrawCount ) + renderer.renderMultiDraw( object._multiDrawStarts, object._multiDrawCounts, object._multiDrawCount ); } else if ( object.isInstancedMesh ) {