From f373ec0de2f9f5bd724105c6ac7629651116f4ea Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Mon, 3 Aug 2026 16:42:50 +0200 Subject: [PATCH 1/2] MeshPhysicalNodeMaterial: Fix specular nodes. (#34164) --- .../screenshots/webgpu_loader_materialx.jpg | Bin 36149 -> 36355 bytes .../nodes/MeshPhysicalNodeMaterial.js | 6 ++++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/screenshots/webgpu_loader_materialx.jpg b/examples/screenshots/webgpu_loader_materialx.jpg index a9d673b5c779ccb02c35924f4e46e074c62320e0..4be4781b12107f3f5fcba57dc5c0754abb1d8a00 100644 GIT binary patch delta 34535 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 diff --git a/src/materials/nodes/MeshPhysicalNodeMaterial.js b/src/materials/nodes/MeshPhysicalNodeMaterial.js index bd0b92411317ea..3293ff37d4b222 100644 --- a/src/materials/nodes/MeshPhysicalNodeMaterial.js +++ b/src/materials/nodes/MeshPhysicalNodeMaterial.js @@ -373,11 +373,13 @@ class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial { setupSpecular() { const iorNode = this.iorNode ? float( this.iorNode ) : materialIOR; + const specularColorNode = this.specularColorNode ? vec3( this.specularColorNode ) : materialSpecularColor; + const specularIntensityNode = this.specularIntensityNode ? float( this.specularIntensityNode ) : materialSpecularIntensity; ior.assign( iorNode ); - specularColor.assign( min( pow2( ior.sub( 1.0 ).div( ior.add( 1.0 ) ) ).mul( materialSpecularColor ), vec3( 1.0 ) ).mul( materialSpecularIntensity ) ); + specularColor.assign( min( pow2( ior.sub( 1.0 ).div( ior.add( 1.0 ) ) ).mul( specularColorNode ), vec3( 1.0 ) ).mul( specularIntensityNode ) ); specularColorBlended.assign( mix( specularColor, diffuseColor.rgb, metalness ) ); - specularF90.assign( mix( materialSpecularIntensity, 1.0, metalness ) ); + specularF90.assign( mix( specularIntensityNode, 1.0, metalness ) ); } From 7cda7e710d884827fc73ff1a3aa63270846513d7 Mon Sep 17 00:00:00 2001 From: Mugen87 Date: Mon, 3 Aug 2026 16:45:16 +0200 Subject: [PATCH 2/2] Updated builds. --- build/three.core.js | 107 ++- build/three.module.js | 19 + build/three.tsl.js | 5 +- build/three.webgpu.js | 1404 +++++++++++++++++++++++++++-------- build/three.webgpu.nodes.js | 1404 +++++++++++++++++++++++++++-------- 5 files changed, 2268 insertions(+), 671 deletions(-) diff --git a/build/three.core.js b/build/three.core.js index 5942e05e58665e..7a5c71f5e01a74 100644 --- a/build/three.core.js +++ b/build/three.core.js @@ -4169,10 +4169,6 @@ class Quaternion { const x = euler._x, y = euler._y, z = euler._z, order = euler._order; - // http://www.mathworks.com/matlabcentral/fileexchange/ - // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ - // content/SpinCalc.m - const cos = Math.cos; const sin = Math.sin; @@ -4248,8 +4244,6 @@ class Quaternion { */ setFromAxisAngle( axis, angle ) { - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm - const halfAngle = angle / 2, s = Math.sin( halfAngle ); this._x = axis.x * s; @@ -4271,8 +4265,6 @@ class Quaternion { */ setFromRotationMatrix( m ) { - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) const te = m.elements, @@ -4560,8 +4552,6 @@ class Quaternion { */ multiplyQuaternions( a, b ) { - // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm - const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; @@ -8521,8 +8511,6 @@ class Vector4 { */ setAxisAngleFromQuaternion( q ) { - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm - // q is assumed to be normalized this.w = 2 * Math.acos( q.w ); @@ -8556,8 +8544,6 @@ class Vector4 { */ setAxisAngleFromRotationMatrix( m ) { - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) let angle, x, y, z; // variables for result @@ -13511,6 +13497,27 @@ class Object3D extends EventDispatcher { } + /** + * Frees the GPU-related resources allocated by this instance. Call this + * method whenever this instance is no longer used in your app. + * + * Geometries, materials and textures are potentially shared with other + * 3D objects and must be disposed of separately. + * + * @fires Object3D#dispose + */ + dispose() { + + /** + * Fires when the 3D object has been disposed of. + * + * @event Object3D#dispose + * @type {Object} + */ + this.dispatchEvent( { type: 'dispose' } ); + + } + } /** @@ -25718,7 +25725,7 @@ class InstancedMesh extends Mesh { */ dispose() { - this.dispatchEvent( { type: 'dispose' } ); + super.dispose(); if ( this.morphTexture !== null ) { @@ -25914,6 +25921,10 @@ class Frustum { /** * Returns `true` if the given bounding sphere is intersecting this frustum. * + * This is a fast, conservative test that favors performance over precision. It can + * report false positives for spheres that lie outside the frustum but are not separated + * by a single frustum plane. It never reports false negatives, so it is safe for culling. + * * @param {Sphere} sphere - The bounding sphere to test. * @return {boolean} Whether the bounding sphere is intersecting this frustum or not. */ @@ -25942,6 +25953,11 @@ class Frustum { /** * Returns `true` if the given bounding box is intersecting this frustum. * + * This is a fast, conservative test that favors performance over precision. It can + * report false positives for large boxes that lie outside the frustum but are not + * separated by a single frustum plane. It never reports false negatives, so it is + * safe for culling. + * * @param {Box3} box - The bounding box to test. * @return {boolean} Whether the bounding box is intersecting this frustum or not. */ @@ -27422,6 +27438,7 @@ class BatchedMesh extends Mesh { this.validateGeometryId( geometryId ); this._instanceInfo[ instanceId ].geometryIndex = geometryId; + this._visibilityChanged = true; return this; @@ -27716,6 +27733,8 @@ class BatchedMesh extends Mesh { */ dispose() { + super.dispose(); + // Assuming the geometry is not shared with other meshes this.geometry.dispose(); @@ -45988,16 +46007,6 @@ class Light extends Object3D { } - /** - * Frees the GPU-related resources allocated by this instance. Call this - * method whenever this instance is no longer used in your app. - */ - dispose() { - - this.dispatchEvent( { type: 'dispose' } ); - - } - copy( source, recursive ) { super.copy( source, recursive ); @@ -50319,6 +50328,8 @@ class ImageBitmapLoader extends Loader { scope.manager.itemEnd( url ); + return imageBitmap; // see #34150 + } ).catch( function ( e ) { if ( onError ) onError( e ); @@ -55867,15 +55878,19 @@ class RenderTarget3D extends RenderTarget { this.depth = depth; - /** - * Overwritten with a different texture type. - * - * @type {Data3DTexture} - */ - this.texture = new Data3DTexture( null, width, height, depth ); - this._setTextureOptions( options ); + // overwrite attachments with 3D textures - this.texture.isRenderTargetTexture = true; + for ( let i = 0; i < this.textures.length; i ++ ) { + + const texture = new Data3DTexture( null, width, height, depth ); + texture.isRenderTargetTexture = true; + texture.renderTarget = this; + + this.textures[ i ] = texture; + + } + + this._setTextureOptions( options ); } @@ -57928,6 +57943,8 @@ class SpotLightHelper extends Object3D { */ dispose() { + super.dispose(); + this.cone.geometry.dispose(); this.cone.material.dispose(); @@ -58139,6 +58156,8 @@ class SkeletonHelper extends LineSegments { */ dispose() { + super.dispose(); + this.geometry.dispose(); this.material.dispose(); @@ -58230,6 +58249,8 @@ class PointLightHelper extends Mesh { */ dispose() { + super.dispose(); + this.geometry.dispose(); this.material.dispose(); @@ -58350,6 +58371,8 @@ class HemisphereLightHelper extends Object3D { */ dispose() { + super.dispose(); + this.children[ 0 ].geometry.dispose(); this.children[ 0 ].material.dispose(); @@ -58463,6 +58486,8 @@ class GridHelper extends LineSegments { */ dispose() { + super.dispose(); + this.geometry.dispose(); this.material.dispose(); @@ -58581,6 +58606,8 @@ class PolarGridHelper extends LineSegments { */ dispose() { + super.dispose(); + this.geometry.dispose(); this.material.dispose(); @@ -58686,6 +58713,8 @@ class DirectionalLightHelper extends Object3D { */ dispose() { + super.dispose(); + this.lightPlane.geometry.dispose(); this.lightPlane.material.dispose(); this.targetLine.geometry.dispose(); @@ -59036,6 +59065,8 @@ class CameraHelper extends LineSegments { */ dispose() { + super.dispose(); + this.geometry.dispose(); this.material.dispose(); @@ -59198,6 +59229,8 @@ class BoxHelper extends LineSegments { */ dispose() { + super.dispose(); + this.geometry.dispose(); this.material.dispose(); @@ -59275,6 +59308,8 @@ class Box3Helper extends LineSegments { */ dispose() { + super.dispose(); + this.geometry.dispose(); this.material.dispose(); @@ -59361,6 +59396,8 @@ class PlaneHelper extends Line { */ dispose() { + super.dispose(); + this.geometry.dispose(); this.material.dispose(); this.children[ 0 ].geometry.dispose(); @@ -59521,6 +59558,8 @@ class ArrowHelper extends Object3D { */ dispose() { + super.dispose(); + this.line.geometry.dispose(); this.line.material.dispose(); this.cone.geometry.dispose(); @@ -59611,6 +59650,8 @@ class AxesHelper extends LineSegments { */ dispose() { + super.dispose(); + this.geometry.dispose(); this.material.dispose(); diff --git a/build/three.module.js b/build/three.module.js index bbe71d42c8ebc6..4618de7be13afe 100644 --- a/build/three.module.js +++ b/build/three.module.js @@ -13845,6 +13845,7 @@ class WebXRManager extends EventDispatcher { const currentSize = new Vector2(); let currentPixelRatio = null; + let currentCameraSettings = null; // @@ -14040,6 +14041,18 @@ class WebXRManager extends EventDispatcher { renderer.setPixelRatio( currentPixelRatio ); renderer.setSize( currentSize.width, currentSize.height, false ); + if ( currentCameraSettings !== null ) { + + const camera = currentCameraSettings.camera; + + camera.fov = currentCameraSettings.fov; + camera.zoom = currentCameraSettings.zoom; + camera.updateProjectionMatrix(); + + currentCameraSettings = null; + + } + scope.dispatchEvent( { type: 'sessionend' } ); } @@ -14561,6 +14574,12 @@ class WebXRManager extends EventDispatcher { // update user camera and its children + if ( currentCameraSettings === null && camera.isPerspectiveCamera ) { + + currentCameraSettings = { camera: camera, fov: camera.fov, zoom: camera.zoom }; + + } + updateUserCamera( camera, cameraXR, parent ); }; diff --git a/build/three.tsl.js b/build/three.tsl.js index 87d142aeb2602b..c50e39db846648 100644 --- a/build/three.tsl.js +++ b/build/three.tsl.js @@ -57,7 +57,6 @@ const anisotropy = TSL.anisotropy; const anisotropyB = TSL.anisotropyB; const anisotropyT = TSL.anisotropyT; const any = TSL.any; -const append = TSL.append; const array = TSL.array; const asin = TSL.asin; const asinh = TSL.asinh; @@ -82,6 +81,7 @@ const backgroundBlurriness = TSL.backgroundBlurriness; const backgroundIntensity = TSL.backgroundIntensity; const backgroundRotation = TSL.backgroundRotation; const batch = TSL.batch; +const batchIndirectIndex = TSL.batchIndirectIndex; const bentNormalView = TSL.bentNormalView; const billboarding = TSL.billboarding; const bitAnd = TSL.bitAnd; @@ -416,6 +416,7 @@ const objectRadius = TSL.objectRadius; const objectScale = TSL.objectScale; const objectViewPosition = TSL.objectViewPosition; const objectWorldMatrix = TSL.objectWorldMatrix; +const OnAfterObjectUpdate = TSL.OnAfterObjectUpdate; const OnBeforeObjectUpdate = TSL.OnBeforeObjectUpdate; const OnBeforeMaterialUpdate = TSL.OnBeforeMaterialUpdate; const OnBeforeRenderPipeline = TSL.OnBeforeRenderPipeline; @@ -664,4 +665,4 @@ for ( const key of Object.keys( THREE.TSL ) ) { log( code ); //*/ -export { BRDF_GGX, BRDF_Lambert, BasicPointShadowFilter, BasicShadowFilter, Break, Const, Continue, DFGLUT, D_GGX, Discard, EPSILON, F_Schlick, Fn, HALF_PI, INFINITY, If, Loop, NodeAccess, NodeShaderStage, NodeType, NodeUpdateType, OnAfterRenderPipeline, OnBeforeMaterialUpdate, OnBeforeObjectUpdate, OnBeforeRenderPipeline, OnMaterialUpdate, OnObjectUpdate, PCFShadowFilter, PI, PI2, PointShadowFilter, Return, Schlick_to_F0, ShaderNode, Stack, Switch, TBNViewMatrix, TWO_PI, VSMShadowFilter, V_GGX_SmithCorrelated, Var, VarIntent, abs, acesFilmicToneMapping, acos, acosh, add, addMethodChaining, addNodeElement, agxToneMapping, all, alphaT, ambientOcclusion, and, anisotropy, anisotropyB, anisotropyT, any, append, array, asin, asinh, assign, atan, atanh, atomicAdd, atomicAnd, atomicFunc, atomicLoad, atomicMax, atomicMin, atomicOr, atomicStore, atomicSub, atomicXor, attenuationColor, attenuationDistance, attribute, attributeArray, backgroundBlurriness, backgroundIntensity, backgroundRotation, batch, bentNormalView, billboarding, bitAnd, bitNot, bitOr, bitXor, bitangentGeometry, bitangentLocal, bitangentView, bitangentWorld, bitcast, blendBurn, blendColor, blendDodge, blendOverlay, blendScreen, bool, buffer, bufferAttribute, builtin, builtinAOContext, builtinShadowContext, bumpMap, bvec2, bvec3, bvec4, bypass, cache, call, cameraFar, cameraIndex, cameraNear, cameraNormalMatrix, cameraPosition, cameraProjectionMatrix, cameraProjectionMatrixInverse, cameraViewMatrix, cameraViewport, cameraWorldMatrix, cbrt, cdl, ceil, checker, cineonToneMapping, clamp, clearcoat, clearcoatNormalView, clearcoatRoughness, clipSpace, code, color, colorSpaceToWorking, colorToDirection, compute, computeKernel, computeSkinning, context, convert, convertColorSpace, convertToTexture, cos, cosh, countLeadingZeros, countOneBits, countTrailingZeros, cross, cubeTexture, cubeTextureBase, dFdx, dFdy, dashSize, debug, decrement, decrementBefore, defaultBuildStages, defaultShaderStages, defined, degrees, deltaTime, densityFog, densityFogFactor, depth, depthPass, determinant, difference, diffuseColor, directPointLight, directionToColor, directionToFaceDirection, dispersion, distance, div, dot, drawIndex, dynamicBufferAttribute, element, emissive, equal, equirectDirection, equirectUV, exp, exp2, exponentialHeightFogFactor, expression, faceDirection, faceForward, faceforward, float, floatBitsToInt, floatBitsToUint, floor, fog, fract, frameGroup, frameId, frontFacing, fwidth, gain, gapSize, getConstNodeType, getCurrentStack, getDistanceAttenuation, getGeometryRoughness, getNormalFromDepth, getParallaxCorrectNormal, getRoughness, getScreenPosition, getScreenPositionFromClip, getShIrradianceAt, getShadowMaterial, getShadowRenderObjectFunction, getTextureIndex, getViewPosition, globalId, glsl, glslFn, grayscale, greaterThan, greaterThanEqual, hash, highpModelNormalViewMatrix, highpModelViewMatrix, hue, increment, incrementBefore, instance, instanceIndex, instancedArray, instancedBufferAttribute, instancedDynamicBufferAttribute, instancedMesh, int, intBitsToFloat, interleavedGradientNoise, inverse, inverseSqrt, inversesqrt, invocationLocalIndex, invocationSubgroupIndex, ior, iridescence, iridescenceIOR, iridescenceThickness, ivec2, ivec3, ivec4, js, label, length, lengthSq, lessThan, lessThanEqual, lightPosition, lightProjectionUV, lightShadowMatrix, lightTargetDirection, lightTargetPosition, lightViewPosition, lightingContext, lights, linearDepth, linearToneMapping, localId, log, log2, logarithmicDepthToViewZ, luminance, mat2, mat3, mat4, matcapUV, materialAO, materialAlphaTest, materialAnisotropy, materialAnisotropyVector, materialAttenuationColor, materialAttenuationDistance, materialClearcoat, materialClearcoatNormal, materialClearcoatRoughness, materialColor, materialDispersion, materialEmissive, materialEnvIntensity, materialEnvRotation, materialIOR, materialIridescence, materialIridescenceIOR, materialIridescenceThickness, materialLightMap, materialLineDashOffset, materialLineDashSize, materialLineGapSize, materialLineScale, materialLineWidth, materialMetalness, materialNormal, materialOpacity, materialPointSize, materialReference, materialReflectivity, materialRefractionRatio, materialRetroreflectivity, materialRotation, materialRoughness, materialSheen, materialSheenRoughness, materialShininess, materialSpecular, materialSpecularColor, materialSpecularIntensity, materialSpecularStrength, materialThickness, materialTransmission, max, maxMipLevel, mediumpModelViewMatrix, metalness, min, mix, mixElement, mod, modelDirection, modelNormalMatrix, modelPosition, modelRadius, modelScale, modelViewMatrix, modelViewPosition, modelViewProjection, modelWorldMatrix, modelWorldMatrixInverse, morphReference, mrt, mul, mx_aastep, mx_add, mx_atan2, mx_cell_noise_float, mx_cell_noise_vec3, mx_contrast, mx_divide, mx_fractal_noise_float, mx_fractal_noise_float_2d, mx_fractal_noise_vec2, mx_fractal_noise_vec3, mx_fractal_noise_vec4, mx_frame, mx_heighttonormal, mx_hsvtorgb, mx_ifequal, mx_ifgreater, mx_ifgreatereq, mx_invert, mx_modulo, mx_multiply, mx_noise_float, mx_noise_vec3, mx_noise_vec4, mx_place2d, mx_power, mx_ramp4, mx_ramplr, mx_ramptb, mx_rgbtohsv, mx_rotate2d, mx_rotate3d, mx_safepower, mx_separate, mx_smoothstep, mx_splitlr, mx_splittb, mx_srgb_texture_to_lin_rec709, mx_subtract, mx_timer, mx_transform_uv, mx_unifiednoise2d, mx_unifiednoise3d, mx_worley_noise_float, mx_worley_noise_float_2d, mx_worley_noise_float_3d, mx_worley_noise_vec2, mx_worley_noise_vec3, mx_worley_noise_vec3_style, negate, negateOnBackSide, neutralToneMapping, nodeArray, nodeImmutable, nodeObject, nodeObjectIntent, nodeObjects, nodeProxy, nodeProxyIntent, normalFlat, normalGeometry, normalLocal, normalMap, normalView, normalViewGeometry, normalWorld, normalWorldGeometry, normalize, not, notEqual, numWorkgroups, objectDirection, objectGroup, objectPosition, objectRadius, objectScale, objectViewPosition, objectWorldMatrix, oneMinus, or, orthographicDepthToViewZ, oscSawtooth, oscSine, oscSquare, oscTriangle, output, outputStruct, overloadingFn, overrideNode, overrideNodes, packHalf2x16, packNormalToRGB, packSnorm2x16, packUnorm2x16, parabola, parallaxDirection, parallaxUV, parameter, pass, passTexture, pcurve, perspectiveDepthToViewZ, pmremTexture, pointShadow, pointUV, pointWidth, positionGeometry, positionLocal, positionPrevious, positionView, positionViewDirection, positionWorld, positionWorldDirection, posterize, pow, pow2, pow3, pow4, premultiplyAlpha, property, radians, rand, range, rangeFog, rangeFogFactor, reciprocal, reference, referenceBuffer, reflect, reflectVector, reflectView, reflector, refract, refractVector, refractView, reinhardToneMapping, remap, remapClamp, renderGroup, renderOutput, rendererReference, replaceDefaultUV, retroreflectivity, rotate, rotateUV, roughness, round, rtt, sRGBTransferEOTF, sRGBTransferOETF, sample, sampler, samplerComparison, saturate, saturation, screen, screenCoordinate, screenDPR, screenSize, screenUV, select, setCurrentStack, setName, shaderStages, shadow, shadowPositionWorld, shapeCircle, sharedUniformGroup, sheen, sheenRoughness, shiftLeft, shiftRight, shininess, sign, sin, sinc, sinh, skinning, smoothstep, smoothstepElement, specularColor, specularF90, spherizeUV, split, spritesheetUV, sqrt, stack, step, stepElement, storage, storageBarrier, storageTexture, storageTexture3D, struct, sub, subBuild, subgroupAdd, subgroupAll, subgroupAnd, subgroupAny, subgroupBallot, subgroupBroadcast, subgroupBroadcastFirst, subgroupElect, subgroupExclusiveAdd, subgroupExclusiveMul, subgroupInclusiveAdd, subgroupInclusiveMul, subgroupIndex, subgroupMax, subgroupMin, subgroupMul, subgroupOr, subgroupShuffle, subgroupShuffleDown, subgroupShuffleUp, subgroupShuffleXor, subgroupSize, subgroupXor, tan, tangentGeometry, tangentLocal, tangentView, tangentWorld, tanh, texture, texture3D, textureBarrier, textureBicubic, textureBicubicLevel, textureLevel, textureLoad, textureSize, textureStore, thickness, time, toneMapping, toneMappingExposure, toonOutlinePass, transformDirection, transformNormal, transformNormalByInverseViewMatrix, transformNormalByViewMatrix, transformNormalToView, transformedClearcoatNormalView, transformedNormalView, transformedNormalWorld, transmission, transpose, triNoise3D, triplanarTexture, triplanarTextures, trunc, uint, uintBitsToFloat, uniform, uniformArray, uniformCubeTexture, uniformFlow, uniformGroup, uniformTexture, unpackHalf2x16, unpackRGBToNormal, unpackSnorm2x16, unpackUnorm2x16, unpremultiplyAlpha, userData, uv, uvec2, uvec3, uvec4, varying, varyingProperty, vec2, vec3, vec4, vectorComponents, velocity, vertexColor, vertexIndex, vertexStage, vibrance, viewZToLogarithmicDepth, viewZToOrthographicDepth, viewZToPerspectiveDepth, viewZToReversedOrthographicDepth, viewZToReversedPerspectiveDepth, viewport, viewportCoordinate, viewportDepthTexture, viewportLinearDepth, viewportMipTexture, viewportOpaqueMipTexture, viewportSafeUV, viewportSharedTexture, viewportSize, viewportTexture, viewportUV, vogelDiskSample, wgsl, wgslFn, workgroupArray, workgroupBarrier, workgroupId, workingToColorSpace, xor }; +export { BRDF_GGX, BRDF_Lambert, BasicPointShadowFilter, BasicShadowFilter, Break, Const, Continue, DFGLUT, D_GGX, Discard, EPSILON, F_Schlick, Fn, HALF_PI, INFINITY, If, Loop, NodeAccess, NodeShaderStage, NodeType, NodeUpdateType, OnAfterObjectUpdate, OnAfterRenderPipeline, OnBeforeMaterialUpdate, OnBeforeObjectUpdate, OnBeforeRenderPipeline, OnMaterialUpdate, OnObjectUpdate, PCFShadowFilter, PI, PI2, PointShadowFilter, Return, Schlick_to_F0, ShaderNode, Stack, Switch, TBNViewMatrix, TWO_PI, VSMShadowFilter, V_GGX_SmithCorrelated, Var, VarIntent, abs, acesFilmicToneMapping, acos, acosh, add, addMethodChaining, addNodeElement, agxToneMapping, all, alphaT, ambientOcclusion, and, anisotropy, anisotropyB, anisotropyT, any, array, asin, asinh, assign, atan, atanh, atomicAdd, atomicAnd, atomicFunc, atomicLoad, atomicMax, atomicMin, atomicOr, atomicStore, atomicSub, atomicXor, attenuationColor, attenuationDistance, attribute, attributeArray, backgroundBlurriness, backgroundIntensity, backgroundRotation, batch, batchIndirectIndex, bentNormalView, billboarding, bitAnd, bitNot, bitOr, bitXor, bitangentGeometry, bitangentLocal, bitangentView, bitangentWorld, bitcast, blendBurn, blendColor, blendDodge, blendOverlay, blendScreen, bool, buffer, bufferAttribute, builtin, builtinAOContext, builtinShadowContext, bumpMap, bvec2, bvec3, bvec4, bypass, cache, call, cameraFar, cameraIndex, cameraNear, cameraNormalMatrix, cameraPosition, cameraProjectionMatrix, cameraProjectionMatrixInverse, cameraViewMatrix, cameraViewport, cameraWorldMatrix, cbrt, cdl, ceil, checker, cineonToneMapping, clamp, clearcoat, clearcoatNormalView, clearcoatRoughness, clipSpace, code, color, colorSpaceToWorking, colorToDirection, compute, computeKernel, computeSkinning, context, convert, convertColorSpace, convertToTexture, cos, cosh, countLeadingZeros, countOneBits, countTrailingZeros, cross, cubeTexture, cubeTextureBase, dFdx, dFdy, dashSize, debug, decrement, decrementBefore, defaultBuildStages, defaultShaderStages, defined, degrees, deltaTime, densityFog, densityFogFactor, depth, depthPass, determinant, difference, diffuseColor, directPointLight, directionToColor, directionToFaceDirection, dispersion, distance, div, dot, drawIndex, dynamicBufferAttribute, element, emissive, equal, equirectDirection, equirectUV, exp, exp2, exponentialHeightFogFactor, expression, faceDirection, faceForward, faceforward, float, floatBitsToInt, floatBitsToUint, floor, fog, fract, frameGroup, frameId, frontFacing, fwidth, gain, gapSize, getConstNodeType, getCurrentStack, getDistanceAttenuation, getGeometryRoughness, getNormalFromDepth, getParallaxCorrectNormal, getRoughness, getScreenPosition, getScreenPositionFromClip, getShIrradianceAt, getShadowMaterial, getShadowRenderObjectFunction, getTextureIndex, getViewPosition, globalId, glsl, glslFn, grayscale, greaterThan, greaterThanEqual, hash, highpModelNormalViewMatrix, highpModelViewMatrix, hue, increment, incrementBefore, instance, instanceIndex, instancedArray, instancedBufferAttribute, instancedDynamicBufferAttribute, instancedMesh, int, intBitsToFloat, interleavedGradientNoise, inverse, inverseSqrt, inversesqrt, invocationLocalIndex, invocationSubgroupIndex, ior, iridescence, iridescenceIOR, iridescenceThickness, ivec2, ivec3, ivec4, js, label, length, lengthSq, lessThan, lessThanEqual, lightPosition, lightProjectionUV, lightShadowMatrix, lightTargetDirection, lightTargetPosition, lightViewPosition, lightingContext, lights, linearDepth, linearToneMapping, localId, log, log2, logarithmicDepthToViewZ, luminance, mat2, mat3, mat4, matcapUV, materialAO, materialAlphaTest, materialAnisotropy, materialAnisotropyVector, materialAttenuationColor, materialAttenuationDistance, materialClearcoat, materialClearcoatNormal, materialClearcoatRoughness, materialColor, materialDispersion, materialEmissive, materialEnvIntensity, materialEnvRotation, materialIOR, materialIridescence, materialIridescenceIOR, materialIridescenceThickness, materialLightMap, materialLineDashOffset, materialLineDashSize, materialLineGapSize, materialLineScale, materialLineWidth, materialMetalness, materialNormal, materialOpacity, materialPointSize, materialReference, materialReflectivity, materialRefractionRatio, materialRetroreflectivity, materialRotation, materialRoughness, materialSheen, materialSheenRoughness, materialShininess, materialSpecular, materialSpecularColor, materialSpecularIntensity, materialSpecularStrength, materialThickness, materialTransmission, max, maxMipLevel, mediumpModelViewMatrix, metalness, min, mix, mixElement, mod, modelDirection, modelNormalMatrix, modelPosition, modelRadius, modelScale, modelViewMatrix, modelViewPosition, modelViewProjection, modelWorldMatrix, modelWorldMatrixInverse, morphReference, mrt, mul, mx_aastep, mx_add, mx_atan2, mx_cell_noise_float, mx_cell_noise_vec3, mx_contrast, mx_divide, mx_fractal_noise_float, mx_fractal_noise_float_2d, mx_fractal_noise_vec2, mx_fractal_noise_vec3, mx_fractal_noise_vec4, mx_frame, mx_heighttonormal, mx_hsvtorgb, mx_ifequal, mx_ifgreater, mx_ifgreatereq, mx_invert, mx_modulo, mx_multiply, mx_noise_float, mx_noise_vec3, mx_noise_vec4, mx_place2d, mx_power, mx_ramp4, mx_ramplr, mx_ramptb, mx_rgbtohsv, mx_rotate2d, mx_rotate3d, mx_safepower, mx_separate, mx_smoothstep, mx_splitlr, mx_splittb, mx_srgb_texture_to_lin_rec709, mx_subtract, mx_timer, mx_transform_uv, mx_unifiednoise2d, mx_unifiednoise3d, mx_worley_noise_float, mx_worley_noise_float_2d, mx_worley_noise_float_3d, mx_worley_noise_vec2, mx_worley_noise_vec3, mx_worley_noise_vec3_style, negate, negateOnBackSide, neutralToneMapping, nodeArray, nodeImmutable, nodeObject, nodeObjectIntent, nodeObjects, nodeProxy, nodeProxyIntent, normalFlat, normalGeometry, normalLocal, normalMap, normalView, normalViewGeometry, normalWorld, normalWorldGeometry, normalize, not, notEqual, numWorkgroups, objectDirection, objectGroup, objectPosition, objectRadius, objectScale, objectViewPosition, objectWorldMatrix, oneMinus, or, orthographicDepthToViewZ, oscSawtooth, oscSine, oscSquare, oscTriangle, output, outputStruct, overloadingFn, overrideNode, overrideNodes, packHalf2x16, packNormalToRGB, packSnorm2x16, packUnorm2x16, parabola, parallaxDirection, parallaxUV, parameter, pass, passTexture, pcurve, perspectiveDepthToViewZ, pmremTexture, pointShadow, pointUV, pointWidth, positionGeometry, positionLocal, positionPrevious, positionView, positionViewDirection, positionWorld, positionWorldDirection, posterize, pow, pow2, pow3, pow4, premultiplyAlpha, property, radians, rand, range, rangeFog, rangeFogFactor, reciprocal, reference, referenceBuffer, reflect, reflectVector, reflectView, reflector, refract, refractVector, refractView, reinhardToneMapping, remap, remapClamp, renderGroup, renderOutput, rendererReference, replaceDefaultUV, retroreflectivity, rotate, rotateUV, roughness, round, rtt, sRGBTransferEOTF, sRGBTransferOETF, sample, sampler, samplerComparison, saturate, saturation, screen, screenCoordinate, screenDPR, screenSize, screenUV, select, setCurrentStack, setName, shaderStages, shadow, shadowPositionWorld, shapeCircle, sharedUniformGroup, sheen, sheenRoughness, shiftLeft, shiftRight, shininess, sign, sin, sinc, sinh, skinning, smoothstep, smoothstepElement, specularColor, specularF90, spherizeUV, split, spritesheetUV, sqrt, stack, step, stepElement, storage, storageBarrier, storageTexture, storageTexture3D, struct, sub, subBuild, subgroupAdd, subgroupAll, subgroupAnd, subgroupAny, subgroupBallot, subgroupBroadcast, subgroupBroadcastFirst, subgroupElect, subgroupExclusiveAdd, subgroupExclusiveMul, subgroupInclusiveAdd, subgroupInclusiveMul, subgroupIndex, subgroupMax, subgroupMin, subgroupMul, subgroupOr, subgroupShuffle, subgroupShuffleDown, subgroupShuffleUp, subgroupShuffleXor, subgroupSize, subgroupXor, tan, tangentGeometry, tangentLocal, tangentView, tangentWorld, tanh, texture, texture3D, textureBarrier, textureBicubic, textureBicubicLevel, textureLevel, textureLoad, textureSize, textureStore, thickness, time, toneMapping, toneMappingExposure, toonOutlinePass, transformDirection, transformNormal, transformNormalByInverseViewMatrix, transformNormalByViewMatrix, transformNormalToView, transformedClearcoatNormalView, transformedNormalView, transformedNormalWorld, transmission, transpose, triNoise3D, triplanarTexture, triplanarTextures, trunc, uint, uintBitsToFloat, uniform, uniformArray, uniformCubeTexture, uniformFlow, uniformGroup, uniformTexture, unpackHalf2x16, unpackRGBToNormal, unpackSnorm2x16, unpackUnorm2x16, unpremultiplyAlpha, userData, uv, uvec2, uvec3, uvec4, varying, varyingProperty, vec2, vec3, vec4, vectorComponents, velocity, vertexColor, vertexIndex, vertexStage, vibrance, viewZToLogarithmicDepth, viewZToOrthographicDepth, viewZToPerspectiveDepth, viewZToReversedOrthographicDepth, viewZToReversedPerspectiveDepth, viewport, viewportCoordinate, viewportDepthTexture, viewportLinearDepth, viewportMipTexture, viewportOpaqueMipTexture, viewportSafeUV, viewportSharedTexture, viewportSize, viewportTexture, viewportUV, vogelDiskSample, wgsl, wgslFn, workgroupArray, workgroupBarrier, workgroupId, workingToColorSpace, xor }; diff --git a/build/three.webgpu.js b/build/three.webgpu.js index a687502dcdf44d..d77665149cbeed 100644 --- a/build/three.webgpu.js +++ b/build/three.webgpu.js @@ -3,8 +3,8 @@ * Copyright 2010-2026 Three.js Authors * SPDX-License-Identifier: MIT */ -import { Color, Vector2, Vector3, Vector4, Matrix2, Matrix3, Matrix4, UnsignedIntType, IntType, error, RedFormat, RedIntegerFormat, DepthFormat, DepthStencilFormat, AlphaFormat, RGFormat, RGIntegerFormat, RGBFormat, RGBIntegerFormat, EventDispatcher, MathUtils, warn, WebGLCoordinateSystem, WebGPUCoordinateSystem, ColorManagement, SRGBTransfer, NoToneMapping, StaticDrawUsage, InterleavedBufferAttribute, InterleavedBuffer, DynamicDrawUsage, NoColorSpace, log as log$1, warnOnce, Texture, Compatibility, LessCompare, LessEqualCompare, GreaterCompare, GreaterEqualCompare, NearestFilter, Sphere, BackSide, DoubleSide, CubeTexture, CubeReflectionMapping, CubeRefractionMapping, TangentSpaceNormalMap, NoNormalPacking, NormalRGPacking, NormalGAPacking, ObjectSpaceNormalMap, RED_GREEN_RGTC2_Format, RG11_EAC_Format, InstancedBufferAttribute, InstancedInterleavedBuffer, DataArrayTexture, FloatType, FramebufferTexture, LinearMipmapLinearFilter, DepthTexture, Material, LineBasicMaterial, LineDashedMaterial, NoBlending, MeshNormalMaterial, SRGBColorSpace, RenderTarget, BoxGeometry, Mesh, Scene, LinearFilter, CubeCamera, EquirectangularReflectionMapping, EquirectangularRefractionMapping, AddOperation, MixOperation, MultiplyOperation, MeshBasicMaterial, MeshLambertMaterial, MeshPhongMaterial, DataTexture, HalfFloatType, ClampToEdgeWrapping, BufferGeometry, OrthographicCamera, PerspectiveCamera, LinearSRGBColorSpace, RGBAFormat, CubeUVReflectionMapping, BufferAttribute, MeshStandardMaterial, MeshPhysicalMaterial, MeshToonMaterial, MeshMatcapMaterial, SpriteMaterial, PointsMaterial, ShadowMaterial, Uint32BufferAttribute, Uint16BufferAttribute, ByteType, UnsignedByteType, ShortType, UnsignedShortType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedInt248Type, UnsignedInt5999Type, UnsignedInt101111Type, NormalBlending, SrcAlphaFactor, OneMinusSrcAlphaFactor, AddEquation, MaterialBlending, Object3D, LinearMipMapLinearFilter, Plane, Float32BufferAttribute, UVMapping, PCFShadowMap, VSMShadowMap, BasicShadowMap, CubeDepthTexture, SphereGeometry, LinearMipmapNearestFilter, NearestMipmapLinearFilter, Float16BufferAttribute, yieldToMain, REVISION, ArrayCamera, PlaneGeometry, FrontSide, CustomBlending, ZeroFactor, CylinderGeometry, Quaternion, WebXRController, RAD2DEG, PCFSoftShadowMap, FrustumArray, Frustum, RGBAIntegerFormat, TimestampQuery, createCanvasElement, ReverseSubtractEquation, SubtractEquation, OneMinusDstAlphaFactor, OneMinusDstColorFactor, OneMinusSrcColorFactor, DstAlphaFactor, DstColorFactor, SrcAlphaSaturateFactor, SrcColorFactor, OneFactor, CullFaceNone, CullFaceBack, CullFaceFront, MultiplyBlending, SubtractiveBlending, AdditiveBlending, NotEqualDepth, GreaterDepth, GreaterEqualDepth, EqualDepth, LessEqualDepth, LessDepth, AlwaysDepth, NeverDepth, ReversedDepthFuncs, RGB_S3TC_DXT1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGB_PVRTC_4BPPV1_Format, RGB_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_PVRTC_2BPPV1_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, R11_EAC_Format, SIGNED_R11_EAC_Format, SIGNED_RG11_EAC_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_BPTC_Format, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, RED_RGTC1_Format, SIGNED_RED_RGTC1_Format, SIGNED_RED_GREEN_RGTC2_Format, MirroredRepeatWrapping, RepeatWrapping, NearestMipmapNearestFilter, NotEqualCompare, EqualCompare, AlwaysCompare, NeverCompare, LinearTransfer, getByteLength, isTypedArray, NotEqualStencilFunc, GreaterStencilFunc, GreaterEqualStencilFunc, EqualStencilFunc, LessEqualStencilFunc, LessStencilFunc, AlwaysStencilFunc, NeverStencilFunc, DecrementWrapStencilOp, IncrementWrapStencilOp, DecrementStencilOp, IncrementStencilOp, InvertStencilOp, ReplaceStencilOp, ZeroStencilOp, KeepStencilOp, MaxEquation, MinEquation, SpotLight, PointLight, DirectionalLight, RectAreaLight, AmbientLight, HemisphereLight, LightProbe, LinearToneMapping, ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping, AgXToneMapping, NeutralToneMapping, Group, Loader, FileLoader, MaterialLoader, ObjectLoader } from './three.core.js'; -export { AdditiveAnimationBlendMode, AnimationAction, AnimationClip, AnimationLoader, AnimationMixer, AnimationObjectGroup, AnimationUtils, ArcCurve, ArrowHelper, AttachedBindMode, Audio, AudioAnalyser, AudioContext, AudioListener, AudioLoader, AxesHelper, BasicDepthPacking, BatchedMesh, BezierInterpolant, Bone, BooleanKeyframeTrack, Box2, Box3, Box3Helper, BoxHelper, BufferGeometryLoader, Cache, Camera, CameraHelper, CanvasTexture, CapsuleGeometry, CatmullRomCurve3, CircleGeometry, Clock, ColorKeyframeTrack, CompressedArrayTexture, CompressedCubeTexture, CompressedTexture, CompressedTextureLoader, ConeGeometry, ConstantAlphaFactor, ConstantColorFactor, Controls, CubeTextureLoader, CubicBezierCurve, CubicBezierCurve3, CubicInterpolant, CullFaceFrontBack, Curve, CurvePath, CustomToneMapping, Cylindrical, Data3DTexture, DataTextureLoader, DataUtils, DefaultLoadingManager, DetachedBindMode, DirectionalLightHelper, DiscreteInterpolant, DodecahedronGeometry, DynamicCopyUsage, DynamicReadUsage, EdgesGeometry, EllipseCurve, Euler, ExternalTexture, ExtrudeGeometry, Fog, FogExp2, GLBufferAttribute, GLSL1, GLSL3, GridHelper, HTMLTexture, HemisphereLightHelper, IcosahedronGeometry, ImageBitmapLoader, ImageLoader, ImageUtils, InstancedBufferGeometry, InstancedMesh, Int16BufferAttribute, Int32BufferAttribute, Int8BufferAttribute, Interpolant, InterpolateBezier, InterpolateDiscrete, InterpolateLinear, InterpolateSmooth, InterpolationSamplingMode, InterpolationSamplingType, KeyframeTrack, LOD, LatheGeometry, Layers, Light, Line, Line3, LineCurve, LineCurve3, LineLoop, LineSegments, LinearInterpolant, LinearMipMapNearestFilter, LoaderUtils, LoadingManager, LoopOnce, LoopPingPong, LoopRepeat, MOUSE, MeshDepthMaterial, MeshDistanceMaterial, NearestMipMapLinearFilter, NearestMipMapNearestFilter, NormalAnimationBlendMode, NumberKeyframeTrack, OctahedronGeometry, OneMinusConstantAlphaFactor, OneMinusConstantColorFactor, Path, PlaneHelper, PointLightHelper, Points, PolarGridHelper, PolyhedronGeometry, PositionalAudio, PropertyBinding, PropertyMixer, QuadraticBezierCurve, QuadraticBezierCurve3, QuaternionKeyframeTrack, QuaternionLinearInterpolant, RGBADepthPacking, RGBDepthPacking, RGDepthPacking, RawShaderMaterial, Ray, Raycaster, RenderTarget3D, RingGeometry, ShaderMaterial, Shape, ShapeGeometry, ShapePath, ShapeUtils, Skeleton, SkeletonHelper, SkinnedMesh, Source, Spherical, SphericalHarmonics3, SplineCurve, SpotLightHelper, Sprite, StaticCopyUsage, StaticReadUsage, StereoCamera, StreamCopyUsage, StreamDrawUsage, StreamReadUsage, StringKeyframeTrack, TOUCH, TetrahedronGeometry, TextureLoader, TextureUtils, Timer, TorusGeometry, TorusKnotGeometry, Triangle, TriangleFanDrawMode, TriangleStripDrawMode, TrianglesDrawMode, TubeGeometry, Uint8BufferAttribute, Uint8ClampedBufferAttribute, Uniform, UniformsGroup, VectorKeyframeTrack, VideoFrameTexture, VideoTexture, WebGL3DRenderTarget, WebGLArrayRenderTarget, WebGLRenderTarget, WireframeGeometry, WrapAroundEnding, ZeroCurvatureEnding, ZeroSlopeEnding, getConsoleFunction, setConsoleFunction } from './three.core.js'; +import { Color, Vector2, Vector3, Vector4, Matrix2, Matrix3, Matrix4, UnsignedIntType, IntType, error, RedFormat, RedIntegerFormat, DepthFormat, DepthStencilFormat, AlphaFormat, RGFormat, RGIntegerFormat, RGBFormat, RGBIntegerFormat, EventDispatcher, MathUtils, warn, WebGLCoordinateSystem, WebGPUCoordinateSystem, ColorManagement, SRGBTransfer, NoToneMapping, StaticDrawUsage, InterleavedBufferAttribute, InterleavedBuffer, DynamicDrawUsage, NoColorSpace, log as log$1, warnOnce, Texture, Compatibility, LessCompare, LessEqualCompare, GreaterCompare, GreaterEqualCompare, NearestFilter, Sphere, BackSide, DoubleSide, CubeTexture, CubeReflectionMapping, CubeRefractionMapping, TangentSpaceNormalMap, NoNormalPacking, NormalRGPacking, NormalGAPacking, ObjectSpaceNormalMap, RED_GREEN_RGTC2_Format, RG11_EAC_Format, InstancedBufferAttribute, InstancedInterleavedBuffer, DataTexture, DataArrayTexture, FloatType, FramebufferTexture, LinearMipmapLinearFilter, DepthTexture, Material, LineBasicMaterial, LineDashedMaterial, NoBlending, MeshNormalMaterial, SRGBColorSpace, RenderTarget, BoxGeometry, Mesh, Scene, LinearFilter, CubeCamera, EquirectangularReflectionMapping, EquirectangularRefractionMapping, AddOperation, MixOperation, MultiplyOperation, MeshBasicMaterial, MeshLambertMaterial, MeshPhongMaterial, HalfFloatType, ClampToEdgeWrapping, BufferGeometry, OrthographicCamera, PerspectiveCamera, LinearSRGBColorSpace, RGBAFormat, CubeUVReflectionMapping, BufferAttribute, MeshStandardMaterial, MeshPhysicalMaterial, MeshToonMaterial, MeshMatcapMaterial, SpriteMaterial, PointsMaterial, ShadowMaterial, Uint32BufferAttribute, Uint16BufferAttribute, ByteType, UnsignedByteType, ShortType, UnsignedShortType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedInt248Type, UnsignedInt5999Type, UnsignedInt101111Type, NormalBlending, SrcAlphaFactor, OneMinusSrcAlphaFactor, AddEquation, MaterialBlending, Object3D, LinearMipMapLinearFilter, Plane, Float32BufferAttribute, UVMapping, PCFShadowMap, VSMShadowMap, BasicShadowMap, CubeDepthTexture, SphereGeometry, LinearMipmapNearestFilter, NearestMipmapLinearFilter, Float16BufferAttribute, yieldToMain, REVISION, ArrayCamera, PlaneGeometry, FrontSide, CustomBlending, ZeroFactor, CylinderGeometry, Quaternion, WebXRController, RAD2DEG, PCFSoftShadowMap, FrustumArray, Frustum, RGBAIntegerFormat, TimestampQuery, createCanvasElement, MaxEquation, MinEquation, ReverseSubtractEquation, SubtractEquation, OneMinusConstantAlphaFactor, ConstantAlphaFactor, OneMinusConstantColorFactor, ConstantColorFactor, OneMinusDstAlphaFactor, OneMinusDstColorFactor, OneMinusSrcColorFactor, DstAlphaFactor, DstColorFactor, SrcAlphaSaturateFactor, SrcColorFactor, OneFactor, CullFaceNone, CullFaceBack, CullFaceFront, MultiplyBlending, SubtractiveBlending, AdditiveBlending, NotEqualDepth, GreaterDepth, GreaterEqualDepth, EqualDepth, LessEqualDepth, LessDepth, AlwaysDepth, NeverDepth, ReversedDepthFuncs, RGB_S3TC_DXT1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGB_PVRTC_4BPPV1_Format, RGB_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_PVRTC_2BPPV1_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, R11_EAC_Format, SIGNED_R11_EAC_Format, SIGNED_RG11_EAC_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_BPTC_Format, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, RED_RGTC1_Format, SIGNED_RED_RGTC1_Format, SIGNED_RED_GREEN_RGTC2_Format, MirroredRepeatWrapping, RepeatWrapping, NearestMipmapNearestFilter, NotEqualCompare, EqualCompare, AlwaysCompare, NeverCompare, LinearTransfer, getByteLength, isTypedArray, NotEqualStencilFunc, GreaterStencilFunc, GreaterEqualStencilFunc, EqualStencilFunc, LessEqualStencilFunc, LessStencilFunc, AlwaysStencilFunc, NeverStencilFunc, DecrementWrapStencilOp, IncrementWrapStencilOp, DecrementStencilOp, IncrementStencilOp, InvertStencilOp, ReplaceStencilOp, ZeroStencilOp, KeepStencilOp, SpotLight, PointLight, DirectionalLight, RectAreaLight, AmbientLight, HemisphereLight, LightProbe, LinearToneMapping, ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping, AgXToneMapping, NeutralToneMapping, Group, Loader, FileLoader, MaterialLoader, ObjectLoader } from './three.core.js'; +export { AdditiveAnimationBlendMode, AnimationAction, AnimationClip, AnimationLoader, AnimationMixer, AnimationObjectGroup, AnimationUtils, ArcCurve, ArrowHelper, AttachedBindMode, Audio, AudioAnalyser, AudioContext, AudioListener, AudioLoader, AxesHelper, BasicDepthPacking, BatchedMesh, BezierInterpolant, Bone, BooleanKeyframeTrack, Box2, Box3, Box3Helper, BoxHelper, BufferGeometryLoader, Cache, Camera, CameraHelper, CanvasTexture, CapsuleGeometry, CatmullRomCurve3, CircleGeometry, Clock, ColorKeyframeTrack, CompressedArrayTexture, CompressedCubeTexture, CompressedTexture, CompressedTextureLoader, ConeGeometry, Controls, CubeTextureLoader, CubicBezierCurve, CubicBezierCurve3, CubicInterpolant, CullFaceFrontBack, Curve, CurvePath, CustomToneMapping, Cylindrical, Data3DTexture, DataTextureLoader, DataUtils, DefaultLoadingManager, DetachedBindMode, DirectionalLightHelper, DiscreteInterpolant, DodecahedronGeometry, DynamicCopyUsage, DynamicReadUsage, EdgesGeometry, EllipseCurve, Euler, ExternalTexture, ExtrudeGeometry, Fog, FogExp2, GLBufferAttribute, GLSL1, GLSL3, GridHelper, HTMLTexture, HemisphereLightHelper, IcosahedronGeometry, ImageBitmapLoader, ImageLoader, ImageUtils, InstancedBufferGeometry, InstancedMesh, Int16BufferAttribute, Int32BufferAttribute, Int8BufferAttribute, Interpolant, InterpolateBezier, InterpolateDiscrete, InterpolateLinear, InterpolateSmooth, InterpolationSamplingMode, InterpolationSamplingType, KeyframeTrack, LOD, LatheGeometry, Layers, Light, Line, Line3, LineCurve, LineCurve3, LineLoop, LineSegments, LinearInterpolant, LinearMipMapNearestFilter, LoaderUtils, LoadingManager, LoopOnce, LoopPingPong, LoopRepeat, MOUSE, MeshDepthMaterial, MeshDistanceMaterial, NearestMipMapLinearFilter, NearestMipMapNearestFilter, NormalAnimationBlendMode, NumberKeyframeTrack, OctahedronGeometry, Path, PlaneHelper, PointLightHelper, Points, PolarGridHelper, PolyhedronGeometry, PositionalAudio, PropertyBinding, PropertyMixer, QuadraticBezierCurve, QuadraticBezierCurve3, QuaternionKeyframeTrack, QuaternionLinearInterpolant, RGBADepthPacking, RGBDepthPacking, RGDepthPacking, RawShaderMaterial, Ray, Raycaster, RenderTarget3D, RingGeometry, ShaderMaterial, Shape, ShapeGeometry, ShapePath, ShapeUtils, Skeleton, SkeletonHelper, SkinnedMesh, Source, Spherical, SphericalHarmonics3, SplineCurve, SpotLightHelper, Sprite, StaticCopyUsage, StaticReadUsage, StereoCamera, StreamCopyUsage, StreamDrawUsage, StreamReadUsage, StringKeyframeTrack, TOUCH, TetrahedronGeometry, TextureLoader, TextureUtils, Timer, TorusGeometry, TorusKnotGeometry, Triangle, TriangleFanDrawMode, TriangleStripDrawMode, TrianglesDrawMode, TubeGeometry, Uint8BufferAttribute, Uint8ClampedBufferAttribute, Uniform, UniformsGroup, VectorKeyframeTrack, VideoFrameTexture, VideoTexture, WebGL3DRenderTarget, WebGLArrayRenderTarget, WebGLRenderTarget, WireframeGeometry, WrapAroundEnding, ZeroCurvatureEnding, ZeroSlopeEnding, getConsoleFunction, setConsoleFunction } from './three.core.js'; const refreshUniforms = [ 'alphaMap', @@ -1372,6 +1372,12 @@ function base64ToArrayBuffer( base64 ) { } +function isArrayAsParameter( params ) { + + return ( params[ 0 ] !== undefined && params[ 0 ] !== null ) && ( params[ 0 ].isNode || Object.getPrototypeOf( params[ 0 ] ) !== Object.prototype ); + +} + var NodeUtils = /*#__PURE__*/Object.freeze({ __proto__: null, arrayBufferToBase64: arrayBufferToBase64, @@ -1387,7 +1393,8 @@ var NodeUtils = /*#__PURE__*/Object.freeze({ getValueType: getValueType, hash: hash$1, hashArray: hashArray, - hashString: hashString + hashString: hashString, + isArrayAsParameter: isArrayAsParameter }); /** @@ -4432,12 +4439,6 @@ class ShaderCallNodeInternal extends Node { } -function isArrayAsParameter( params ) { - - return params[ 0 ] && ( params[ 0 ].isNode || Object.getPrototypeOf( params[ 0 ] ) !== Object.prototype ); - -} - function getLayoutParameters( params ) { let output; @@ -13246,6 +13247,18 @@ class TextureNode extends UniformNode { } + /** + * Returns `true` if the texture is sampled with a plain gather (`textureGather`), + * meaning a gather without a compare value. + * + * @return {boolean} Whether a plain gather is used or not. + */ + isPlainGather() { + + return this.gatherNode !== null && this.compareNode === null; + + } + /** * Samples the texture by defining a depth node. * @@ -17740,6 +17753,10 @@ class EventNode extends Node { this.updateType = NodeUpdateType.FRAME; + } else if ( eventType === EventNode.AFTER_OBJECT ) { + + this.updateAfterType = NodeUpdateType.OBJECT; + } else if ( eventType === EventNode.BEFORE_OBJECT ) { this.updateBeforeType = NodeUpdateType.OBJECT; @@ -17798,11 +17815,18 @@ class EventNode extends Node { } + updateAfter( frame ) { + + this.callback( frame ); + + } + } EventNode.OBJECT = 'object'; EventNode.MATERIAL = 'material'; EventNode.FRAME = 'frame'; +EventNode.AFTER_OBJECT = 'afterObject'; EventNode.BEFORE_OBJECT = 'beforeObject'; EventNode.BEFORE_MATERIAL = 'beforeMaterial'; EventNode.BEFORE_FRAME = 'beforeFrame'; @@ -17848,6 +17872,16 @@ const OnMaterialUpdate = ( callback ) => createEvent( EventNode.MATERIAL, callba */ const OnFrameUpdate = ( callback ) => createEvent( EventNode.FRAME, callback ); +/** + * Creates an event that triggers a function every time an object (Mesh|Sprite) has been rendered. + * + * The event will be bound to the declared TSL function `Fn()`; it must be declared within a `Fn()` or the JS function call must be inherited from one. + * + * @param {Function} callback - The callback function. + * @returns {EventNode} + */ +const OnAfterObjectUpdate = ( callback ) => createEvent( EventNode.AFTER_OBJECT, callback ); + /** * Creates an event that triggers a function before an object (Mesh|Sprite) is updated. * @@ -18633,11 +18667,18 @@ const instance = /*@__PURE__*/ Fn( ( [ matrices, colors = null ], builder ) => { const instancedMesh = builder.object; - OnObjectUpdate( ( { object } ) => { + OnAfterObjectUpdate( ( { object } ) => { - const previousInstanceData = _previousInstanceMatrices.get( object ); + const { previousInstanceMatrix } = _previousInstanceMatrices.get( object ); - previousInstanceData.previousInstanceMatrix.array.set( matrices.array ); + previousInstanceMatrix.array.set( matrices.array ); + previousInstanceMatrix.version = matrices.version; + + // handle interleaved path + + const previousInterleavedMatrix = _matrixBuffers.get( previousInstanceMatrix ); + + if ( previousInterleavedMatrix !== undefined ) previousInterleavedMatrix.version = matrices.version; } ); @@ -18680,6 +18721,8 @@ const instancedMesh = /*@__PURE__*/ Fn( ( [ instancedMesh ] ) => { }, 'void' ); +const _previousBatchingMatrices = /*@__PURE__*/ new WeakMap(); + /** * TSL function that retrieves the batching color for a given instance ID from a colors texture. * @@ -18713,6 +18756,61 @@ const getIndirectIndex = /*@__PURE__*/ Fn( ( [ indirectTexture, id ] ) => { } ); +/** + * Creates the node that reads a batching matrix from the given matrices texture. + * + * @param {Texture} matricesTexture - The matrices texture. + * @param {Node} id - The indirect instance ID. + * @returns {Node} The matrix node. + */ +function createBatchingMatrixNode( matricesTexture, id ) { + + const size = int( textureSize( textureLoad( matricesTexture ), 0 ).x ).toConst(); + const j = float( id ).mul( 4 ).toInt().toConst(); + + const x = j.mod( size ).toConst(); + const y = j.div( size ).toConst(); + + return mat4( + textureLoad( matricesTexture, ivec2( x, y ) ), + textureLoad( matricesTexture, ivec2( x.add( 1 ), y ) ), + textureLoad( matricesTexture, ivec2( x.add( 2 ), y ) ), + textureLoad( matricesTexture, ivec2( x.add( 3 ), y ) ) + ); + +} + +/** + * Retrieves or initializes the previous frame batching matrix node for motion vectors. + * Uses a WeakMap to cache previous frame matrices textures and their TSL nodes. + * + * @param {BatchedMesh} batchMesh - The batched mesh. + * @param {Node} id - The indirect instance ID. + * @returns {Node} The previous frame batching matrix node. + */ +function getPreviousNode( batchMesh, id ) { + + let data = _previousBatchingMatrices.get( batchMesh ); + + if ( data === undefined ) { + + const { image, format, type } = batchMesh._matricesTexture; + + const previousMatricesTexture = new DataTexture( image.data.slice(), image.width, image.height, format, type ); + + data = { + previousMatricesTexture, + node: createBatchingMatrixNode( previousMatricesTexture, id ) + }; + + _previousBatchingMatrices.set( batchMesh, data ); + + } + + return data.node; + +} + /** * TSL object representing a varying property for the batching color vector. * @@ -18720,6 +18818,13 @@ const getIndirectIndex = /*@__PURE__*/ Fn( ( [ indirectTexture, id ] ) => { */ const batchColor = /*@__PURE__*/ varyingProperty( 'vec4', 'vBatchColor' ); +/** + * TSL object representing a varying property for the batch indirect index (instance ID). + * + * @type {VaryingNode} + */ +const batchIndirectIndex = /*@__PURE__*/ varyingProperty( 'uint', 'vBatchIndirectId' ); + /** * TSL function representing the vertex shader batching setup. * Applies the batch transformation matrix to positionLocal, normalLocal, and tangentLocal. @@ -18735,19 +18840,9 @@ const batch = /*@__PURE__*/ Fn( ( [ batchMesh ], builder ) => { const indirectId = getIndirectIndex( batchMesh._indirectTexture, int( batchingIdNode ) ); - const matricesTexture = batchMesh._matricesTexture; + batchIndirectIndex.assign( indirectId ); - const size = int( textureSize( textureLoad( matricesTexture ), 0 ).x ).toConst(); - const j = float( indirectId ).mul( 4 ).toInt().toConst(); - - const x = j.mod( size ).toConst(); - const y = j.div( size ).toConst(); - const batchingMatrix = mat4( - textureLoad( matricesTexture, ivec2( x, y ) ), - textureLoad( matricesTexture, ivec2( x.add( 1 ), y ) ), - textureLoad( matricesTexture, ivec2( x.add( 2 ), y ) ), - textureLoad( matricesTexture, ivec2( x.add( 3 ), y ) ) - ); + const batchingMatrix = createBatchingMatrixNode( batchMesh._matricesTexture, indirectId ); const colorsTexture = batchMesh._colorsTexture; @@ -18763,6 +18858,22 @@ const batch = /*@__PURE__*/ Fn( ( [ batchMesh ], builder ) => { positionLocal.assign( batchingMatrix.mul( positionLocal ) ); + if ( builder.needsPreviousData() ) { + + OnAfterObjectUpdate( ( { object } ) => { + + const previousBatchData = _previousBatchingMatrices.get( object ); + + previousBatchData.previousMatricesTexture.image.data.set( object._matricesTexture.image.data ); + previousBatchData.previousMatricesTexture.needsUpdate = true; + + } ); + + const previousBatchingMatrixNode = getPreviousNode( batchMesh, indirectId ); + positionPrevious.assign( previousBatchingMatrixNode.mul( positionPrevious ).xyz ); + + } + const transformedNormal = normalLocal.div( vec3( bm[ 0 ].dot( bm[ 0 ] ), bm[ 1 ].dot( bm[ 1 ] ), bm[ 2 ].dot( bm[ 2 ] ) ) ); const batchingNormal = bm.mul( transformedNormal ).xyz; @@ -24808,10 +24919,10 @@ const getTransmissionSample = /*@__PURE__*/ Fn( ( [ fragCoord, roughness, ior ], const vTexture = material.side === BackSide ? viewportBackSideTexture : viewportFrontSideTexture; - const transmissionSample = vTexture.sample( fragCoord ); + const transmissionSample = vTexture.sample( fragCoord.mul( cameraViewport.zw ).add( cameraViewport.xy ).div( screenSize ) ); //const transmissionSample = viewportMipTexture( fragCoord ); - const lod = log2( screenSize.x ).mul( applyIorToRoughness( roughness, ior ) ); + const lod = log2( cameraViewport.z ).mul( applyIorToRoughness( roughness, ior ) ); return textureBicubicLevel( transmissionSample, lod ); @@ -28042,11 +28153,13 @@ class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial { setupSpecular() { const iorNode = this.iorNode ? float( this.iorNode ) : materialIOR; + const specularColorNode = this.specularColorNode ? vec3( this.specularColorNode ) : materialSpecularColor; + const specularIntensityNode = this.specularIntensityNode ? float( this.specularIntensityNode ) : materialSpecularIntensity; ior.assign( iorNode ); - specularColor.assign( min$1( pow2( ior.sub( 1.0 ).div( ior.add( 1.0 ) ) ).mul( materialSpecularColor ), vec3( 1.0 ) ).mul( materialSpecularIntensity ) ); + specularColor.assign( min$1( pow2( ior.sub( 1.0 ).div( ior.add( 1.0 ) ) ).mul( specularColorNode ), vec3( 1.0 ) ).mul( specularIntensityNode ) ); specularColorBlended.assign( mix( specularColor, diffuseColor.rgb, metalness ) ); - specularF90.assign( mix( materialSpecularIntensity, 1.0, metalness ) ); + specularF90.assign( mix( specularIntensityNode, 1.0, metalness ) ); } @@ -30040,6 +30153,19 @@ class RenderObject { }; + /** + * An event listener which is executed when `dispose()` is called on + * the 3D object of this render object. + * + * @method + */ + this.onObjectDispose = () => { + + this.dispose(); + + }; + + this.object.addEventListener( 'dispose', this.onObjectDispose ); this.material.addEventListener( 'dispose', this.onMaterialDispose ); this.geometry.addEventListener( 'dispose', this.onGeometryDispose ); @@ -30654,6 +30780,7 @@ class RenderObject { */ dispose() { + this.object.removeEventListener( 'dispose', this.onObjectDispose ); this.material.removeEventListener( 'dispose', this.onMaterialDispose ); this.geometry.removeEventListener( 'dispose', this.onGeometryDispose ); @@ -30983,11 +31110,6 @@ const AttributeType = { const GPU_CHUNK_BYTES = 16; -// @TODO: Move to src/constants.js - -const BlendColorFactor = 211; -const OneMinusBlendColorFactor = 212; - /** * This renderer module manages geometry attributes. * @@ -32319,9 +32441,10 @@ class Pipelines extends DataMap { * * @param {Node} computeNode - The compute node. * @param {Array} bindings - The bindings. + * @param {?Array} [promises=null] - An array of compilation promises which is only relevant in context of `Renderer.compileComputeAsync()`. * @return {ComputePipeline} The compute pipeline. */ - getForCompute( computeNode, bindings ) { + getForCompute( computeNode, bindings, promises = null ) { const { backend } = this; @@ -32368,7 +32491,7 @@ class Pipelines extends DataMap { if ( previousPipeline && previousPipeline.usedTimes === 0 ) this._releasePipeline( previousPipeline ); - pipeline = this._getComputePipeline( computeNode, stageCompute, cacheKey, bindings ); + pipeline = this._getComputePipeline( computeNode, stageCompute, cacheKey, bindings, promises ); } @@ -32580,9 +32703,10 @@ class Pipelines extends DataMap { * @param {ProgrammableStage} stageCompute - The programmable stage representing the compute shader. * @param {string} cacheKey - The cache key. * @param {Array} bindings - The bindings. + * @param {?Array} promises - An array of compilation promises which is only relevant in context of `Renderer.compileComputeAsync()`. * @return {ComputePipeline} The compute pipeline. */ - _getComputePipeline( computeNode, stageCompute, cacheKey, bindings ) { + _getComputePipeline( computeNode, stageCompute, cacheKey, bindings, promises ) { // check for existing pipeline @@ -32596,7 +32720,7 @@ class Pipelines extends DataMap { this.caches.set( cacheKey, pipeline ); - this.backend.createComputePipeline( pipeline, bindings ); + this.backend.createComputePipeline( pipeline, bindings, promises ); } @@ -35816,7 +35940,7 @@ const struct = ( membersLayout, name = null ) => { if ( params.length > 0 ) { - if ( params[ 0 ].isNode ) { + if ( isArrayAsParameter( params ) ) { values = {}; @@ -35824,7 +35948,7 @@ const struct = ( membersLayout, name = null ) => { for ( let i = 0; i < params.length; i ++ ) { - values[ names[ i ] ] = params[ i ]; + values[ names[ i ] ] = nodeObject( params[ i ] ); } @@ -42021,7 +42145,7 @@ class CodeNode extends Node { for ( const include of includes ) { - include.build( builder ); + include.build( builder, 'void' ); } @@ -48717,6 +48841,7 @@ var TSL = /*#__PURE__*/Object.freeze({ NodeShaderStage: NodeShaderStage, NodeType: NodeType, NodeUpdateType: NodeUpdateType, + OnAfterObjectUpdate: OnAfterObjectUpdate, OnAfterRenderPipeline: OnAfterRenderPipeline, OnBeforeFrameUpdate: OnBeforeFrameUpdate, OnBeforeMaterialUpdate: OnBeforeMaterialUpdate, @@ -48781,6 +48906,7 @@ var TSL = /*#__PURE__*/Object.freeze({ backgroundRotation: backgroundRotation, batch: batch, batchColor: batchColor, + batchIndirectIndex: batchIndirectIndex, bentNormalView: bentNormalView, billboarding: billboarding, bitAnd: bitAnd, @@ -56102,9 +56228,10 @@ class NodeManager extends DataMap { * Returns a node builder state for the given compute node. * * @param {Node} computeNode - The compute node. - * @return {NodeBuilderState} The node builder state. + * @param {boolean} [useAsync=false] - Whether to use async build with yielding. + * @return {NodeBuilderState|Promise} The node builder state (or Promise if async). */ - getForCompute( computeNode ) { + getForCompute( computeNode, useAsync = false ) { const computeData = this.get( computeNode ); @@ -56117,6 +56244,21 @@ class NodeManager extends DataMap { if ( onNodeBuilderCreated !== null ) onNodeBuilderCreated( nodeBuilder, computeNode ); + if ( useAsync ) { + + return nodeBuilder.buildAsync().then( () => { + + nodeBuilderState = this._createNodeBuilderState( nodeBuilder ); + + computeData.nodeBuilderState = nodeBuilderState; + computeData.version = computeNode.version; + + return nodeBuilderState; + + } ); + + } + nodeBuilder.build(); nodeBuilderState = this._createNodeBuilderState( nodeBuilder ); @@ -56130,6 +56272,27 @@ class NodeManager extends DataMap { } + /** + * Async version of getForCompute() that yields to main thread during build. + * Use this in compileComputeAsync() to prevent blocking the main thread. + * + * @param {Node} computeNode - The compute node. + * @return {Promise} A promise that resolves to the node builder state. + */ + getForComputeAsync( computeNode ) { + + const result = this.getForCompute( computeNode, true ); + + if ( result.then ) { + + return result; + + } + + return Promise.resolve( result ); + + } + /** * Creates a node builder state for the given node builder. * @@ -56392,6 +56555,27 @@ class NodeManager extends DataMap { if ( node === undefined || forceUpdate ) { + if ( node === undefined && object.isTexture === true ) { + + const onTextureDispose = () => { + + object.removeEventListener( 'dispose', onTextureDispose ); + + const node = nodeCache.get( object ); + + if ( node !== undefined ) { + + nodeCache.delete( object ); + node.dispose(); + + } + + }; + + object.addEventListener( 'dispose', onTextureDispose ); + + } + node = callback(); nodeCache.set( object, node ); @@ -57647,22 +57831,22 @@ class XRManager extends EventDispatcher { this._currentPixelRatio = null; /** - * The renderer's sample count before XR temporarily overrides it. + * The current size of the renderer's canvas + * in logical pixel unit. * * @private - * @type {?number} - * @default null + * @type {Vector2} */ - this._currentSamples = null; + this._currentSize = new Vector2(); /** - * The current size of the renderer's canvas - * in logical pixel unit. + * Holds a reference to the user camera and its current settings. * * @private - * @type {Vector2} + * @type {?Object} + * @default null */ - this._currentSize = new Vector2(); + this._currentCameraSettings = null; /** * The default event listener for handling events inside a XR session. @@ -58065,11 +58249,11 @@ class XRManager extends EventDispatcher { * Browser-side `XRWebGLBinding.foveateBoundTexture()` failures are treated as * non-fatal so they do not interrupt rendering. * - * @param {RenderTarget} renderTarget - The internal render target. + * @param {?RenderTarget} renderTarget - The internal render target. */ foveateBoundTexture( renderTarget ) { - if ( renderTarget.isPostProcessingRenderTarget !== true ) return; + if ( renderTarget === null || renderTarget.isPostProcessingRenderTarget !== true ) return; if ( this.isPresenting !== true ) return; if ( this._glProjLayer === null ) return; @@ -58154,9 +58338,7 @@ class XRManager extends EventDispatcher { */ _validateWebGPUSession() { - const renderer = this._renderer; - - if ( renderer.backend.isWebGPUBackend !== true ) return; + if ( this._renderer.backend.isWebGPUBackend !== true ) return; if ( this._session.enabledFeatures.includes( 'webgpu' ) === false ) { @@ -58164,15 +58346,6 @@ class XRManager extends EventDispatcher { } - if ( renderer.samples > 0 ) { - - warnOnce( 'THREE.XRManager: WebGPU XR does not support MSAA yet. Disabling MSAA for this XR session.' ); - - if ( this._currentSamples === null ) this._currentSamples = renderer.samples; - renderer._samples = 0; - - } - } /** @@ -58187,8 +58360,7 @@ class XRManager extends EventDispatcher { const webgpuBinding = this.getWebGPUBinding(); const glProjLayer = webgpuBinding.createProjectionLayer( { - colorFormat: webgpuBinding.getPreferredColorFormat(), - depthStencilFormat: 'depth24plus' + colorFormat: webgpuBinding.getPreferredColorFormat() } ); this._glProjLayer = glProjLayer; @@ -58204,7 +58376,10 @@ class XRManager extends EventDispatcher { depthBuffer: true, multiview: false, useArrayDepthTexture: true, - samples: 0 + storeMultisampledColorBuffer: false, + storeMultisampledDepthBuffer: false, + storeMultisampledStencilBuffer: false, + samples: this._renderer.samples } ); this._xrRenderTarget.texture.isArrayTexture = true; @@ -58227,46 +58402,27 @@ class XRManager extends EventDispatcher { _disposeWebGPUSession() { const renderer = this._renderer; - const xrRenderTarget = this._xrRenderTarget; - - if ( xrRenderTarget === null || renderer.backend.isWebGPUBackend !== true ) return; - - // XR textures are external (from XRGPUBinding), so clear cached state before disposal. const backend = renderer.backend; - const texturesModule = renderer._textures; - - const renderTargetData = backend.get ? backend.get( xrRenderTarget ) : null; - if ( renderTargetData ) { - - renderTargetData.descriptors = undefined; - - } - - const deleteResource = ( resource ) => { - - if ( resource === null || resource === undefined ) return; + const xrRenderTarget = this._xrRenderTarget; - if ( backend.delete ) backend.delete( resource ); - if ( texturesModule.delete ) texturesModule.delete( resource ); + if ( xrRenderTarget === null || backend.isWebGPUBackend !== true ) return; - }; - - for ( let i = 0; i < xrRenderTarget.textures.length; i ++ ) { + if ( renderer._renderContexts && renderer._renderContexts.dispose ) { - deleteResource( xrRenderTarget.textures[ i ] ); + renderer._renderContexts.dispose(); } - deleteResource( xrRenderTarget.depthTexture ); - deleteResource( xrRenderTarget ); + xrRenderTarget.dispose(); - if ( renderer._renderContexts && renderer._renderContexts.dispose ) { + // The external texture can be registered before the render target is initialized. + for ( const texture of xrRenderTarget.textures ) { - renderer._renderContexts.dispose(); + if ( backend.has( texture ) ) backend.destroyTexture( texture ); } - xrRenderTarget.dispose(); + backend.delete( xrRenderTarget ); } @@ -58345,6 +58501,7 @@ class XRManager extends EventDispatcher { * @param {Function} rendercall - A callback function that renders the layer. Similar to code in * the default animation loop, this method can be used to update/transform 3D object in the layer's scene. * @param {Object} [attributes={}] - Allows to configure the layer's render target. + * @param {number} [attributes.samples] - The scene MSAA sample count. Defaults to the renderer's sample count. * @return {Mesh} A mesh representing the quadratic XR layer. This mesh should be added to the XR scene. */ createQuadLayer( width, height, translation, quaternion, pixelwidth, pixelheight, rendercall, attributes = {} ) { @@ -58369,14 +58526,14 @@ class XRManager extends EventDispatcher { attributes.stencil ? DepthStencilFormat : DepthFormat ), stencilBuffer: attributes.stencil, + samples: attributes.samples ?? this._renderer.samples, resolveDepthBuffer: false, resolveStencilBuffer: false, + storeMultisampledColorBuffer: false, storeMultisampledDepthBuffer: false, storeMultisampledStencilBuffer: false } ); - renderTarget._autoAllocateDepthBuffer = true; - const material = new MeshBasicMaterial( { color: 0xffffff, side: FrontSide } ); material.map = renderTarget.texture; material.map.offset.y = 1; @@ -58393,6 +58550,7 @@ class XRManager extends EventDispatcher { quaternion: quaternion, pixelwidth: pixelwidth, pixelheight: pixelheight, + samples: renderTarget.samples, plane: plane, material: material, rendercall: rendercall, @@ -58438,6 +58596,7 @@ class XRManager extends EventDispatcher { * @param {Function} rendercall - A callback function that renders the layer. Similar to code in * the default animation loop, this method can be used to update/transform 3D object in the layer's scene. * @param {Object} [attributes={}] - Allows to configure the layer's render target. + * @param {number} [attributes.samples] - The scene MSAA sample count. Defaults to the renderer's sample count. * @return {Mesh} A mesh representing the cylindrical XR layer. This mesh should be added to the XR scene. */ createCylinderLayer( radius, centralAngle, aspectratio, translation, quaternion, pixelwidth, pixelheight, rendercall, attributes = {} ) { @@ -58462,14 +58621,14 @@ class XRManager extends EventDispatcher { attributes.stencil ? DepthStencilFormat : DepthFormat ), stencilBuffer: attributes.stencil, + samples: attributes.samples ?? this._renderer.samples, resolveDepthBuffer: false, resolveStencilBuffer: false, + storeMultisampledColorBuffer: false, storeMultisampledDepthBuffer: false, storeMultisampledStencilBuffer: false } ); - renderTarget._autoAllocateDepthBuffer = true; - const material = new MeshBasicMaterial( { color: 0xffffff, side: BackSide } ); material.map = renderTarget.texture; material.map.offset.y = 1; @@ -58487,6 +58646,7 @@ class XRManager extends EventDispatcher { quaternion: quaternion, pixelwidth: pixelwidth, pixelheight: pixelheight, + samples: renderTarget.samples, plane: plane, material: material, rendercall: rendercall, @@ -58784,11 +58944,7 @@ class XRManager extends EventDispatcher { format: RGBAFormat, type: UnsignedByteType, colorSpace: renderer.outputColorSpace, - stencilBuffer: renderer.stencil, - resolveDepthBuffer: ( glBaseLayer.ignoreDepthValues === false ), - resolveStencilBuffer: ( glBaseLayer.ignoreDepthValues === false ), - storeMultisampledDepthBuffer: ( glBaseLayer.ignoreDepthValues === false ), - storeMultisampledStencilBuffer: ( glBaseLayer.ignoreDepthValues === false ), + stencilBuffer: renderer.stencil } ); @@ -58882,6 +59038,12 @@ class XRManager extends EventDispatcher { // update user camera and its children + if ( this._currentCameraSettings === null && camera.isPerspectiveCamera ) { + + this._currentCameraSettings = { camera: camera, fov: camera.fov, zoom: camera.zoom }; + + } + updateUserCamera( camera, cameraXR, parent ); @@ -59096,13 +59258,6 @@ function onSessionEnd() { this._currentDepthNear = null; this._currentDepthFar = null; - if ( this._currentSamples !== null ) { - - renderer._samples = this._currentSamples; - this._currentSamples = null; - - } - // restore framebuffer/rendering state renderer._resetXRState(); @@ -59141,8 +59296,10 @@ function onSessionEnd() { layer.stencilBuffer ? DepthStencilFormat : DepthFormat ), stencilBuffer: layer.stencilBuffer, + samples: layer.samples, resolveDepthBuffer: false, resolveStencilBuffer: false, + storeMultisampledColorBuffer: false, storeMultisampledDepthBuffer: false, storeMultisampledStencilBuffer: false } ); @@ -59172,6 +59329,18 @@ function onSessionEnd() { renderer.setPixelRatio( this._currentPixelRatio ); renderer.setSize( this._currentSize.width, this._currentSize.height, false ); + if ( this._currentCameraSettings !== null ) { + + const camera = this._currentCameraSettings.camera; + + camera.fov = this._currentCameraSettings.fov; + camera.zoom = this._currentCameraSettings.zoom; + camera.updateProjectionMatrix(); + + this._currentCameraSettings = null; + + } + this.dispatchEvent( { type: 'sessionend' } ); } @@ -59396,6 +59565,14 @@ function onAnimationFrame( time, frame ) { renderer.setOutputRenderTarget( this._xrRenderTarget ); const frameBufferTarget = renderer._getFrameBufferTarget(); + + if ( webgpuViewData !== null ) { + + this._xrRenderTarget.samples = frameBufferTarget === null ? renderer.samples : 0; + this._xrRenderTarget.depthBuffer = frameBufferTarget === null; + + } + renderer.xr.foveateBoundTexture( frameBufferTarget ); } @@ -60620,9 +60797,10 @@ class Renderer { * @param {Object3D} scene - The scene or 3D object to precompile. * @param {Camera} camera - The camera that is used to render the scene. * @param {?Scene} targetScene - If the first argument is a 3D object, this parameter must represent the scene the 3D object is going to be added. + * @param {onProgressCallback} [onProgress] - Executed while the compilation is in progress. * @return {Promise} A Promise that resolves when the compile has been finished. */ - async compileAsync( scene, camera, targetScene = null ) { + async compileAsync( scene, camera, targetScene = null, onProgress = null ) { if ( this._isDeviceLost === true ) return; @@ -60656,7 +60834,9 @@ class Renderer { // Match render()'s logic: use frameBufferTarget when needsFrameBufferTarget is true const useFrameBufferTarget = this.needsFrameBufferTarget && this._renderTarget === null; - const renderTarget = useFrameBufferTarget ? this._getFrameBufferTarget() : ( this._renderTarget || this._outputRenderTarget ); + const outputRenderTarget = this._renderTarget || this._outputRenderTarget; + const useXRCamera = this.xr.isPresenting === true && this.isOutputTarget; + const renderTarget = useFrameBufferTarget ? this._getFrameBufferTarget() : outputRenderTarget; const renderContext = this._renderContexts.get( renderTarget, this._mrt ); const activeMipmapLevel = this._activeMipmapLevel; @@ -60687,7 +60867,7 @@ class Renderer { if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); - camera = this._updateCamera( camera ); + camera = this._updateCamera( camera, useXRCamera ); // @@ -60782,6 +60962,9 @@ class Renderer { // Process compilation work items sequentially to avoid freezing // Yields between objects to keep animation smooth + const total = compilationPromises.length; + let loaded = 0; + for ( const item of compilationPromises ) { const renderObject = this._objects.get( item.object, item.material, item.scene, item.camera, item.lightsNode, item.renderContext, item.clippingContext, item.passId ); @@ -60811,6 +60994,14 @@ class Renderer { this._nodes.updateAfter( renderObject ); this._isPreCompiling = false; + loaded ++; + + if ( onProgress !== null ) { + + onProgress( new ProgressEvent( 'progress', { lengthComputable: true, loaded, total } ) ); + + } + // Yield between objects to allow animation frames await yieldToMain(); @@ -60818,6 +61009,90 @@ class Renderer { } + /** + * Compile compute programs. This can be useful to avoid a + * phenomenon which is called "shader compilation stutter", which occurs when + * rendering an object with a new shader for the first time. + * + * @async + * @param {Node|Array} computeNodes - The compute node(s). + * @param {onProgressCallback} [onProgress] - Executed while the compilation is in progress. + * @return {Promise} A Promise that resolves when the compile has been finished. + */ + async compileComputeAsync( computeNodes, onProgress = null ) { + + if ( this._isDeviceLost === true ) return; + + if ( this._initialized === false ) await this.init(); + + const computeList = Array.isArray( computeNodes ) ? computeNodes : [ computeNodes ]; + + if ( computeList.length === 0 || computeList.some( ( computeNode ) => computeNode === undefined || computeNode === null || computeNode.isComputeNode !== true ) ) { + + throw new Error( 'THREE.Renderer: .compileComputeAsync() expects a ComputeNode.' ); + + } + + const total = computeList.length; + let loaded = 0; + + // + + const pipelines = this._pipelines; + const bindings = this._bindings; + const nodes = this._nodes; + + for ( const computeNode of computeList ) { + + if ( pipelines.has( computeNode ) === false ) { + + const dispose = () => { + + computeNode.removeEventListener( 'dispose', dispose ); + + pipelines.delete( computeNode ); + bindings.deleteForCompute( computeNode ); + nodes.delete( computeNode ); + + }; + + computeNode.addEventListener( 'dispose', dispose ); + + const onInitFn = computeNode.onInitFunction; + + if ( onInitFn !== null ) { + + onInitFn.call( computeNode, { renderer: this } ); + + } + + } + + await nodes.getForComputeAsync( computeNode ); + + nodes.updateForCompute( computeNode ); + bindings.updateForCompute( computeNode ); + + const computeBindings = bindings.getForCompute( computeNode ); + const compilationPromises = []; + + pipelines.getForCompute( computeNode, computeBindings, compilationPromises ); + await Promise.all( compilationPromises ); + + loaded ++; + + if ( onProgress !== null ) { + + onProgress( new ProgressEvent( 'progress', { lengthComputable: true, loaded, total } ) ); + + } + + if ( loaded < total ) await yieldToMain(); + + } + + } + /** * Renders the scene in an async fashion. * @@ -61137,7 +61412,9 @@ class Renderer { _renderOutputLayers( quad, renderTarget ) { - if ( renderTarget.texture.isArrayTexture !== true || renderTarget.texture.image.depth <= 1 ) { + const useMultiview = this.backend.isWebGLBackend === true && renderTarget.multiview === true; + + if ( useMultiview || renderTarget.texture.isArrayTexture !== true || renderTarget.texture.image.depth <= 1 ) { this._renderScene( quad, quad.camera, false ); return; @@ -61176,12 +61453,7 @@ class Renderer { */ _getFrameBufferTarget() { - const { currentToneMapping, currentColorSpace } = this; - - const useToneMapping = currentToneMapping !== NoToneMapping; - const useColorSpace = currentColorSpace !== ColorManagement.workingColorSpace; - - if ( useToneMapping === false && useColorSpace === false ) return null; + if ( this.needsFrameBufferTarget === false ) return null; const { width, height } = this.getDrawingBufferSize( _drawingBufferSize ); const { depth, stencil } = this; @@ -61306,6 +61578,7 @@ class Renderer { const sceneRef = ( scene.isScene === true ) ? scene : _scene; const outputRenderTarget = this._renderTarget || this._outputRenderTarget; + const useXRCamera = this.xr.isPresenting === true && this.isOutputTarget; const activeCubeFace = this._activeCubeFace; const activeMipmapLevel = this._activeMipmapLevel; @@ -61374,7 +61647,7 @@ class Renderer { if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); - camera = this._updateCamera( camera ); + camera = this._updateCamera( camera, useXRCamera ); // @@ -62224,6 +62497,7 @@ class Renderer { * Returns `true` if a framebuffer target is needed to perform tone mapping or color space conversion. * If this is the case, the renderer allocates an internal render target for that purpose. * + * @type {boolean} */ get needsFrameBufferTarget() { @@ -62839,7 +63113,7 @@ class Renderer { * @param {number} width - The width of the copy region. * @param {number} height - The height of the copy region. * @param {number} [textureIndex=0] - The texture index of a MRT render target. - * @param {number} [faceIndex=0] - The active cube face index. + * @param {number} [faceIndex=0] - The cube face, depth slice or array layer index. * @return {Promise} A Promise that resolves when the read has been finished. The resolve provides the read data as a typed array. */ async readRenderTargetPixelsAsync( renderTarget, x, y, width, height, textureIndex = 0, faceIndex = 0 ) { @@ -63221,13 +63495,14 @@ class Renderer { * * @private * @param {Camera} camera - The camera to update. + * @param {boolean} useXRCamera - Whether the XR camera should be used when presenting. * @return {Camera} The returned camera might be different depending on whether XR is used or not. */ - _updateCamera( camera ) { + _updateCamera( camera, useXRCamera ) { const xr = this.xr; - if ( xr.isPresenting === false ) { + if ( xr.isPresenting === false || useXRCamera === false ) { let projectionMatrixNeedsUpdate = false; @@ -63297,7 +63572,7 @@ class Renderer { // handle XR - if ( xr.enabled === true && xr.isPresenting === true ) { + if ( useXRCamera === true && xr.enabled === true && xr.isPresenting === true ) { if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera ); camera = xr.getCamera(); // use XR camera for rendering @@ -63588,7 +63863,8 @@ class Renderer { * @param {Object3D} scene - The scene or 3D object to precompile. * @param {Camera} camera - The camera that is used to render the scene. * @param {Scene} targetScene - If the first argument is a 3D object, this parameter must represent the scene the 3D object is going to be added. - * @return {function(Object3D, Camera, ?Scene): Promise|undefined} A Promise that resolves when the compile has been finished. + * @param {onProgressCallback} [onProgress] - Executed while the compilation is in progress. + * @return {function(Object3D, Camera, ?Scene, ?onProgressCallback): Promise|undefined} A Promise that resolves when the compile has been finished. */ get compile() { @@ -65773,7 +66049,7 @@ ${ flowData.code } snippet = `${typePrefix}sampler3D ${ uniform.name };`; - } else if ( texture.compareFunction && textureNode.compareNode !== null ) { + } else if ( texture.compareFunction && textureNode.isPlainGather() === false ) { if ( texture.isArrayTexture === true ) { @@ -66982,8 +67258,9 @@ class Backend { * @abstract * @param {ComputePipeline} computePipeline - The compute pipeline. * @param {Array} bindings - The bindings. + * @param {?Array} [promises=null] - Optional compilation promises. */ - createComputePipeline( /*computePipeline, bindings*/ ) { } + createComputePipeline( /*computePipeline, bindings, promises*/ ) { } // cache key @@ -67090,7 +67367,7 @@ class Backend { * @param {number} y - The y coordinate of the copy origin. * @param {number} width - The width of the copy. * @param {number} height - The height of the copy. - * @param {number} faceIndex - The face index. + * @param {number} faceIndex - The cube face, depth slice or array layer index. * @return {Promise} A Promise that resolves with a typed array when the copy operation has finished. */ async copyTextureToBuffer( /*texture, x, y, width, height, faceIndex*/ ) {} @@ -67964,6 +68241,8 @@ class WebGLState { this.currentBlendDst = null; this.currentBlendSrcAlpha = null; this.currentBlendDstAlpha = null; + this.currentBlendColor = new Color( 0, 0, 0 ); + this.currentBlendAlpha = 0; this.currentPremultipledAlpha = null; this.currentPolygonOffsetFactor = null; this.currentPolygonOffsetUnits = null; @@ -68010,7 +68289,9 @@ class WebGLState { equationToGL = { [ AddEquation ]: gl.FUNC_ADD, [ SubtractEquation ]: gl.FUNC_SUBTRACT, - [ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT + [ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT, + [ MinEquation ]: gl.MIN, + [ MaxEquation ]: gl.MAX }; factorToGL = { @@ -68024,7 +68305,11 @@ class WebGLState { [ OneMinusSrcColorFactor ]: gl.ONE_MINUS_SRC_COLOR, [ OneMinusSrcAlphaFactor ]: gl.ONE_MINUS_SRC_ALPHA, [ OneMinusDstColorFactor ]: gl.ONE_MINUS_DST_COLOR, - [ OneMinusDstAlphaFactor ]: gl.ONE_MINUS_DST_ALPHA + [ OneMinusDstAlphaFactor ]: gl.ONE_MINUS_DST_ALPHA, + [ ConstantColorFactor ]: gl.CONSTANT_COLOR, + [ OneMinusConstantColorFactor ]: gl.ONE_MINUS_CONSTANT_COLOR, + [ ConstantAlphaFactor ]: gl.CONSTANT_ALPHA, + [ OneMinusConstantAlphaFactor ]: gl.ONE_MINUS_CONSTANT_ALPHA }; const scissorParam = gl.getParameter( gl.SCISSOR_BOX ); @@ -68326,7 +68611,7 @@ class WebGLState { * Defines the blending. * * This method caches the state so `gl.blendEquation()`, `gl.blendEquationSeparate()`, - * `gl.blendFunc()` and `gl.blendFuncSeparate()` are only called when necessary. + * `gl.blendFunc()`, `gl.blendFuncSeparate()` and `gl.blendColor()` are only called when necessary. * * @param {number} blending - The blending type. * @param {number} blendEquation - The blending equation. @@ -68335,9 +68620,11 @@ class WebGLState { * @param {number} blendEquationAlpha - Only relevant for custom blending. The blending equation for alpha. * @param {number} blendSrcAlpha - Only relevant for custom blending. The alpha source blending factor. * @param {number} blendDstAlpha - Only relevant for custom blending. The alpha destination blending factor. + * @param {Color} blendColor - Only relevant for custom blending. The RGB values of the constant blend color. + * @param {number} blendAlpha - Only relevant for custom blending. The alpha value of the constant blend color. * @param {boolean} premultipliedAlpha - Whether premultiplied alpha is enabled or not. */ - setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { + setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, blendColor, blendAlpha, premultipliedAlpha ) { const { gl } = this; @@ -68468,6 +68755,15 @@ class WebGLState { } + if ( blendColor.equals( this.currentBlendColor ) === false || blendAlpha !== this.currentBlendAlpha ) { + + gl.blendColor( blendColor.r, blendColor.g, blendColor.b, blendAlpha ); + + this.currentBlendColor.copy( blendColor ); + this.currentBlendAlpha = blendAlpha; + + } + this.currentBlending = blending; this.currentPremultipledAlpha = false; @@ -68817,7 +69113,7 @@ class WebGLState { ( material.blending === NormalBlending && material.transparent === false ) ? this.setBlending( NoBlending ) - : this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); + : this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.blendColor, material.blendAlpha, material.premultipliedAlpha ); this.setDepthFunc( material.depthFunc ); this.setDepthTest( material.depthTest ); @@ -69344,6 +69640,8 @@ class WebGLState { this.currentBlendDst = null; this.currentBlendSrcAlpha = null; this.currentBlendDstAlpha = null; + this.currentBlendColor.set( 0, 0, 0 ); + this.currentBlendAlpha = 0; this.currentPremultipledAlpha = null; this.currentPolygonOffsetFactor = null; this.currentPolygonOffsetUnits = null; @@ -70929,7 +71227,7 @@ class WebGLTextureUtils { * @param {number} y - The y coordinate of the copy origin. * @param {number} width - The width of the copy. * @param {number} height - The height of the copy. - * @param {number} faceIndex - The face index. + * @param {number} faceIndex - The cube face, depth slice or array layer index. * @return {Promise} A Promise that resolves with a typed array when the copy operation has finished. */ async copyTextureToBuffer( texture, x, y, width, height, faceIndex ) { @@ -70942,9 +71240,17 @@ class WebGLTextureUtils { backend.state.bindFramebuffer( gl.READ_FRAMEBUFFER, fb ); - const target = texture.isCubeTexture ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex : gl.TEXTURE_2D; + if ( texture.isData3DTexture || texture.isDataArrayTexture || texture.isArrayTexture ) { + + gl.framebufferTextureLayer( gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, textureGPU, 0, faceIndex ); + + } else { + + const target = texture.isCubeTexture ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex : gl.TEXTURE_2D; - gl.framebufferTexture2D( gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, target, textureGPU, 0 ); + gl.framebufferTexture2D( gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, target, textureGPU, 0 ); + + } const typedArrayType = this._getTypedArrayType( glType ); const bytesPerTexel = this._getBytesPerTexel( glType, glFormat ); @@ -73321,7 +73627,7 @@ class WebGLBackend extends Backend { * @param {number} y - The y coordinate of the copy origin. * @param {number} width - The width of the copy. * @param {number} height - The height of the copy. - * @param {number} faceIndex - The face index. + * @param {number} faceIndex - The cube face, depth slice or array layer index. * @return {Promise} A Promise that resolves with a typed array when the copy operation has finished. */ async copyTextureToBuffer( texture, x, y, width, height, faceIndex ) { @@ -73605,10 +73911,11 @@ class WebGLBackend extends Backend { * * @param {ComputePipeline} computePipeline - The compute pipeline. * @param {Array} bindings - The bindings. + * @param {?Array} [promises=null] - Optional compilation promises. */ - createComputePipeline( computePipeline, bindings ) { + createComputePipeline( computePipeline, bindings, promises = null ) { - const { state, gl } = this; + const { gl } = this; // Program @@ -73651,19 +73958,6 @@ class WebGLBackend extends Backend { gl.linkProgram( programGPU ); - if ( gl.getProgramParameter( programGPU, gl.LINK_STATUS ) === false ) { - - this._logProgramError( programGPU, fragmentShader, vertexShader ); - - - } - - state.useProgram( programGPU ); - - // Bindings - - this._setupBindings( bindings, programGPU ); - const attributeNodes = computeProgram.attributes; const attributes = []; const transformBuffers = []; @@ -73690,14 +73984,73 @@ class WebGLBackend extends Backend { } - // + // Store pipeline data this.set( computePipeline, { programGPU, + fragmentShader, + vertexShader, transformBuffers, attributes } ); + if ( promises !== null && this.parallel ) { + + const parallel = this.parallel; + + const p = new Promise( ( resolve ) => { + + const checkStatus = () => { + + if ( gl.getProgramParameter( programGPU, parallel.COMPLETION_STATUS_KHR ) ) { + + this._completeComputeCompile( computePipeline, bindings ); + resolve(); + + } else { + + requestAnimationFrame( checkStatus ); + + } + + }; + + checkStatus(); + + } ); + + promises.push( p ); + return; + + } + + // Sync fallback + this._completeComputeCompile( computePipeline, bindings ); + + } + + /** + * Completes the compute pipeline setup for the given compute pipeline. + * + * @param {ComputePipeline} computePipeline - The compute pipeline. + * @param {Array} bindings - Array of bind groups. + */ + _completeComputeCompile( computePipeline, bindings ) { + + const { state, gl } = this; + const { programGPU, fragmentShader, vertexShader } = this.get( computePipeline ); + + if ( gl.getProgramParameter( programGPU, gl.LINK_STATUS ) === false ) { + + this._logProgramError( programGPU, fragmentShader, vertexShader ); + + } + + state.useProgram( programGPU ); + + // Bindings (must be after link completion) + this._setupBindings( bindings, programGPU ); + } /** @@ -74722,7 +75075,8 @@ class WebGLBackend extends Backend { } - } else if ( renderTarget.storeMultisampledDepthBuffer === false && renderTargetContextData.framebuffers ) { + } else if ( this._supportsInvalidateFramebuffer === true && renderTargetContextData.framebuffers && + ( renderTarget._autoAllocateDepthBuffer === true || ( renderTarget.samples > 0 && renderTarget.storeMultisampledDepthBuffer === false ) ) ) { const fb = renderTargetContextData.framebuffers[ renderContext.getCacheKey() ]; state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb ); @@ -75363,10 +75717,21 @@ class WebGPUUtils { } else if ( texture.isDepthTexture && ! texture.renderTarget ) { - const renderer = this.backend.renderer; - const renderTarget = renderer.getRenderTarget(); + const textureData = this.backend.get( texture ); + + if ( textureData.texture !== undefined ) { + + // use the effective sample count of the allocated texture + + samples = textureData.texture.sampleCount; + + } else { - samples = renderTarget ? renderTarget.samples : renderer.currentSamples; + // otherwise use the current samples of the renderer + + samples = this.backend.renderer.currentSamples; + + } } else if ( texture.renderTarget ) { @@ -75377,7 +75742,8 @@ class WebGPUUtils { samples = this.getSampleCount( samples || 1 ); const isMSAA = samples > 1 && texture.renderTarget !== null && ( texture.isDepthTexture !== true && texture.isFramebufferTexture !== true ); - const primarySamples = isMSAA ? 1 : samples; + const isMSAAArrayDepthTexture = samples > 1 && texture.renderTarget !== null && texture.isDepthTexture === true && texture.isArrayTexture === true; + const primarySamples = isMSAA || isMSAAArrayDepthTexture ? 1 : samples; return { samples, primarySamples, isMSAA }; @@ -76287,7 +76653,7 @@ const _renderPassDescriptor = new GPURenderPassDescriptor(); const _renderPipelineDescriptor$1 = new GPURenderPipelineDescriptor(); const _colorAttachment = new GPURenderPassColorAttachment(); const _shaderModuleDescriptor$1 = new GPUShaderModuleDescriptor(); -const _textureDescriptor$1 = new GPUTextureDescriptor(); +const _textureDescriptor$2 = new GPUTextureDescriptor(); const _viewDescriptor$2 = new GPUTextureViewDescriptor(); /** @@ -76498,14 +76864,14 @@ fn main_cube( Varys: VarysStruct ) -> @location( 0 ) vec4 { const format = textureGPUDescriptor.format; const { width, height } = textureGPUDescriptor.size; - _textureDescriptor$1.size.width = width; - _textureDescriptor$1.size.height = height; - _textureDescriptor$1.format = format; - _textureDescriptor$1.usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING; + _textureDescriptor$2.size.width = width; + _textureDescriptor$2.size.height = height; + _textureDescriptor$2.format = format; + _textureDescriptor$2.usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING; - const tempTexture = this.device.createTexture( _textureDescriptor$1 ); + const tempTexture = this.device.createTexture( _textureDescriptor$2 ); - _textureDescriptor$1.reset(); + _textureDescriptor$2.reset(); const copyTransferPipeline = this.getTransferPipeline( format, textureGPU.textureBindingViewDimension ); const flipTransferPipeline = this.getTransferPipeline( format, tempTexture.textureBindingViewDimension ); @@ -77148,7 +77514,7 @@ const _texelCopyBufferInfo = new GPUTexelCopyBufferInfo(); const _texelCopyBufferLayout = new GPUTexelCopyBufferLayout(); const _copyExternalImageSourceInfo = new GPUCopyExternalImageSourceInfo(); const _copyExternalImageDestInfo = new GPUCopyExternalImageDestInfo(); -const _textureDescriptor = new GPUTextureDescriptor(); +const _textureDescriptor$1 = new GPUTextureDescriptor(); const _extent3D$1 = new GPUExtent3D(); const _compareToWebGPU = { @@ -77264,10 +77630,12 @@ class WebGPUTextureUtils { const texture = binding.texture; const textureNode = binding.textureNode; + const isComparison = texture.isDepthTexture === true && texture.compareFunction !== null && textureNode.isPlainGather() === false && backend.hasCompatibility( Compatibility.TEXTURE_COMPARE ); + const samplerKey = texture.minFilter + '-' + texture.magFilter + '-' + texture.wrapS + '-' + texture.wrapT + '-' + ( texture.wrapR || '0' ) + '-' + texture.anisotropy + '-' + ( texture.isDepthTexture === true ? 1 : 0 ) + '-' + - ( texture.compareFunction !== null && textureNode.compareNode !== null ? texture.compareFunction : 0 ); + ( isComparison ? texture.compareFunction : 0 ); let samplerData = this._samplerCache.get( samplerKey ); @@ -77281,7 +77649,7 @@ class WebGPUTextureUtils { _samplerDescriptor.mipmapFilter = this._convertMipmapFilterMode( texture.minFilter ); // Depth textures without compare function must use non-filtering (nearest) sampling - if ( texture.isDepthTexture && ( texture.compareFunction === null || textureNode.compareNode === null ) ) { + if ( texture.isDepthTexture && isComparison === false ) { _samplerDescriptor.magFilter = GPUFilterMode.Nearest; _samplerDescriptor.minFilter = GPUFilterMode.Nearest; @@ -77297,7 +77665,7 @@ class WebGPUTextureUtils { } - if ( texture.isDepthTexture && texture.compareFunction !== null && textureNode.compareNode !== null && backend.hasCompatibility( Compatibility.TEXTURE_COMPARE ) ) { + if ( isComparison ) { _samplerDescriptor.compare = _compareToWebGPU[ texture.compareFunction ]; @@ -77457,6 +77825,20 @@ class WebGPUTextureUtils { textureData.format = format; const { samples, primarySamples, isMSAA } = backend.utils.getTextureSampleData( texture ); + const renderTarget = texture.renderTarget; + + // WebGPU multisampled 2D textures can only have a single array layer. + const useSeparateMSAATextures = samples > 1 && renderTarget !== null && depth > 1 && dimension === GPUTextureDimension.TwoD; + const supportsTransientAttachments = GPUTextureUsage.TRANSIENT_ATTACHMENT !== undefined; + // Layered rendering can resume after a framebuffer copy, so its attachments must support loading. + const useTransientAttachments = supportsTransientAttachments && useSeparateMSAATextures === false; + const useTransientDepthAttachment = texture.isDepthTexture === true && + useTransientAttachments && + renderTarget?.storeMultisampledDepthBuffer === false && + ( renderTarget.stencilBuffer === false || renderTarget.storeMultisampledStencilBuffer === false ); + const useTransientColorAttachment = texture.isDepthTexture !== true && + useTransientAttachments && + renderTarget?.storeMultisampledColorBuffer === false; let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC; @@ -77472,17 +77854,11 @@ class WebGPUTextureUtils { } - const renderTarget = texture.renderTarget; - // when the multisampled data are discarded, try to use a transient attachment if possible - if ( texture.isDepthTexture === true && primarySamples > 1 && GPUTextureUsage.TRANSIENT_ATTACHMENT !== undefined ) { - - if ( renderTarget?.storeMultisampledDepthBuffer === false && ( renderTarget.stencilBuffer === false || renderTarget.storeMultisampledStencilBuffer === false ) ) { + if ( primarySamples > 1 && useTransientDepthAttachment ) { - usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TRANSIENT_ATTACHMENT; - - } + usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TRANSIENT_ATTACHMENT; } @@ -77526,7 +77902,7 @@ class WebGPUTextureUtils { } - if ( isMSAA ) { + if ( isMSAA || useSeparateMSAATextures ) { const msaaTextureDescriptorGPU = Object.assign( {}, textureDescriptorGPU ); @@ -77536,13 +77912,30 @@ class WebGPUTextureUtils { // when the multisampled data are discarded, try to use a transient attachment if possible - if ( renderTarget?.storeMultisampledColorBuffer === false && GPUTextureUsage.TRANSIENT_ATTACHMENT !== undefined ) { + if ( useTransientDepthAttachment || useTransientColorAttachment ) { msaaTextureDescriptorGPU.usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TRANSIENT_ATTACHMENT; } - textureData.msaaTexture = backend.device.createTexture( msaaTextureDescriptorGPU ); + if ( useSeparateMSAATextures ) { + + msaaTextureDescriptorGPU.size = Object.assign( {}, msaaTextureDescriptorGPU.size, { depthOrArrayLayers: 1 } ); + + textureData.msaaTextures = []; + + for ( let i = 0; i < depth; i ++ ) { + + msaaTextureDescriptorGPU.label = textureDescriptorGPU.label + '-msaa-' + i; + textureData.msaaTextures.push( backend.device.createTexture( msaaTextureDescriptorGPU ) ); + + } + + } else { + + textureData.msaaTexture = backend.device.createTexture( msaaTextureDescriptorGPU ); + + } } @@ -77563,10 +77956,16 @@ class WebGPUTextureUtils { const backend = this.backend; const textureData = backend.get( texture ); - if ( textureData.texture !== undefined && isDefaultTexture === false && texture.isExternalTexture !== true ) textureData.texture.destroy(); + if ( textureData.texture !== undefined && isDefaultTexture === false && texture.isExternalTexture !== true && textureData.externalTexture !== true ) textureData.texture.destroy(); if ( textureData.msaaTexture !== undefined ) textureData.msaaTexture.destroy(); + if ( textureData.msaaTextures !== undefined ) { + + for ( const msaaTexture of textureData.msaaTextures ) msaaTexture.destroy(); + + } + backend.delete( texture ); } @@ -77613,16 +78012,16 @@ class WebGPUTextureUtils { if ( colorBuffer ) colorBuffer.destroy(); - _textureDescriptor.label = 'colorBuffer'; - _textureDescriptor.size.width = width; - _textureDescriptor.size.height = height; - _textureDescriptor.sampleCount = backend.utils.getSampleCount( backend.renderer.currentSamples ); - _textureDescriptor.format = backend.utils.getPreferredCanvasFormat(); - _textureDescriptor.usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC; + _textureDescriptor$1.label = 'colorBuffer'; + _textureDescriptor$1.size.width = width; + _textureDescriptor$1.size.height = height; + _textureDescriptor$1.sampleCount = backend.utils.getSampleCount( backend.renderer.currentSamples ); + _textureDescriptor$1.format = backend.utils.getPreferredCanvasFormat(); + _textureDescriptor$1.usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC; - colorBuffer = backend.device.createTexture( _textureDescriptor ); + colorBuffer = backend.device.createTexture( _textureDescriptor$1 ); - _textureDescriptor.reset(); + _textureDescriptor$1.reset(); // @@ -77871,7 +78270,7 @@ class WebGPUTextureUtils { * @param {number} y - The y coordinate of the copy origin. * @param {number} width - The width of the copy. * @param {number} height - The height of the copy. - * @param {number} faceIndex - The face index. + * @param {number} faceIndex - The cube face, depth slice or array layer index. * @return {Promise} A Promise that resolves with a typed array when the copy operation has finished. */ async copyTextureToBuffer( texture, x, y, width, height, faceIndex ) { @@ -79004,6 +79403,7 @@ const wgslTypeLib$1 = { 'mat4x4f': 'mat4', 'sampler': 'sampler', + 'sampler_comparison': 'samplerComparison', 'texture_1d': 'texture', @@ -81280,7 +81680,7 @@ ${ flowData.code } if ( needsSampler ) { - if ( this.isSampleCompare( texture ) && textureNode.compareNode !== null ) { + if ( this.isSampleCompare( texture ) && textureNode.isPlainGather() === false ) { bindingSnippets.push( `@binding( ${ uniformIndexes.binding ++ } ) @group( ${ uniformIndexes.group } ) var ${ uniform.name }_sampler : sampler_comparison;` ); @@ -81893,8 +82293,8 @@ const typedArraysToVertexFormatPrefix = new Map( [ [ Uint8Array, [ 'uint8', 'unorm8' ]], [ Int16Array, [ 'sint16', 'snorm16' ]], [ Uint16Array, [ 'uint16', 'unorm16' ]], - [ Int32Array, [ 'sint32', 'snorm32' ]], - [ Uint32Array, [ 'uint32', 'unorm32' ]], + [ Int32Array, [ 'sint32' ]], + [ Uint32Array, [ 'uint32' ]], [ Float32Array, [ 'float32', ]], ] ); @@ -81910,9 +82310,7 @@ const typedAttributeToVertexFormatPrefix = new Map( [ const typeArraysToVertexFormatPrefixForItemSize1 = new Map( [ [ Int32Array, 'sint32' ], - [ Int16Array, 'sint32' ], // patch for INT16 [ Uint32Array, 'uint32' ], - [ Uint16Array, 'uint32' ], // patch for UINT16 [ Float32Array, 'float32' ] ] ); @@ -81961,7 +82359,7 @@ class WebGPUAttributeUtils { let array = bufferAttribute.array; // patch for INT16 and UINT16 - if ( attribute.normalized === false ) { + if ( attribute.normalized === false && attribute.isInterleavedBufferAttribute !== true ) { if ( array.constructor === Int16Array || array.constructor === Int8Array ) { @@ -82200,13 +82598,6 @@ class WebGPUAttributeUtils { } - // patch for INT16 and UINT16 - if ( geometryAttribute.normalized === false && ( geometryAttribute.array.constructor === Int16Array || geometryAttribute.array.constructor === Uint16Array ) ) { - - arrayStride = 4; - - } - vertexBufferLayout = { arrayStride, attributes: [], @@ -83030,7 +83421,7 @@ class WebGPUBindingUtils { if ( binding.texture.isDepthTexture ) { - if ( binding.texture.compareFunction !== null && binding.textureNode.compareNode !== null && backend.hasCompatibility( Compatibility.TEXTURE_COMPARE ) ) { + if ( binding.texture.compareFunction !== null && binding.textureNode.isPlainGather() === false && backend.hasCompatibility( Compatibility.TEXTURE_COMPARE ) ) { sampler.type = GPUSamplerBindingType.Comparison; @@ -83585,8 +83976,9 @@ class WebGPUPipelineUtils { * * @param {ComputePipeline} pipeline - The compute pipeline. * @param {Array} bindings - The bindings. + * @param {?Array} [promises=null] - Optional compilation promises. */ - createComputePipeline( pipeline, bindings ) { + createComputePipeline( pipeline, bindings, promises = null ) { const backend = this.backend; const device = backend.device; @@ -83623,23 +84015,87 @@ class WebGPUPipelineUtils { _computePipelineDescriptor.compute = computeProgram; _computePipelineDescriptor.layout = pipelineLayout; - pipelineGPU.pipeline = device.createComputePipeline( _computePipelineDescriptor ); + if ( promises === null ) { - _computePipelineDescriptor.reset(); + pipelineGPU.pipeline = device.createComputePipeline( _computePipelineDescriptor ); - device.popErrorScope().then( ( err ) => { + _computePipelineDescriptor.reset(); - if ( err !== null ) { + device.popErrorScope().then( ( err ) => { - pipelineGPU.error = true; + if ( err !== null ) { - error( `WebGPURenderer: Compute pipeline creation failed (${ pipelineLabel }): ${ err.message }` ); + pipelineGPU.error = true; - this._reportShaderDiagnostics( [ { program: computeStage, module: computeProgram.module } ], pipelineLabel ); + error( `WebGPURenderer: Compute pipeline creation failed (${ pipelineLabel }): ${ err.message }` ); - } + this._reportShaderDiagnostics( [ { program: computeStage, module: computeProgram.module } ], pipelineLabel ); - } ); + } + + } ); + + } else { + + const promise = new Promise( async ( resolve /*, reject*/ ) => { + + try { + + let asyncError = null; + let pipelinePromise = null; + + try { + + pipelinePromise = device.createComputePipelineAsync( _computePipelineDescriptor ); + + } catch ( err ) { + + asyncError = err; + + } + + _computePipelineDescriptor.reset(); + + if ( pipelinePromise !== null ) { + + try { + + pipelineGPU.pipeline = await pipelinePromise; + + } catch ( err ) { + + asyncError = err; + + } + + } + + const errorScope = await device.popErrorScope(); + + if ( errorScope !== null || asyncError !== null ) { + + pipelineGPU.error = true; + + const reason = ( errorScope && errorScope.message ) || ( asyncError && asyncError.message ) || 'unknown'; + error( `WebGPURenderer: Async compute pipeline creation failed (${ pipelineLabel }): ${ reason }` ); + + await this._reportShaderDiagnostics( [ { program: computeStage, module: computeProgram.module } ], pipelineLabel ); + + } + + } finally { + + // Guarantee resolution so `compileComputeAsync`'s Promise.all cannot hang on an + // unexpected throw from any await above. + resolve(); + + } + + } ); + + promises.push( promise ); + + } } @@ -83858,11 +84314,13 @@ class WebGPUPipelineUtils { blendFactor = GPUBlendFactor.SrcAlphaSaturated; break; - case BlendColorFactor: + case ConstantColorFactor: + case ConstantAlphaFactor: // WebGPU has no dedicated constant alpha blend factors blendFactor = GPUBlendFactor.Constant; break; - case OneMinusBlendColorFactor: + case OneMinusConstantColorFactor: + case OneMinusConstantAlphaFactor: // WebGPU has no dedicated constant alpha blend factors blendFactor = GPUBlendFactor.OneMinusConstant; break; @@ -84698,6 +85156,7 @@ class GPURenderPassTimestampWrites { const _clearValue = { r: 0, g: 0, b: 0, a: 1 }; +const _blendConstant = { r: 0, g: 0, b: 0, a: 0 }; const _bufferDescriptor = new GPUBufferDescriptor(); const _commandEncoderDescriptor = new GPUCommandEncoderDescriptor(); const _computePassDescriptor = new GPUComputePassDescriptor(); @@ -84706,6 +85165,7 @@ const _shaderModuleDescriptor = new GPUShaderModuleDescriptor(); const _renderPassTimestampWrites = new GPURenderPassTimestampWrites(); const _texelCopyTextureInfoSrc = new GPUTexelCopyTextureInfo(); const _texelCopyTextureInfoDst = new GPUTexelCopyTextureInfo(); +const _textureDescriptor = new GPUTextureDescriptor(); const _viewDescriptor = new GPUTextureViewDescriptor(); const _extent3D = new GPUExtent3D(); @@ -84973,13 +85433,14 @@ class WebGPUBackend extends Backend { */ setXRRenderTargetTextures( renderTarget, colorTexture, viewDescriptors = null ) { - this.set( renderTarget.texture, { - texture: colorTexture, - format: colorTexture.format, - externalTexture: true, - xrViewDescriptors: viewDescriptors, - initialized: true - } ); + // Update the external XR texture without replacing the cached MSAA attachments. + const textureData = this.get( renderTarget.texture ); + + textureData.texture = colorTexture; + textureData.format = colorTexture.format; + textureData.externalTexture = true; + textureData.xrViewDescriptors = viewDescriptors; + textureData.initialized = true; } @@ -85195,6 +85656,81 @@ class WebGPUBackend extends Backend { } + /** + * Returns multisampled color textures for an external render target. + * + * @private + * @param {RenderContext} renderContext - The render context. + * @param {Object} textureData - The backend data for the external texture. + * @param {number} count - The number of textures to create. + * @return {?Array} The multisampled textures. + */ + _getExternalMSAATextures( renderContext, textureData, count ) { + + const samples = this.utils.getSampleCount( renderContext.sampleCount ); + + if ( samples === 1 ) { + + if ( textureData.msaaTextures !== undefined ) { + + for ( const texture of textureData.msaaTextures ) texture.destroy(); + + textureData.msaaTextures = undefined; + + } + + return null; + + } + + const renderTarget = renderContext.renderTarget; + const width = renderTarget.width; + const height = renderTarget.height; + const format = textureData.format; + + if ( textureData.msaaTextures === undefined || + textureData.msaaTextures.length !== count || + textureData.msaaWidth !== width || + textureData.msaaHeight !== height || + textureData.msaaSamples !== samples || + textureData.msaaFormat !== format ) { + + if ( textureData.msaaTextures !== undefined ) { + + for ( const texture of textureData.msaaTextures ) texture.destroy(); + + } + + _textureDescriptor.size.width = width; + _textureDescriptor.size.height = height; + _textureDescriptor.sampleCount = samples; + _textureDescriptor.format = format; + // Layered rendering can resume after a framebuffer copy, + // so these attachments must support loading. + _textureDescriptor.usage = GPUTextureUsage.RENDER_ATTACHMENT; + + textureData.msaaTextures = []; + + for ( let i = 0; i < count; i ++ ) { + + _textureDescriptor.label = renderTarget.texture.name + '-msaa-' + i; + textureData.msaaTextures.push( this.device.createTexture( _textureDescriptor ) ); + + } + + _textureDescriptor.reset(); + + textureData.msaaWidth = width; + textureData.msaaHeight = height; + textureData.msaaSamples = samples; + textureData.msaaFormat = format; + + } + + return textureData.msaaTextures; + + } + /** * Creates attachment views for an external texture render target. * @@ -85207,14 +85743,19 @@ class WebGPUBackend extends Backend { const textureViews = []; const camera = renderContext.camera; + const viewDescriptors = textureData.xrViewDescriptors; + const viewCount = Math.max( viewDescriptors?.length || 0, renderContext.activeCubeFace + 1, 1 ); + const msaaTextures = this._getExternalMSAATextures( renderContext, textureData, viewCount ); - if ( textureData.xrViewDescriptors && camera !== null && camera.isArrayCamera === true ) { + if ( viewDescriptors && camera !== null && camera.isArrayCamera === true ) { - for ( let i = 0; i < textureData.xrViewDescriptors.length; i ++ ) { + for ( let i = 0; i < viewDescriptors.length; i ++ ) { + + const textureView = textureData.texture.createView( viewDescriptors[ i ] ); textureViews.push( { - view: textureData.texture.createView( textureData.xrViewDescriptors[ i ] ), - resolveTarget: undefined, + view: msaaTextures !== null ? msaaTextures[ i ].createView() : textureView, + resolveTarget: msaaTextures !== null && renderContext.renderTarget.resolveColorBuffer === true ? textureView : undefined, depthSlice: undefined } ); @@ -85222,13 +85763,16 @@ class WebGPUBackend extends Backend { } else { + const layer = renderContext.activeCubeFace; + const textureView = textureData.texture.createView( { + dimension: GPUTextureViewDimension.TwoD, + baseArrayLayer: layer, + arrayLayerCount: 1 + } ); + textureViews.push( { - view: textureData.texture.createView( { - dimension: GPUTextureViewDimension.TwoD, - baseArrayLayer: renderContext.activeCubeFace, - arrayLayerCount: 1 - } ), - resolveTarget: undefined, + view: msaaTextures !== null ? msaaTextures[ layer ].createView() : textureView, + resolveTarget: msaaTextures !== null && renderContext.renderTarget.resolveColorBuffer === true ? textureView : undefined, depthSlice: undefined } ); @@ -85316,9 +85860,10 @@ class WebGPUBackend extends Backend { _viewDescriptor.dimension = GPUTextureViewDimension.TwoD; const textureView = textureData.texture.createView( _viewDescriptor ); + const msaaTexture = textureData.msaaTextures?.[ layer ]; textureViews.push( { - view: textureView, - resolveTarget: undefined, + view: msaaTexture !== undefined ? msaaTexture.createView() : textureView, + resolveTarget: msaaTexture !== undefined && renderTarget.resolveColorBuffer === true ? textureView : undefined, depthSlice: undefined } ); @@ -85343,6 +85888,11 @@ class WebGPUBackend extends Backend { view = textureData.msaaTexture.createView(); resolveTarget = renderTarget.resolveColorBuffer === true ? textureView : undefined; + } else if ( textureData.msaaTextures !== undefined ) { + + view = textureData.msaaTextures[ renderContext.activeCubeFace ].createView(); + resolveTarget = renderTarget.resolveColorBuffer === true ? textureView : undefined; + } else { view = textureView; @@ -85394,7 +85944,8 @@ class WebGPUBackend extends Backend { } const depthStencilAttachment = new GPURenderPassDepthStencilAttachment(); - depthStencilAttachment.view = depthTextureData.texture.createView( _viewDescriptor ); + const msaaDepthTexture = depthTextureData.msaaTextures?.[ renderContext.activeCubeFace ]; + depthStencilAttachment.view = msaaDepthTexture !== undefined ? msaaDepthTexture.createView() : depthTextureData.texture.createView( _viewDescriptor ); descriptorBase.depthStencilAttachment = depthStencilAttachment; _viewDescriptor.reset(); @@ -85518,6 +86069,12 @@ class WebGPUBackend extends Backend { const depthStencilAttachment = descriptor.depthStencilAttachment; const renderTarget = renderContext.renderTarget; + const discardColor = renderContext.sampleCount > 1 && renderTarget?.storeMultisampledColorBuffer === false; + const discardDepth = renderContext.sampleCount > 1 && renderTarget?.storeMultisampledDepthBuffer === false; + const discardStencil = renderContext.sampleCount > 1 && renderTarget?.storeMultisampledStencilBuffer === false; + + // A fullscreen pass overwrites the external texture, so its previous contents do not need to be loaded. + const clearExternalColor = renderContext.fullscreenPass === true && this._hasExternalTexture( renderContext ); if ( renderContext.textures !== null ) { @@ -85527,7 +86084,7 @@ class WebGPUBackend extends Backend { const colorAttachment = colorAttachments[ i ]; - if ( renderContext.clearColor ) { + if ( renderContext.clearColor || discardColor || clearExternalColor ) { if ( i === 0 ) { @@ -85552,7 +86109,7 @@ class WebGPUBackend extends Backend { } - if ( renderContext.sampleCount > 1 && renderTarget?.storeMultisampledColorBuffer === false ) { + if ( discardColor ) { colorAttachment.storeOp = GPUStoreOp.Discard; @@ -85587,7 +86144,7 @@ class WebGPUBackend extends Backend { if ( renderContext.depth ) { - if ( renderContext.clearDepth ) { + if ( renderContext.clearDepth || discardDepth ) { depthStencilAttachment.depthClearValue = renderContext.clearDepthValue; depthStencilAttachment.depthLoadOp = GPULoadOp.Clear; @@ -85598,7 +86155,7 @@ class WebGPUBackend extends Backend { } - if ( renderContext.sampleCount > 1 && renderTarget?.storeMultisampledDepthBuffer === false ) { + if ( discardDepth ) { depthStencilAttachment.depthStoreOp = GPUStoreOp.Discard; @@ -85612,7 +86169,7 @@ class WebGPUBackend extends Backend { if ( renderContext.stencil ) { - if ( renderContext.clearStencil ) { + if ( renderContext.clearStencil || discardStencil ) { depthStencilAttachment.stencilClearValue = renderContext.clearStencilValue; depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear; @@ -85623,7 +86180,7 @@ class WebGPUBackend extends Backend { } - if ( renderContext.sampleCount > 1 && renderTarget?.storeMultisampledStencilBuffer === false ) { + if ( discardStencil ) { depthStencilAttachment.stencilStoreOp = GPUStoreOp.Discard; @@ -85653,40 +86210,22 @@ class WebGPUBackend extends Backend { } else { - this._updateArrayCameraLayerDescriptors( renderContext, renderContextData, cameras ); + this._updateArrayCameraLayerDescriptors( renderContext, renderContextData, descriptor, cameras ); } - // Create bundle encoders for each layer - renderContextData.bundleEncoders = []; - renderContextData.bundleSets = []; - - // Create separate bundle encoders for each camera in the array - for ( let i = 0; i < cameras.length; i ++ ) { - - const bundleEncoder = this.pipelineUtils.createBundleEncoder( - renderContext, - 'renderBundleArrayCamera_' + i - ); - - // Initialize state tracking for this bundle - const bundleSets = { - attributes: {}, - bindingGroups: [], - pipeline: null, - index: null - }; - - renderContextData.bundleEncoders.push( bundleEncoder ); - renderContextData.bundleSets.push( bundleSets ); - - } + this._createArrayCameraBundleEncoders( renderContext, renderContextData ); + renderContextData.arrayCameraRenderStages = []; // We'll complete the bundles in finishRender renderContextData.currentPass = null; } else { + renderContextData.bundleEncoders = undefined; + renderContextData.bundleSets = undefined; + renderContextData.arrayCameraRenderStages = undefined; + const currentPass = encoder.beginRenderPass( descriptor ); renderContextData.currentPass = currentPass; @@ -85708,9 +86247,87 @@ class WebGPUBackend extends Backend { renderContextData.descriptor = descriptor; renderContextData.encoder = encoder; - renderContextData.currentSets = { attributes: {}, bindingGroups: [], pipeline: null, index: null }; renderContextData.renderBundles = []; + this._resetRenderContextData( renderContextData ); + + } + + /** + * Resets the state cache of the given render context data. + * + * A new render pass encoder starts with a blend constant and a stencil reference + * of zero so the cached values must be reset as well. + * + * @private + * @param {Object} renderContextData - The render context data. + */ + _resetRenderContextData( renderContextData ) { + + renderContextData.currentSets = { attributes: {}, bindingGroups: [], pipeline: null, index: null }; + + if ( renderContextData.currentBlendColor === undefined ) renderContextData.currentBlendColor = new Color(); + + renderContextData.currentBlendColor.setRGB( 0, 0, 0 ); + renderContextData.currentBlendAlpha = 0; + renderContextData.currentStencilRef = 0; + + } + + /** + * Creates a render bundle encoder and state cache for each camera layer. + * + * @param {RenderContext} renderContext - The render context. + * @param {Object} renderContextData - The render context data. + * @private + */ + _createArrayCameraBundleEncoders( renderContext, renderContextData ) { + + const cameras = renderContext.camera.cameras; + + renderContextData.bundleEncoders = []; + renderContextData.bundleSets = []; + + for ( let i = 0; i < cameras.length; i ++ ) { + + const bundleEncoder = this.pipelineUtils.createBundleEncoder( + renderContext, + 'renderBundleArrayCamera_' + i + ); + + const bundleSets = { + attributes: {}, + bindingGroups: [], + pipeline: null, + index: null + }; + + renderContextData.bundleEncoders.push( bundleEncoder ); + renderContextData.bundleSets.push( bundleSets ); + + } + + } + + /** + * Finishes the render bundle encoders for all camera layers. + * + * @param {Object} renderContextData - The render context data. + * @return {Array} The completed render bundles. + * @private + */ + _finishArrayCameraBundleEncoders( renderContextData ) { + + const bundles = []; + + for ( const bundleEncoder of renderContextData.bundleEncoders ) { + + bundles.push( bundleEncoder.finish() ); + + } + + return bundles; + } /** @@ -85738,11 +86355,12 @@ class WebGPUBackend extends Backend { for ( let i = 0; i < cameras.length; i ++ ) { const sourceAttachment = descriptor.colorAttachments[ 0 ]; + const layerAttachment = descriptor.colorAttachments[ i ]; const layerColorAttachment = new GPURenderPassColorAttachment(); - layerColorAttachment.view = descriptor.colorAttachments[ i ].view; - layerColorAttachment.depthSlice = sourceAttachment.depthSlice; - layerColorAttachment.resolveTarget = sourceAttachment.resolveTarget; + layerColorAttachment.view = layerAttachment.view; + layerColorAttachment.depthSlice = layerAttachment.depthSlice; + layerColorAttachment.resolveTarget = layerAttachment.resolveTarget; layerColorAttachment.loadOp = sourceAttachment.loadOp; layerColorAttachment.storeOp = sourceAttachment.storeOp; layerColorAttachment.clearValue = sourceAttachment.clearValue; @@ -85759,11 +86377,21 @@ class WebGPUBackend extends Backend { if ( ! depthTextureData.viewCache[ layerIndex ] ) { - _viewDescriptor.dimension = GPUTextureViewDimension.TwoD; - _viewDescriptor.baseArrayLayer = i; - _viewDescriptor.arrayLayerCount = 1; + const msaaTexture = depthTextureData.msaaTextures?.[ layerIndex ]; - depthTextureData.viewCache[ layerIndex ] = depthTextureData.texture.createView( _viewDescriptor ); + if ( msaaTexture !== undefined ) { + + depthTextureData.viewCache[ layerIndex ] = msaaTexture.createView(); + + } else { + + _viewDescriptor.dimension = GPUTextureViewDimension.TwoD; + _viewDescriptor.baseArrayLayer = i; + _viewDescriptor.arrayLayerCount = 1; + + depthTextureData.viewCache[ layerIndex ] = depthTextureData.texture.createView( _viewDescriptor ); + + } _viewDescriptor.reset(); @@ -85812,14 +86440,29 @@ class WebGPUBackend extends Backend { * * @param {RenderContext} renderContext - The render context. * @param {Object} renderContextData - The render context data. + * @param {Object} descriptor - The render pass descriptor. * @param {ArrayCamera} cameras - The array camera. * */ - _updateArrayCameraLayerDescriptors( renderContext, renderContextData, cameras ) { + _updateArrayCameraLayerDescriptors( renderContext, renderContextData, descriptor, cameras ) { + + const renderTarget = renderContext.renderTarget; + const discardDepth = renderContext.sampleCount > 1 && renderTarget?.storeMultisampledDepthBuffer === false; + const discardStencil = renderContext.sampleCount > 1 && renderTarget?.storeMultisampledStencilBuffer === false; for ( let i = 0; i < cameras.length; i ++ ) { const layerDescriptor = renderContextData.layerDescriptors[ i ]; + const sourceColorAttachment = descriptor.colorAttachments[ 0 ]; + const layerColorAttachment = descriptor.colorAttachments[ i ]; + const colorAttachment = layerDescriptor.colorAttachments[ 0 ]; + + colorAttachment.view = layerColorAttachment.view; + colorAttachment.resolveTarget = layerColorAttachment.resolveTarget; + colorAttachment.depthSlice = layerColorAttachment.depthSlice; + colorAttachment.loadOp = sourceColorAttachment.loadOp; + colorAttachment.storeOp = sourceColorAttachment.storeOp; + colorAttachment.clearValue = sourceColorAttachment.clearValue; if ( layerDescriptor.depthStencilAttachment ) { @@ -85827,7 +86470,7 @@ class WebGPUBackend extends Backend { if ( renderContext.depth ) { - if ( renderContext.clearDepth ) { + if ( renderContext.clearDepth || discardDepth ) { depthAttachment.depthClearValue = renderContext.clearDepthValue; depthAttachment.depthLoadOp = GPULoadOp.Clear; @@ -85842,7 +86485,7 @@ class WebGPUBackend extends Backend { if ( renderContext.stencil ) { - if ( renderContext.clearStencil ) { + if ( renderContext.clearStencil || discardStencil ) { depthAttachment.stencilClearValue = renderContext.clearStencilValue; depthAttachment.stencilLoadOp = GPULoadOp.Clear; @@ -85892,20 +86535,49 @@ class WebGPUBackend extends Backend { if ( this._isRenderCameraDepthArray( renderContext ) === true ) { - const bundles = []; + const bundles = this._finishArrayCameraBundleEncoders( renderContextData ); + const renderStages = renderContextData.arrayCameraRenderStages; + renderStages.push( { bundles } ); - for ( let i = 0; i < renderContextData.bundleEncoders.length; i ++ ) { + // A viewport texture is 2D, so each layer must be captured immediately before that layer samples it. + for ( let i = 0; i < renderContextData.layerDescriptors.length; i ++ ) { - const bundleEncoder = renderContextData.bundleEncoders[ i ]; - bundles.push( bundleEncoder.finish() ); + const layerDescriptor = renderContextData.layerDescriptors[ i ]; + const colorLoadOps = layerDescriptor.colorAttachments.map( attachment => attachment.loadOp ); + const colorStoreOps = layerDescriptor.colorAttachments.map( attachment => attachment.storeOp ); + const depthLoadOp = layerDescriptor.depthStencilAttachment?.depthLoadOp; + const depthStoreOp = layerDescriptor.depthStencilAttachment?.depthStoreOp; + const stencilLoadOp = layerDescriptor.depthStencilAttachment?.stencilLoadOp; + const stencilStoreOp = layerDescriptor.depthStencilAttachment?.stencilStoreOp; - } + for ( let stageIndex = 0; stageIndex < renderStages.length; stageIndex ++ ) { + + const renderStage = renderStages[ stageIndex ]; + const bundle = renderStage.bundles[ i ]; + const isLastStage = stageIndex === renderStages.length - 1; + + for ( let j = 0; j < layerDescriptor.colorAttachments.length; j ++ ) { + + const attachment = layerDescriptor.colorAttachments[ j ]; + attachment.loadOp = stageIndex === 0 ? colorLoadOps[ j ] : GPULoadOp.Load; + attachment.storeOp = isLastStage ? colorStoreOps[ j ] : GPUStoreOp.Store; + + } + + if ( renderContext.depth ) { - for ( let i = 0; i < renderContextData.layerDescriptors.length; i ++ ) { + layerDescriptor.depthStencilAttachment.depthLoadOp = stageIndex === 0 ? depthLoadOp : GPULoadOp.Load; + layerDescriptor.depthStencilAttachment.depthStoreOp = isLastStage ? depthStoreOp : GPUStoreOp.Store; + + } - if ( i < bundles.length ) { + if ( renderContext.stencil ) { + + layerDescriptor.depthStencilAttachment.stencilLoadOp = stageIndex === 0 ? stencilLoadOp : GPULoadOp.Load; + layerDescriptor.depthStencilAttachment.stencilStoreOp = isLastStage ? stencilStoreOp : GPUStoreOp.Store; + + } - const layerDescriptor = renderContextData.layerDescriptors[ i ]; const renderPass = encoder.beginRenderPass( layerDescriptor ); if ( renderContext.viewport ) { @@ -85922,10 +86594,39 @@ class WebGPUBackend extends Backend { } - renderPass.executeBundles( [ bundles[ i ] ] ); + renderPass.executeBundles( [ bundle ] ); renderPass.end(); + if ( renderStage.framebufferCopy !== undefined ) { + + const { texture, sourceGPU, destinationGPU, rectangle, generateMipmaps } = renderStage.framebufferCopy; + + this._copyFramebufferToTexture( encoder, texture, sourceGPU, destinationGPU, rectangle, i, generateMipmaps ); + + } + + } + + for ( let j = 0; j < layerDescriptor.colorAttachments.length; j ++ ) { + + layerDescriptor.colorAttachments[ j ].loadOp = colorLoadOps[ j ]; + layerDescriptor.colorAttachments[ j ].storeOp = colorStoreOps[ j ]; + + } + + if ( renderContext.depth ) { + + layerDescriptor.depthStencilAttachment.depthLoadOp = depthLoadOp; + layerDescriptor.depthStencilAttachment.depthStoreOp = depthStoreOp; + + } + + if ( renderContext.stencil ) { + + layerDescriptor.depthStencilAttachment.stencilLoadOp = stencilLoadOp; + layerDescriptor.depthStencilAttachment.stencilStoreOp = stencilStoreOp; + } } @@ -86508,6 +87209,29 @@ class WebGPUBackend extends Backend { } + // blend constant + + if ( material.blending === CustomBlending && passEncoderGPU.setBlendConstant !== undefined ) { + + const blendColor = material.blendColor; + const blendAlpha = material.blendAlpha; + + if ( blendColor.equals( renderContextData.currentBlendColor ) === false || blendAlpha !== renderContextData.currentBlendAlpha ) { + + _blendConstant.r = blendColor.r; + _blendConstant.g = blendColor.g; + _blendConstant.b = blendColor.b; + _blendConstant.a = blendAlpha; + + passEncoderGPU.setBlendConstant( _blendConstant ); + + renderContextData.currentBlendColor.copy( blendColor ); + renderContextData.currentBlendAlpha = blendAlpha; + + } + + } + if ( object.isBatchedMesh === true ) { const starts = object._multiDrawStarts; @@ -86941,7 +87665,7 @@ class WebGPUBackend extends Backend { * @param {number} y - The y coordinate of the copy origin. * @param {number} width - The width of the copy. * @param {number} height - The height of the copy. - * @param {number} faceIndex - The face index. + * @param {number} faceIndex - The cube face, depth slice or array layer index. * @return {Promise} A Promise that resolves with a typed array when the copy operation has finished. */ async copyTextureToBuffer( texture, x, y, width, height, faceIndex ) { @@ -87049,10 +87773,11 @@ class WebGPUBackend extends Backend { * * @param {ComputePipeline} computePipeline - The compute pipeline. * @param {Array} bindings - The bindings. + * @param {?Array} [promises=null] - Optional compilation promises. */ - createComputePipeline( computePipeline, bindings ) { + createComputePipeline( computePipeline, bindings, promises = null ) { - this.pipelineUtils.createComputePipeline( computePipeline, bindings ); + this.pipelineUtils.createComputePipeline( computePipeline, bindings, promises ); } @@ -87482,6 +88207,29 @@ class WebGPUBackend extends Backend { } + if ( this._isRenderCameraDepthArray( renderContext ) === true ) { + + // Layered draws are only executed in finishRender(), so preserve this copy as a render-stage boundary. + const bundles = this._finishArrayCameraBundleEncoders( renderContextData ); + + renderContextData.arrayCameraRenderStages.push( { + bundles, + framebufferCopy: { + texture, + sourceGPU, + destinationGPU, + rectangle: { x: rectangle.x, y: rectangle.y, z: rectangle.z, w: rectangle.w }, + // ViewportTextureNode restores this flag before the deferred copy executes. + generateMipmaps: texture.generateMipmaps + } + } ); + + this._createArrayCameraBundleEncoders( renderContext, renderContextData ); + + return; + + } + let encoder; if ( renderContextData.currentPass ) { @@ -87498,33 +88246,10 @@ class WebGPUBackend extends Backend { } - _texelCopyTextureInfoSrc.texture = sourceGPU; - _texelCopyTextureInfoSrc.origin.x = rectangle.x; - _texelCopyTextureInfoSrc.origin.y = rectangle.y; - - _texelCopyTextureInfoDst.texture = destinationGPU; - - _extent3D.width = rectangle.z; - _extent3D.height = rectangle.w; - - encoder.copyTextureToTexture( - _texelCopyTextureInfoSrc, - _texelCopyTextureInfoDst, - _extent3D - ); - - _texelCopyTextureInfoSrc.reset(); - _texelCopyTextureInfoDst.reset(); - _extent3D.reset(); - // mipmaps must be genereated with the same encoder otherwise the copied texture data // might be out-of-sync, see #31768 - if ( texture.generateMipmaps ) { - - this.textureUtils.generateMipmaps( texture, encoder ); - - } + this._copyFramebufferToTexture( encoder, texture, sourceGPU, destinationGPU, rectangle ); if ( renderContextData.currentPass ) { @@ -87540,7 +88265,8 @@ class WebGPUBackend extends Backend { if ( renderContext.stencil ) descriptor.depthStencilAttachment.stencilLoadOp = GPULoadOp.Load; renderContextData.currentPass = encoder.beginRenderPass( descriptor ); - renderContextData.currentSets = { attributes: {}, bindingGroups: [], pipeline: null, index: null }; + + this._resetRenderContextData( renderContextData ); if ( renderContext.viewport ) { @@ -87562,6 +88288,48 @@ class WebGPUBackend extends Backend { } + /** + * Encodes a framebuffer copy from a specific texture-array layer. + * + * @param {GPUCommandEncoder} encoder - The command encoder. + * @param {Texture} texture - The destination texture. + * @param {GPUTexture} sourceGPU - The source GPU texture. + * @param {GPUTexture} destinationGPU - The destination GPU texture. + * @param {Object} rectangle - The source rectangle. + * @param {number} [sourceLayer=0] - The source array layer. + * @param {boolean} [generateMipmaps=texture.generateMipmaps] - Whether mipmaps should be generated. + * @private + */ + _copyFramebufferToTexture( encoder, texture, sourceGPU, destinationGPU, rectangle, sourceLayer = 0, generateMipmaps = texture.generateMipmaps ) { + + _texelCopyTextureInfoSrc.texture = sourceGPU; + _texelCopyTextureInfoSrc.origin.x = rectangle.x; + _texelCopyTextureInfoSrc.origin.y = rectangle.y; + _texelCopyTextureInfoSrc.origin.z = sourceLayer; + + _texelCopyTextureInfoDst.texture = destinationGPU; + + _extent3D.width = rectangle.z; + _extent3D.height = rectangle.w; + + encoder.copyTextureToTexture( + _texelCopyTextureInfoSrc, + _texelCopyTextureInfoDst, + _extent3D + ); + + _texelCopyTextureInfoSrc.reset(); + _texelCopyTextureInfoDst.reset(); + _extent3D.reset(); + + if ( generateMipmaps ) { + + this.textureUtils.generateMipmaps( texture, encoder ); + + } + + } + /** * Checks if the given compatibility is supported by the backend. * @@ -89145,4 +89913,4 @@ class ClippingGroup extends Group { } -export { ACESFilmicToneMapping, AONode, AddEquation, AddOperation, AdditiveBlending, AgXToneMapping, AlphaFormat, AlwaysCompare, AlwaysDepth, AlwaysStencilFunc, AmbientLight, AmbientLightNode, AnalyticLightNode, ArrayCamera, ArrayElementNode, ArrayNode, AssignNode, AtomicFunctionNode, AttributeNode, BackSide, Backend, BarrierNode, BasicEnvironmentNode, BasicLightMapNode, BasicNodeLibrary, BasicShadowMap, BitcastNode, BitcountNode, BlendMode, BoxGeometry, BufferAttribute, BufferAttributeNode, BufferGeometry, BufferNode, BuiltinNode, BumpMapNode, BundleGroup, BypassNode, ByteType, CanvasTarget, CineonToneMapping, ClampToEdgeWrapping, ClippingGroup, ClippingNode, CodeNode, Color, ColorManagement, ColorSpaceNode, Compatibility, ComputeBuiltinNode, ComputeNode, ConditionalNode, ConstNode, ContextNode, ConvertNode, CubeCamera, CubeDepthTexture, CubeMapNode, CubeReflectionMapping, CubeRefractionMapping, CubeRenderTarget, CubeTexture, CubeTextureNode, CubeUVReflectionMapping, CullFaceBack, CullFaceFront, CullFaceNone, CustomBlending, CylinderGeometry, DataArrayTexture, DataTexture, DebugNode, DecrementStencilOp, DecrementWrapStencilOp, DepthFormat, DepthStencilFormat, DepthTexture, DirectionalLight, DirectionalLightNode, DoubleSide, DstAlphaFactor, DstColorFactor, DynamicDrawUsage, EnvironmentNode, EqualCompare, EqualDepth, EqualStencilFunc, EquirectangularReflectionMapping, EquirectangularRefractionMapping, EventDispatcher, EventNode, ExpressionNode, FileLoader, FlipNode, Float16BufferAttribute, Float32BufferAttribute, FloatType, FramebufferTexture, FrontFacingNode, FrontSide, Frustum, FrustumArray, FunctionCallNode, FunctionNode, FunctionOverloadingNode, GLSLNodeBuilder, GLSLNodeParser, GreaterCompare, GreaterDepth, GreaterEqualCompare, GreaterEqualDepth, GreaterEqualStencilFunc, GreaterStencilFunc, Group, HalfFloatType, HemisphereLight, HemisphereLightNode, IESSpotLight, IESSpotLightNode, IncrementStencilOp, IncrementWrapStencilOp, IndexNode, IndirectStorageBufferAttribute, InputNode, InspectorBase, InspectorNode, InstancedBufferAttribute, InstancedInterleavedBuffer, IntType, InterleavedBuffer, InterleavedBufferAttribute, InvertStencilOp, IrradianceNode, IsolateNode, JoinNode, KeepStencilOp, LessCompare, LessDepth, LessEqualCompare, LessEqualDepth, LessEqualStencilFunc, LessStencilFunc, LightProbe, LightProbeNode, Lighting, LightingContextNode, LightingModel, LightingNode, LightsNode, Line2NodeMaterial, LineBasicMaterial, LineBasicNodeMaterial, LineDashedMaterial, LineDashedNodeMaterial, LinearFilter, LinearMipMapLinearFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, LinearSRGBColorSpace, LinearToneMapping, LinearTransfer, Loader, LoopNode, MRTNode, Material, MaterialBlending, MaterialLoader, MaterialNode, MaterialReferenceNode, MathNode, MathUtils, Matrix2, Matrix3, Matrix4, MaxEquation, MaxMipLevelNode, MemberNode, Mesh, MeshBasicMaterial, MeshBasicNodeMaterial, MeshLambertMaterial, MeshLambertNodeMaterial, MeshMatcapMaterial, MeshMatcapNodeMaterial, MeshNormalMaterial, MeshNormalNodeMaterial, MeshPhongMaterial, MeshPhongNodeMaterial, MeshPhysicalMaterial, MeshPhysicalNodeMaterial, MeshSSSNodeMaterial, MeshStandardMaterial, MeshStandardNodeMaterial, MeshToonMaterial, MeshToonNodeMaterial, MinEquation, MirroredRepeatWrapping, MixOperation, ModelNode, MultiplyBlending, MultiplyOperation, NearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, NeutralToneMapping, NeverCompare, NeverDepth, NeverStencilFunc, NoBlending, NoColorSpace, NoNormalPacking, NoToneMapping, Node, NodeAccess, NodeAttribute, NodeBuilder, NodeCache, NodeCode, NodeError, NodeFrame, NodeFunctionInput, NodeLoader, NodeMaterial, NodeMaterialLoader, NodeMaterialObserver, NodeObjectLoader, NodeShaderStage, NodeType, NodeUniform, NodeUpdateType, NodeUtils, NodeVar, NodeVarying, NormalBlending, NormalGAPacking, NormalMapNode, NormalRGPacking, NotEqualCompare, NotEqualDepth, NotEqualStencilFunc, Object3D, Object3DNode, ObjectLoader, ObjectSpaceNormalMap, OneFactor, OneMinusDstAlphaFactor, OneMinusDstColorFactor, OneMinusSrcAlphaFactor, OneMinusSrcColorFactor, OperatorNode, OrthographicCamera, OutputStructNode, OverrideContextNode, PCFShadowMap, PCFSoftShadowMap, PMREMGenerator, PMREMNode, PackFloatNode, ParameterNode, PassNode, PerspectiveCamera, PhongLightingModel, PhysicalLightingModel, Plane, PlaneGeometry, PointLight, PointLightNode, PointShadowNode, PointUVNode, PointsMaterial, PointsNodeMaterial, PostProcessing, ProjectorLight, ProjectorLightNode, PropertyNode, QuadMesh, Quaternion, R11_EAC_Format, RED_GREEN_RGTC2_Format, RED_RGTC1_Format, REVISION, RG11_EAC_Format, RGBAFormat, RGBAIntegerFormat, RGBA_ASTC_10x10_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_BPTC_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGBFormat, RGBIntegerFormat, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGB_S3TC_DXT1_Format, RGFormat, RGIntegerFormat, RTTNode, RangeNode, ReadbackBuffer, RectAreaLight, RectAreaLightNode, RedFormat, RedIntegerFormat, ReferenceBaseNode, ReferenceNode, ReflectorNode, ReinhardToneMapping, RenderOutputNode, RenderPipeline, RenderTarget, Renderer, RendererReferenceNode, RendererUtils, RepeatWrapping, ReplaceStencilOp, ReverseSubtractEquation, RotateNode, SIGNED_R11_EAC_Format, SIGNED_RED_GREEN_RGTC2_Format, SIGNED_RED_RGTC1_Format, SIGNED_RG11_EAC_Format, SRGBColorSpace, SRGBTransfer, SampleNode, Scene, ScreenNode, SetNode, ShadowBaseNode, ShadowMaterial, ShadowNode, ShadowNodeMaterial, ShortType, Sphere, SphereGeometry, SplitNode, SpotLight, SpotLightNode, SpriteMaterial, SpriteNodeMaterial, SrcAlphaFactor, SrcAlphaSaturateFactor, SrcColorFactor, StackNode, StackTrace, StandardNodeLibrary, StaticDrawUsage, Storage3DTexture, StorageArrayElementNode, StorageArrayTexture, StorageBufferAttribute, StorageBufferNode, StorageInstancedBufferAttribute, StorageTexture, StorageTexture3DNode, StorageTextureNode, StructNode, StructTypeNode, SubBuildNode, SubgroupFunctionNode, SubtractEquation, SubtractiveBlending, TSL, TangentSpaceNormalMap, TempNode, Texture, Texture3DNode, TextureNode, TextureSizeNode, TimestampQuery, ToneMappingNode, ToonOutlinePassNode, UVMapping, Uint16BufferAttribute, Uint32BufferAttribute, UniformArrayNode, UniformGroupNode, UniformNode, UnpackFloatNode, UnsignedByteType, UnsignedInt101111Type, UnsignedInt248Type, UnsignedInt5999Type, UnsignedIntType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedShortType, UserDataNode, VSMShadowMap, VarNode, VaryingNode, Vector2, Vector3, Vector4, VelocityNode, VertexColorNode, ViewportDepthNode, ViewportDepthTextureNode, ViewportSharedTextureNode, ViewportTextureNode, VolumeNodeMaterial, WGSLNodeBuilder, WebGLBackend, WebGLCapabilities, WebGLCoordinateSystem, WebGPUBackend, WebGPUCoordinateSystem, WebGPURenderer, WebXRController, WorkgroupInfoNode, ZeroFactor, ZeroStencilOp, createCanvasElement, defaultBuildStages, defaultShaderStages, error, log$1 as log, shaderStages, vectorComponents, warn, warnOnce }; +export { ACESFilmicToneMapping, AONode, AddEquation, AddOperation, AdditiveBlending, AgXToneMapping, AlphaFormat, AlwaysCompare, AlwaysDepth, AlwaysStencilFunc, AmbientLight, AmbientLightNode, AnalyticLightNode, ArrayCamera, ArrayElementNode, ArrayNode, AssignNode, AtomicFunctionNode, AttributeNode, BackSide, Backend, BarrierNode, BasicEnvironmentNode, BasicLightMapNode, BasicNodeLibrary, BasicShadowMap, BitcastNode, BitcountNode, BlendMode, BoxGeometry, BufferAttribute, BufferAttributeNode, BufferGeometry, BufferNode, BuiltinNode, BumpMapNode, BundleGroup, BypassNode, ByteType, CanvasTarget, CineonToneMapping, ClampToEdgeWrapping, ClippingGroup, ClippingNode, CodeNode, Color, ColorManagement, ColorSpaceNode, Compatibility, ComputeBuiltinNode, ComputeNode, ConditionalNode, ConstNode, ConstantAlphaFactor, ConstantColorFactor, ContextNode, ConvertNode, CubeCamera, CubeDepthTexture, CubeMapNode, CubeReflectionMapping, CubeRefractionMapping, CubeRenderTarget, CubeTexture, CubeTextureNode, CubeUVReflectionMapping, CullFaceBack, CullFaceFront, CullFaceNone, CustomBlending, CylinderGeometry, DataArrayTexture, DataTexture, DebugNode, DecrementStencilOp, DecrementWrapStencilOp, DepthFormat, DepthStencilFormat, DepthTexture, DirectionalLight, DirectionalLightNode, DoubleSide, DstAlphaFactor, DstColorFactor, DynamicDrawUsage, EnvironmentNode, EqualCompare, EqualDepth, EqualStencilFunc, EquirectangularReflectionMapping, EquirectangularRefractionMapping, EventDispatcher, EventNode, ExpressionNode, FileLoader, FlipNode, Float16BufferAttribute, Float32BufferAttribute, FloatType, FramebufferTexture, FrontFacingNode, FrontSide, Frustum, FrustumArray, FunctionCallNode, FunctionNode, FunctionOverloadingNode, GLSLNodeBuilder, GLSLNodeParser, GreaterCompare, GreaterDepth, GreaterEqualCompare, GreaterEqualDepth, GreaterEqualStencilFunc, GreaterStencilFunc, Group, HalfFloatType, HemisphereLight, HemisphereLightNode, IESSpotLight, IESSpotLightNode, IncrementStencilOp, IncrementWrapStencilOp, IndexNode, IndirectStorageBufferAttribute, InputNode, InspectorBase, InspectorNode, InstancedBufferAttribute, InstancedInterleavedBuffer, IntType, InterleavedBuffer, InterleavedBufferAttribute, InvertStencilOp, IrradianceNode, IsolateNode, JoinNode, KeepStencilOp, LessCompare, LessDepth, LessEqualCompare, LessEqualDepth, LessEqualStencilFunc, LessStencilFunc, LightProbe, LightProbeNode, Lighting, LightingContextNode, LightingModel, LightingNode, LightsNode, Line2NodeMaterial, LineBasicMaterial, LineBasicNodeMaterial, LineDashedMaterial, LineDashedNodeMaterial, LinearFilter, LinearMipMapLinearFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, LinearSRGBColorSpace, LinearToneMapping, LinearTransfer, Loader, LoopNode, MRTNode, Material, MaterialBlending, MaterialLoader, MaterialNode, MaterialReferenceNode, MathNode, MathUtils, Matrix2, Matrix3, Matrix4, MaxEquation, MaxMipLevelNode, MemberNode, Mesh, MeshBasicMaterial, MeshBasicNodeMaterial, MeshLambertMaterial, MeshLambertNodeMaterial, MeshMatcapMaterial, MeshMatcapNodeMaterial, MeshNormalMaterial, MeshNormalNodeMaterial, MeshPhongMaterial, MeshPhongNodeMaterial, MeshPhysicalMaterial, MeshPhysicalNodeMaterial, MeshSSSNodeMaterial, MeshStandardMaterial, MeshStandardNodeMaterial, MeshToonMaterial, MeshToonNodeMaterial, MinEquation, MirroredRepeatWrapping, MixOperation, ModelNode, MultiplyBlending, MultiplyOperation, NearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, NeutralToneMapping, NeverCompare, NeverDepth, NeverStencilFunc, NoBlending, NoColorSpace, NoNormalPacking, NoToneMapping, Node, NodeAccess, NodeAttribute, NodeBuilder, NodeCache, NodeCode, NodeError, NodeFrame, NodeFunctionInput, NodeLoader, NodeMaterial, NodeMaterialLoader, NodeMaterialObserver, NodeObjectLoader, NodeShaderStage, NodeType, NodeUniform, NodeUpdateType, NodeUtils, NodeVar, NodeVarying, NormalBlending, NormalGAPacking, NormalMapNode, NormalRGPacking, NotEqualCompare, NotEqualDepth, NotEqualStencilFunc, Object3D, Object3DNode, ObjectLoader, ObjectSpaceNormalMap, OneFactor, OneMinusConstantAlphaFactor, OneMinusConstantColorFactor, OneMinusDstAlphaFactor, OneMinusDstColorFactor, OneMinusSrcAlphaFactor, OneMinusSrcColorFactor, OperatorNode, OrthographicCamera, OutputStructNode, OverrideContextNode, PCFShadowMap, PCFSoftShadowMap, PMREMGenerator, PMREMNode, PackFloatNode, ParameterNode, PassNode, PerspectiveCamera, PhongLightingModel, PhysicalLightingModel, Plane, PlaneGeometry, PointLight, PointLightNode, PointShadowNode, PointUVNode, PointsMaterial, PointsNodeMaterial, PostProcessing, ProjectorLight, ProjectorLightNode, PropertyNode, QuadMesh, Quaternion, R11_EAC_Format, RED_GREEN_RGTC2_Format, RED_RGTC1_Format, REVISION, RG11_EAC_Format, RGBAFormat, RGBAIntegerFormat, RGBA_ASTC_10x10_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_BPTC_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGBFormat, RGBIntegerFormat, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGB_S3TC_DXT1_Format, RGFormat, RGIntegerFormat, RTTNode, RangeNode, ReadbackBuffer, RectAreaLight, RectAreaLightNode, RedFormat, RedIntegerFormat, ReferenceBaseNode, ReferenceNode, ReflectorNode, ReinhardToneMapping, RenderOutputNode, RenderPipeline, RenderTarget, Renderer, RendererReferenceNode, RendererUtils, RepeatWrapping, ReplaceStencilOp, ReverseSubtractEquation, RotateNode, SIGNED_R11_EAC_Format, SIGNED_RED_GREEN_RGTC2_Format, SIGNED_RED_RGTC1_Format, SIGNED_RG11_EAC_Format, SRGBColorSpace, SRGBTransfer, SampleNode, Scene, ScreenNode, SetNode, ShadowBaseNode, ShadowMaterial, ShadowNode, ShadowNodeMaterial, ShortType, Sphere, SphereGeometry, SplitNode, SpotLight, SpotLightNode, SpriteMaterial, SpriteNodeMaterial, SrcAlphaFactor, SrcAlphaSaturateFactor, SrcColorFactor, StackNode, StackTrace, StandardNodeLibrary, StaticDrawUsage, Storage3DTexture, StorageArrayElementNode, StorageArrayTexture, StorageBufferAttribute, StorageBufferNode, StorageInstancedBufferAttribute, StorageTexture, StorageTexture3DNode, StorageTextureNode, StructNode, StructTypeNode, SubBuildNode, SubgroupFunctionNode, SubtractEquation, SubtractiveBlending, TSL, TangentSpaceNormalMap, TempNode, Texture, Texture3DNode, TextureNode, TextureSizeNode, TimestampQuery, ToneMappingNode, ToonOutlinePassNode, UVMapping, Uint16BufferAttribute, Uint32BufferAttribute, UniformArrayNode, UniformGroupNode, UniformNode, UnpackFloatNode, UnsignedByteType, UnsignedInt101111Type, UnsignedInt248Type, UnsignedInt5999Type, UnsignedIntType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedShortType, UserDataNode, VSMShadowMap, VarNode, VaryingNode, Vector2, Vector3, Vector4, VelocityNode, VertexColorNode, ViewportDepthNode, ViewportDepthTextureNode, ViewportSharedTextureNode, ViewportTextureNode, VolumeNodeMaterial, WGSLNodeBuilder, WebGLBackend, WebGLCapabilities, WebGLCoordinateSystem, WebGPUBackend, WebGPUCoordinateSystem, WebGPURenderer, WebXRController, WorkgroupInfoNode, ZeroFactor, ZeroStencilOp, createCanvasElement, defaultBuildStages, defaultShaderStages, error, log$1 as log, shaderStages, vectorComponents, warn, warnOnce }; diff --git a/build/three.webgpu.nodes.js b/build/three.webgpu.nodes.js index 9afc24ec85acdb..ed7a708140ac8c 100644 --- a/build/three.webgpu.nodes.js +++ b/build/three.webgpu.nodes.js @@ -3,8 +3,8 @@ * Copyright 2010-2026 Three.js Authors * SPDX-License-Identifier: MIT */ -import { Color, Vector2, Vector3, Vector4, Matrix2, Matrix3, Matrix4, UnsignedIntType, IntType, error, RedFormat, RedIntegerFormat, DepthFormat, DepthStencilFormat, AlphaFormat, RGFormat, RGIntegerFormat, RGBFormat, RGBIntegerFormat, EventDispatcher, MathUtils, warn, WebGLCoordinateSystem, WebGPUCoordinateSystem, ColorManagement, SRGBTransfer, NoToneMapping, StaticDrawUsage, InterleavedBufferAttribute, InterleavedBuffer, DynamicDrawUsage, NoColorSpace, log as log$1, warnOnce, Texture, Compatibility, LessCompare, LessEqualCompare, GreaterCompare, GreaterEqualCompare, NearestFilter, Sphere, BackSide, DoubleSide, CubeTexture, CubeReflectionMapping, CubeRefractionMapping, TangentSpaceNormalMap, NoNormalPacking, NormalRGPacking, NormalGAPacking, ObjectSpaceNormalMap, RED_GREEN_RGTC2_Format, RG11_EAC_Format, InstancedBufferAttribute, InstancedInterleavedBuffer, DataArrayTexture, FloatType, FramebufferTexture, LinearMipmapLinearFilter, DepthTexture, Material, LineBasicMaterial, LineDashedMaterial, NoBlending, MeshNormalMaterial, SRGBColorSpace, RenderTarget, BoxGeometry, Mesh, Scene, LinearFilter, CubeCamera, EquirectangularReflectionMapping, EquirectangularRefractionMapping, AddOperation, MixOperation, MultiplyOperation, MeshBasicMaterial, MeshLambertMaterial, MeshPhongMaterial, DataTexture, HalfFloatType, ClampToEdgeWrapping, BufferGeometry, OrthographicCamera, PerspectiveCamera, LinearSRGBColorSpace, RGBAFormat, CubeUVReflectionMapping, BufferAttribute, MeshStandardMaterial, MeshPhysicalMaterial, MeshToonMaterial, MeshMatcapMaterial, SpriteMaterial, PointsMaterial, ShadowMaterial, Uint32BufferAttribute, Uint16BufferAttribute, ByteType, UnsignedByteType, ShortType, UnsignedShortType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedInt248Type, UnsignedInt5999Type, UnsignedInt101111Type, NormalBlending, SrcAlphaFactor, OneMinusSrcAlphaFactor, AddEquation, MaterialBlending, Object3D, LinearMipMapLinearFilter, Plane, Float32BufferAttribute, UVMapping, PCFShadowMap, VSMShadowMap, BasicShadowMap, CubeDepthTexture, SphereGeometry, LinearMipmapNearestFilter, NearestMipmapLinearFilter, Float16BufferAttribute, yieldToMain, REVISION, ArrayCamera, PlaneGeometry, FrontSide, CustomBlending, ZeroFactor, CylinderGeometry, Quaternion, WebXRController, RAD2DEG, PCFSoftShadowMap, FrustumArray, Frustum, RGBAIntegerFormat, TimestampQuery, createCanvasElement, ReverseSubtractEquation, SubtractEquation, OneMinusDstAlphaFactor, OneMinusDstColorFactor, OneMinusSrcColorFactor, DstAlphaFactor, DstColorFactor, SrcAlphaSaturateFactor, SrcColorFactor, OneFactor, CullFaceNone, CullFaceBack, CullFaceFront, MultiplyBlending, SubtractiveBlending, AdditiveBlending, NotEqualDepth, GreaterDepth, GreaterEqualDepth, EqualDepth, LessEqualDepth, LessDepth, AlwaysDepth, NeverDepth, ReversedDepthFuncs, RGB_S3TC_DXT1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGB_PVRTC_4BPPV1_Format, RGB_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_PVRTC_2BPPV1_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, R11_EAC_Format, SIGNED_R11_EAC_Format, SIGNED_RG11_EAC_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_BPTC_Format, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, RED_RGTC1_Format, SIGNED_RED_RGTC1_Format, SIGNED_RED_GREEN_RGTC2_Format, MirroredRepeatWrapping, RepeatWrapping, NearestMipmapNearestFilter, NotEqualCompare, EqualCompare, AlwaysCompare, NeverCompare, LinearTransfer, getByteLength, isTypedArray, NotEqualStencilFunc, GreaterStencilFunc, GreaterEqualStencilFunc, EqualStencilFunc, LessEqualStencilFunc, LessStencilFunc, AlwaysStencilFunc, NeverStencilFunc, DecrementWrapStencilOp, IncrementWrapStencilOp, DecrementStencilOp, IncrementStencilOp, InvertStencilOp, ReplaceStencilOp, ZeroStencilOp, KeepStencilOp, MaxEquation, MinEquation, SpotLight, PointLight, DirectionalLight, RectAreaLight, AmbientLight, HemisphereLight, LightProbe, LinearToneMapping, ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping, AgXToneMapping, NeutralToneMapping, Group, Loader, FileLoader, MaterialLoader, ObjectLoader } from './three.core.js'; -export { AdditiveAnimationBlendMode, AnimationAction, AnimationClip, AnimationLoader, AnimationMixer, AnimationObjectGroup, AnimationUtils, ArcCurve, ArrowHelper, AttachedBindMode, Audio, AudioAnalyser, AudioContext, AudioListener, AudioLoader, AxesHelper, BasicDepthPacking, BatchedMesh, BezierInterpolant, Bone, BooleanKeyframeTrack, Box2, Box3, Box3Helper, BoxHelper, BufferGeometryLoader, Cache, Camera, CameraHelper, CanvasTexture, CapsuleGeometry, CatmullRomCurve3, CircleGeometry, Clock, ColorKeyframeTrack, CompressedArrayTexture, CompressedCubeTexture, CompressedTexture, CompressedTextureLoader, ConeGeometry, ConstantAlphaFactor, ConstantColorFactor, Controls, CubeTextureLoader, CubicBezierCurve, CubicBezierCurve3, CubicInterpolant, CullFaceFrontBack, Curve, CurvePath, CustomToneMapping, Cylindrical, Data3DTexture, DataTextureLoader, DataUtils, DefaultLoadingManager, DetachedBindMode, DirectionalLightHelper, DiscreteInterpolant, DodecahedronGeometry, DynamicCopyUsage, DynamicReadUsage, EdgesGeometry, EllipseCurve, Euler, ExternalTexture, ExtrudeGeometry, Fog, FogExp2, GLBufferAttribute, GLSL1, GLSL3, GridHelper, HTMLTexture, HemisphereLightHelper, IcosahedronGeometry, ImageBitmapLoader, ImageLoader, ImageUtils, InstancedBufferGeometry, InstancedMesh, Int16BufferAttribute, Int32BufferAttribute, Int8BufferAttribute, Interpolant, InterpolateBezier, InterpolateDiscrete, InterpolateLinear, InterpolateSmooth, InterpolationSamplingMode, InterpolationSamplingType, KeyframeTrack, LOD, LatheGeometry, Layers, Light, Line, Line3, LineCurve, LineCurve3, LineLoop, LineSegments, LinearInterpolant, LinearMipMapNearestFilter, LoaderUtils, LoadingManager, LoopOnce, LoopPingPong, LoopRepeat, MOUSE, MeshDepthMaterial, MeshDistanceMaterial, NearestMipMapLinearFilter, NearestMipMapNearestFilter, NormalAnimationBlendMode, NumberKeyframeTrack, OctahedronGeometry, OneMinusConstantAlphaFactor, OneMinusConstantColorFactor, Path, PlaneHelper, PointLightHelper, Points, PolarGridHelper, PolyhedronGeometry, PositionalAudio, PropertyBinding, PropertyMixer, QuadraticBezierCurve, QuadraticBezierCurve3, QuaternionKeyframeTrack, QuaternionLinearInterpolant, RGBADepthPacking, RGBDepthPacking, RGDepthPacking, RawShaderMaterial, Ray, Raycaster, RenderTarget3D, RingGeometry, ShaderMaterial, Shape, ShapeGeometry, ShapePath, ShapeUtils, Skeleton, SkeletonHelper, SkinnedMesh, Source, Spherical, SphericalHarmonics3, SplineCurve, SpotLightHelper, Sprite, StaticCopyUsage, StaticReadUsage, StereoCamera, StreamCopyUsage, StreamDrawUsage, StreamReadUsage, StringKeyframeTrack, TOUCH, TetrahedronGeometry, TextureLoader, TextureUtils, Timer, TorusGeometry, TorusKnotGeometry, Triangle, TriangleFanDrawMode, TriangleStripDrawMode, TrianglesDrawMode, TubeGeometry, Uint8BufferAttribute, Uint8ClampedBufferAttribute, Uniform, UniformsGroup, VectorKeyframeTrack, VideoFrameTexture, VideoTexture, WebGL3DRenderTarget, WebGLArrayRenderTarget, WebGLRenderTarget, WireframeGeometry, WrapAroundEnding, ZeroCurvatureEnding, ZeroSlopeEnding, getConsoleFunction, setConsoleFunction } from './three.core.js'; +import { Color, Vector2, Vector3, Vector4, Matrix2, Matrix3, Matrix4, UnsignedIntType, IntType, error, RedFormat, RedIntegerFormat, DepthFormat, DepthStencilFormat, AlphaFormat, RGFormat, RGIntegerFormat, RGBFormat, RGBIntegerFormat, EventDispatcher, MathUtils, warn, WebGLCoordinateSystem, WebGPUCoordinateSystem, ColorManagement, SRGBTransfer, NoToneMapping, StaticDrawUsage, InterleavedBufferAttribute, InterleavedBuffer, DynamicDrawUsage, NoColorSpace, log as log$1, warnOnce, Texture, Compatibility, LessCompare, LessEqualCompare, GreaterCompare, GreaterEqualCompare, NearestFilter, Sphere, BackSide, DoubleSide, CubeTexture, CubeReflectionMapping, CubeRefractionMapping, TangentSpaceNormalMap, NoNormalPacking, NormalRGPacking, NormalGAPacking, ObjectSpaceNormalMap, RED_GREEN_RGTC2_Format, RG11_EAC_Format, InstancedBufferAttribute, InstancedInterleavedBuffer, DataTexture, DataArrayTexture, FloatType, FramebufferTexture, LinearMipmapLinearFilter, DepthTexture, Material, LineBasicMaterial, LineDashedMaterial, NoBlending, MeshNormalMaterial, SRGBColorSpace, RenderTarget, BoxGeometry, Mesh, Scene, LinearFilter, CubeCamera, EquirectangularReflectionMapping, EquirectangularRefractionMapping, AddOperation, MixOperation, MultiplyOperation, MeshBasicMaterial, MeshLambertMaterial, MeshPhongMaterial, HalfFloatType, ClampToEdgeWrapping, BufferGeometry, OrthographicCamera, PerspectiveCamera, LinearSRGBColorSpace, RGBAFormat, CubeUVReflectionMapping, BufferAttribute, MeshStandardMaterial, MeshPhysicalMaterial, MeshToonMaterial, MeshMatcapMaterial, SpriteMaterial, PointsMaterial, ShadowMaterial, Uint32BufferAttribute, Uint16BufferAttribute, ByteType, UnsignedByteType, ShortType, UnsignedShortType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedInt248Type, UnsignedInt5999Type, UnsignedInt101111Type, NormalBlending, SrcAlphaFactor, OneMinusSrcAlphaFactor, AddEquation, MaterialBlending, Object3D, LinearMipMapLinearFilter, Plane, Float32BufferAttribute, UVMapping, PCFShadowMap, VSMShadowMap, BasicShadowMap, CubeDepthTexture, SphereGeometry, LinearMipmapNearestFilter, NearestMipmapLinearFilter, Float16BufferAttribute, yieldToMain, REVISION, ArrayCamera, PlaneGeometry, FrontSide, CustomBlending, ZeroFactor, CylinderGeometry, Quaternion, WebXRController, RAD2DEG, PCFSoftShadowMap, FrustumArray, Frustum, RGBAIntegerFormat, TimestampQuery, createCanvasElement, MaxEquation, MinEquation, ReverseSubtractEquation, SubtractEquation, OneMinusConstantAlphaFactor, ConstantAlphaFactor, OneMinusConstantColorFactor, ConstantColorFactor, OneMinusDstAlphaFactor, OneMinusDstColorFactor, OneMinusSrcColorFactor, DstAlphaFactor, DstColorFactor, SrcAlphaSaturateFactor, SrcColorFactor, OneFactor, CullFaceNone, CullFaceBack, CullFaceFront, MultiplyBlending, SubtractiveBlending, AdditiveBlending, NotEqualDepth, GreaterDepth, GreaterEqualDepth, EqualDepth, LessEqualDepth, LessDepth, AlwaysDepth, NeverDepth, ReversedDepthFuncs, RGB_S3TC_DXT1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGB_PVRTC_4BPPV1_Format, RGB_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_PVRTC_2BPPV1_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, R11_EAC_Format, SIGNED_R11_EAC_Format, SIGNED_RG11_EAC_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_BPTC_Format, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, RED_RGTC1_Format, SIGNED_RED_RGTC1_Format, SIGNED_RED_GREEN_RGTC2_Format, MirroredRepeatWrapping, RepeatWrapping, NearestMipmapNearestFilter, NotEqualCompare, EqualCompare, AlwaysCompare, NeverCompare, LinearTransfer, getByteLength, isTypedArray, NotEqualStencilFunc, GreaterStencilFunc, GreaterEqualStencilFunc, EqualStencilFunc, LessEqualStencilFunc, LessStencilFunc, AlwaysStencilFunc, NeverStencilFunc, DecrementWrapStencilOp, IncrementWrapStencilOp, DecrementStencilOp, IncrementStencilOp, InvertStencilOp, ReplaceStencilOp, ZeroStencilOp, KeepStencilOp, SpotLight, PointLight, DirectionalLight, RectAreaLight, AmbientLight, HemisphereLight, LightProbe, LinearToneMapping, ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping, AgXToneMapping, NeutralToneMapping, Group, Loader, FileLoader, MaterialLoader, ObjectLoader } from './three.core.js'; +export { AdditiveAnimationBlendMode, AnimationAction, AnimationClip, AnimationLoader, AnimationMixer, AnimationObjectGroup, AnimationUtils, ArcCurve, ArrowHelper, AttachedBindMode, Audio, AudioAnalyser, AudioContext, AudioListener, AudioLoader, AxesHelper, BasicDepthPacking, BatchedMesh, BezierInterpolant, Bone, BooleanKeyframeTrack, Box2, Box3, Box3Helper, BoxHelper, BufferGeometryLoader, Cache, Camera, CameraHelper, CanvasTexture, CapsuleGeometry, CatmullRomCurve3, CircleGeometry, Clock, ColorKeyframeTrack, CompressedArrayTexture, CompressedCubeTexture, CompressedTexture, CompressedTextureLoader, ConeGeometry, Controls, CubeTextureLoader, CubicBezierCurve, CubicBezierCurve3, CubicInterpolant, CullFaceFrontBack, Curve, CurvePath, CustomToneMapping, Cylindrical, Data3DTexture, DataTextureLoader, DataUtils, DefaultLoadingManager, DetachedBindMode, DirectionalLightHelper, DiscreteInterpolant, DodecahedronGeometry, DynamicCopyUsage, DynamicReadUsage, EdgesGeometry, EllipseCurve, Euler, ExternalTexture, ExtrudeGeometry, Fog, FogExp2, GLBufferAttribute, GLSL1, GLSL3, GridHelper, HTMLTexture, HemisphereLightHelper, IcosahedronGeometry, ImageBitmapLoader, ImageLoader, ImageUtils, InstancedBufferGeometry, InstancedMesh, Int16BufferAttribute, Int32BufferAttribute, Int8BufferAttribute, Interpolant, InterpolateBezier, InterpolateDiscrete, InterpolateLinear, InterpolateSmooth, InterpolationSamplingMode, InterpolationSamplingType, KeyframeTrack, LOD, LatheGeometry, Layers, Light, Line, Line3, LineCurve, LineCurve3, LineLoop, LineSegments, LinearInterpolant, LinearMipMapNearestFilter, LoaderUtils, LoadingManager, LoopOnce, LoopPingPong, LoopRepeat, MOUSE, MeshDepthMaterial, MeshDistanceMaterial, NearestMipMapLinearFilter, NearestMipMapNearestFilter, NormalAnimationBlendMode, NumberKeyframeTrack, OctahedronGeometry, Path, PlaneHelper, PointLightHelper, Points, PolarGridHelper, PolyhedronGeometry, PositionalAudio, PropertyBinding, PropertyMixer, QuadraticBezierCurve, QuadraticBezierCurve3, QuaternionKeyframeTrack, QuaternionLinearInterpolant, RGBADepthPacking, RGBDepthPacking, RGDepthPacking, RawShaderMaterial, Ray, Raycaster, RenderTarget3D, RingGeometry, ShaderMaterial, Shape, ShapeGeometry, ShapePath, ShapeUtils, Skeleton, SkeletonHelper, SkinnedMesh, Source, Spherical, SphericalHarmonics3, SplineCurve, SpotLightHelper, Sprite, StaticCopyUsage, StaticReadUsage, StereoCamera, StreamCopyUsage, StreamDrawUsage, StreamReadUsage, StringKeyframeTrack, TOUCH, TetrahedronGeometry, TextureLoader, TextureUtils, Timer, TorusGeometry, TorusKnotGeometry, Triangle, TriangleFanDrawMode, TriangleStripDrawMode, TrianglesDrawMode, TubeGeometry, Uint8BufferAttribute, Uint8ClampedBufferAttribute, Uniform, UniformsGroup, VectorKeyframeTrack, VideoFrameTexture, VideoTexture, WebGL3DRenderTarget, WebGLArrayRenderTarget, WebGLRenderTarget, WireframeGeometry, WrapAroundEnding, ZeroCurvatureEnding, ZeroSlopeEnding, getConsoleFunction, setConsoleFunction } from './three.core.js'; const refreshUniforms = [ 'alphaMap', @@ -1372,6 +1372,12 @@ function base64ToArrayBuffer( base64 ) { } +function isArrayAsParameter( params ) { + + return ( params[ 0 ] !== undefined && params[ 0 ] !== null ) && ( params[ 0 ].isNode || Object.getPrototypeOf( params[ 0 ] ) !== Object.prototype ); + +} + var NodeUtils = /*#__PURE__*/Object.freeze({ __proto__: null, arrayBufferToBase64: arrayBufferToBase64, @@ -1387,7 +1393,8 @@ var NodeUtils = /*#__PURE__*/Object.freeze({ getValueType: getValueType, hash: hash$1, hashArray: hashArray, - hashString: hashString + hashString: hashString, + isArrayAsParameter: isArrayAsParameter }); /** @@ -4432,12 +4439,6 @@ class ShaderCallNodeInternal extends Node { } -function isArrayAsParameter( params ) { - - return params[ 0 ] && ( params[ 0 ].isNode || Object.getPrototypeOf( params[ 0 ] ) !== Object.prototype ); - -} - function getLayoutParameters( params ) { let output; @@ -13246,6 +13247,18 @@ class TextureNode extends UniformNode { } + /** + * Returns `true` if the texture is sampled with a plain gather (`textureGather`), + * meaning a gather without a compare value. + * + * @return {boolean} Whether a plain gather is used or not. + */ + isPlainGather() { + + return this.gatherNode !== null && this.compareNode === null; + + } + /** * Samples the texture by defining a depth node. * @@ -17740,6 +17753,10 @@ class EventNode extends Node { this.updateType = NodeUpdateType.FRAME; + } else if ( eventType === EventNode.AFTER_OBJECT ) { + + this.updateAfterType = NodeUpdateType.OBJECT; + } else if ( eventType === EventNode.BEFORE_OBJECT ) { this.updateBeforeType = NodeUpdateType.OBJECT; @@ -17798,11 +17815,18 @@ class EventNode extends Node { } + updateAfter( frame ) { + + this.callback( frame ); + + } + } EventNode.OBJECT = 'object'; EventNode.MATERIAL = 'material'; EventNode.FRAME = 'frame'; +EventNode.AFTER_OBJECT = 'afterObject'; EventNode.BEFORE_OBJECT = 'beforeObject'; EventNode.BEFORE_MATERIAL = 'beforeMaterial'; EventNode.BEFORE_FRAME = 'beforeFrame'; @@ -17848,6 +17872,16 @@ const OnMaterialUpdate = ( callback ) => createEvent( EventNode.MATERIAL, callba */ const OnFrameUpdate = ( callback ) => createEvent( EventNode.FRAME, callback ); +/** + * Creates an event that triggers a function every time an object (Mesh|Sprite) has been rendered. + * + * The event will be bound to the declared TSL function `Fn()`; it must be declared within a `Fn()` or the JS function call must be inherited from one. + * + * @param {Function} callback - The callback function. + * @returns {EventNode} + */ +const OnAfterObjectUpdate = ( callback ) => createEvent( EventNode.AFTER_OBJECT, callback ); + /** * Creates an event that triggers a function before an object (Mesh|Sprite) is updated. * @@ -18633,11 +18667,18 @@ const instance = /*@__PURE__*/ Fn( ( [ matrices, colors = null ], builder ) => { const instancedMesh = builder.object; - OnObjectUpdate( ( { object } ) => { + OnAfterObjectUpdate( ( { object } ) => { - const previousInstanceData = _previousInstanceMatrices.get( object ); + const { previousInstanceMatrix } = _previousInstanceMatrices.get( object ); - previousInstanceData.previousInstanceMatrix.array.set( matrices.array ); + previousInstanceMatrix.array.set( matrices.array ); + previousInstanceMatrix.version = matrices.version; + + // handle interleaved path + + const previousInterleavedMatrix = _matrixBuffers.get( previousInstanceMatrix ); + + if ( previousInterleavedMatrix !== undefined ) previousInterleavedMatrix.version = matrices.version; } ); @@ -18680,6 +18721,8 @@ const instancedMesh = /*@__PURE__*/ Fn( ( [ instancedMesh ] ) => { }, 'void' ); +const _previousBatchingMatrices = /*@__PURE__*/ new WeakMap(); + /** * TSL function that retrieves the batching color for a given instance ID from a colors texture. * @@ -18713,6 +18756,61 @@ const getIndirectIndex = /*@__PURE__*/ Fn( ( [ indirectTexture, id ] ) => { } ); +/** + * Creates the node that reads a batching matrix from the given matrices texture. + * + * @param {Texture} matricesTexture - The matrices texture. + * @param {Node} id - The indirect instance ID. + * @returns {Node} The matrix node. + */ +function createBatchingMatrixNode( matricesTexture, id ) { + + const size = int( textureSize( textureLoad( matricesTexture ), 0 ).x ).toConst(); + const j = float( id ).mul( 4 ).toInt().toConst(); + + const x = j.mod( size ).toConst(); + const y = j.div( size ).toConst(); + + return mat4( + textureLoad( matricesTexture, ivec2( x, y ) ), + textureLoad( matricesTexture, ivec2( x.add( 1 ), y ) ), + textureLoad( matricesTexture, ivec2( x.add( 2 ), y ) ), + textureLoad( matricesTexture, ivec2( x.add( 3 ), y ) ) + ); + +} + +/** + * Retrieves or initializes the previous frame batching matrix node for motion vectors. + * Uses a WeakMap to cache previous frame matrices textures and their TSL nodes. + * + * @param {BatchedMesh} batchMesh - The batched mesh. + * @param {Node} id - The indirect instance ID. + * @returns {Node} The previous frame batching matrix node. + */ +function getPreviousNode( batchMesh, id ) { + + let data = _previousBatchingMatrices.get( batchMesh ); + + if ( data === undefined ) { + + const { image, format, type } = batchMesh._matricesTexture; + + const previousMatricesTexture = new DataTexture( image.data.slice(), image.width, image.height, format, type ); + + data = { + previousMatricesTexture, + node: createBatchingMatrixNode( previousMatricesTexture, id ) + }; + + _previousBatchingMatrices.set( batchMesh, data ); + + } + + return data.node; + +} + /** * TSL object representing a varying property for the batching color vector. * @@ -18720,6 +18818,13 @@ const getIndirectIndex = /*@__PURE__*/ Fn( ( [ indirectTexture, id ] ) => { */ const batchColor = /*@__PURE__*/ varyingProperty( 'vec4', 'vBatchColor' ); +/** + * TSL object representing a varying property for the batch indirect index (instance ID). + * + * @type {VaryingNode} + */ +const batchIndirectIndex = /*@__PURE__*/ varyingProperty( 'uint', 'vBatchIndirectId' ); + /** * TSL function representing the vertex shader batching setup. * Applies the batch transformation matrix to positionLocal, normalLocal, and tangentLocal. @@ -18735,19 +18840,9 @@ const batch = /*@__PURE__*/ Fn( ( [ batchMesh ], builder ) => { const indirectId = getIndirectIndex( batchMesh._indirectTexture, int( batchingIdNode ) ); - const matricesTexture = batchMesh._matricesTexture; + batchIndirectIndex.assign( indirectId ); - const size = int( textureSize( textureLoad( matricesTexture ), 0 ).x ).toConst(); - const j = float( indirectId ).mul( 4 ).toInt().toConst(); - - const x = j.mod( size ).toConst(); - const y = j.div( size ).toConst(); - const batchingMatrix = mat4( - textureLoad( matricesTexture, ivec2( x, y ) ), - textureLoad( matricesTexture, ivec2( x.add( 1 ), y ) ), - textureLoad( matricesTexture, ivec2( x.add( 2 ), y ) ), - textureLoad( matricesTexture, ivec2( x.add( 3 ), y ) ) - ); + const batchingMatrix = createBatchingMatrixNode( batchMesh._matricesTexture, indirectId ); const colorsTexture = batchMesh._colorsTexture; @@ -18763,6 +18858,22 @@ const batch = /*@__PURE__*/ Fn( ( [ batchMesh ], builder ) => { positionLocal.assign( batchingMatrix.mul( positionLocal ) ); + if ( builder.needsPreviousData() ) { + + OnAfterObjectUpdate( ( { object } ) => { + + const previousBatchData = _previousBatchingMatrices.get( object ); + + previousBatchData.previousMatricesTexture.image.data.set( object._matricesTexture.image.data ); + previousBatchData.previousMatricesTexture.needsUpdate = true; + + } ); + + const previousBatchingMatrixNode = getPreviousNode( batchMesh, indirectId ); + positionPrevious.assign( previousBatchingMatrixNode.mul( positionPrevious ).xyz ); + + } + const transformedNormal = normalLocal.div( vec3( bm[ 0 ].dot( bm[ 0 ] ), bm[ 1 ].dot( bm[ 1 ] ), bm[ 2 ].dot( bm[ 2 ] ) ) ); const batchingNormal = bm.mul( transformedNormal ).xyz; @@ -24808,10 +24919,10 @@ const getTransmissionSample = /*@__PURE__*/ Fn( ( [ fragCoord, roughness, ior ], const vTexture = material.side === BackSide ? viewportBackSideTexture : viewportFrontSideTexture; - const transmissionSample = vTexture.sample( fragCoord ); + const transmissionSample = vTexture.sample( fragCoord.mul( cameraViewport.zw ).add( cameraViewport.xy ).div( screenSize ) ); //const transmissionSample = viewportMipTexture( fragCoord ); - const lod = log2( screenSize.x ).mul( applyIorToRoughness( roughness, ior ) ); + const lod = log2( cameraViewport.z ).mul( applyIorToRoughness( roughness, ior ) ); return textureBicubicLevel( transmissionSample, lod ); @@ -28042,11 +28153,13 @@ class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial { setupSpecular() { const iorNode = this.iorNode ? float( this.iorNode ) : materialIOR; + const specularColorNode = this.specularColorNode ? vec3( this.specularColorNode ) : materialSpecularColor; + const specularIntensityNode = this.specularIntensityNode ? float( this.specularIntensityNode ) : materialSpecularIntensity; ior.assign( iorNode ); - specularColor.assign( min$1( pow2( ior.sub( 1.0 ).div( ior.add( 1.0 ) ) ).mul( materialSpecularColor ), vec3( 1.0 ) ).mul( materialSpecularIntensity ) ); + specularColor.assign( min$1( pow2( ior.sub( 1.0 ).div( ior.add( 1.0 ) ) ).mul( specularColorNode ), vec3( 1.0 ) ).mul( specularIntensityNode ) ); specularColorBlended.assign( mix( specularColor, diffuseColor.rgb, metalness ) ); - specularF90.assign( mix( materialSpecularIntensity, 1.0, metalness ) ); + specularF90.assign( mix( specularIntensityNode, 1.0, metalness ) ); } @@ -30040,6 +30153,19 @@ class RenderObject { }; + /** + * An event listener which is executed when `dispose()` is called on + * the 3D object of this render object. + * + * @method + */ + this.onObjectDispose = () => { + + this.dispose(); + + }; + + this.object.addEventListener( 'dispose', this.onObjectDispose ); this.material.addEventListener( 'dispose', this.onMaterialDispose ); this.geometry.addEventListener( 'dispose', this.onGeometryDispose ); @@ -30654,6 +30780,7 @@ class RenderObject { */ dispose() { + this.object.removeEventListener( 'dispose', this.onObjectDispose ); this.material.removeEventListener( 'dispose', this.onMaterialDispose ); this.geometry.removeEventListener( 'dispose', this.onGeometryDispose ); @@ -30983,11 +31110,6 @@ const AttributeType = { const GPU_CHUNK_BYTES = 16; -// @TODO: Move to src/constants.js - -const BlendColorFactor = 211; -const OneMinusBlendColorFactor = 212; - /** * This renderer module manages geometry attributes. * @@ -32319,9 +32441,10 @@ class Pipelines extends DataMap { * * @param {Node} computeNode - The compute node. * @param {Array} bindings - The bindings. + * @param {?Array} [promises=null] - An array of compilation promises which is only relevant in context of `Renderer.compileComputeAsync()`. * @return {ComputePipeline} The compute pipeline. */ - getForCompute( computeNode, bindings ) { + getForCompute( computeNode, bindings, promises = null ) { const { backend } = this; @@ -32368,7 +32491,7 @@ class Pipelines extends DataMap { if ( previousPipeline && previousPipeline.usedTimes === 0 ) this._releasePipeline( previousPipeline ); - pipeline = this._getComputePipeline( computeNode, stageCompute, cacheKey, bindings ); + pipeline = this._getComputePipeline( computeNode, stageCompute, cacheKey, bindings, promises ); } @@ -32580,9 +32703,10 @@ class Pipelines extends DataMap { * @param {ProgrammableStage} stageCompute - The programmable stage representing the compute shader. * @param {string} cacheKey - The cache key. * @param {Array} bindings - The bindings. + * @param {?Array} promises - An array of compilation promises which is only relevant in context of `Renderer.compileComputeAsync()`. * @return {ComputePipeline} The compute pipeline. */ - _getComputePipeline( computeNode, stageCompute, cacheKey, bindings ) { + _getComputePipeline( computeNode, stageCompute, cacheKey, bindings, promises ) { // check for existing pipeline @@ -32596,7 +32720,7 @@ class Pipelines extends DataMap { this.caches.set( cacheKey, pipeline ); - this.backend.createComputePipeline( pipeline, bindings ); + this.backend.createComputePipeline( pipeline, bindings, promises ); } @@ -35816,7 +35940,7 @@ const struct = ( membersLayout, name = null ) => { if ( params.length > 0 ) { - if ( params[ 0 ].isNode ) { + if ( isArrayAsParameter( params ) ) { values = {}; @@ -35824,7 +35948,7 @@ const struct = ( membersLayout, name = null ) => { for ( let i = 0; i < params.length; i ++ ) { - values[ names[ i ] ] = params[ i ]; + values[ names[ i ] ] = nodeObject( params[ i ] ); } @@ -42021,7 +42145,7 @@ class CodeNode extends Node { for ( const include of includes ) { - include.build( builder ); + include.build( builder, 'void' ); } @@ -48717,6 +48841,7 @@ var TSL = /*#__PURE__*/Object.freeze({ NodeShaderStage: NodeShaderStage, NodeType: NodeType, NodeUpdateType: NodeUpdateType, + OnAfterObjectUpdate: OnAfterObjectUpdate, OnAfterRenderPipeline: OnAfterRenderPipeline, OnBeforeFrameUpdate: OnBeforeFrameUpdate, OnBeforeMaterialUpdate: OnBeforeMaterialUpdate, @@ -48781,6 +48906,7 @@ var TSL = /*#__PURE__*/Object.freeze({ backgroundRotation: backgroundRotation, batch: batch, batchColor: batchColor, + batchIndirectIndex: batchIndirectIndex, bentNormalView: bentNormalView, billboarding: billboarding, bitAnd: bitAnd, @@ -56102,9 +56228,10 @@ class NodeManager extends DataMap { * Returns a node builder state for the given compute node. * * @param {Node} computeNode - The compute node. - * @return {NodeBuilderState} The node builder state. + * @param {boolean} [useAsync=false] - Whether to use async build with yielding. + * @return {NodeBuilderState|Promise} The node builder state (or Promise if async). */ - getForCompute( computeNode ) { + getForCompute( computeNode, useAsync = false ) { const computeData = this.get( computeNode ); @@ -56117,6 +56244,21 @@ class NodeManager extends DataMap { if ( onNodeBuilderCreated !== null ) onNodeBuilderCreated( nodeBuilder, computeNode ); + if ( useAsync ) { + + return nodeBuilder.buildAsync().then( () => { + + nodeBuilderState = this._createNodeBuilderState( nodeBuilder ); + + computeData.nodeBuilderState = nodeBuilderState; + computeData.version = computeNode.version; + + return nodeBuilderState; + + } ); + + } + nodeBuilder.build(); nodeBuilderState = this._createNodeBuilderState( nodeBuilder ); @@ -56130,6 +56272,27 @@ class NodeManager extends DataMap { } + /** + * Async version of getForCompute() that yields to main thread during build. + * Use this in compileComputeAsync() to prevent blocking the main thread. + * + * @param {Node} computeNode - The compute node. + * @return {Promise} A promise that resolves to the node builder state. + */ + getForComputeAsync( computeNode ) { + + const result = this.getForCompute( computeNode, true ); + + if ( result.then ) { + + return result; + + } + + return Promise.resolve( result ); + + } + /** * Creates a node builder state for the given node builder. * @@ -56392,6 +56555,27 @@ class NodeManager extends DataMap { if ( node === undefined || forceUpdate ) { + if ( node === undefined && object.isTexture === true ) { + + const onTextureDispose = () => { + + object.removeEventListener( 'dispose', onTextureDispose ); + + const node = nodeCache.get( object ); + + if ( node !== undefined ) { + + nodeCache.delete( object ); + node.dispose(); + + } + + }; + + object.addEventListener( 'dispose', onTextureDispose ); + + } + node = callback(); nodeCache.set( object, node ); @@ -57647,22 +57831,22 @@ class XRManager extends EventDispatcher { this._currentPixelRatio = null; /** - * The renderer's sample count before XR temporarily overrides it. + * The current size of the renderer's canvas + * in logical pixel unit. * * @private - * @type {?number} - * @default null + * @type {Vector2} */ - this._currentSamples = null; + this._currentSize = new Vector2(); /** - * The current size of the renderer's canvas - * in logical pixel unit. + * Holds a reference to the user camera and its current settings. * * @private - * @type {Vector2} + * @type {?Object} + * @default null */ - this._currentSize = new Vector2(); + this._currentCameraSettings = null; /** * The default event listener for handling events inside a XR session. @@ -58065,11 +58249,11 @@ class XRManager extends EventDispatcher { * Browser-side `XRWebGLBinding.foveateBoundTexture()` failures are treated as * non-fatal so they do not interrupt rendering. * - * @param {RenderTarget} renderTarget - The internal render target. + * @param {?RenderTarget} renderTarget - The internal render target. */ foveateBoundTexture( renderTarget ) { - if ( renderTarget.isPostProcessingRenderTarget !== true ) return; + if ( renderTarget === null || renderTarget.isPostProcessingRenderTarget !== true ) return; if ( this.isPresenting !== true ) return; if ( this._glProjLayer === null ) return; @@ -58154,9 +58338,7 @@ class XRManager extends EventDispatcher { */ _validateWebGPUSession() { - const renderer = this._renderer; - - if ( renderer.backend.isWebGPUBackend !== true ) return; + if ( this._renderer.backend.isWebGPUBackend !== true ) return; if ( this._session.enabledFeatures.includes( 'webgpu' ) === false ) { @@ -58164,15 +58346,6 @@ class XRManager extends EventDispatcher { } - if ( renderer.samples > 0 ) { - - warnOnce( 'THREE.XRManager: WebGPU XR does not support MSAA yet. Disabling MSAA for this XR session.' ); - - if ( this._currentSamples === null ) this._currentSamples = renderer.samples; - renderer._samples = 0; - - } - } /** @@ -58187,8 +58360,7 @@ class XRManager extends EventDispatcher { const webgpuBinding = this.getWebGPUBinding(); const glProjLayer = webgpuBinding.createProjectionLayer( { - colorFormat: webgpuBinding.getPreferredColorFormat(), - depthStencilFormat: 'depth24plus' + colorFormat: webgpuBinding.getPreferredColorFormat() } ); this._glProjLayer = glProjLayer; @@ -58204,7 +58376,10 @@ class XRManager extends EventDispatcher { depthBuffer: true, multiview: false, useArrayDepthTexture: true, - samples: 0 + storeMultisampledColorBuffer: false, + storeMultisampledDepthBuffer: false, + storeMultisampledStencilBuffer: false, + samples: this._renderer.samples } ); this._xrRenderTarget.texture.isArrayTexture = true; @@ -58227,46 +58402,27 @@ class XRManager extends EventDispatcher { _disposeWebGPUSession() { const renderer = this._renderer; - const xrRenderTarget = this._xrRenderTarget; - - if ( xrRenderTarget === null || renderer.backend.isWebGPUBackend !== true ) return; - - // XR textures are external (from XRGPUBinding), so clear cached state before disposal. const backend = renderer.backend; - const texturesModule = renderer._textures; - - const renderTargetData = backend.get ? backend.get( xrRenderTarget ) : null; - if ( renderTargetData ) { - - renderTargetData.descriptors = undefined; - - } - - const deleteResource = ( resource ) => { - - if ( resource === null || resource === undefined ) return; + const xrRenderTarget = this._xrRenderTarget; - if ( backend.delete ) backend.delete( resource ); - if ( texturesModule.delete ) texturesModule.delete( resource ); + if ( xrRenderTarget === null || backend.isWebGPUBackend !== true ) return; - }; - - for ( let i = 0; i < xrRenderTarget.textures.length; i ++ ) { + if ( renderer._renderContexts && renderer._renderContexts.dispose ) { - deleteResource( xrRenderTarget.textures[ i ] ); + renderer._renderContexts.dispose(); } - deleteResource( xrRenderTarget.depthTexture ); - deleteResource( xrRenderTarget ); + xrRenderTarget.dispose(); - if ( renderer._renderContexts && renderer._renderContexts.dispose ) { + // The external texture can be registered before the render target is initialized. + for ( const texture of xrRenderTarget.textures ) { - renderer._renderContexts.dispose(); + if ( backend.has( texture ) ) backend.destroyTexture( texture ); } - xrRenderTarget.dispose(); + backend.delete( xrRenderTarget ); } @@ -58345,6 +58501,7 @@ class XRManager extends EventDispatcher { * @param {Function} rendercall - A callback function that renders the layer. Similar to code in * the default animation loop, this method can be used to update/transform 3D object in the layer's scene. * @param {Object} [attributes={}] - Allows to configure the layer's render target. + * @param {number} [attributes.samples] - The scene MSAA sample count. Defaults to the renderer's sample count. * @return {Mesh} A mesh representing the quadratic XR layer. This mesh should be added to the XR scene. */ createQuadLayer( width, height, translation, quaternion, pixelwidth, pixelheight, rendercall, attributes = {} ) { @@ -58369,14 +58526,14 @@ class XRManager extends EventDispatcher { attributes.stencil ? DepthStencilFormat : DepthFormat ), stencilBuffer: attributes.stencil, + samples: attributes.samples ?? this._renderer.samples, resolveDepthBuffer: false, resolveStencilBuffer: false, + storeMultisampledColorBuffer: false, storeMultisampledDepthBuffer: false, storeMultisampledStencilBuffer: false } ); - renderTarget._autoAllocateDepthBuffer = true; - const material = new MeshBasicMaterial( { color: 0xffffff, side: FrontSide } ); material.map = renderTarget.texture; material.map.offset.y = 1; @@ -58393,6 +58550,7 @@ class XRManager extends EventDispatcher { quaternion: quaternion, pixelwidth: pixelwidth, pixelheight: pixelheight, + samples: renderTarget.samples, plane: plane, material: material, rendercall: rendercall, @@ -58438,6 +58596,7 @@ class XRManager extends EventDispatcher { * @param {Function} rendercall - A callback function that renders the layer. Similar to code in * the default animation loop, this method can be used to update/transform 3D object in the layer's scene. * @param {Object} [attributes={}] - Allows to configure the layer's render target. + * @param {number} [attributes.samples] - The scene MSAA sample count. Defaults to the renderer's sample count. * @return {Mesh} A mesh representing the cylindrical XR layer. This mesh should be added to the XR scene. */ createCylinderLayer( radius, centralAngle, aspectratio, translation, quaternion, pixelwidth, pixelheight, rendercall, attributes = {} ) { @@ -58462,14 +58621,14 @@ class XRManager extends EventDispatcher { attributes.stencil ? DepthStencilFormat : DepthFormat ), stencilBuffer: attributes.stencil, + samples: attributes.samples ?? this._renderer.samples, resolveDepthBuffer: false, resolveStencilBuffer: false, + storeMultisampledColorBuffer: false, storeMultisampledDepthBuffer: false, storeMultisampledStencilBuffer: false } ); - renderTarget._autoAllocateDepthBuffer = true; - const material = new MeshBasicMaterial( { color: 0xffffff, side: BackSide } ); material.map = renderTarget.texture; material.map.offset.y = 1; @@ -58487,6 +58646,7 @@ class XRManager extends EventDispatcher { quaternion: quaternion, pixelwidth: pixelwidth, pixelheight: pixelheight, + samples: renderTarget.samples, plane: plane, material: material, rendercall: rendercall, @@ -58784,11 +58944,7 @@ class XRManager extends EventDispatcher { format: RGBAFormat, type: UnsignedByteType, colorSpace: renderer.outputColorSpace, - stencilBuffer: renderer.stencil, - resolveDepthBuffer: ( glBaseLayer.ignoreDepthValues === false ), - resolveStencilBuffer: ( glBaseLayer.ignoreDepthValues === false ), - storeMultisampledDepthBuffer: ( glBaseLayer.ignoreDepthValues === false ), - storeMultisampledStencilBuffer: ( glBaseLayer.ignoreDepthValues === false ), + stencilBuffer: renderer.stencil } ); @@ -58882,6 +59038,12 @@ class XRManager extends EventDispatcher { // update user camera and its children + if ( this._currentCameraSettings === null && camera.isPerspectiveCamera ) { + + this._currentCameraSettings = { camera: camera, fov: camera.fov, zoom: camera.zoom }; + + } + updateUserCamera( camera, cameraXR, parent ); @@ -59096,13 +59258,6 @@ function onSessionEnd() { this._currentDepthNear = null; this._currentDepthFar = null; - if ( this._currentSamples !== null ) { - - renderer._samples = this._currentSamples; - this._currentSamples = null; - - } - // restore framebuffer/rendering state renderer._resetXRState(); @@ -59141,8 +59296,10 @@ function onSessionEnd() { layer.stencilBuffer ? DepthStencilFormat : DepthFormat ), stencilBuffer: layer.stencilBuffer, + samples: layer.samples, resolveDepthBuffer: false, resolveStencilBuffer: false, + storeMultisampledColorBuffer: false, storeMultisampledDepthBuffer: false, storeMultisampledStencilBuffer: false } ); @@ -59172,6 +59329,18 @@ function onSessionEnd() { renderer.setPixelRatio( this._currentPixelRatio ); renderer.setSize( this._currentSize.width, this._currentSize.height, false ); + if ( this._currentCameraSettings !== null ) { + + const camera = this._currentCameraSettings.camera; + + camera.fov = this._currentCameraSettings.fov; + camera.zoom = this._currentCameraSettings.zoom; + camera.updateProjectionMatrix(); + + this._currentCameraSettings = null; + + } + this.dispatchEvent( { type: 'sessionend' } ); } @@ -59396,6 +59565,14 @@ function onAnimationFrame( time, frame ) { renderer.setOutputRenderTarget( this._xrRenderTarget ); const frameBufferTarget = renderer._getFrameBufferTarget(); + + if ( webgpuViewData !== null ) { + + this._xrRenderTarget.samples = frameBufferTarget === null ? renderer.samples : 0; + this._xrRenderTarget.depthBuffer = frameBufferTarget === null; + + } + renderer.xr.foveateBoundTexture( frameBufferTarget ); } @@ -60620,9 +60797,10 @@ class Renderer { * @param {Object3D} scene - The scene or 3D object to precompile. * @param {Camera} camera - The camera that is used to render the scene. * @param {?Scene} targetScene - If the first argument is a 3D object, this parameter must represent the scene the 3D object is going to be added. + * @param {onProgressCallback} [onProgress] - Executed while the compilation is in progress. * @return {Promise} A Promise that resolves when the compile has been finished. */ - async compileAsync( scene, camera, targetScene = null ) { + async compileAsync( scene, camera, targetScene = null, onProgress = null ) { if ( this._isDeviceLost === true ) return; @@ -60656,7 +60834,9 @@ class Renderer { // Match render()'s logic: use frameBufferTarget when needsFrameBufferTarget is true const useFrameBufferTarget = this.needsFrameBufferTarget && this._renderTarget === null; - const renderTarget = useFrameBufferTarget ? this._getFrameBufferTarget() : ( this._renderTarget || this._outputRenderTarget ); + const outputRenderTarget = this._renderTarget || this._outputRenderTarget; + const useXRCamera = this.xr.isPresenting === true && this.isOutputTarget; + const renderTarget = useFrameBufferTarget ? this._getFrameBufferTarget() : outputRenderTarget; const renderContext = this._renderContexts.get( renderTarget, this._mrt ); const activeMipmapLevel = this._activeMipmapLevel; @@ -60687,7 +60867,7 @@ class Renderer { if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); - camera = this._updateCamera( camera ); + camera = this._updateCamera( camera, useXRCamera ); // @@ -60782,6 +60962,9 @@ class Renderer { // Process compilation work items sequentially to avoid freezing // Yields between objects to keep animation smooth + const total = compilationPromises.length; + let loaded = 0; + for ( const item of compilationPromises ) { const renderObject = this._objects.get( item.object, item.material, item.scene, item.camera, item.lightsNode, item.renderContext, item.clippingContext, item.passId ); @@ -60811,6 +60994,14 @@ class Renderer { this._nodes.updateAfter( renderObject ); this._isPreCompiling = false; + loaded ++; + + if ( onProgress !== null ) { + + onProgress( new ProgressEvent( 'progress', { lengthComputable: true, loaded, total } ) ); + + } + // Yield between objects to allow animation frames await yieldToMain(); @@ -60818,6 +61009,90 @@ class Renderer { } + /** + * Compile compute programs. This can be useful to avoid a + * phenomenon which is called "shader compilation stutter", which occurs when + * rendering an object with a new shader for the first time. + * + * @async + * @param {Node|Array} computeNodes - The compute node(s). + * @param {onProgressCallback} [onProgress] - Executed while the compilation is in progress. + * @return {Promise} A Promise that resolves when the compile has been finished. + */ + async compileComputeAsync( computeNodes, onProgress = null ) { + + if ( this._isDeviceLost === true ) return; + + if ( this._initialized === false ) await this.init(); + + const computeList = Array.isArray( computeNodes ) ? computeNodes : [ computeNodes ]; + + if ( computeList.length === 0 || computeList.some( ( computeNode ) => computeNode === undefined || computeNode === null || computeNode.isComputeNode !== true ) ) { + + throw new Error( 'THREE.Renderer: .compileComputeAsync() expects a ComputeNode.' ); + + } + + const total = computeList.length; + let loaded = 0; + + // + + const pipelines = this._pipelines; + const bindings = this._bindings; + const nodes = this._nodes; + + for ( const computeNode of computeList ) { + + if ( pipelines.has( computeNode ) === false ) { + + const dispose = () => { + + computeNode.removeEventListener( 'dispose', dispose ); + + pipelines.delete( computeNode ); + bindings.deleteForCompute( computeNode ); + nodes.delete( computeNode ); + + }; + + computeNode.addEventListener( 'dispose', dispose ); + + const onInitFn = computeNode.onInitFunction; + + if ( onInitFn !== null ) { + + onInitFn.call( computeNode, { renderer: this } ); + + } + + } + + await nodes.getForComputeAsync( computeNode ); + + nodes.updateForCompute( computeNode ); + bindings.updateForCompute( computeNode ); + + const computeBindings = bindings.getForCompute( computeNode ); + const compilationPromises = []; + + pipelines.getForCompute( computeNode, computeBindings, compilationPromises ); + await Promise.all( compilationPromises ); + + loaded ++; + + if ( onProgress !== null ) { + + onProgress( new ProgressEvent( 'progress', { lengthComputable: true, loaded, total } ) ); + + } + + if ( loaded < total ) await yieldToMain(); + + } + + } + /** * Renders the scene in an async fashion. * @@ -61137,7 +61412,9 @@ class Renderer { _renderOutputLayers( quad, renderTarget ) { - if ( renderTarget.texture.isArrayTexture !== true || renderTarget.texture.image.depth <= 1 ) { + const useMultiview = this.backend.isWebGLBackend === true && renderTarget.multiview === true; + + if ( useMultiview || renderTarget.texture.isArrayTexture !== true || renderTarget.texture.image.depth <= 1 ) { this._renderScene( quad, quad.camera, false ); return; @@ -61176,12 +61453,7 @@ class Renderer { */ _getFrameBufferTarget() { - const { currentToneMapping, currentColorSpace } = this; - - const useToneMapping = currentToneMapping !== NoToneMapping; - const useColorSpace = currentColorSpace !== ColorManagement.workingColorSpace; - - if ( useToneMapping === false && useColorSpace === false ) return null; + if ( this.needsFrameBufferTarget === false ) return null; const { width, height } = this.getDrawingBufferSize( _drawingBufferSize ); const { depth, stencil } = this; @@ -61306,6 +61578,7 @@ class Renderer { const sceneRef = ( scene.isScene === true ) ? scene : _scene; const outputRenderTarget = this._renderTarget || this._outputRenderTarget; + const useXRCamera = this.xr.isPresenting === true && this.isOutputTarget; const activeCubeFace = this._activeCubeFace; const activeMipmapLevel = this._activeMipmapLevel; @@ -61374,7 +61647,7 @@ class Renderer { if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); - camera = this._updateCamera( camera ); + camera = this._updateCamera( camera, useXRCamera ); // @@ -62224,6 +62497,7 @@ class Renderer { * Returns `true` if a framebuffer target is needed to perform tone mapping or color space conversion. * If this is the case, the renderer allocates an internal render target for that purpose. * + * @type {boolean} */ get needsFrameBufferTarget() { @@ -62839,7 +63113,7 @@ class Renderer { * @param {number} width - The width of the copy region. * @param {number} height - The height of the copy region. * @param {number} [textureIndex=0] - The texture index of a MRT render target. - * @param {number} [faceIndex=0] - The active cube face index. + * @param {number} [faceIndex=0] - The cube face, depth slice or array layer index. * @return {Promise} A Promise that resolves when the read has been finished. The resolve provides the read data as a typed array. */ async readRenderTargetPixelsAsync( renderTarget, x, y, width, height, textureIndex = 0, faceIndex = 0 ) { @@ -63221,13 +63495,14 @@ class Renderer { * * @private * @param {Camera} camera - The camera to update. + * @param {boolean} useXRCamera - Whether the XR camera should be used when presenting. * @return {Camera} The returned camera might be different depending on whether XR is used or not. */ - _updateCamera( camera ) { + _updateCamera( camera, useXRCamera ) { const xr = this.xr; - if ( xr.isPresenting === false ) { + if ( xr.isPresenting === false || useXRCamera === false ) { let projectionMatrixNeedsUpdate = false; @@ -63297,7 +63572,7 @@ class Renderer { // handle XR - if ( xr.enabled === true && xr.isPresenting === true ) { + if ( useXRCamera === true && xr.enabled === true && xr.isPresenting === true ) { if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera ); camera = xr.getCamera(); // use XR camera for rendering @@ -63588,7 +63863,8 @@ class Renderer { * @param {Object3D} scene - The scene or 3D object to precompile. * @param {Camera} camera - The camera that is used to render the scene. * @param {Scene} targetScene - If the first argument is a 3D object, this parameter must represent the scene the 3D object is going to be added. - * @return {function(Object3D, Camera, ?Scene): Promise|undefined} A Promise that resolves when the compile has been finished. + * @param {onProgressCallback} [onProgress] - Executed while the compilation is in progress. + * @return {function(Object3D, Camera, ?Scene, ?onProgressCallback): Promise|undefined} A Promise that resolves when the compile has been finished. */ get compile() { @@ -65773,7 +66049,7 @@ ${ flowData.code } snippet = `${typePrefix}sampler3D ${ uniform.name };`; - } else if ( texture.compareFunction && textureNode.compareNode !== null ) { + } else if ( texture.compareFunction && textureNode.isPlainGather() === false ) { if ( texture.isArrayTexture === true ) { @@ -66982,8 +67258,9 @@ class Backend { * @abstract * @param {ComputePipeline} computePipeline - The compute pipeline. * @param {Array} bindings - The bindings. + * @param {?Array} [promises=null] - Optional compilation promises. */ - createComputePipeline( /*computePipeline, bindings*/ ) { } + createComputePipeline( /*computePipeline, bindings, promises*/ ) { } // cache key @@ -67090,7 +67367,7 @@ class Backend { * @param {number} y - The y coordinate of the copy origin. * @param {number} width - The width of the copy. * @param {number} height - The height of the copy. - * @param {number} faceIndex - The face index. + * @param {number} faceIndex - The cube face, depth slice or array layer index. * @return {Promise} A Promise that resolves with a typed array when the copy operation has finished. */ async copyTextureToBuffer( /*texture, x, y, width, height, faceIndex*/ ) {} @@ -67964,6 +68241,8 @@ class WebGLState { this.currentBlendDst = null; this.currentBlendSrcAlpha = null; this.currentBlendDstAlpha = null; + this.currentBlendColor = new Color( 0, 0, 0 ); + this.currentBlendAlpha = 0; this.currentPremultipledAlpha = null; this.currentPolygonOffsetFactor = null; this.currentPolygonOffsetUnits = null; @@ -68010,7 +68289,9 @@ class WebGLState { equationToGL = { [ AddEquation ]: gl.FUNC_ADD, [ SubtractEquation ]: gl.FUNC_SUBTRACT, - [ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT + [ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT, + [ MinEquation ]: gl.MIN, + [ MaxEquation ]: gl.MAX }; factorToGL = { @@ -68024,7 +68305,11 @@ class WebGLState { [ OneMinusSrcColorFactor ]: gl.ONE_MINUS_SRC_COLOR, [ OneMinusSrcAlphaFactor ]: gl.ONE_MINUS_SRC_ALPHA, [ OneMinusDstColorFactor ]: gl.ONE_MINUS_DST_COLOR, - [ OneMinusDstAlphaFactor ]: gl.ONE_MINUS_DST_ALPHA + [ OneMinusDstAlphaFactor ]: gl.ONE_MINUS_DST_ALPHA, + [ ConstantColorFactor ]: gl.CONSTANT_COLOR, + [ OneMinusConstantColorFactor ]: gl.ONE_MINUS_CONSTANT_COLOR, + [ ConstantAlphaFactor ]: gl.CONSTANT_ALPHA, + [ OneMinusConstantAlphaFactor ]: gl.ONE_MINUS_CONSTANT_ALPHA }; const scissorParam = gl.getParameter( gl.SCISSOR_BOX ); @@ -68326,7 +68611,7 @@ class WebGLState { * Defines the blending. * * This method caches the state so `gl.blendEquation()`, `gl.blendEquationSeparate()`, - * `gl.blendFunc()` and `gl.blendFuncSeparate()` are only called when necessary. + * `gl.blendFunc()`, `gl.blendFuncSeparate()` and `gl.blendColor()` are only called when necessary. * * @param {number} blending - The blending type. * @param {number} blendEquation - The blending equation. @@ -68335,9 +68620,11 @@ class WebGLState { * @param {number} blendEquationAlpha - Only relevant for custom blending. The blending equation for alpha. * @param {number} blendSrcAlpha - Only relevant for custom blending. The alpha source blending factor. * @param {number} blendDstAlpha - Only relevant for custom blending. The alpha destination blending factor. + * @param {Color} blendColor - Only relevant for custom blending. The RGB values of the constant blend color. + * @param {number} blendAlpha - Only relevant for custom blending. The alpha value of the constant blend color. * @param {boolean} premultipliedAlpha - Whether premultiplied alpha is enabled or not. */ - setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { + setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, blendColor, blendAlpha, premultipliedAlpha ) { const { gl } = this; @@ -68468,6 +68755,15 @@ class WebGLState { } + if ( blendColor.equals( this.currentBlendColor ) === false || blendAlpha !== this.currentBlendAlpha ) { + + gl.blendColor( blendColor.r, blendColor.g, blendColor.b, blendAlpha ); + + this.currentBlendColor.copy( blendColor ); + this.currentBlendAlpha = blendAlpha; + + } + this.currentBlending = blending; this.currentPremultipledAlpha = false; @@ -68817,7 +69113,7 @@ class WebGLState { ( material.blending === NormalBlending && material.transparent === false ) ? this.setBlending( NoBlending ) - : this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); + : this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.blendColor, material.blendAlpha, material.premultipliedAlpha ); this.setDepthFunc( material.depthFunc ); this.setDepthTest( material.depthTest ); @@ -69344,6 +69640,8 @@ class WebGLState { this.currentBlendDst = null; this.currentBlendSrcAlpha = null; this.currentBlendDstAlpha = null; + this.currentBlendColor.set( 0, 0, 0 ); + this.currentBlendAlpha = 0; this.currentPremultipledAlpha = null; this.currentPolygonOffsetFactor = null; this.currentPolygonOffsetUnits = null; @@ -70929,7 +71227,7 @@ class WebGLTextureUtils { * @param {number} y - The y coordinate of the copy origin. * @param {number} width - The width of the copy. * @param {number} height - The height of the copy. - * @param {number} faceIndex - The face index. + * @param {number} faceIndex - The cube face, depth slice or array layer index. * @return {Promise} A Promise that resolves with a typed array when the copy operation has finished. */ async copyTextureToBuffer( texture, x, y, width, height, faceIndex ) { @@ -70942,9 +71240,17 @@ class WebGLTextureUtils { backend.state.bindFramebuffer( gl.READ_FRAMEBUFFER, fb ); - const target = texture.isCubeTexture ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex : gl.TEXTURE_2D; + if ( texture.isData3DTexture || texture.isDataArrayTexture || texture.isArrayTexture ) { + + gl.framebufferTextureLayer( gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, textureGPU, 0, faceIndex ); + + } else { + + const target = texture.isCubeTexture ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex : gl.TEXTURE_2D; - gl.framebufferTexture2D( gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, target, textureGPU, 0 ); + gl.framebufferTexture2D( gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, target, textureGPU, 0 ); + + } const typedArrayType = this._getTypedArrayType( glType ); const bytesPerTexel = this._getBytesPerTexel( glType, glFormat ); @@ -73321,7 +73627,7 @@ class WebGLBackend extends Backend { * @param {number} y - The y coordinate of the copy origin. * @param {number} width - The width of the copy. * @param {number} height - The height of the copy. - * @param {number} faceIndex - The face index. + * @param {number} faceIndex - The cube face, depth slice or array layer index. * @return {Promise} A Promise that resolves with a typed array when the copy operation has finished. */ async copyTextureToBuffer( texture, x, y, width, height, faceIndex ) { @@ -73605,10 +73911,11 @@ class WebGLBackend extends Backend { * * @param {ComputePipeline} computePipeline - The compute pipeline. * @param {Array} bindings - The bindings. + * @param {?Array} [promises=null] - Optional compilation promises. */ - createComputePipeline( computePipeline, bindings ) { + createComputePipeline( computePipeline, bindings, promises = null ) { - const { state, gl } = this; + const { gl } = this; // Program @@ -73651,19 +73958,6 @@ class WebGLBackend extends Backend { gl.linkProgram( programGPU ); - if ( gl.getProgramParameter( programGPU, gl.LINK_STATUS ) === false ) { - - this._logProgramError( programGPU, fragmentShader, vertexShader ); - - - } - - state.useProgram( programGPU ); - - // Bindings - - this._setupBindings( bindings, programGPU ); - const attributeNodes = computeProgram.attributes; const attributes = []; const transformBuffers = []; @@ -73690,14 +73984,73 @@ class WebGLBackend extends Backend { } - // + // Store pipeline data this.set( computePipeline, { programGPU, + fragmentShader, + vertexShader, transformBuffers, attributes } ); + if ( promises !== null && this.parallel ) { + + const parallel = this.parallel; + + const p = new Promise( ( resolve ) => { + + const checkStatus = () => { + + if ( gl.getProgramParameter( programGPU, parallel.COMPLETION_STATUS_KHR ) ) { + + this._completeComputeCompile( computePipeline, bindings ); + resolve(); + + } else { + + requestAnimationFrame( checkStatus ); + + } + + }; + + checkStatus(); + + } ); + + promises.push( p ); + return; + + } + + // Sync fallback + this._completeComputeCompile( computePipeline, bindings ); + + } + + /** + * Completes the compute pipeline setup for the given compute pipeline. + * + * @param {ComputePipeline} computePipeline - The compute pipeline. + * @param {Array} bindings - Array of bind groups. + */ + _completeComputeCompile( computePipeline, bindings ) { + + const { state, gl } = this; + const { programGPU, fragmentShader, vertexShader } = this.get( computePipeline ); + + if ( gl.getProgramParameter( programGPU, gl.LINK_STATUS ) === false ) { + + this._logProgramError( programGPU, fragmentShader, vertexShader ); + + } + + state.useProgram( programGPU ); + + // Bindings (must be after link completion) + this._setupBindings( bindings, programGPU ); + } /** @@ -74722,7 +75075,8 @@ class WebGLBackend extends Backend { } - } else if ( renderTarget.storeMultisampledDepthBuffer === false && renderTargetContextData.framebuffers ) { + } else if ( this._supportsInvalidateFramebuffer === true && renderTargetContextData.framebuffers && + ( renderTarget._autoAllocateDepthBuffer === true || ( renderTarget.samples > 0 && renderTarget.storeMultisampledDepthBuffer === false ) ) ) { const fb = renderTargetContextData.framebuffers[ renderContext.getCacheKey() ]; state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb ); @@ -75363,10 +75717,21 @@ class WebGPUUtils { } else if ( texture.isDepthTexture && ! texture.renderTarget ) { - const renderer = this.backend.renderer; - const renderTarget = renderer.getRenderTarget(); + const textureData = this.backend.get( texture ); + + if ( textureData.texture !== undefined ) { + + // use the effective sample count of the allocated texture + + samples = textureData.texture.sampleCount; + + } else { - samples = renderTarget ? renderTarget.samples : renderer.currentSamples; + // otherwise use the current samples of the renderer + + samples = this.backend.renderer.currentSamples; + + } } else if ( texture.renderTarget ) { @@ -75377,7 +75742,8 @@ class WebGPUUtils { samples = this.getSampleCount( samples || 1 ); const isMSAA = samples > 1 && texture.renderTarget !== null && ( texture.isDepthTexture !== true && texture.isFramebufferTexture !== true ); - const primarySamples = isMSAA ? 1 : samples; + const isMSAAArrayDepthTexture = samples > 1 && texture.renderTarget !== null && texture.isDepthTexture === true && texture.isArrayTexture === true; + const primarySamples = isMSAA || isMSAAArrayDepthTexture ? 1 : samples; return { samples, primarySamples, isMSAA }; @@ -76287,7 +76653,7 @@ const _renderPassDescriptor = new GPURenderPassDescriptor(); const _renderPipelineDescriptor$1 = new GPURenderPipelineDescriptor(); const _colorAttachment = new GPURenderPassColorAttachment(); const _shaderModuleDescriptor$1 = new GPUShaderModuleDescriptor(); -const _textureDescriptor$1 = new GPUTextureDescriptor(); +const _textureDescriptor$2 = new GPUTextureDescriptor(); const _viewDescriptor$2 = new GPUTextureViewDescriptor(); /** @@ -76498,14 +76864,14 @@ fn main_cube( Varys: VarysStruct ) -> @location( 0 ) vec4 { const format = textureGPUDescriptor.format; const { width, height } = textureGPUDescriptor.size; - _textureDescriptor$1.size.width = width; - _textureDescriptor$1.size.height = height; - _textureDescriptor$1.format = format; - _textureDescriptor$1.usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING; + _textureDescriptor$2.size.width = width; + _textureDescriptor$2.size.height = height; + _textureDescriptor$2.format = format; + _textureDescriptor$2.usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING; - const tempTexture = this.device.createTexture( _textureDescriptor$1 ); + const tempTexture = this.device.createTexture( _textureDescriptor$2 ); - _textureDescriptor$1.reset(); + _textureDescriptor$2.reset(); const copyTransferPipeline = this.getTransferPipeline( format, textureGPU.textureBindingViewDimension ); const flipTransferPipeline = this.getTransferPipeline( format, tempTexture.textureBindingViewDimension ); @@ -77148,7 +77514,7 @@ const _texelCopyBufferInfo = new GPUTexelCopyBufferInfo(); const _texelCopyBufferLayout = new GPUTexelCopyBufferLayout(); const _copyExternalImageSourceInfo = new GPUCopyExternalImageSourceInfo(); const _copyExternalImageDestInfo = new GPUCopyExternalImageDestInfo(); -const _textureDescriptor = new GPUTextureDescriptor(); +const _textureDescriptor$1 = new GPUTextureDescriptor(); const _extent3D$1 = new GPUExtent3D(); const _compareToWebGPU = { @@ -77264,10 +77630,12 @@ class WebGPUTextureUtils { const texture = binding.texture; const textureNode = binding.textureNode; + const isComparison = texture.isDepthTexture === true && texture.compareFunction !== null && textureNode.isPlainGather() === false && backend.hasCompatibility( Compatibility.TEXTURE_COMPARE ); + const samplerKey = texture.minFilter + '-' + texture.magFilter + '-' + texture.wrapS + '-' + texture.wrapT + '-' + ( texture.wrapR || '0' ) + '-' + texture.anisotropy + '-' + ( texture.isDepthTexture === true ? 1 : 0 ) + '-' + - ( texture.compareFunction !== null && textureNode.compareNode !== null ? texture.compareFunction : 0 ); + ( isComparison ? texture.compareFunction : 0 ); let samplerData = this._samplerCache.get( samplerKey ); @@ -77281,7 +77649,7 @@ class WebGPUTextureUtils { _samplerDescriptor.mipmapFilter = this._convertMipmapFilterMode( texture.minFilter ); // Depth textures without compare function must use non-filtering (nearest) sampling - if ( texture.isDepthTexture && ( texture.compareFunction === null || textureNode.compareNode === null ) ) { + if ( texture.isDepthTexture && isComparison === false ) { _samplerDescriptor.magFilter = GPUFilterMode.Nearest; _samplerDescriptor.minFilter = GPUFilterMode.Nearest; @@ -77297,7 +77665,7 @@ class WebGPUTextureUtils { } - if ( texture.isDepthTexture && texture.compareFunction !== null && textureNode.compareNode !== null && backend.hasCompatibility( Compatibility.TEXTURE_COMPARE ) ) { + if ( isComparison ) { _samplerDescriptor.compare = _compareToWebGPU[ texture.compareFunction ]; @@ -77457,6 +77825,20 @@ class WebGPUTextureUtils { textureData.format = format; const { samples, primarySamples, isMSAA } = backend.utils.getTextureSampleData( texture ); + const renderTarget = texture.renderTarget; + + // WebGPU multisampled 2D textures can only have a single array layer. + const useSeparateMSAATextures = samples > 1 && renderTarget !== null && depth > 1 && dimension === GPUTextureDimension.TwoD; + const supportsTransientAttachments = GPUTextureUsage.TRANSIENT_ATTACHMENT !== undefined; + // Layered rendering can resume after a framebuffer copy, so its attachments must support loading. + const useTransientAttachments = supportsTransientAttachments && useSeparateMSAATextures === false; + const useTransientDepthAttachment = texture.isDepthTexture === true && + useTransientAttachments && + renderTarget?.storeMultisampledDepthBuffer === false && + ( renderTarget.stencilBuffer === false || renderTarget.storeMultisampledStencilBuffer === false ); + const useTransientColorAttachment = texture.isDepthTexture !== true && + useTransientAttachments && + renderTarget?.storeMultisampledColorBuffer === false; let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC; @@ -77472,17 +77854,11 @@ class WebGPUTextureUtils { } - const renderTarget = texture.renderTarget; - // when the multisampled data are discarded, try to use a transient attachment if possible - if ( texture.isDepthTexture === true && primarySamples > 1 && GPUTextureUsage.TRANSIENT_ATTACHMENT !== undefined ) { - - if ( renderTarget?.storeMultisampledDepthBuffer === false && ( renderTarget.stencilBuffer === false || renderTarget.storeMultisampledStencilBuffer === false ) ) { + if ( primarySamples > 1 && useTransientDepthAttachment ) { - usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TRANSIENT_ATTACHMENT; - - } + usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TRANSIENT_ATTACHMENT; } @@ -77526,7 +77902,7 @@ class WebGPUTextureUtils { } - if ( isMSAA ) { + if ( isMSAA || useSeparateMSAATextures ) { const msaaTextureDescriptorGPU = Object.assign( {}, textureDescriptorGPU ); @@ -77536,13 +77912,30 @@ class WebGPUTextureUtils { // when the multisampled data are discarded, try to use a transient attachment if possible - if ( renderTarget?.storeMultisampledColorBuffer === false && GPUTextureUsage.TRANSIENT_ATTACHMENT !== undefined ) { + if ( useTransientDepthAttachment || useTransientColorAttachment ) { msaaTextureDescriptorGPU.usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TRANSIENT_ATTACHMENT; } - textureData.msaaTexture = backend.device.createTexture( msaaTextureDescriptorGPU ); + if ( useSeparateMSAATextures ) { + + msaaTextureDescriptorGPU.size = Object.assign( {}, msaaTextureDescriptorGPU.size, { depthOrArrayLayers: 1 } ); + + textureData.msaaTextures = []; + + for ( let i = 0; i < depth; i ++ ) { + + msaaTextureDescriptorGPU.label = textureDescriptorGPU.label + '-msaa-' + i; + textureData.msaaTextures.push( backend.device.createTexture( msaaTextureDescriptorGPU ) ); + + } + + } else { + + textureData.msaaTexture = backend.device.createTexture( msaaTextureDescriptorGPU ); + + } } @@ -77563,10 +77956,16 @@ class WebGPUTextureUtils { const backend = this.backend; const textureData = backend.get( texture ); - if ( textureData.texture !== undefined && isDefaultTexture === false && texture.isExternalTexture !== true ) textureData.texture.destroy(); + if ( textureData.texture !== undefined && isDefaultTexture === false && texture.isExternalTexture !== true && textureData.externalTexture !== true ) textureData.texture.destroy(); if ( textureData.msaaTexture !== undefined ) textureData.msaaTexture.destroy(); + if ( textureData.msaaTextures !== undefined ) { + + for ( const msaaTexture of textureData.msaaTextures ) msaaTexture.destroy(); + + } + backend.delete( texture ); } @@ -77613,16 +78012,16 @@ class WebGPUTextureUtils { if ( colorBuffer ) colorBuffer.destroy(); - _textureDescriptor.label = 'colorBuffer'; - _textureDescriptor.size.width = width; - _textureDescriptor.size.height = height; - _textureDescriptor.sampleCount = backend.utils.getSampleCount( backend.renderer.currentSamples ); - _textureDescriptor.format = backend.utils.getPreferredCanvasFormat(); - _textureDescriptor.usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC; + _textureDescriptor$1.label = 'colorBuffer'; + _textureDescriptor$1.size.width = width; + _textureDescriptor$1.size.height = height; + _textureDescriptor$1.sampleCount = backend.utils.getSampleCount( backend.renderer.currentSamples ); + _textureDescriptor$1.format = backend.utils.getPreferredCanvasFormat(); + _textureDescriptor$1.usage = GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC; - colorBuffer = backend.device.createTexture( _textureDescriptor ); + colorBuffer = backend.device.createTexture( _textureDescriptor$1 ); - _textureDescriptor.reset(); + _textureDescriptor$1.reset(); // @@ -77871,7 +78270,7 @@ class WebGPUTextureUtils { * @param {number} y - The y coordinate of the copy origin. * @param {number} width - The width of the copy. * @param {number} height - The height of the copy. - * @param {number} faceIndex - The face index. + * @param {number} faceIndex - The cube face, depth slice or array layer index. * @return {Promise} A Promise that resolves with a typed array when the copy operation has finished. */ async copyTextureToBuffer( texture, x, y, width, height, faceIndex ) { @@ -79004,6 +79403,7 @@ const wgslTypeLib$1 = { 'mat4x4f': 'mat4', 'sampler': 'sampler', + 'sampler_comparison': 'samplerComparison', 'texture_1d': 'texture', @@ -81280,7 +81680,7 @@ ${ flowData.code } if ( needsSampler ) { - if ( this.isSampleCompare( texture ) && textureNode.compareNode !== null ) { + if ( this.isSampleCompare( texture ) && textureNode.isPlainGather() === false ) { bindingSnippets.push( `@binding( ${ uniformIndexes.binding ++ } ) @group( ${ uniformIndexes.group } ) var ${ uniform.name }_sampler : sampler_comparison;` ); @@ -81893,8 +82293,8 @@ const typedArraysToVertexFormatPrefix = new Map( [ [ Uint8Array, [ 'uint8', 'unorm8' ]], [ Int16Array, [ 'sint16', 'snorm16' ]], [ Uint16Array, [ 'uint16', 'unorm16' ]], - [ Int32Array, [ 'sint32', 'snorm32' ]], - [ Uint32Array, [ 'uint32', 'unorm32' ]], + [ Int32Array, [ 'sint32' ]], + [ Uint32Array, [ 'uint32' ]], [ Float32Array, [ 'float32', ]], ] ); @@ -81910,9 +82310,7 @@ const typedAttributeToVertexFormatPrefix = new Map( [ const typeArraysToVertexFormatPrefixForItemSize1 = new Map( [ [ Int32Array, 'sint32' ], - [ Int16Array, 'sint32' ], // patch for INT16 [ Uint32Array, 'uint32' ], - [ Uint16Array, 'uint32' ], // patch for UINT16 [ Float32Array, 'float32' ] ] ); @@ -81961,7 +82359,7 @@ class WebGPUAttributeUtils { let array = bufferAttribute.array; // patch for INT16 and UINT16 - if ( attribute.normalized === false ) { + if ( attribute.normalized === false && attribute.isInterleavedBufferAttribute !== true ) { if ( array.constructor === Int16Array || array.constructor === Int8Array ) { @@ -82200,13 +82598,6 @@ class WebGPUAttributeUtils { } - // patch for INT16 and UINT16 - if ( geometryAttribute.normalized === false && ( geometryAttribute.array.constructor === Int16Array || geometryAttribute.array.constructor === Uint16Array ) ) { - - arrayStride = 4; - - } - vertexBufferLayout = { arrayStride, attributes: [], @@ -83030,7 +83421,7 @@ class WebGPUBindingUtils { if ( binding.texture.isDepthTexture ) { - if ( binding.texture.compareFunction !== null && binding.textureNode.compareNode !== null && backend.hasCompatibility( Compatibility.TEXTURE_COMPARE ) ) { + if ( binding.texture.compareFunction !== null && binding.textureNode.isPlainGather() === false && backend.hasCompatibility( Compatibility.TEXTURE_COMPARE ) ) { sampler.type = GPUSamplerBindingType.Comparison; @@ -83585,8 +83976,9 @@ class WebGPUPipelineUtils { * * @param {ComputePipeline} pipeline - The compute pipeline. * @param {Array} bindings - The bindings. + * @param {?Array} [promises=null] - Optional compilation promises. */ - createComputePipeline( pipeline, bindings ) { + createComputePipeline( pipeline, bindings, promises = null ) { const backend = this.backend; const device = backend.device; @@ -83623,23 +84015,87 @@ class WebGPUPipelineUtils { _computePipelineDescriptor.compute = computeProgram; _computePipelineDescriptor.layout = pipelineLayout; - pipelineGPU.pipeline = device.createComputePipeline( _computePipelineDescriptor ); + if ( promises === null ) { - _computePipelineDescriptor.reset(); + pipelineGPU.pipeline = device.createComputePipeline( _computePipelineDescriptor ); - device.popErrorScope().then( ( err ) => { + _computePipelineDescriptor.reset(); - if ( err !== null ) { + device.popErrorScope().then( ( err ) => { - pipelineGPU.error = true; + if ( err !== null ) { - error( `WebGPURenderer: Compute pipeline creation failed (${ pipelineLabel }): ${ err.message }` ); + pipelineGPU.error = true; - this._reportShaderDiagnostics( [ { program: computeStage, module: computeProgram.module } ], pipelineLabel ); + error( `WebGPURenderer: Compute pipeline creation failed (${ pipelineLabel }): ${ err.message }` ); - } + this._reportShaderDiagnostics( [ { program: computeStage, module: computeProgram.module } ], pipelineLabel ); - } ); + } + + } ); + + } else { + + const promise = new Promise( async ( resolve /*, reject*/ ) => { + + try { + + let asyncError = null; + let pipelinePromise = null; + + try { + + pipelinePromise = device.createComputePipelineAsync( _computePipelineDescriptor ); + + } catch ( err ) { + + asyncError = err; + + } + + _computePipelineDescriptor.reset(); + + if ( pipelinePromise !== null ) { + + try { + + pipelineGPU.pipeline = await pipelinePromise; + + } catch ( err ) { + + asyncError = err; + + } + + } + + const errorScope = await device.popErrorScope(); + + if ( errorScope !== null || asyncError !== null ) { + + pipelineGPU.error = true; + + const reason = ( errorScope && errorScope.message ) || ( asyncError && asyncError.message ) || 'unknown'; + error( `WebGPURenderer: Async compute pipeline creation failed (${ pipelineLabel }): ${ reason }` ); + + await this._reportShaderDiagnostics( [ { program: computeStage, module: computeProgram.module } ], pipelineLabel ); + + } + + } finally { + + // Guarantee resolution so `compileComputeAsync`'s Promise.all cannot hang on an + // unexpected throw from any await above. + resolve(); + + } + + } ); + + promises.push( promise ); + + } } @@ -83858,11 +84314,13 @@ class WebGPUPipelineUtils { blendFactor = GPUBlendFactor.SrcAlphaSaturated; break; - case BlendColorFactor: + case ConstantColorFactor: + case ConstantAlphaFactor: // WebGPU has no dedicated constant alpha blend factors blendFactor = GPUBlendFactor.Constant; break; - case OneMinusBlendColorFactor: + case OneMinusConstantColorFactor: + case OneMinusConstantAlphaFactor: // WebGPU has no dedicated constant alpha blend factors blendFactor = GPUBlendFactor.OneMinusConstant; break; @@ -84698,6 +85156,7 @@ class GPURenderPassTimestampWrites { const _clearValue = { r: 0, g: 0, b: 0, a: 1 }; +const _blendConstant = { r: 0, g: 0, b: 0, a: 0 }; const _bufferDescriptor = new GPUBufferDescriptor(); const _commandEncoderDescriptor = new GPUCommandEncoderDescriptor(); const _computePassDescriptor = new GPUComputePassDescriptor(); @@ -84706,6 +85165,7 @@ const _shaderModuleDescriptor = new GPUShaderModuleDescriptor(); const _renderPassTimestampWrites = new GPURenderPassTimestampWrites(); const _texelCopyTextureInfoSrc = new GPUTexelCopyTextureInfo(); const _texelCopyTextureInfoDst = new GPUTexelCopyTextureInfo(); +const _textureDescriptor = new GPUTextureDescriptor(); const _viewDescriptor = new GPUTextureViewDescriptor(); const _extent3D = new GPUExtent3D(); @@ -84973,13 +85433,14 @@ class WebGPUBackend extends Backend { */ setXRRenderTargetTextures( renderTarget, colorTexture, viewDescriptors = null ) { - this.set( renderTarget.texture, { - texture: colorTexture, - format: colorTexture.format, - externalTexture: true, - xrViewDescriptors: viewDescriptors, - initialized: true - } ); + // Update the external XR texture without replacing the cached MSAA attachments. + const textureData = this.get( renderTarget.texture ); + + textureData.texture = colorTexture; + textureData.format = colorTexture.format; + textureData.externalTexture = true; + textureData.xrViewDescriptors = viewDescriptors; + textureData.initialized = true; } @@ -85195,6 +85656,81 @@ class WebGPUBackend extends Backend { } + /** + * Returns multisampled color textures for an external render target. + * + * @private + * @param {RenderContext} renderContext - The render context. + * @param {Object} textureData - The backend data for the external texture. + * @param {number} count - The number of textures to create. + * @return {?Array} The multisampled textures. + */ + _getExternalMSAATextures( renderContext, textureData, count ) { + + const samples = this.utils.getSampleCount( renderContext.sampleCount ); + + if ( samples === 1 ) { + + if ( textureData.msaaTextures !== undefined ) { + + for ( const texture of textureData.msaaTextures ) texture.destroy(); + + textureData.msaaTextures = undefined; + + } + + return null; + + } + + const renderTarget = renderContext.renderTarget; + const width = renderTarget.width; + const height = renderTarget.height; + const format = textureData.format; + + if ( textureData.msaaTextures === undefined || + textureData.msaaTextures.length !== count || + textureData.msaaWidth !== width || + textureData.msaaHeight !== height || + textureData.msaaSamples !== samples || + textureData.msaaFormat !== format ) { + + if ( textureData.msaaTextures !== undefined ) { + + for ( const texture of textureData.msaaTextures ) texture.destroy(); + + } + + _textureDescriptor.size.width = width; + _textureDescriptor.size.height = height; + _textureDescriptor.sampleCount = samples; + _textureDescriptor.format = format; + // Layered rendering can resume after a framebuffer copy, + // so these attachments must support loading. + _textureDescriptor.usage = GPUTextureUsage.RENDER_ATTACHMENT; + + textureData.msaaTextures = []; + + for ( let i = 0; i < count; i ++ ) { + + _textureDescriptor.label = renderTarget.texture.name + '-msaa-' + i; + textureData.msaaTextures.push( this.device.createTexture( _textureDescriptor ) ); + + } + + _textureDescriptor.reset(); + + textureData.msaaWidth = width; + textureData.msaaHeight = height; + textureData.msaaSamples = samples; + textureData.msaaFormat = format; + + } + + return textureData.msaaTextures; + + } + /** * Creates attachment views for an external texture render target. * @@ -85207,14 +85743,19 @@ class WebGPUBackend extends Backend { const textureViews = []; const camera = renderContext.camera; + const viewDescriptors = textureData.xrViewDescriptors; + const viewCount = Math.max( viewDescriptors?.length || 0, renderContext.activeCubeFace + 1, 1 ); + const msaaTextures = this._getExternalMSAATextures( renderContext, textureData, viewCount ); - if ( textureData.xrViewDescriptors && camera !== null && camera.isArrayCamera === true ) { + if ( viewDescriptors && camera !== null && camera.isArrayCamera === true ) { - for ( let i = 0; i < textureData.xrViewDescriptors.length; i ++ ) { + for ( let i = 0; i < viewDescriptors.length; i ++ ) { + + const textureView = textureData.texture.createView( viewDescriptors[ i ] ); textureViews.push( { - view: textureData.texture.createView( textureData.xrViewDescriptors[ i ] ), - resolveTarget: undefined, + view: msaaTextures !== null ? msaaTextures[ i ].createView() : textureView, + resolveTarget: msaaTextures !== null && renderContext.renderTarget.resolveColorBuffer === true ? textureView : undefined, depthSlice: undefined } ); @@ -85222,13 +85763,16 @@ class WebGPUBackend extends Backend { } else { + const layer = renderContext.activeCubeFace; + const textureView = textureData.texture.createView( { + dimension: GPUTextureViewDimension.TwoD, + baseArrayLayer: layer, + arrayLayerCount: 1 + } ); + textureViews.push( { - view: textureData.texture.createView( { - dimension: GPUTextureViewDimension.TwoD, - baseArrayLayer: renderContext.activeCubeFace, - arrayLayerCount: 1 - } ), - resolveTarget: undefined, + view: msaaTextures !== null ? msaaTextures[ layer ].createView() : textureView, + resolveTarget: msaaTextures !== null && renderContext.renderTarget.resolveColorBuffer === true ? textureView : undefined, depthSlice: undefined } ); @@ -85316,9 +85860,10 @@ class WebGPUBackend extends Backend { _viewDescriptor.dimension = GPUTextureViewDimension.TwoD; const textureView = textureData.texture.createView( _viewDescriptor ); + const msaaTexture = textureData.msaaTextures?.[ layer ]; textureViews.push( { - view: textureView, - resolveTarget: undefined, + view: msaaTexture !== undefined ? msaaTexture.createView() : textureView, + resolveTarget: msaaTexture !== undefined && renderTarget.resolveColorBuffer === true ? textureView : undefined, depthSlice: undefined } ); @@ -85343,6 +85888,11 @@ class WebGPUBackend extends Backend { view = textureData.msaaTexture.createView(); resolveTarget = renderTarget.resolveColorBuffer === true ? textureView : undefined; + } else if ( textureData.msaaTextures !== undefined ) { + + view = textureData.msaaTextures[ renderContext.activeCubeFace ].createView(); + resolveTarget = renderTarget.resolveColorBuffer === true ? textureView : undefined; + } else { view = textureView; @@ -85394,7 +85944,8 @@ class WebGPUBackend extends Backend { } const depthStencilAttachment = new GPURenderPassDepthStencilAttachment(); - depthStencilAttachment.view = depthTextureData.texture.createView( _viewDescriptor ); + const msaaDepthTexture = depthTextureData.msaaTextures?.[ renderContext.activeCubeFace ]; + depthStencilAttachment.view = msaaDepthTexture !== undefined ? msaaDepthTexture.createView() : depthTextureData.texture.createView( _viewDescriptor ); descriptorBase.depthStencilAttachment = depthStencilAttachment; _viewDescriptor.reset(); @@ -85518,6 +86069,12 @@ class WebGPUBackend extends Backend { const depthStencilAttachment = descriptor.depthStencilAttachment; const renderTarget = renderContext.renderTarget; + const discardColor = renderContext.sampleCount > 1 && renderTarget?.storeMultisampledColorBuffer === false; + const discardDepth = renderContext.sampleCount > 1 && renderTarget?.storeMultisampledDepthBuffer === false; + const discardStencil = renderContext.sampleCount > 1 && renderTarget?.storeMultisampledStencilBuffer === false; + + // A fullscreen pass overwrites the external texture, so its previous contents do not need to be loaded. + const clearExternalColor = renderContext.fullscreenPass === true && this._hasExternalTexture( renderContext ); if ( renderContext.textures !== null ) { @@ -85527,7 +86084,7 @@ class WebGPUBackend extends Backend { const colorAttachment = colorAttachments[ i ]; - if ( renderContext.clearColor ) { + if ( renderContext.clearColor || discardColor || clearExternalColor ) { if ( i === 0 ) { @@ -85552,7 +86109,7 @@ class WebGPUBackend extends Backend { } - if ( renderContext.sampleCount > 1 && renderTarget?.storeMultisampledColorBuffer === false ) { + if ( discardColor ) { colorAttachment.storeOp = GPUStoreOp.Discard; @@ -85587,7 +86144,7 @@ class WebGPUBackend extends Backend { if ( renderContext.depth ) { - if ( renderContext.clearDepth ) { + if ( renderContext.clearDepth || discardDepth ) { depthStencilAttachment.depthClearValue = renderContext.clearDepthValue; depthStencilAttachment.depthLoadOp = GPULoadOp.Clear; @@ -85598,7 +86155,7 @@ class WebGPUBackend extends Backend { } - if ( renderContext.sampleCount > 1 && renderTarget?.storeMultisampledDepthBuffer === false ) { + if ( discardDepth ) { depthStencilAttachment.depthStoreOp = GPUStoreOp.Discard; @@ -85612,7 +86169,7 @@ class WebGPUBackend extends Backend { if ( renderContext.stencil ) { - if ( renderContext.clearStencil ) { + if ( renderContext.clearStencil || discardStencil ) { depthStencilAttachment.stencilClearValue = renderContext.clearStencilValue; depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear; @@ -85623,7 +86180,7 @@ class WebGPUBackend extends Backend { } - if ( renderContext.sampleCount > 1 && renderTarget?.storeMultisampledStencilBuffer === false ) { + if ( discardStencil ) { depthStencilAttachment.stencilStoreOp = GPUStoreOp.Discard; @@ -85653,40 +86210,22 @@ class WebGPUBackend extends Backend { } else { - this._updateArrayCameraLayerDescriptors( renderContext, renderContextData, cameras ); + this._updateArrayCameraLayerDescriptors( renderContext, renderContextData, descriptor, cameras ); } - // Create bundle encoders for each layer - renderContextData.bundleEncoders = []; - renderContextData.bundleSets = []; - - // Create separate bundle encoders for each camera in the array - for ( let i = 0; i < cameras.length; i ++ ) { - - const bundleEncoder = this.pipelineUtils.createBundleEncoder( - renderContext, - 'renderBundleArrayCamera_' + i - ); - - // Initialize state tracking for this bundle - const bundleSets = { - attributes: {}, - bindingGroups: [], - pipeline: null, - index: null - }; - - renderContextData.bundleEncoders.push( bundleEncoder ); - renderContextData.bundleSets.push( bundleSets ); - - } + this._createArrayCameraBundleEncoders( renderContext, renderContextData ); + renderContextData.arrayCameraRenderStages = []; // We'll complete the bundles in finishRender renderContextData.currentPass = null; } else { + renderContextData.bundleEncoders = undefined; + renderContextData.bundleSets = undefined; + renderContextData.arrayCameraRenderStages = undefined; + const currentPass = encoder.beginRenderPass( descriptor ); renderContextData.currentPass = currentPass; @@ -85708,9 +86247,87 @@ class WebGPUBackend extends Backend { renderContextData.descriptor = descriptor; renderContextData.encoder = encoder; - renderContextData.currentSets = { attributes: {}, bindingGroups: [], pipeline: null, index: null }; renderContextData.renderBundles = []; + this._resetRenderContextData( renderContextData ); + + } + + /** + * Resets the state cache of the given render context data. + * + * A new render pass encoder starts with a blend constant and a stencil reference + * of zero so the cached values must be reset as well. + * + * @private + * @param {Object} renderContextData - The render context data. + */ + _resetRenderContextData( renderContextData ) { + + renderContextData.currentSets = { attributes: {}, bindingGroups: [], pipeline: null, index: null }; + + if ( renderContextData.currentBlendColor === undefined ) renderContextData.currentBlendColor = new Color(); + + renderContextData.currentBlendColor.setRGB( 0, 0, 0 ); + renderContextData.currentBlendAlpha = 0; + renderContextData.currentStencilRef = 0; + + } + + /** + * Creates a render bundle encoder and state cache for each camera layer. + * + * @param {RenderContext} renderContext - The render context. + * @param {Object} renderContextData - The render context data. + * @private + */ + _createArrayCameraBundleEncoders( renderContext, renderContextData ) { + + const cameras = renderContext.camera.cameras; + + renderContextData.bundleEncoders = []; + renderContextData.bundleSets = []; + + for ( let i = 0; i < cameras.length; i ++ ) { + + const bundleEncoder = this.pipelineUtils.createBundleEncoder( + renderContext, + 'renderBundleArrayCamera_' + i + ); + + const bundleSets = { + attributes: {}, + bindingGroups: [], + pipeline: null, + index: null + }; + + renderContextData.bundleEncoders.push( bundleEncoder ); + renderContextData.bundleSets.push( bundleSets ); + + } + + } + + /** + * Finishes the render bundle encoders for all camera layers. + * + * @param {Object} renderContextData - The render context data. + * @return {Array} The completed render bundles. + * @private + */ + _finishArrayCameraBundleEncoders( renderContextData ) { + + const bundles = []; + + for ( const bundleEncoder of renderContextData.bundleEncoders ) { + + bundles.push( bundleEncoder.finish() ); + + } + + return bundles; + } /** @@ -85738,11 +86355,12 @@ class WebGPUBackend extends Backend { for ( let i = 0; i < cameras.length; i ++ ) { const sourceAttachment = descriptor.colorAttachments[ 0 ]; + const layerAttachment = descriptor.colorAttachments[ i ]; const layerColorAttachment = new GPURenderPassColorAttachment(); - layerColorAttachment.view = descriptor.colorAttachments[ i ].view; - layerColorAttachment.depthSlice = sourceAttachment.depthSlice; - layerColorAttachment.resolveTarget = sourceAttachment.resolveTarget; + layerColorAttachment.view = layerAttachment.view; + layerColorAttachment.depthSlice = layerAttachment.depthSlice; + layerColorAttachment.resolveTarget = layerAttachment.resolveTarget; layerColorAttachment.loadOp = sourceAttachment.loadOp; layerColorAttachment.storeOp = sourceAttachment.storeOp; layerColorAttachment.clearValue = sourceAttachment.clearValue; @@ -85759,11 +86377,21 @@ class WebGPUBackend extends Backend { if ( ! depthTextureData.viewCache[ layerIndex ] ) { - _viewDescriptor.dimension = GPUTextureViewDimension.TwoD; - _viewDescriptor.baseArrayLayer = i; - _viewDescriptor.arrayLayerCount = 1; + const msaaTexture = depthTextureData.msaaTextures?.[ layerIndex ]; - depthTextureData.viewCache[ layerIndex ] = depthTextureData.texture.createView( _viewDescriptor ); + if ( msaaTexture !== undefined ) { + + depthTextureData.viewCache[ layerIndex ] = msaaTexture.createView(); + + } else { + + _viewDescriptor.dimension = GPUTextureViewDimension.TwoD; + _viewDescriptor.baseArrayLayer = i; + _viewDescriptor.arrayLayerCount = 1; + + depthTextureData.viewCache[ layerIndex ] = depthTextureData.texture.createView( _viewDescriptor ); + + } _viewDescriptor.reset(); @@ -85812,14 +86440,29 @@ class WebGPUBackend extends Backend { * * @param {RenderContext} renderContext - The render context. * @param {Object} renderContextData - The render context data. + * @param {Object} descriptor - The render pass descriptor. * @param {ArrayCamera} cameras - The array camera. * */ - _updateArrayCameraLayerDescriptors( renderContext, renderContextData, cameras ) { + _updateArrayCameraLayerDescriptors( renderContext, renderContextData, descriptor, cameras ) { + + const renderTarget = renderContext.renderTarget; + const discardDepth = renderContext.sampleCount > 1 && renderTarget?.storeMultisampledDepthBuffer === false; + const discardStencil = renderContext.sampleCount > 1 && renderTarget?.storeMultisampledStencilBuffer === false; for ( let i = 0; i < cameras.length; i ++ ) { const layerDescriptor = renderContextData.layerDescriptors[ i ]; + const sourceColorAttachment = descriptor.colorAttachments[ 0 ]; + const layerColorAttachment = descriptor.colorAttachments[ i ]; + const colorAttachment = layerDescriptor.colorAttachments[ 0 ]; + + colorAttachment.view = layerColorAttachment.view; + colorAttachment.resolveTarget = layerColorAttachment.resolveTarget; + colorAttachment.depthSlice = layerColorAttachment.depthSlice; + colorAttachment.loadOp = sourceColorAttachment.loadOp; + colorAttachment.storeOp = sourceColorAttachment.storeOp; + colorAttachment.clearValue = sourceColorAttachment.clearValue; if ( layerDescriptor.depthStencilAttachment ) { @@ -85827,7 +86470,7 @@ class WebGPUBackend extends Backend { if ( renderContext.depth ) { - if ( renderContext.clearDepth ) { + if ( renderContext.clearDepth || discardDepth ) { depthAttachment.depthClearValue = renderContext.clearDepthValue; depthAttachment.depthLoadOp = GPULoadOp.Clear; @@ -85842,7 +86485,7 @@ class WebGPUBackend extends Backend { if ( renderContext.stencil ) { - if ( renderContext.clearStencil ) { + if ( renderContext.clearStencil || discardStencil ) { depthAttachment.stencilClearValue = renderContext.clearStencilValue; depthAttachment.stencilLoadOp = GPULoadOp.Clear; @@ -85892,20 +86535,49 @@ class WebGPUBackend extends Backend { if ( this._isRenderCameraDepthArray( renderContext ) === true ) { - const bundles = []; + const bundles = this._finishArrayCameraBundleEncoders( renderContextData ); + const renderStages = renderContextData.arrayCameraRenderStages; + renderStages.push( { bundles } ); - for ( let i = 0; i < renderContextData.bundleEncoders.length; i ++ ) { + // A viewport texture is 2D, so each layer must be captured immediately before that layer samples it. + for ( let i = 0; i < renderContextData.layerDescriptors.length; i ++ ) { - const bundleEncoder = renderContextData.bundleEncoders[ i ]; - bundles.push( bundleEncoder.finish() ); + const layerDescriptor = renderContextData.layerDescriptors[ i ]; + const colorLoadOps = layerDescriptor.colorAttachments.map( attachment => attachment.loadOp ); + const colorStoreOps = layerDescriptor.colorAttachments.map( attachment => attachment.storeOp ); + const depthLoadOp = layerDescriptor.depthStencilAttachment?.depthLoadOp; + const depthStoreOp = layerDescriptor.depthStencilAttachment?.depthStoreOp; + const stencilLoadOp = layerDescriptor.depthStencilAttachment?.stencilLoadOp; + const stencilStoreOp = layerDescriptor.depthStencilAttachment?.stencilStoreOp; - } + for ( let stageIndex = 0; stageIndex < renderStages.length; stageIndex ++ ) { + + const renderStage = renderStages[ stageIndex ]; + const bundle = renderStage.bundles[ i ]; + const isLastStage = stageIndex === renderStages.length - 1; + + for ( let j = 0; j < layerDescriptor.colorAttachments.length; j ++ ) { + + const attachment = layerDescriptor.colorAttachments[ j ]; + attachment.loadOp = stageIndex === 0 ? colorLoadOps[ j ] : GPULoadOp.Load; + attachment.storeOp = isLastStage ? colorStoreOps[ j ] : GPUStoreOp.Store; + + } + + if ( renderContext.depth ) { - for ( let i = 0; i < renderContextData.layerDescriptors.length; i ++ ) { + layerDescriptor.depthStencilAttachment.depthLoadOp = stageIndex === 0 ? depthLoadOp : GPULoadOp.Load; + layerDescriptor.depthStencilAttachment.depthStoreOp = isLastStage ? depthStoreOp : GPUStoreOp.Store; + + } - if ( i < bundles.length ) { + if ( renderContext.stencil ) { + + layerDescriptor.depthStencilAttachment.stencilLoadOp = stageIndex === 0 ? stencilLoadOp : GPULoadOp.Load; + layerDescriptor.depthStencilAttachment.stencilStoreOp = isLastStage ? stencilStoreOp : GPUStoreOp.Store; + + } - const layerDescriptor = renderContextData.layerDescriptors[ i ]; const renderPass = encoder.beginRenderPass( layerDescriptor ); if ( renderContext.viewport ) { @@ -85922,10 +86594,39 @@ class WebGPUBackend extends Backend { } - renderPass.executeBundles( [ bundles[ i ] ] ); + renderPass.executeBundles( [ bundle ] ); renderPass.end(); + if ( renderStage.framebufferCopy !== undefined ) { + + const { texture, sourceGPU, destinationGPU, rectangle, generateMipmaps } = renderStage.framebufferCopy; + + this._copyFramebufferToTexture( encoder, texture, sourceGPU, destinationGPU, rectangle, i, generateMipmaps ); + + } + + } + + for ( let j = 0; j < layerDescriptor.colorAttachments.length; j ++ ) { + + layerDescriptor.colorAttachments[ j ].loadOp = colorLoadOps[ j ]; + layerDescriptor.colorAttachments[ j ].storeOp = colorStoreOps[ j ]; + + } + + if ( renderContext.depth ) { + + layerDescriptor.depthStencilAttachment.depthLoadOp = depthLoadOp; + layerDescriptor.depthStencilAttachment.depthStoreOp = depthStoreOp; + + } + + if ( renderContext.stencil ) { + + layerDescriptor.depthStencilAttachment.stencilLoadOp = stencilLoadOp; + layerDescriptor.depthStencilAttachment.stencilStoreOp = stencilStoreOp; + } } @@ -86508,6 +87209,29 @@ class WebGPUBackend extends Backend { } + // blend constant + + if ( material.blending === CustomBlending && passEncoderGPU.setBlendConstant !== undefined ) { + + const blendColor = material.blendColor; + const blendAlpha = material.blendAlpha; + + if ( blendColor.equals( renderContextData.currentBlendColor ) === false || blendAlpha !== renderContextData.currentBlendAlpha ) { + + _blendConstant.r = blendColor.r; + _blendConstant.g = blendColor.g; + _blendConstant.b = blendColor.b; + _blendConstant.a = blendAlpha; + + passEncoderGPU.setBlendConstant( _blendConstant ); + + renderContextData.currentBlendColor.copy( blendColor ); + renderContextData.currentBlendAlpha = blendAlpha; + + } + + } + if ( object.isBatchedMesh === true ) { const starts = object._multiDrawStarts; @@ -86941,7 +87665,7 @@ class WebGPUBackend extends Backend { * @param {number} y - The y coordinate of the copy origin. * @param {number} width - The width of the copy. * @param {number} height - The height of the copy. - * @param {number} faceIndex - The face index. + * @param {number} faceIndex - The cube face, depth slice or array layer index. * @return {Promise} A Promise that resolves with a typed array when the copy operation has finished. */ async copyTextureToBuffer( texture, x, y, width, height, faceIndex ) { @@ -87049,10 +87773,11 @@ class WebGPUBackend extends Backend { * * @param {ComputePipeline} computePipeline - The compute pipeline. * @param {Array} bindings - The bindings. + * @param {?Array} [promises=null] - Optional compilation promises. */ - createComputePipeline( computePipeline, bindings ) { + createComputePipeline( computePipeline, bindings, promises = null ) { - this.pipelineUtils.createComputePipeline( computePipeline, bindings ); + this.pipelineUtils.createComputePipeline( computePipeline, bindings, promises ); } @@ -87482,6 +88207,29 @@ class WebGPUBackend extends Backend { } + if ( this._isRenderCameraDepthArray( renderContext ) === true ) { + + // Layered draws are only executed in finishRender(), so preserve this copy as a render-stage boundary. + const bundles = this._finishArrayCameraBundleEncoders( renderContextData ); + + renderContextData.arrayCameraRenderStages.push( { + bundles, + framebufferCopy: { + texture, + sourceGPU, + destinationGPU, + rectangle: { x: rectangle.x, y: rectangle.y, z: rectangle.z, w: rectangle.w }, + // ViewportTextureNode restores this flag before the deferred copy executes. + generateMipmaps: texture.generateMipmaps + } + } ); + + this._createArrayCameraBundleEncoders( renderContext, renderContextData ); + + return; + + } + let encoder; if ( renderContextData.currentPass ) { @@ -87498,33 +88246,10 @@ class WebGPUBackend extends Backend { } - _texelCopyTextureInfoSrc.texture = sourceGPU; - _texelCopyTextureInfoSrc.origin.x = rectangle.x; - _texelCopyTextureInfoSrc.origin.y = rectangle.y; - - _texelCopyTextureInfoDst.texture = destinationGPU; - - _extent3D.width = rectangle.z; - _extent3D.height = rectangle.w; - - encoder.copyTextureToTexture( - _texelCopyTextureInfoSrc, - _texelCopyTextureInfoDst, - _extent3D - ); - - _texelCopyTextureInfoSrc.reset(); - _texelCopyTextureInfoDst.reset(); - _extent3D.reset(); - // mipmaps must be genereated with the same encoder otherwise the copied texture data // might be out-of-sync, see #31768 - if ( texture.generateMipmaps ) { - - this.textureUtils.generateMipmaps( texture, encoder ); - - } + this._copyFramebufferToTexture( encoder, texture, sourceGPU, destinationGPU, rectangle ); if ( renderContextData.currentPass ) { @@ -87540,7 +88265,8 @@ class WebGPUBackend extends Backend { if ( renderContext.stencil ) descriptor.depthStencilAttachment.stencilLoadOp = GPULoadOp.Load; renderContextData.currentPass = encoder.beginRenderPass( descriptor ); - renderContextData.currentSets = { attributes: {}, bindingGroups: [], pipeline: null, index: null }; + + this._resetRenderContextData( renderContextData ); if ( renderContext.viewport ) { @@ -87562,6 +88288,48 @@ class WebGPUBackend extends Backend { } + /** + * Encodes a framebuffer copy from a specific texture-array layer. + * + * @param {GPUCommandEncoder} encoder - The command encoder. + * @param {Texture} texture - The destination texture. + * @param {GPUTexture} sourceGPU - The source GPU texture. + * @param {GPUTexture} destinationGPU - The destination GPU texture. + * @param {Object} rectangle - The source rectangle. + * @param {number} [sourceLayer=0] - The source array layer. + * @param {boolean} [generateMipmaps=texture.generateMipmaps] - Whether mipmaps should be generated. + * @private + */ + _copyFramebufferToTexture( encoder, texture, sourceGPU, destinationGPU, rectangle, sourceLayer = 0, generateMipmaps = texture.generateMipmaps ) { + + _texelCopyTextureInfoSrc.texture = sourceGPU; + _texelCopyTextureInfoSrc.origin.x = rectangle.x; + _texelCopyTextureInfoSrc.origin.y = rectangle.y; + _texelCopyTextureInfoSrc.origin.z = sourceLayer; + + _texelCopyTextureInfoDst.texture = destinationGPU; + + _extent3D.width = rectangle.z; + _extent3D.height = rectangle.w; + + encoder.copyTextureToTexture( + _texelCopyTextureInfoSrc, + _texelCopyTextureInfoDst, + _extent3D + ); + + _texelCopyTextureInfoSrc.reset(); + _texelCopyTextureInfoDst.reset(); + _extent3D.reset(); + + if ( generateMipmaps ) { + + this.textureUtils.generateMipmaps( texture, encoder ); + + } + + } + /** * Checks if the given compatibility is supported by the backend. * @@ -88863,4 +89631,4 @@ class ClippingGroup extends Group { } -export { ACESFilmicToneMapping, AONode, AddEquation, AddOperation, AdditiveBlending, AgXToneMapping, AlphaFormat, AlwaysCompare, AlwaysDepth, AlwaysStencilFunc, AmbientLight, AmbientLightNode, AnalyticLightNode, ArrayCamera, ArrayElementNode, ArrayNode, AssignNode, AtomicFunctionNode, AttributeNode, BackSide, BarrierNode, BasicEnvironmentNode, BasicLightMapNode, BasicShadowMap, BitcastNode, BitcountNode, BlendMode, BoxGeometry, BufferAttribute, BufferAttributeNode, BufferGeometry, BufferNode, BuiltinNode, BumpMapNode, BundleGroup, BypassNode, ByteType, CanvasTarget, CineonToneMapping, ClampToEdgeWrapping, ClippingGroup, ClippingNode, CodeNode, Color, ColorManagement, ColorSpaceNode, Compatibility, ComputeBuiltinNode, ComputeNode, ConditionalNode, ConstNode, ContextNode, ConvertNode, CubeCamera, CubeDepthTexture, CubeMapNode, CubeReflectionMapping, CubeRefractionMapping, CubeTexture, CubeTextureNode, CubeUVReflectionMapping, CullFaceBack, CullFaceFront, CullFaceNone, CustomBlending, CylinderGeometry, DataArrayTexture, DataTexture, DebugNode, DecrementStencilOp, DecrementWrapStencilOp, DepthFormat, DepthStencilFormat, DepthTexture, DirectionalLight, DirectionalLightNode, DoubleSide, DstAlphaFactor, DstColorFactor, DynamicDrawUsage, EnvironmentNode, EqualCompare, EqualDepth, EqualStencilFunc, EquirectangularReflectionMapping, EquirectangularRefractionMapping, EventDispatcher, EventNode, ExpressionNode, FileLoader, FlipNode, Float16BufferAttribute, Float32BufferAttribute, FloatType, FramebufferTexture, FrontFacingNode, FrontSide, Frustum, FrustumArray, FunctionCallNode, FunctionNode, FunctionOverloadingNode, GLSLNodeParser, GreaterCompare, GreaterDepth, GreaterEqualCompare, GreaterEqualDepth, GreaterEqualStencilFunc, GreaterStencilFunc, Group, HalfFloatType, HemisphereLight, HemisphereLightNode, IESSpotLight, IESSpotLightNode, IncrementStencilOp, IncrementWrapStencilOp, IndexNode, IndirectStorageBufferAttribute, InputNode, InspectorBase, InspectorNode, InstancedBufferAttribute, InstancedInterleavedBuffer, IntType, InterleavedBuffer, InterleavedBufferAttribute, InvertStencilOp, IrradianceNode, IsolateNode, JoinNode, KeepStencilOp, LessCompare, LessDepth, LessEqualCompare, LessEqualDepth, LessEqualStencilFunc, LessStencilFunc, LightProbe, LightProbeNode, Lighting, LightingContextNode, LightingModel, LightingNode, LightsNode, Line2NodeMaterial, LineBasicMaterial, LineBasicNodeMaterial, LineDashedMaterial, LineDashedNodeMaterial, LinearFilter, LinearMipMapLinearFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, LinearSRGBColorSpace, LinearToneMapping, LinearTransfer, Loader, LoopNode, MRTNode, Material, MaterialBlending, MaterialLoader, MaterialNode, MaterialReferenceNode, MathNode, MathUtils, Matrix2, Matrix3, Matrix4, MaxEquation, MaxMipLevelNode, MemberNode, Mesh, MeshBasicMaterial, MeshBasicNodeMaterial, MeshLambertMaterial, MeshLambertNodeMaterial, MeshMatcapMaterial, MeshMatcapNodeMaterial, MeshNormalMaterial, MeshNormalNodeMaterial, MeshPhongMaterial, MeshPhongNodeMaterial, MeshPhysicalMaterial, MeshPhysicalNodeMaterial, MeshSSSNodeMaterial, MeshStandardMaterial, MeshStandardNodeMaterial, MeshToonMaterial, MeshToonNodeMaterial, MinEquation, MirroredRepeatWrapping, MixOperation, ModelNode, MultiplyBlending, MultiplyOperation, NearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, NeutralToneMapping, NeverCompare, NeverDepth, NeverStencilFunc, NoBlending, NoColorSpace, NoNormalPacking, NoToneMapping, Node, NodeAccess, NodeAttribute, NodeBuilder, NodeCache, NodeCode, NodeError, NodeFrame, NodeFunctionInput, NodeLoader, NodeMaterial, NodeMaterialLoader, NodeMaterialObserver, NodeObjectLoader, NodeShaderStage, NodeType, NodeUniform, NodeUpdateType, NodeUtils, NodeVar, NodeVarying, NormalBlending, NormalGAPacking, NormalMapNode, NormalRGPacking, NotEqualCompare, NotEqualDepth, NotEqualStencilFunc, Object3D, Object3DNode, ObjectLoader, ObjectSpaceNormalMap, OneFactor, OneMinusDstAlphaFactor, OneMinusDstColorFactor, OneMinusSrcAlphaFactor, OneMinusSrcColorFactor, OperatorNode, OrthographicCamera, OutputStructNode, OverrideContextNode, PCFShadowMap, PCFSoftShadowMap, PMREMGenerator, PMREMNode, PackFloatNode, ParameterNode, PassNode, PerspectiveCamera, PhongLightingModel, PhysicalLightingModel, Plane, PlaneGeometry, PointLight, PointLightNode, PointShadowNode, PointUVNode, PointsMaterial, PointsNodeMaterial, PostProcessing, ProjectorLight, ProjectorLightNode, PropertyNode, QuadMesh, Quaternion, R11_EAC_Format, RED_GREEN_RGTC2_Format, RED_RGTC1_Format, REVISION, RG11_EAC_Format, RGBAFormat, RGBAIntegerFormat, RGBA_ASTC_10x10_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_BPTC_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGBFormat, RGBIntegerFormat, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGB_S3TC_DXT1_Format, RGFormat, RGIntegerFormat, RTTNode, RangeNode, ReadbackBuffer, RectAreaLight, RectAreaLightNode, RedFormat, RedIntegerFormat, ReferenceBaseNode, ReferenceNode, ReflectorNode, ReinhardToneMapping, RenderOutputNode, RenderPipeline, RenderTarget, RendererReferenceNode, RendererUtils, RepeatWrapping, ReplaceStencilOp, ReverseSubtractEquation, RotateNode, SIGNED_R11_EAC_Format, SIGNED_RED_GREEN_RGTC2_Format, SIGNED_RED_RGTC1_Format, SIGNED_RG11_EAC_Format, SRGBColorSpace, SRGBTransfer, SampleNode, Scene, ScreenNode, SetNode, ShadowBaseNode, ShadowMaterial, ShadowNode, ShadowNodeMaterial, ShortType, Sphere, SphereGeometry, SplitNode, SpotLight, SpotLightNode, SpriteMaterial, SpriteNodeMaterial, SrcAlphaFactor, SrcAlphaSaturateFactor, SrcColorFactor, StackNode, StackTrace, StaticDrawUsage, StorageArrayElementNode, StorageBufferAttribute, StorageBufferNode, StorageInstancedBufferAttribute, StorageTexture, StorageTexture3DNode, StorageTextureNode, StructNode, StructTypeNode, SubBuildNode, SubgroupFunctionNode, SubtractEquation, SubtractiveBlending, TSL, TangentSpaceNormalMap, TempNode, Texture, Texture3DNode, TextureNode, TextureSizeNode, TimestampQuery, ToneMappingNode, ToonOutlinePassNode, UVMapping, Uint16BufferAttribute, Uint32BufferAttribute, UniformArrayNode, UniformGroupNode, UniformNode, UnpackFloatNode, UnsignedByteType, UnsignedInt101111Type, UnsignedInt248Type, UnsignedInt5999Type, UnsignedIntType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedShortType, UserDataNode, VSMShadowMap, VarNode, VaryingNode, Vector2, Vector3, Vector4, VelocityNode, VertexColorNode, ViewportDepthNode, ViewportDepthTextureNode, ViewportSharedTextureNode, ViewportTextureNode, VolumeNodeMaterial, WebGLBackend, WebGLCoordinateSystem, WebGPUBackend, WebGPUCoordinateSystem, WebGPURenderer, WebXRController, WorkgroupInfoNode, ZeroFactor, ZeroStencilOp, createCanvasElement, defaultBuildStages, defaultShaderStages, error, log$1 as log, shaderStages, vectorComponents, warn, warnOnce }; +export { ACESFilmicToneMapping, AONode, AddEquation, AddOperation, AdditiveBlending, AgXToneMapping, AlphaFormat, AlwaysCompare, AlwaysDepth, AlwaysStencilFunc, AmbientLight, AmbientLightNode, AnalyticLightNode, ArrayCamera, ArrayElementNode, ArrayNode, AssignNode, AtomicFunctionNode, AttributeNode, BackSide, BarrierNode, BasicEnvironmentNode, BasicLightMapNode, BasicShadowMap, BitcastNode, BitcountNode, BlendMode, BoxGeometry, BufferAttribute, BufferAttributeNode, BufferGeometry, BufferNode, BuiltinNode, BumpMapNode, BundleGroup, BypassNode, ByteType, CanvasTarget, CineonToneMapping, ClampToEdgeWrapping, ClippingGroup, ClippingNode, CodeNode, Color, ColorManagement, ColorSpaceNode, Compatibility, ComputeBuiltinNode, ComputeNode, ConditionalNode, ConstNode, ConstantAlphaFactor, ConstantColorFactor, ContextNode, ConvertNode, CubeCamera, CubeDepthTexture, CubeMapNode, CubeReflectionMapping, CubeRefractionMapping, CubeTexture, CubeTextureNode, CubeUVReflectionMapping, CullFaceBack, CullFaceFront, CullFaceNone, CustomBlending, CylinderGeometry, DataArrayTexture, DataTexture, DebugNode, DecrementStencilOp, DecrementWrapStencilOp, DepthFormat, DepthStencilFormat, DepthTexture, DirectionalLight, DirectionalLightNode, DoubleSide, DstAlphaFactor, DstColorFactor, DynamicDrawUsage, EnvironmentNode, EqualCompare, EqualDepth, EqualStencilFunc, EquirectangularReflectionMapping, EquirectangularRefractionMapping, EventDispatcher, EventNode, ExpressionNode, FileLoader, FlipNode, Float16BufferAttribute, Float32BufferAttribute, FloatType, FramebufferTexture, FrontFacingNode, FrontSide, Frustum, FrustumArray, FunctionCallNode, FunctionNode, FunctionOverloadingNode, GLSLNodeParser, GreaterCompare, GreaterDepth, GreaterEqualCompare, GreaterEqualDepth, GreaterEqualStencilFunc, GreaterStencilFunc, Group, HalfFloatType, HemisphereLight, HemisphereLightNode, IESSpotLight, IESSpotLightNode, IncrementStencilOp, IncrementWrapStencilOp, IndexNode, IndirectStorageBufferAttribute, InputNode, InspectorBase, InspectorNode, InstancedBufferAttribute, InstancedInterleavedBuffer, IntType, InterleavedBuffer, InterleavedBufferAttribute, InvertStencilOp, IrradianceNode, IsolateNode, JoinNode, KeepStencilOp, LessCompare, LessDepth, LessEqualCompare, LessEqualDepth, LessEqualStencilFunc, LessStencilFunc, LightProbe, LightProbeNode, Lighting, LightingContextNode, LightingModel, LightingNode, LightsNode, Line2NodeMaterial, LineBasicMaterial, LineBasicNodeMaterial, LineDashedMaterial, LineDashedNodeMaterial, LinearFilter, LinearMipMapLinearFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, LinearSRGBColorSpace, LinearToneMapping, LinearTransfer, Loader, LoopNode, MRTNode, Material, MaterialBlending, MaterialLoader, MaterialNode, MaterialReferenceNode, MathNode, MathUtils, Matrix2, Matrix3, Matrix4, MaxEquation, MaxMipLevelNode, MemberNode, Mesh, MeshBasicMaterial, MeshBasicNodeMaterial, MeshLambertMaterial, MeshLambertNodeMaterial, MeshMatcapMaterial, MeshMatcapNodeMaterial, MeshNormalMaterial, MeshNormalNodeMaterial, MeshPhongMaterial, MeshPhongNodeMaterial, MeshPhysicalMaterial, MeshPhysicalNodeMaterial, MeshSSSNodeMaterial, MeshStandardMaterial, MeshStandardNodeMaterial, MeshToonMaterial, MeshToonNodeMaterial, MinEquation, MirroredRepeatWrapping, MixOperation, ModelNode, MultiplyBlending, MultiplyOperation, NearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, NeutralToneMapping, NeverCompare, NeverDepth, NeverStencilFunc, NoBlending, NoColorSpace, NoNormalPacking, NoToneMapping, Node, NodeAccess, NodeAttribute, NodeBuilder, NodeCache, NodeCode, NodeError, NodeFrame, NodeFunctionInput, NodeLoader, NodeMaterial, NodeMaterialLoader, NodeMaterialObserver, NodeObjectLoader, NodeShaderStage, NodeType, NodeUniform, NodeUpdateType, NodeUtils, NodeVar, NodeVarying, NormalBlending, NormalGAPacking, NormalMapNode, NormalRGPacking, NotEqualCompare, NotEqualDepth, NotEqualStencilFunc, Object3D, Object3DNode, ObjectLoader, ObjectSpaceNormalMap, OneFactor, OneMinusConstantAlphaFactor, OneMinusConstantColorFactor, OneMinusDstAlphaFactor, OneMinusDstColorFactor, OneMinusSrcAlphaFactor, OneMinusSrcColorFactor, OperatorNode, OrthographicCamera, OutputStructNode, OverrideContextNode, PCFShadowMap, PCFSoftShadowMap, PMREMGenerator, PMREMNode, PackFloatNode, ParameterNode, PassNode, PerspectiveCamera, PhongLightingModel, PhysicalLightingModel, Plane, PlaneGeometry, PointLight, PointLightNode, PointShadowNode, PointUVNode, PointsMaterial, PointsNodeMaterial, PostProcessing, ProjectorLight, ProjectorLightNode, PropertyNode, QuadMesh, Quaternion, R11_EAC_Format, RED_GREEN_RGTC2_Format, RED_RGTC1_Format, REVISION, RG11_EAC_Format, RGBAFormat, RGBAIntegerFormat, RGBA_ASTC_10x10_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_BPTC_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGBFormat, RGBIntegerFormat, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGB_S3TC_DXT1_Format, RGFormat, RGIntegerFormat, RTTNode, RangeNode, ReadbackBuffer, RectAreaLight, RectAreaLightNode, RedFormat, RedIntegerFormat, ReferenceBaseNode, ReferenceNode, ReflectorNode, ReinhardToneMapping, RenderOutputNode, RenderPipeline, RenderTarget, RendererReferenceNode, RendererUtils, RepeatWrapping, ReplaceStencilOp, ReverseSubtractEquation, RotateNode, SIGNED_R11_EAC_Format, SIGNED_RED_GREEN_RGTC2_Format, SIGNED_RED_RGTC1_Format, SIGNED_RG11_EAC_Format, SRGBColorSpace, SRGBTransfer, SampleNode, Scene, ScreenNode, SetNode, ShadowBaseNode, ShadowMaterial, ShadowNode, ShadowNodeMaterial, ShortType, Sphere, SphereGeometry, SplitNode, SpotLight, SpotLightNode, SpriteMaterial, SpriteNodeMaterial, SrcAlphaFactor, SrcAlphaSaturateFactor, SrcColorFactor, StackNode, StackTrace, StaticDrawUsage, StorageArrayElementNode, StorageBufferAttribute, StorageBufferNode, StorageInstancedBufferAttribute, StorageTexture, StorageTexture3DNode, StorageTextureNode, StructNode, StructTypeNode, SubBuildNode, SubgroupFunctionNode, SubtractEquation, SubtractiveBlending, TSL, TangentSpaceNormalMap, TempNode, Texture, Texture3DNode, TextureNode, TextureSizeNode, TimestampQuery, ToneMappingNode, ToonOutlinePassNode, UVMapping, Uint16BufferAttribute, Uint32BufferAttribute, UniformArrayNode, UniformGroupNode, UniformNode, UnpackFloatNode, UnsignedByteType, UnsignedInt101111Type, UnsignedInt248Type, UnsignedInt5999Type, UnsignedIntType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedShortType, UserDataNode, VSMShadowMap, VarNode, VaryingNode, Vector2, Vector3, Vector4, VelocityNode, VertexColorNode, ViewportDepthNode, ViewportDepthTextureNode, ViewportSharedTextureNode, ViewportTextureNode, VolumeNodeMaterial, WebGLBackend, WebGLCoordinateSystem, WebGPUBackend, WebGPUCoordinateSystem, WebGPURenderer, WebXRController, WorkgroupInfoNode, ZeroFactor, ZeroStencilOp, createCanvasElement, defaultBuildStages, defaultShaderStages, error, log$1 as log, shaderStages, vectorComponents, warn, warnOnce };