super-three 0.154.0 → 0.155.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/build/three.cjs +257 -40
  2. package/build/three.js +257 -40
  3. package/build/three.min.js +1 -2
  4. package/build/three.module.js +256 -41
  5. package/build/three.module.min.js +1 -2
  6. package/examples/jsm/controls/OrbitControls.js +143 -21
  7. package/examples/jsm/environments/RoomEnvironment.js +1 -1
  8. package/examples/jsm/exporters/USDZExporter.js +1 -0
  9. package/examples/jsm/interactive/SelectionHelper.js +7 -0
  10. package/examples/jsm/loaders/FBXLoader.js +1 -1
  11. package/examples/jsm/loaders/GLTFLoader.js +8 -3
  12. package/examples/jsm/loaders/KTX2Loader.js +4 -4
  13. package/examples/jsm/loaders/LDrawLoader.js +1 -1
  14. package/examples/jsm/loaders/NRRDLoader.js +3 -3
  15. package/examples/jsm/loaders/PLYLoader.js +1 -1
  16. package/examples/jsm/loaders/TGALoader.js +10 -10
  17. package/examples/jsm/loaders/VRMLLoader.js +1 -1
  18. package/examples/jsm/misc/GPUComputationRenderer.js +0 -9
  19. package/examples/jsm/misc/Volume.js +4 -6
  20. package/examples/jsm/nodes/Nodes.js +5 -6
  21. package/examples/jsm/nodes/accessors/BufferAttributeNode.js +1 -1
  22. package/examples/jsm/nodes/accessors/CubeTextureNode.js +2 -0
  23. package/examples/jsm/nodes/accessors/ExtendedMaterialNode.js +14 -1
  24. package/examples/jsm/nodes/accessors/InstanceNode.js +8 -8
  25. package/examples/jsm/nodes/accessors/MaterialNode.js +34 -44
  26. package/examples/jsm/nodes/accessors/SceneNode.js +6 -0
  27. package/examples/jsm/nodes/accessors/TextureNode.js +48 -7
  28. package/examples/jsm/nodes/core/LightingModel.js +7 -9
  29. package/examples/jsm/nodes/core/Node.js +11 -2
  30. package/examples/jsm/nodes/core/PropertyNode.js +4 -0
  31. package/examples/jsm/nodes/display/BumpMapNode.js +77 -0
  32. package/examples/jsm/nodes/display/NormalMapNode.js +2 -2
  33. package/examples/jsm/nodes/functions/BSDF/BRDF_GGX.js +10 -2
  34. package/examples/jsm/nodes/functions/BSDF/Schlick_to_F0.js +13 -0
  35. package/examples/jsm/nodes/functions/PhongLightingModel.js +53 -14
  36. package/examples/jsm/nodes/functions/PhysicalLightingModel.js +236 -97
  37. package/examples/jsm/nodes/geometry/RangeNode.js +2 -2
  38. package/examples/jsm/nodes/gpgpu/ComputeNode.js +13 -0
  39. package/examples/jsm/nodes/lighting/AONode.js +1 -1
  40. package/examples/jsm/nodes/lighting/AnalyticLightNode.js +58 -20
  41. package/examples/jsm/nodes/lighting/DirectionalLightNode.js +7 -10
  42. package/examples/jsm/nodes/lighting/EnvironmentNode.js +22 -12
  43. package/examples/jsm/nodes/lighting/HemisphereLightNode.js +1 -1
  44. package/examples/jsm/nodes/lighting/LightingContextNode.js +12 -9
  45. package/examples/jsm/nodes/lighting/PointLightNode.js +7 -10
  46. package/examples/jsm/nodes/lighting/SpotLightNode.js +7 -10
  47. package/examples/jsm/nodes/materials/Materials.js +1 -0
  48. package/examples/jsm/nodes/materials/MeshLambertNodeMaterial.js +34 -0
  49. package/examples/jsm/nodes/materials/MeshNormalNodeMaterial.js +2 -0
  50. package/examples/jsm/nodes/materials/MeshPhongNodeMaterial.js +2 -2
  51. package/examples/jsm/nodes/materials/MeshPhysicalNodeMaterial.js +19 -2
  52. package/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js +2 -2
  53. package/examples/jsm/nodes/materials/NodeMaterial.js +50 -24
  54. package/examples/jsm/nodes/materials/SpriteNodeMaterial.js +2 -2
  55. package/examples/jsm/nodes/shadernode/ShaderNode.js +2 -2
  56. package/examples/jsm/objects/Reflector.js +1 -9
  57. package/examples/jsm/objects/Refractor.js +0 -8
  58. package/examples/jsm/offscreen/scene.js +0 -1
  59. package/examples/jsm/postprocessing/MaskPass.js +4 -1
  60. package/examples/jsm/postprocessing/OutputPass.js +33 -14
  61. package/examples/jsm/postprocessing/UnrealBloomPass.js +21 -17
  62. package/examples/jsm/renderers/common/Background.js +22 -20
  63. package/examples/jsm/renderers/common/Binding.js +8 -0
  64. package/examples/jsm/renderers/common/Bindings.js +2 -6
  65. package/examples/jsm/renderers/common/Geometries.js +1 -1
  66. package/examples/jsm/renderers/common/Pipelines.js +94 -45
  67. package/examples/jsm/renderers/common/RenderContext.js +1 -0
  68. package/examples/jsm/renderers/common/RenderContexts.js +16 -5
  69. package/examples/jsm/renderers/common/RenderObject.js +20 -4
  70. package/examples/jsm/renderers/common/RenderObjects.js +10 -7
  71. package/examples/jsm/renderers/common/Renderer.js +70 -39
  72. package/examples/jsm/renderers/common/Textures.js +20 -8
  73. package/examples/jsm/renderers/common/nodes/Nodes.js +15 -4
  74. package/examples/jsm/renderers/webgpu/WebGPUBackend.js +51 -54
  75. package/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js +33 -9
  76. package/examples/jsm/renderers/webgpu/utils/WebGPUAttributeUtils.js +55 -36
  77. package/examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js +83 -5
  78. package/examples/jsm/renderers/webgpu/utils/WebGPUConstants.js +24 -22
  79. package/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js +83 -130
  80. package/examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js +35 -8
  81. package/examples/jsm/renderers/webgpu/utils/WebGPUUtils.js +1 -1
  82. package/examples/jsm/shaders/OutputShader.js +19 -2
  83. package/examples/jsm/utils/BufferGeometryUtils.js +13 -4
  84. package/examples/jsm/webxr/OculusHandPointerModel.js +2 -2
  85. package/package.json +2 -2
  86. package/src/Three.js +2 -0
  87. package/src/animation/PropertyBinding.js +1 -1
  88. package/src/cameras/CubeCamera.js +1 -4
  89. package/src/constants.js +1 -1
  90. package/src/core/BufferAttribute.js +20 -0
  91. package/src/core/Object3D.js +1 -1
  92. package/src/core/RenderTarget.js +122 -0
  93. package/src/geometries/CapsuleGeometry.js +1 -1
  94. package/src/loaders/DataTextureLoader.js +21 -2
  95. package/src/renderers/WebGLMultipleRenderTargets.js +0 -2
  96. package/src/renderers/WebGLRenderTarget.js +3 -110
  97. package/src/renderers/WebGLRenderer.js +62 -8
  98. package/src/renderers/shaders/ShaderChunk/iridescence_fragment.glsl.js +1 -2
  99. package/src/renderers/webgl/WebGLGeometries.js +5 -1
  100. package/src/renderers/webgl/WebGLMaterials.js +1 -1
  101. package/src/renderers/webgl/WebGLPrograms.js +14 -2
  102. package/src/renderers/webgl/WebGLTextures.js +97 -12
  103. package/src/renderers/webxr/WebXRManager.js +1 -1
  104. package/src/textures/CompressedCubeTexture.js +19 -0
  105. package/examples/jsm/nodes/functions/BSDF/BRDF_BlinnPhong.js +0 -30
  106. package/examples/jsm/renderers/common/RenderTarget.js +0 -15
  107. package/examples/jsm/renderers/common/nodes/NodeRender.js +0 -302
@@ -1,49 +1,68 @@
1
1
  import {
2
- ShaderMaterial,
2
+ RawShaderMaterial,
3
3
  UniformsUtils,
4
- NoToneMapping,
5
4
  LinearToneMapping,
6
5
  ReinhardToneMapping,
7
6
  CineonToneMapping,
8
- ACESFilmicToneMapping
7
+ ACESFilmicToneMapping,
8
+ SRGBColorSpace
9
9
  } from 'three';
10
10
  import { Pass, FullScreenQuad } from './Pass.js';
11
11
  import { OutputShader } from '../shaders/OutputShader.js';
12
12
 
13
13
  class OutputPass extends Pass {
14
14
 
15
- constructor( toneMapping = NoToneMapping, toneMappingExposure = 1 ) {
15
+ constructor() {
16
16
 
17
17
  super();
18
18
 
19
- this.toneMapping = toneMapping;
20
- this.toneMappingExposure = toneMappingExposure;
21
-
22
19
  //
23
20
 
24
21
  const shader = OutputShader;
25
22
 
26
23
  this.uniforms = UniformsUtils.clone( shader.uniforms );
27
24
 
28
- this.material = new ShaderMaterial( {
25
+ this.material = new RawShaderMaterial( {
29
26
  uniforms: this.uniforms,
30
27
  vertexShader: shader.vertexShader,
31
28
  fragmentShader: shader.fragmentShader
32
29
  } );
33
30
 
34
- if ( toneMapping === LinearToneMapping ) this.material.defines.LINEAR_TONE_MAPPING = '';
35
- else if ( toneMapping === ReinhardToneMapping ) this.material.defines.REINHARD_TONE_MAPPING = '';
36
- else if ( toneMapping === CineonToneMapping ) this.material.defines.CINEON_TONE_MAPPING = '';
37
- else if ( toneMapping === ACESFilmicToneMapping ) this.material.defines.ACES_FILMIC_TONE_MAPPING = '';
38
-
39
31
  this.fsQuad = new FullScreenQuad( this.material );
40
32
 
33
+ // internal cache
34
+
35
+ this._outputColorSpace = null;
36
+ this._toneMapping = null;
37
+
41
38
  }
42
39
 
43
40
  render( renderer, writeBuffer, readBuffer/*, deltaTime, maskActive */ ) {
44
41
 
45
42
  this.uniforms[ 'tDiffuse' ].value = readBuffer.texture;
46
- this.uniforms[ 'toneMappingExposure' ].value = this.toneMappingExposure;
43
+ this.uniforms[ 'toneMappingExposure' ].value = renderer.toneMappingExposure;
44
+
45
+ // rebuild defines if required
46
+
47
+ if ( this._outputColorSpace !== renderer.outputColorSpace || this._toneMapping !== renderer.toneMapping ) {
48
+
49
+ this._outputColorSpace = renderer.outputColorSpace;
50
+ this._toneMapping = renderer.toneMapping;
51
+
52
+ this.material.defines = {};
53
+
54
+ if ( this._outputColorSpace == SRGBColorSpace ) this.material.defines.SRGB_COLOR_SPACE = '';
55
+
56
+ if ( this._toneMapping === LinearToneMapping ) this.material.defines.LINEAR_TONE_MAPPING = '';
57
+ else if ( this._toneMapping === ReinhardToneMapping ) this.material.defines.REINHARD_TONE_MAPPING = '';
58
+ else if ( this._toneMapping === CineonToneMapping ) this.material.defines.CINEON_TONE_MAPPING = '';
59
+ else if ( this._toneMapping === ACESFilmicToneMapping ) this.material.defines.ACES_FILMIC_TONE_MAPPING = '';
60
+
61
+ this.material.needsUpdate = true;
62
+
63
+ }
64
+
65
+ //
47
66
 
48
67
  if ( this.renderToScreen === true ) {
49
68
 
@@ -94,7 +94,7 @@ class UnrealBloomPass extends Pass {
94
94
 
95
95
  this.separableBlurMaterials.push( this.getSeperableBlurMaterial( kernelSizeArray[ i ] ) );
96
96
 
97
- this.separableBlurMaterials[ i ].uniforms[ 'texSize' ].value = new Vector2( resx, resy );
97
+ this.separableBlurMaterials[ i ].uniforms[ 'invSize' ].value = new Vector2( 1 / resx, 1 / resy );
98
98
 
99
99
  resx = Math.round( resx / 2 );
100
100
 
@@ -192,7 +192,7 @@ class UnrealBloomPass extends Pass {
192
192
  this.renderTargetsHorizontal[ i ].setSize( resx, resy );
193
193
  this.renderTargetsVertical[ i ].setSize( resx, resy );
194
194
 
195
- this.separableBlurMaterials[ i ].uniforms[ 'texSize' ].value = new Vector2( resx, resy );
195
+ this.separableBlurMaterials[ i ].uniforms[ 'invSize' ].value = new Vector2( 1 / resx, 1 / resy );
196
196
 
197
197
  resx = Math.round( resx / 2 );
198
198
  resy = Math.round( resy / 2 );
@@ -298,17 +298,25 @@ class UnrealBloomPass extends Pass {
298
298
 
299
299
  getSeperableBlurMaterial( kernelRadius ) {
300
300
 
301
+ const coefficients = [];
302
+
303
+ for ( let i = 0; i < kernelRadius; i ++ ) {
304
+
305
+ coefficients.push( 0.39894 * Math.exp( - 0.5 * i * i / ( kernelRadius * kernelRadius ) ) / kernelRadius );
306
+
307
+ }
308
+
301
309
  return new ShaderMaterial( {
302
310
 
303
311
  defines: {
304
- 'KERNEL_RADIUS': kernelRadius,
305
- 'SIGMA': kernelRadius
312
+ 'KERNEL_RADIUS': kernelRadius
306
313
  },
307
314
 
308
315
  uniforms: {
309
316
  'colorTexture': { value: null },
310
- 'texSize': { value: new Vector2( 0.5, 0.5 ) },
311
- 'direction': { value: new Vector2( 0.5, 0.5 ) }
317
+ 'invSize': { value: new Vector2( 0.5, 0.5 ) }, // inverse texture size
318
+ 'direction': { value: new Vector2( 0.5, 0.5 ) },
319
+ 'gaussianCoefficients': { value: coefficients } // precomputed Gaussian coefficients
312
320
  },
313
321
 
314
322
  vertexShader:
@@ -322,23 +330,19 @@ class UnrealBloomPass extends Pass {
322
330
  `#include <common>
323
331
  varying vec2 vUv;
324
332
  uniform sampler2D colorTexture;
325
- uniform vec2 texSize;
333
+ uniform vec2 invSize;
326
334
  uniform vec2 direction;
335
+ uniform float gaussianCoefficients[KERNEL_RADIUS];
327
336
 
328
- float gaussianPdf(in float x, in float sigma) {
329
- return 0.39894 * exp( -0.5 * x * x/( sigma * sigma))/sigma;
330
- }
331
337
  void main() {
332
- vec2 invSize = 1.0 / texSize;
333
- float fSigma = float(SIGMA);
334
- float weightSum = gaussianPdf(0.0, fSigma);
335
- vec3 diffuseSum = texture2D( colorTexture, vUv).rgb * weightSum;
338
+ float weightSum = gaussianCoefficients[0];
339
+ vec3 diffuseSum = texture2D( colorTexture, vUv ).rgb * weightSum;
336
340
  for( int i = 1; i < KERNEL_RADIUS; i ++ ) {
337
341
  float x = float(i);
338
- float w = gaussianPdf(x, fSigma);
342
+ float w = gaussianCoefficients[i];
339
343
  vec2 uvOffset = direction * invSize * x;
340
- vec3 sample1 = texture2D( colorTexture, vUv + uvOffset).rgb;
341
- vec3 sample2 = texture2D( colorTexture, vUv - uvOffset).rgb;
344
+ vec3 sample1 = texture2D( colorTexture, vUv + uvOffset ).rgb;
345
+ vec3 sample2 = texture2D( colorTexture, vUv - uvOffset ).rgb;
342
346
  diffuseSum += (sample1 + sample2) * w;
343
347
  weightSum += 2.0 * w;
344
348
  }
@@ -1,6 +1,6 @@
1
1
  import DataMap from './DataMap.js';
2
2
  import { Color, Mesh, SphereGeometry, BackSide } from 'three';
3
- import { context, positionWorldDirection, backgroundBlurriness, MeshBasicNodeMaterial } from '../../nodes/Nodes.js';
3
+ import { vec4, context, normalWorld, backgroundBlurriness, backgroundIntensity, NodeMaterial, modelViewProjection } from '../../nodes/Nodes.js';
4
4
 
5
5
  let _clearAlpha;
6
6
  const _clearColor = new Color();
@@ -14,15 +14,15 @@ class Background extends DataMap {
14
14
  this.renderer = renderer;
15
15
  this.nodes = nodes;
16
16
 
17
- this.boxMesh = null;
18
- this.boxMeshNode = null;
17
+ this.backgroundMesh = null;
18
+ this.backgroundMeshNode = null;
19
19
 
20
20
  }
21
21
 
22
22
  update( scene, renderList, renderContext ) {
23
23
 
24
24
  const renderer = this.renderer;
25
- const background = ( scene.isScene === true ) ? this.nodes.getBackgroundNode( scene ) || scene.background : null;
25
+ const background = this.nodes.getBackgroundNode( scene ) || scene.background;
26
26
 
27
27
  let forceClear = false;
28
28
 
@@ -49,31 +49,33 @@ class Background extends DataMap {
49
49
  _clearColor.copy( renderer._clearColor );
50
50
  _clearAlpha = renderer._clearAlpha;
51
51
 
52
- let boxMesh = this.boxMesh;
52
+ let backgroundMesh = this.backgroundMesh;
53
53
 
54
- if ( boxMesh === null ) {
54
+ if ( backgroundMesh === null ) {
55
55
 
56
- this.boxMeshNode = context( backgroundNode, {
56
+ this.backgroundMeshNode = context( backgroundNode, {
57
57
  // @TODO: Add Texture2D support using node context
58
- getUVNode: () => positionWorldDirection,
58
+ getUVNode: () => normalWorld,
59
59
  getSamplerLevelNode: () => backgroundBlurriness
60
- } );
60
+ } ).mul( backgroundIntensity );
61
61
 
62
- const nodeMaterial = new MeshBasicNodeMaterial();
63
- nodeMaterial.colorNode = this.boxMeshNode;
62
+ let viewProj = modelViewProjection();
63
+ viewProj = vec4( viewProj.x, viewProj.y, viewProj.w, viewProj.w );
64
+
65
+ const nodeMaterial = new NodeMaterial();
66
+ nodeMaterial.outputNode = this.backgroundMeshNode;
64
67
  nodeMaterial.side = BackSide;
65
68
  nodeMaterial.depthTest = false;
66
69
  nodeMaterial.depthWrite = false;
67
70
  nodeMaterial.fog = false;
71
+ nodeMaterial.vertexNode = viewProj;
68
72
 
69
- this.boxMesh = boxMesh = new Mesh( new SphereGeometry( 1, 32, 32 ), nodeMaterial );
70
- boxMesh.frustumCulled = false;
71
-
72
- boxMesh.onBeforeRender = function ( renderer, scene, camera ) {
73
+ this.backgroundMesh = backgroundMesh = new Mesh( new SphereGeometry( 1, 32, 32 ), nodeMaterial );
74
+ backgroundMesh.frustumCulled = false;
73
75
 
74
- const scale = camera.far;
76
+ backgroundMesh.onBeforeRender = function ( renderer, scene, camera ) {
75
77
 
76
- this.matrixWorld.makeScale( scale, scale, scale ).copyPosition( camera.matrixWorld );
78
+ this.matrixWorld.copyPosition( camera.matrixWorld );
77
79
 
78
80
  };
79
81
 
@@ -83,15 +85,15 @@ class Background extends DataMap {
83
85
 
84
86
  if ( sceneData.backgroundCacheKey !== backgroundCacheKey ) {
85
87
 
86
- this.boxMeshNode.node = backgroundNode;
88
+ this.backgroundMeshNode.node = backgroundNode;
87
89
 
88
- boxMesh.material.needsUpdate = true;
90
+ backgroundMesh.material.needsUpdate = true;
89
91
 
90
92
  sceneData.backgroundCacheKey = backgroundCacheKey;
91
93
 
92
94
  }
93
95
 
94
- renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null );
96
+ renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null );
95
97
 
96
98
  } else {
97
99
 
@@ -4,6 +4,14 @@ class Binding {
4
4
 
5
5
  this.name = name;
6
6
 
7
+ this.visibility = 0;
8
+
9
+ }
10
+
11
+ setVisibility( visibility ) {
12
+
13
+ this.visibility |= visibility;
14
+
7
15
  }
8
16
 
9
17
  }
@@ -34,9 +34,7 @@ class Bindings extends DataMap {
34
34
 
35
35
  this._init( bindings );
36
36
 
37
- const pipeline = this.pipelines.getForRender( renderObject );
38
-
39
- this.backend.createBindings( bindings, pipeline );
37
+ this.backend.createBindings( bindings );
40
38
 
41
39
  }
42
40
 
@@ -58,9 +56,7 @@ class Bindings extends DataMap {
58
56
 
59
57
  this._init( bindings );
60
58
 
61
- const pipeline = this.pipelines.getForCompute( computeNode );
62
-
63
- this.backend.createBindings( bindings, pipeline );
59
+ this.backend.createBindings( bindings );
64
60
 
65
61
  }
66
62
 
@@ -88,7 +88,7 @@ class Geometries extends DataMap {
88
88
 
89
89
  }
90
90
 
91
- update( renderObject ) {
91
+ updateForRender( renderObject ) {
92
92
 
93
93
  if ( this.has( renderObject ) === false ) this.initGeometry( renderObject );
94
94
 
@@ -23,17 +23,22 @@ class Pipelines extends DataMap {
23
23
 
24
24
  }
25
25
 
26
- getForCompute( computeNode ) {
26
+ getForCompute( computeNode, bindings ) {
27
27
 
28
28
  const { backend } = this;
29
29
 
30
30
  const data = this.get( computeNode );
31
31
 
32
- if ( data.pipeline === undefined ) {
32
+ if ( this._needsComputeUpdate( computeNode ) ) {
33
33
 
34
- // release previous cache
34
+ const previousPipeline = data.pipeline;
35
35
 
36
- const previousPipeline = this._releasePipeline( computeNode );
36
+ if ( previousPipeline ) {
37
+
38
+ previousPipeline.usedTimes --;
39
+ previousPipeline.computeProgram.usedTimes --;
40
+
41
+ }
37
42
 
38
43
  // get shader
39
44
 
@@ -45,7 +50,7 @@ class Pipelines extends DataMap {
45
50
 
46
51
  if ( stageCompute === undefined ) {
47
52
 
48
- if ( previousPipeline ) this._releaseProgram( previousPipeline.computeShader );
53
+ if ( previousPipeline && previousPipeline.computeProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.computeProgram );
49
54
 
50
55
  stageCompute = new ProgrammableStage( nodeBuilder.computeShader, 'compute' );
51
56
  this.programs.compute.set( nodeBuilder.computeShader, stageCompute );
@@ -56,7 +61,17 @@ class Pipelines extends DataMap {
56
61
 
57
62
  // determine compute pipeline
58
63
 
59
- const pipeline = this._getComputePipeline( stageCompute );
64
+ const cacheKey = this._getComputeCacheKey( computeNode, stageCompute );
65
+
66
+ let pipeline = this.caches.get( cacheKey );
67
+
68
+ if ( pipeline === undefined ) {
69
+
70
+ if ( previousPipeline && previousPipeline.usedTimes === 0 ) this._releasePipeline( computeNode );
71
+
72
+ pipeline = this._getComputePipeline( computeNode, stageCompute, cacheKey, bindings );
73
+
74
+ }
60
75
 
61
76
  // keep track of all used times
62
77
 
@@ -65,6 +80,7 @@ class Pipelines extends DataMap {
65
80
 
66
81
  //
67
82
 
83
+ data.version = computeNode.version;
68
84
  data.pipeline = pipeline;
69
85
 
70
86
  }
@@ -79,11 +95,17 @@ class Pipelines extends DataMap {
79
95
 
80
96
  const data = this.get( renderObject );
81
97
 
82
- if ( this._needsUpdate( renderObject ) ) {
98
+ if ( this._needsRenderUpdate( renderObject ) ) {
99
+
100
+ const previousPipeline = data.pipeline;
83
101
 
84
- // release previous cache
102
+ if ( previousPipeline ) {
85
103
 
86
- const previousPipeline = this._releasePipeline( renderObject );
104
+ previousPipeline.usedTimes --;
105
+ previousPipeline.vertexProgram.usedTimes --;
106
+ previousPipeline.fragmentProgram.usedTimes --;
107
+
108
+ }
87
109
 
88
110
  // get shader
89
111
 
@@ -95,7 +117,7 @@ class Pipelines extends DataMap {
95
117
 
96
118
  if ( stageVertex === undefined ) {
97
119
 
98
- if ( previousPipeline ) this._releaseProgram( previousPipeline.vertexProgram );
120
+ if ( previousPipeline && previousPipeline.vertexProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.vertexProgram );
99
121
 
100
122
  stageVertex = new ProgrammableStage( nodeBuilder.vertexShader, 'vertex' );
101
123
  this.programs.vertex.set( nodeBuilder.vertexShader, stageVertex );
@@ -108,7 +130,7 @@ class Pipelines extends DataMap {
108
130
 
109
131
  if ( stageFragment === undefined ) {
110
132
 
111
- if ( previousPipeline ) this._releaseProgram( previousPipeline.fragmentShader );
133
+ if ( previousPipeline && previousPipeline.fragmentProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.fragmentProgram );
112
134
 
113
135
  stageFragment = new ProgrammableStage( nodeBuilder.fragmentShader, 'fragment' );
114
136
  this.programs.fragment.set( nodeBuilder.fragmentShader, stageFragment );
@@ -119,7 +141,21 @@ class Pipelines extends DataMap {
119
141
 
120
142
  // determine render pipeline
121
143
 
122
- const pipeline = this._getRenderPipeline( renderObject, stageVertex, stageFragment );
144
+ const cacheKey = this._getRenderCacheKey( renderObject, stageVertex, stageFragment );
145
+
146
+ let pipeline = this.caches.get( cacheKey );
147
+
148
+ if ( pipeline === undefined ) {
149
+
150
+ if ( previousPipeline && previousPipeline.usedTimes === 0 ) this._releasePipeline( previousPipeline );
151
+
152
+ pipeline = this._getRenderPipeline( renderObject, stageVertex, stageFragment, cacheKey );
153
+
154
+ } else {
155
+
156
+ renderObject.pipeline = pipeline;
157
+
158
+ }
123
159
 
124
160
  // keep track of all used times
125
161
 
@@ -139,18 +175,31 @@ class Pipelines extends DataMap {
139
175
 
140
176
  delete( object ) {
141
177
 
142
- const pipeline = this._releasePipeline( object );
178
+ const pipeline = this.get( object ).pipeline;
179
+
180
+ if ( pipeline ) {
181
+
182
+ // pipeline
183
+
184
+ pipeline.usedTimes --;
143
185
 
144
- if ( pipeline && pipeline.usedTimes === 0 ) {
186
+ if ( pipeline.usedTimes === 0 ) this._releasePipeline( pipeline );
187
+
188
+ // programs
145
189
 
146
190
  if ( pipeline.isComputePipeline ) {
147
191
 
148
- this._releaseProgram( pipeline.computeProgram );
192
+ pipeline.computeProgram.usedTimes --;
193
+
194
+ if ( pipeline.computeProgram.usedTimes === 0 ) this._releaseProgram( pipeline.computeProgram );
149
195
 
150
196
  } else {
151
197
 
152
- this._releaseProgram( pipeline.vertexProgram );
153
- this._releaseProgram( pipeline.fragmentProgram );
198
+ pipeline.fragmentProgram.usedTimes --;
199
+ pipeline.vertexProgram.usedTimes --;
200
+
201
+ if ( pipeline.vertexProgram.usedTimes === 0 ) this._releaseProgram( pipeline.vertexProgram );
202
+ if ( pipeline.fragmentProgram.usedTimes === 0 ) this._releaseProgram( pipeline.fragmentProgram );
154
203
 
155
204
  }
156
205
 
@@ -173,11 +222,17 @@ class Pipelines extends DataMap {
173
222
 
174
223
  }
175
224
 
176
- _getComputePipeline( stageCompute ) {
225
+ updateForRender( renderObject ) {
226
+
227
+ this.getForRender( renderObject );
228
+
229
+ }
230
+
231
+ _getComputePipeline( computeNode, stageCompute, cacheKey, bindings ) {
177
232
 
178
233
  // check for existing pipeline
179
234
 
180
- const cacheKey = 'compute:' + stageCompute.id;
235
+ cacheKey = cacheKey || this._getComputeCacheKey( computeNode, stageCompute );
181
236
 
182
237
  let pipeline = this.caches.get( cacheKey );
183
238
 
@@ -187,7 +242,7 @@ class Pipelines extends DataMap {
187
242
 
188
243
  this.caches.set( cacheKey, pipeline );
189
244
 
190
- this.backend.createComputePipeline( pipeline );
245
+ this.backend.createComputePipeline( pipeline, bindings );
191
246
 
192
247
  }
193
248
 
@@ -195,11 +250,11 @@ class Pipelines extends DataMap {
195
250
 
196
251
  }
197
252
 
198
- _getRenderPipeline( renderObject, stageVertex, stageFragment ) {
253
+ _getRenderPipeline( renderObject, stageVertex, stageFragment, cacheKey ) {
199
254
 
200
255
  // check for existing pipeline
201
256
 
202
- const cacheKey = this._getRenderCacheKey( renderObject, stageVertex, stageFragment );
257
+ cacheKey = cacheKey || this._getRenderCacheKey( renderObject, stageVertex, stageFragment );
203
258
 
204
259
  let pipeline = this.caches.get( cacheKey );
205
260
 
@@ -213,15 +268,15 @@ class Pipelines extends DataMap {
213
268
 
214
269
  this.backend.createRenderPipeline( renderObject );
215
270
 
216
- } else {
271
+ }
217
272
 
218
- // assign a shared pipeline to renderObject
273
+ return pipeline;
219
274
 
220
- renderObject.pipeline = pipeline;
275
+ }
221
276
 
222
- }
277
+ _getComputeCacheKey( computeNode, stageCompute ) {
223
278
 
224
- return pipeline;
279
+ return 'compute' + computeNode.id + stageCompute.id;
225
280
 
226
281
  }
227
282
 
@@ -247,36 +302,30 @@ class Pipelines extends DataMap {
247
302
 
248
303
  }
249
304
 
250
- _releasePipeline( object ) {
251
-
252
- const pipeline = this.get( object ).pipeline;
305
+ _releasePipeline( pipeline ) {
253
306
 
254
- //this.bindings.delete( object );
307
+ this.caches.delete( pipeline.cacheKey );
255
308
 
256
- if ( pipeline && -- pipeline.usedTimes === 0 ) {
309
+ }
257
310
 
258
- this.caches.delete( pipeline.cacheKey );
311
+ _releaseProgram( program ) {
259
312
 
260
- }
313
+ const code = program.code;
314
+ const stage = program.stage;
261
315
 
262
- return pipeline;
316
+ this.programs[ stage ].delete( code );
263
317
 
264
318
  }
265
319
 
266
- _releaseProgram( program ) {
267
-
268
- if ( -- program.usedTimes === 0 ) {
320
+ _needsComputeUpdate( computeNode ) {
269
321
 
270
- const code = program.code;
271
- const stage = program.stage;
272
-
273
- this.programs[ stage ].delete( code );
322
+ const data = this.get( computeNode );
274
323
 
275
- }
324
+ return data.pipeline === undefined || data.version !== computeNode.version;
276
325
 
277
326
  }
278
327
 
279
- _needsUpdate( renderObject ) {
328
+ _needsRenderUpdate( renderObject ) {
280
329
 
281
330
  const data = this.get( renderObject );
282
331
  const material = renderObject.material;
@@ -312,7 +361,7 @@ class Pipelines extends DataMap {
312
361
 
313
362
  }
314
363
 
315
- return needsUpdate || data.pipeline !== undefined;
364
+ return needsUpdate || data.pipeline === undefined;
316
365
 
317
366
  }
318
367
 
@@ -29,6 +29,7 @@ class RenderContext {
29
29
  this.texture = null;
30
30
  this.depthTexture = null;
31
31
  this.activeCubeFace = 0;
32
+ this.sampleCount = 1;
32
33
 
33
34
  }
34
35
 
@@ -5,31 +5,42 @@ class RenderContexts {
5
5
 
6
6
  constructor() {
7
7
 
8
- this.renderStates = new ChainMap();
8
+ this.chainMaps = {};
9
9
 
10
10
  }
11
11
 
12
- get( scene, camera ) {
12
+ get( scene, camera, renderTarget = null ) {
13
13
 
14
14
  const chainKey = [ scene, camera ];
15
+ const attachmentState = renderTarget === null ? 'default' : `${renderTarget.texture.format}:${renderTarget.samples}:${renderTarget.depthBuffer}:${renderTarget.stencilBuffer}`;
15
16
 
16
- let renderState = this.renderStates.get( chainKey );
17
+ const chainMap = this.getChainMap( attachmentState );
18
+
19
+ let renderState = chainMap.get( chainKey );
17
20
 
18
21
  if ( renderState === undefined ) {
19
22
 
20
23
  renderState = new RenderContext();
21
24
 
22
- this.renderStates.set( chainKey, renderState );
25
+ chainMap.set( chainKey, renderState );
23
26
 
24
27
  }
25
28
 
29
+ if ( renderTarget !== null ) renderState.sampleCount = renderTarget.samples === 0 ? 1 : renderTarget.samples;
30
+
26
31
  return renderState;
27
32
 
28
33
  }
29
34
 
35
+ getChainMap( attachmentState ) {
36
+
37
+ return this.chainMaps[ attachmentState ] || ( this.chainMaps[ attachmentState ] = new ChainMap() );
38
+
39
+ }
40
+
30
41
  dispose() {
31
42
 
32
- this.renderStates = new ChainMap();
43
+ this.chainMaps = {};
33
44
 
34
45
  }
35
46