diff --git a/examples/jsm/inspector/Inspector.js b/examples/jsm/inspector/Inspector.js index 1e24605e794bf3..7206722701b1f6 100644 --- a/examples/jsm/inspector/Inspector.js +++ b/examples/jsm/inspector/Inspector.js @@ -490,9 +490,9 @@ class Inspector extends RendererInspector { resolveFrame( frame ) { - const nextFrame = this.getFrameById( frame.frameId + 1 ); + const previousFrame = this.getFrameById( frame.frameId - 1 ); - if ( ! nextFrame ) return; + if ( ! previousFrame ) return; frame.cpu = 0; frame.gpu = 0; @@ -510,9 +510,9 @@ class Inspector extends RendererInspector { } - // improve stats using next frame + // improve stats using previous frame - frame.deltaTime = nextFrame.startTime - frame.startTime; + frame.deltaTime = frame.startTime - previousFrame.startTime; frame.miscellaneous = frame.deltaTime - frame.total; if ( frame.miscellaneous < 0 ) { diff --git a/examples/jsm/inspector/RendererInspector.js b/examples/jsm/inspector/RendererInspector.js index dbb6f4215d2ecb..0f4bf34c384e1a 100644 --- a/examples/jsm/inspector/RendererInspector.js +++ b/examples/jsm/inspector/RendererInspector.js @@ -100,6 +100,8 @@ export class RendererInspector extends InspectorBase { begin() { + super.begin(); + this.currentFrame = this._createFrame(); this.currentRender = this.currentFrame; this.currentNodes = []; @@ -108,6 +110,8 @@ export class RendererInspector extends InspectorBase { finish() { + super.finish(); + const now = performance.now(); const frame = this.currentFrame; @@ -368,9 +372,9 @@ export class RendererInspector extends InspectorBase { } - const nextFrame = this.getFrameById( frame.frameId + 1 ); + const previousFrame = this.getFrameById( frame.frameId - 1 ); - if ( nextFrame === null ) continue; + if ( previousFrame === null ) continue; if ( frame.resolvedCompute === false ) { @@ -453,6 +457,8 @@ export class RendererInspector extends InspectorBase { inspect( node ) { + if ( this.enabled === false ) return; + const currentNodes = this.currentNodes; if ( currentNodes !== null ) { diff --git a/examples/jsm/lighting/LightProbeGrid.js b/examples/jsm/lighting/LightProbeGrid.js index 884a11b7585ef6..6bd6cf7b066fb7 100644 --- a/examples/jsm/lighting/LightProbeGrid.js +++ b/examples/jsm/lighting/LightProbeGrid.js @@ -457,6 +457,10 @@ class LightProbeGrid extends Light { const { cubemapSize = 8, near = 0.1, far = 100, bounces = 0, sampleCount = 512 } = options; + const currentInspectorEnabled = renderer.inspector.enabled; + + renderer.inspector.enabled = false; + this._ensureTextures(); this.updateBoundingBox(); @@ -603,6 +607,8 @@ class LightProbeGrid extends Light { this.visible = true; + renderer.inspector.enabled = currentInspectorEnabled; + } } diff --git a/src/nodes/core/ContextNode.js b/src/nodes/core/ContextNode.js index 149b40deb8e5b7..0bee75f01ecf45 100644 --- a/src/nodes/core/ContextNode.js +++ b/src/nodes/core/ContextNode.js @@ -123,11 +123,17 @@ class ContextNode extends Node { analyze( builder ) { - const previousContext = builder.addContext( this.value ); + const usageCount = builder.increaseUsage( this ); - this.node.build( builder ); + if ( usageCount === 1 ) { - builder.setContext( previousContext ); + const previousContext = builder.addContext( this.value ); + + this.node.build( builder, this ); + + builder.setContext( previousContext ); + + } } @@ -135,10 +141,12 @@ class ContextNode extends Node { const previousContext = builder.addContext( this.value ); - this.node.build( builder ); + const node = this.node.build( builder ); builder.setContext( previousContext ); + return node; + } generate( builder, output ) { diff --git a/src/nodes/core/MRTNode.js b/src/nodes/core/MRTNode.js index eaf54b6bcd4f9e..f09554b926d671 100644 --- a/src/nodes/core/MRTNode.js +++ b/src/nodes/core/MRTNode.js @@ -2,6 +2,7 @@ import OutputStructNode from './OutputStructNode.js'; import { nodeProxy } from '../tsl/TSLBase.js'; import { MaterialBlending, NoBlending } from '../../constants.js'; import BlendMode from '../../renderers/common/BlendMode.js'; +import Color4 from '../../renderers/common/Color4.js'; // Predefined blend modes for MRT nodes. const _noBlending = /**@__PURE__*/ new BlendMode( NoBlending ); @@ -78,6 +79,13 @@ class MRTNode extends OutputStructNode { output: _materialBlending }; + /** + * A dictionary storing the clear colors for each output. + * + * @type {Object} + */ + this.clearColors = {}; + /** * This flag can be used for type testing. * @@ -116,6 +124,38 @@ class MRTNode extends OutputStructNode { } + /** + * Sets the clear color for the given output name. + * + * @param {string} name - The name of the output. + * @param {number|string|Color} color - The clear color. + * @param {number} [alpha=1] - The clear alpha. + * @return {MRTNode} The current MRT node. + */ + setClearColor( name, color, alpha = 1 ) { + + const clearColor = this.clearColors[ name ] || ( this.clearColors[ name ] = new Color4() ); + + clearColor.set( color ); + clearColor.a = alpha; + + return this; + + } + + /** + * Returns the clear color for the given output name. + * + * @param {string} name - The name of the output. + * @return {?Color4} The clear color. Returns `null` if no clear color is defined + * which means the renderer's default clear policy is applied. + */ + getClearColor( name ) { + + return this.clearColors[ name ] || null; + + } + /** * Returns `true` if the MRT node has an output with the given name. * @@ -149,10 +189,12 @@ class MRTNode extends OutputStructNode { merge( mrtNode ) { const outputs = { ...this.outputNodes, ...mrtNode.outputNodes }; - const blendings = { ...this.blendModes, ...mrtNode.blendModes }; + const blendModes = { ...this.blendModes, ...mrtNode.blendModes }; + const clearColors = { ...this.clearColors, ...mrtNode.clearColors }; const mrtTarget = mrt( outputs ); - mrtTarget.blendings = blendings; + mrtTarget.blendModes = blendModes; + mrtTarget.clearColors = clearColors; return mrtTarget; diff --git a/src/renderers/common/Animation.js b/src/renderers/common/Animation.js index 2a0a0ed3476414..30272a85fce293 100644 --- a/src/renderers/common/Animation.js +++ b/src/renderers/common/Animation.js @@ -72,6 +72,12 @@ class Animation { this._requestId = this._context.requestAnimationFrame( update ); + if ( this.renderer._inspector.isRunning ) { + + this.renderer._inspector.finish(); + + } + if ( this.info.autoReset === true ) this.info.reset(); this.nodes.nodeFrame.update(); @@ -80,9 +86,11 @@ class Animation { this.renderer._inspector.begin(); - if ( this._animationLoop !== null ) this._animationLoop( time, xrFrame ); + if ( this._animationLoop !== null ) { - this.renderer._inspector.finish(); + this._animationLoop( time, xrFrame ); + + } }; @@ -95,6 +103,8 @@ class Animation { */ stop() { + if ( this.renderer._inspector.isRunning ) this.renderer._inspector.finish(); + if ( this._context !== null ) this._context.cancelAnimationFrame( this._requestId ); this._requestId = null; diff --git a/src/renderers/common/Color4.js b/src/renderers/common/Color4.js index 6684e772ef0166..7ec67f26ab3608 100644 --- a/src/renderers/common/Color4.js +++ b/src/renderers/common/Color4.js @@ -72,6 +72,15 @@ class Color4 extends Color { } + *[ Symbol.iterator ]() { + + yield this.r; + yield this.g; + yield this.b; + yield this.a; + + } + } export default Color4; diff --git a/src/renderers/common/InspectorBase.js b/src/renderers/common/InspectorBase.js index 71c13d63dc7fa0..9ee396163c5195 100644 --- a/src/renderers/common/InspectorBase.js +++ b/src/renderers/common/InspectorBase.js @@ -29,6 +29,22 @@ class InspectorBase extends EventDispatcher { */ this.currentFrame = null; + /** + * Indicates whether the inspector is running. + * + * @type {boolean} + * @default false + */ + this.isRunning = false; + + /** + * Indicates whether the inspector is enabled. + * + * @type {boolean} + * @default true + */ + this.enabled = true; + } /** @@ -75,12 +91,20 @@ class InspectorBase extends EventDispatcher { /** * Called when a frame begins. */ - begin() { } + begin() { + + this.isRunning = true; + + } /** * Called when a frame ends. */ - finish() { } + finish() { + + this.isRunning = false; + + } /** * Inspects a node. diff --git a/src/renderers/webgl-fallback/WebGLBackend.js b/src/renderers/webgl-fallback/WebGLBackend.js index 67d730548b2e74..fe80a3258522fc 100644 --- a/src/renderers/webgl-fallback/WebGLBackend.js +++ b/src/renderers/webgl-fallback/WebGLBackend.js @@ -835,7 +835,13 @@ class WebGLBackend extends Backend { for ( let i = 0; i < descriptor.textures.length; i ++ ) { - if ( i === 0 ) { + const mrtClearColor = descriptor.mrt ? descriptor.mrt.getClearColor( descriptor.textures[ i ].name ) : null; + + if ( mrtClearColor !== null ) { + + gl.clearBufferfv( gl.COLOR, i, [ mrtClearColor.r, mrtClearColor.g, mrtClearColor.b, mrtClearColor.a ] ); + + } else if ( i === 0 ) { gl.clearBufferfv( gl.COLOR, i, [ clearColor.r, clearColor.g, clearColor.b, clearColor.a ] ); diff --git a/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js b/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js index 96973bc28a3ec4..73a0fbee26e0ec 100644 --- a/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js +++ b/src/renderers/webgl-fallback/utils/WebGLTimestampQueryPool.js @@ -1,4 +1,4 @@ -import { error, warnOnce, warn } from '../../../utils.js'; +import { error, warn } from '../../../utils.js'; import TimestampQueryPool from '../../common/TimestampQueryPool.js'; /** @@ -58,11 +58,14 @@ class WebGLTimestampQueryPool extends TimestampQueryPool { if ( ! this.trackTimestamp ) return null; - // Check if we have enough space for a new query pair if ( this.currentQueryIndex + 2 > this.maxQueries ) { - warnOnce( `WebGLTimestampQueryPool [${ this.type }]: Maximum number of queries exceeded, when using trackTimestamp it is necessary to resolves the queries via renderer.resolveTimestampsAsync( THREE.TimestampQuery.${ this.type.toUpperCase() } ).` ); - return null; + this.resolveQueriesAsync(); + + this.currentQueryIndex = 0; + this.queryOffsets.clear(); + this.queryStates.clear(); + this.activeQuery = null; } @@ -222,6 +225,8 @@ class WebGLTimestampQueryPool extends TimestampQueryPool { const frames = []; + this.timestamps.clear(); + for ( const [ uid, promise ] of resolvePromises ) { const match = uid.match( /^(.*):f(\d+)$/ ); @@ -386,6 +391,8 @@ class WebGLTimestampQueryPool extends TimestampQueryPool { this.queries = []; this.queryStates.clear(); this.queryOffsets.clear(); + this.timestamps.clear(); + this.frames = []; this.lastValue = 0; this.activeQuery = null; diff --git a/src/renderers/webgpu/WebGPUBackend.js b/src/renderers/webgpu/WebGPUBackend.js index b833ccb6956f00..0da7ad4d68da9c 100644 --- a/src/renderers/webgpu/WebGPUBackend.js +++ b/src/renderers/webgpu/WebGPUBackend.js @@ -963,7 +963,13 @@ class WebGPUBackend extends Backend { if ( renderContext.clearColor || discardColor || clearExternalColor ) { - if ( i === 0 ) { + const clearColor = renderContext.mrt ? renderContext.mrt.getClearColor( renderContext.textures[ i ].name ) : null; + + if ( clearColor !== null ) { + + colorAttachment.clearValue = clearColor; + + } else if ( i === 0 ) { colorAttachment.clearValue = renderContext.clearColorValue; diff --git a/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js b/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js index b19dde83e50bb5..4c417487d7ab9e 100644 --- a/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js +++ b/src/renderers/webgpu/utils/WebGPUTimestampQueryPool.js @@ -1,4 +1,4 @@ -import { error, warnOnce } from '../../../utils.js'; +import { error } from '../../../utils.js'; import TimestampQueryPool from '../../common/TimestampQueryPool.js'; import { submit } from './WebGPUUtils.js'; import GPUBufferDescriptor from '../descriptors/GPUBufferDescriptor.js'; @@ -70,8 +70,10 @@ class WebGPUTimestampQueryPool extends TimestampQueryPool { if ( this.currentQueryIndex + 2 > this.maxQueries ) { - warnOnce( `WebGPUTimestampQueryPool [${ this.type }]: Maximum number of queries exceeded, when using trackTimestamp it is necessary to resolves the queries via renderer.resolveTimestampsAsync( THREE.TimestampQuery.${ this.type.toUpperCase() } ).` ); - return null; + this.resolveQueriesAsync(); + + this.currentQueryIndex = 0; + this.queryOffsets.clear(); } @@ -200,6 +202,8 @@ class WebGPUTimestampQueryPool extends TimestampQueryPool { const frames = []; + this.timestamps.clear(); + for ( const [ uid, baseOffset ] of currentOffsets ) { const match = uid.match( /^(.*):f(\d+)$/ ); @@ -316,6 +320,8 @@ class WebGPUTimestampQueryPool extends TimestampQueryPool { } this.queryOffsets.clear(); + this.timestamps.clear(); + this.frames = []; this.pendingResolve = null; } diff --git a/test/e2e/puppeteer.js b/test/e2e/puppeteer.js index 570f916f700176..5de20c544489e0 100644 --- a/test/e2e/puppeteer.js +++ b/test/e2e/puppeteer.js @@ -41,6 +41,7 @@ const exceptionList = [ 'webgpu_compute_sort_bitonic', 'webgpu_storage_buffer', 'webgpu_tsl_editor', + 'webgpu_tsl_graph', 'webxr_vr_video', 'webgpu_tsl_transpiler', 'webgpu_rendertarget_2d-array_3d',