From 9b054a2013153ff9afd7b4f36e47d22f87376c26 Mon Sep 17 00:00:00 2001 From: PoseidonEnergy <109360495+PoseidonEnergy@users.noreply.github.com> Date: Mon, 16 Mar 2026 14:42:18 -0500 Subject: [PATCH 1/2] WebGPU `reversedDepthBuffer`: Use `depth32float` instead of `depth24plus` (#33184) Co-authored-by: Michael Herzog --- src/nodes/display/PassNode.js | 8 +++++++- .../webgpu/utils/WebGPUTextureUtils.js | 4 ++-- src/renderers/webgpu/utils/WebGPUUtils.js | 20 +++++++++++++++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/nodes/display/PassNode.js b/src/nodes/display/PassNode.js index 9b9eeac8c9ad2a..f6add436d0386a 100644 --- a/src/nodes/display/PassNode.js +++ b/src/nodes/display/PassNode.js @@ -5,7 +5,7 @@ import { context } from '../tsl/TSLBase.js'; import { uniform } from '../core/UniformNode.js'; import { viewZToOrthographicDepth, perspectiveDepthToViewZ } from './ViewportDepthNode.js'; -import { HalfFloatType/*, FloatType*/ } from '../../constants.js'; +import { HalfFloatType, FloatType } from '../../constants.js'; import { Vector2 } from '../../math/Vector2.js'; import { Vector4 } from '../../math/Vector4.js'; import { DepthTexture } from '../../textures/DepthTexture.js'; @@ -757,6 +757,12 @@ class PassNode extends TempNode { this.renderTarget.texture.type = renderer.getOutputBufferType(); + if ( renderer.reversedDepthBuffer === true ) { + + this.renderTarget.depthTexture.type = FloatType; + + } + return this.scope === PassNode.COLOR ? this.getTextureNode() : this.getLinearDepthNode(); } diff --git a/src/renderers/webgpu/utils/WebGPUTextureUtils.js b/src/renderers/webgpu/utils/WebGPUTextureUtils.js index b52aedc9983704..f12d5cf897b099 100644 --- a/src/renderers/webgpu/utils/WebGPUTextureUtils.js +++ b/src/renderers/webgpu/utils/WebGPUTextureUtils.js @@ -463,12 +463,12 @@ class WebGPUTextureUtils { if ( stencil ) { format = DepthStencilFormat; - type = UnsignedInt248Type; + type = backend.renderer.reversedDepthBuffer === true ? FloatType : UnsignedInt248Type; } else if ( depth ) { format = DepthFormat; - type = UnsignedIntType; + type = backend.renderer.reversedDepthBuffer === true ? FloatType : UnsignedIntType; } diff --git a/src/renderers/webgpu/utils/WebGPUUtils.js b/src/renderers/webgpu/utils/WebGPUUtils.js index 92cac6975ea372..9baee7b3f3d239 100644 --- a/src/renderers/webgpu/utils/WebGPUUtils.js +++ b/src/renderers/webgpu/utils/WebGPUUtils.js @@ -42,11 +42,27 @@ class WebGPUUtils { } else if ( renderContext.stencil ) { - format = GPUTextureFormat.Depth24PlusStencil8; + if ( this.backend.renderer.reversedDepthBuffer === true ) { + + format = GPUTextureFormat.Depth32FloatStencil8; + + } else { + + format = GPUTextureFormat.Depth24PlusStencil8; + + } } else { - format = GPUTextureFormat.Depth24Plus; + if ( this.backend.renderer.reversedDepthBuffer === true ) { + + format = GPUTextureFormat.Depth32Float; + + } else { + + format = GPUTextureFormat.Depth24Plus; + + } } From 0f538a230ff535f655b3d26aa4ee76b9e5992453 Mon Sep 17 00:00:00 2001 From: Makio64 Date: Tue, 17 Mar 2026 05:19:38 +0900 Subject: [PATCH 2/2] Inspector: Add export functionality to Inspector Timeline tab and enhance call detail logging (#33192) Co-authored-by: sunag --- examples/jsm/inspector/tabs/Timeline.js | 132 ++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 7 deletions(-) diff --git a/examples/jsm/inspector/tabs/Timeline.js b/examples/jsm/inspector/tabs/Timeline.js index 603ab4b45b8673..f0d9a02b224f0f 100644 --- a/examples/jsm/inspector/tabs/Timeline.js +++ b/examples/jsm/inspector/tabs/Timeline.js @@ -102,11 +102,22 @@ class Timeline extends Tab { } ); + this.exportButton = document.createElement( 'button' ); + this.exportButton.className = 'console-copy-button'; + this.exportButton.title = 'Export'; + this.exportButton.innerHTML = ''; + this.exportButton.style.padding = '0 10px'; + this.exportButton.style.lineHeight = '24px'; + this.exportButton.style.display = 'flex'; + this.exportButton.style.alignItems = 'center'; + this.exportButton.addEventListener( 'click', () => this.exportData() ); + const buttonsGroup = document.createElement( 'div' ); buttonsGroup.className = 'console-buttons-group'; buttonsGroup.appendChild( this.viewModeButton ); buttonsGroup.appendChild( this.recordButton ); buttonsGroup.appendChild( this.recordRefreshButton ); + buttonsGroup.appendChild( this.exportButton ); buttonsGroup.appendChild( clearButton ); header.style.display = 'flex'; @@ -597,8 +608,12 @@ class Timeline extends Tab { } - // Only record method name as requested, skipping detail arguments - this.currentFrame.calls.push( { method: methodLabel } ); + const call = { method: methodLabel }; + const details = this.getCallDetail( prop, args ); + + if ( details ) call.details = details; + + this.currentFrame.calls.push( call ); return originalFunc.apply( backend, args ); @@ -647,6 +662,108 @@ class Timeline extends Tab { } + exportData() { + + if ( this.frames.length === 0 ) return; + + const data = JSON.stringify( this.frames, null, '\t' ); + const blob = new Blob( [ data ], { type: 'application/json' } ); + const url = URL.createObjectURL( blob ); + const a = document.createElement( 'a' ); + a.href = url; + a.download = 'threejs-timeline.json'; + a.click(); + URL.revokeObjectURL( url ); + + } + + getCallDetail( method, args ) { + + switch ( method ) { + + case 'draw': { + + const renderObject = args[ 0 ]; + if ( ! renderObject ) return null; + + const details = {}; + if ( renderObject.object ) { + + details.object = renderObject.object.name || renderObject.object.type; + + if ( renderObject.object.count > 1 ) { + + details.instance = renderObject.object.count; + + } + + } + + if ( renderObject.material ) details.material = renderObject.material.name || renderObject.material.type; + if ( renderObject.geometry ) details.geometry = renderObject.geometry.name || undefined; + + if ( renderObject.camera ) details.camera = renderObject.camera.name || renderObject.camera.type; + + return details; + + } + + case 'updateBinding': { + + const binding = args[ 0 ]; + return binding?.name ? { binding: binding.name } : null; + + } + + case 'createProgram': { + + const program = args[ 0 ]; + if ( ! program ) return null; + return { stage: program.stage, name: program.name || undefined }; + + } + + case 'createBindings': { + + const bindGroup = args[ 0 ]; + return bindGroup?.name ? { group: bindGroup.name } : null; + + } + + case 'createTexture': + case 'destroyTexture': { + + const texture = args[ 0 ]; + return texture?.name ? { texture: texture.name } : null; + + } + + } + + return null; + + } + + formatDetails( details ) { + + const parts = []; + + for ( const key in details ) { + + if ( details[ key ] !== undefined ) { + + parts.push( `${key}: ${details[ key ]}` ); + + } + + } + + if ( parts.length === 0 ) return ''; + + return `{ ${parts.join( ', ' )} }`; + + } + renderSlider() { if ( this.frames.length === 0 ) { @@ -768,14 +885,15 @@ class Timeline extends Tab { const call = frame.calls[ i ]; const isStructural = call.method.startsWith( 'begin' ) || call.method.startsWith( 'finish' ); + const formatedDetails = call.details ? this.formatDetails( call.details ) : ''; - if ( currentGroup && currentGroup.method === call.method && ! isStructural ) { + if ( currentGroup && currentGroup.method === call.method && currentGroup.formatedDetails === formatedDetails && ! isStructural ) { currentGroup.count ++; } else { - currentGroup = { method: call.method, count: 1 }; + currentGroup = { method: call.method, count: 1, formatedDetails }; groupedCalls.push( currentGroup ); } @@ -836,7 +954,7 @@ class Timeline extends Tab { // Title const title = document.createElement( 'span' ); - title.textContent = call.method + ( call.count > 1 ? ` ( ${call.count} )` : '' ); + title.innerHTML = call.method + ( call.formatedDetails ? call.formatedDetails : '' ) + ( call.count > 1 ? ` ( ${call.count} )` : '' ); block.appendChild( title ); block.addEventListener( 'click', ( e ) => { @@ -862,14 +980,14 @@ class Timeline extends Tab { } else if ( call.method.startsWith( 'finish' ) ) { - block.textContent = call.method + ( call.count > 1 ? ` ( ${call.count} )` : '' ); + block.innerHTML = call.method + ( call.formatedDetails ? call.formatedDetails : '' ) + ( call.count > 1 ? ` ( ${call.count} )` : '' ); currentIndent = Math.max( 0, currentIndent - 1 ); elementStack.pop(); } else { - block.textContent = call.method + ( call.count > 1 ? ` ( ${call.count} )` : '' ); + block.innerHTML = call.method + ( call.formatedDetails ? call.formatedDetails : '' ) + ( call.count > 1 ? ` ( ${call.count} )` : '' ); }