Parcourir la source

Updated builds.

Mugen87 il y a 1 semaine
Parent
commit
9f3d53ce1c

+ 42 - 2
build/three.cjs

@@ -2076,7 +2076,7 @@ function warnOnce( ...params ) {
  * main thread. This is useful for GPU-CPU synchronization in WebGL contexts.
  *
  * @private
- * @param {WebGLRenderingContext|WebGL2RenderingContext} gl - The WebGL rendering context.
+ * @param {WebGL2RenderingContext} gl - The WebGL rendering context.
  * @param {WebGLSync} sync - The WebGL sync object to wait for.
  * @param {number} interval - The polling interval in milliseconds.
  * @return {Promise<void>} A promise that resolves when the sync completes or rejects if it fails.
@@ -43184,6 +43184,8 @@ const Cache = {
 
 		if ( this.enabled === false ) return;
 
+		if ( isBlobURL( key ) ) return;
+
 		// log( 'Cache', 'Adding key:', key );
 
 		this.files[ key ] = file;
@@ -43201,6 +43203,8 @@ const Cache = {
 
 		if ( this.enabled === false ) return;
 
+		if ( isBlobURL( key ) ) return;
+
 		// log( 'Cache', 'Checking key:', key );
 
 		return this.files[ key ];
@@ -43232,6 +43236,31 @@ const Cache = {
 
 };
 
+/**
+ * Returns true if the given cache key contains the blob: scheme.
+ *
+ * @private
+ * @param {string} key - The cache key.
+ * @return {boolean} Whether the given cache key contains the blob: scheme or not.
+ */
+function isBlobURL( key ) {
+
+	try {
+
+		const urlString = key.slice( key.indexOf( ':' ) + 1 ); // remove type identifier
+
+		const url = new URL( urlString );
+		return url.protocol === 'blob:';
+
+	} catch ( e ) {
+
+		// If the string is not a valid URL, it throws an error
+		return false;
+
+	}
+
+}
+
 /**
  * Handles and keeps track of loaded and pending data. A default global
  * instance of this class is created and used by loaders if not supplied
@@ -49912,7 +49941,18 @@ class CubeCamera extends Object3D {
 		renderTarget.texture.generateMipmaps = false;
 
 		// https://github.com/mrdoob/three.js/issues/31413#issuecomment-3095966812
-		const reversedDepthBuffer = !! ( renderer.isWebGLRenderer && renderer.state.buffers.depth.getReversed() );
+
+		let reversedDepthBuffer = false;
+
+		if ( renderer.isWebGLRenderer === true ) {
+
+			reversedDepthBuffer = renderer.state.buffers.depth.getReversed();
+
+		} else {
+
+			reversedDepthBuffer = renderer.reversedDepthBuffer;
+
+		}
 
 		renderer.setRenderTarget( renderTarget, 0, activeMipmapLevel );
 		if ( reversedDepthBuffer && renderer.autoClear === false ) renderer.clearDepth();

+ 42 - 2
build/three.core.js

@@ -2074,7 +2074,7 @@ function warnOnce( ...params ) {
  * main thread. This is useful for GPU-CPU synchronization in WebGL contexts.
  *
  * @private
- * @param {WebGLRenderingContext|WebGL2RenderingContext} gl - The WebGL rendering context.
+ * @param {WebGL2RenderingContext} gl - The WebGL rendering context.
  * @param {WebGLSync} sync - The WebGL sync object to wait for.
  * @param {number} interval - The polling interval in milliseconds.
  * @return {Promise<void>} A promise that resolves when the sync completes or rejects if it fails.
@@ -43182,6 +43182,8 @@ const Cache = {
 
 		if ( this.enabled === false ) return;
 
+		if ( isBlobURL( key ) ) return;
+
 		// log( 'Cache', 'Adding key:', key );
 
 		this.files[ key ] = file;
@@ -43199,6 +43201,8 @@ const Cache = {
 
 		if ( this.enabled === false ) return;
 
+		if ( isBlobURL( key ) ) return;
+
 		// log( 'Cache', 'Checking key:', key );
 
 		return this.files[ key ];
@@ -43230,6 +43234,31 @@ const Cache = {
 
 };
 
+/**
+ * Returns true if the given cache key contains the blob: scheme.
+ *
+ * @private
+ * @param {string} key - The cache key.
+ * @return {boolean} Whether the given cache key contains the blob: scheme or not.
+ */
+function isBlobURL( key ) {
+
+	try {
+
+		const urlString = key.slice( key.indexOf( ':' ) + 1 ); // remove type identifier
+
+		const url = new URL( urlString );
+		return url.protocol === 'blob:';
+
+	} catch ( e ) {
+
+		// If the string is not a valid URL, it throws an error
+		return false;
+
+	}
+
+}
+
 /**
  * Handles and keeps track of loaded and pending data. A default global
  * instance of this class is created and used by loaders if not supplied
@@ -49910,7 +49939,18 @@ class CubeCamera extends Object3D {
 		renderTarget.texture.generateMipmaps = false;
 
 		// https://github.com/mrdoob/three.js/issues/31413#issuecomment-3095966812
-		const reversedDepthBuffer = !! ( renderer.isWebGLRenderer && renderer.state.buffers.depth.getReversed() );
+
+		let reversedDepthBuffer = false;
+
+		if ( renderer.isWebGLRenderer === true ) {
+
+			reversedDepthBuffer = renderer.state.buffers.depth.getReversed();
+
+		} else {
+
+			reversedDepthBuffer = renderer.reversedDepthBuffer;
+
+		}
 
 		renderer.setRenderTarget( renderTarget, 0, activeMipmapLevel );
 		if ( reversedDepthBuffer && renderer.autoClear === false ) renderer.clearDepth();

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
build/three.core.min.js


Fichier diff supprimé car celui-ci est trop grand
+ 2 - 0
build/three.tsl.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
build/three.tsl.min.js


+ 60 - 60
build/three.webgpu.js

@@ -19554,7 +19554,7 @@ class ViewportDepthNode extends Node {
 
 			if ( value !== null ) {
 
- 				node = depthBase().assign( value );
+				node = depthBase().assign( value );
 
 			}
 
@@ -19618,6 +19618,18 @@ ViewportDepthNode.LINEAR_DEPTH = 'linearDepth';
  */
 const viewZToOrthographicDepth = ( viewZ, near, far ) => viewZ.add( near ).div( near.sub( far ) );
 
+/**
+ * TSL function for converting a viewZ value to a reversed orthographic depth value.
+ *
+ * @tsl
+ * @function
+ * @param {Node<float>} viewZ - The viewZ node.
+ * @param {Node<float>} near - The camera's near value.
+ * @param {Node<float>} far - The camera's far value.
+ * @returns {Node<float>}
+ */
+const viewZToReversedOrthographicDepth = ( viewZ, near, far ) => viewZ.add( far ).div( far.sub( near ) );
+
 /**
  * TSL function for converting an orthographic depth value to a viewZ value.
  *
@@ -19628,7 +19640,19 @@ const viewZToOrthographicDepth = ( viewZ, near, far ) => viewZ.add( near ).div(
  * @param {Node<float>} far - The camera's far value.
  * @returns {Node<float>}
  */
-const orthographicDepthToViewZ = ( depth, near, far ) => near.sub( far ).mul( depth ).sub( near );
+const orthographicDepthToViewZ = /*@__PURE__*/ Fn( ( [ depth, near, far ], builder ) => {
+
+	if ( builder.renderer.reversedDepthBuffer === true ) {
+
+		return far.sub( near ).mul( depth ).sub( far );
+
+	} else {
+
+		return near.sub( far ).mul( depth ).sub( near );
+
+	}
+
+} );
 
 /**
  * TSL function for converting a viewZ value to a perspective depth value.
@@ -19666,7 +19690,19 @@ const viewZToReversedPerspectiveDepth = ( viewZ, near, far ) => near.mul( viewZ.
  * @param {Node<float>} far - The camera's far value.
  * @returns {Node<float>}
  */
-const perspectiveDepthToViewZ = ( depth, near, far ) => near.mul( far ).div( far.sub( near ).mul( depth ).sub( far ) );
+const perspectiveDepthToViewZ = /*@__PURE__*/ Fn( ( [ depth, near, far ], builder ) => {
+
+	if ( builder.renderer.reversedDepthBuffer === true ) {
+
+		return near.mul( far ).div( near.sub( far ).mul( depth ).sub( near ) );
+
+	} else {
+
+		return near.mul( far ).div( far.sub( near ).mul( depth ).sub( far ) );
+
+	}
+
+} );
 
 /**
  * TSL function for converting a viewZ value to a logarithmic depth value.
@@ -47173,6 +47209,7 @@ var TSL = /*#__PURE__*/Object.freeze({
 	viewZToLogarithmicDepth: viewZToLogarithmicDepth,
 	viewZToOrthographicDepth: viewZToOrthographicDepth,
 	viewZToPerspectiveDepth: viewZToPerspectiveDepth,
+	viewZToReversedOrthographicDepth: viewZToReversedOrthographicDepth,
 	viewZToReversedPerspectiveDepth: viewZToReversedPerspectiveDepth,
 	viewport: viewport,
 	viewportCoordinate: viewportCoordinate,
@@ -49367,14 +49404,6 @@ class NodeBuilder {
 
 				bindingGroupsCache.set( cacheKey, bindGroup );
 
-			} else {
-
-				for ( let i = 0; i < bindings.length; i ++ ) {
-
-					bindGroup.bindings[ i ].visibility |= bindings[ i ].visibility;
-
-				}
-
 			}
 
 		} else {
@@ -74455,14 +74484,12 @@ class WGSLNodeBuilder extends NodeBuilder {
 				if ( uniformsGroup === undefined ) {
 
 					uniformsGroup = new NodeUniformsGroup( groupName, group );
+					uniformsGroup.setVisibility( GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE );
 
 					this.uniformGroups[ groupName ] = uniformsGroup;
 
 				}
 
-				// Update visibility to include this shader stage (bitwise OR)
-				uniformsGroup.setVisibility( uniformsGroup.getVisibility() | gpuShaderStageLib[ shaderStage ] );
-
 				// Add to bindings for this stage if not already present
 				if ( bindings.indexOf( uniformsGroup ) === -1 ) {
 
@@ -76554,63 +76581,36 @@ class WebGPUBindingUtils {
 
 		const bindingsData = backend.get( bindGroup );
 
-		const entries = this._createLayoutEntries( bindGroup );
-		const bindGroupLayoutHash = hashString( JSON.stringify( entries ) );
-
-		let layoutChanged = false;
-
-		// check if the bind group already has a layout and if it's still valid
+		// check if the the bind group already has a layout
 
 		if ( bindingsData.layout ) {
 
-			// if the layout hash changed (e.g. visibility was updated), invalidate the old layout
-
-			if ( bindingsData.layoutHash !== bindGroupLayoutHash ) {
-
-				bindingsData.layout.usedTimes --;
-
-				if ( bindingsData.layout.usedTimes === 0 ) {
-
-					this._bindGroupLayoutCache.delete( bindingsData.layoutHash );
+			return bindingsData.layout.layoutGPU;
 
-				}
-
-				bindingsData.layout = undefined;
-				bindingsData.layoutHash = undefined;
-
-				layoutChanged = true;
-
-			} else {
+		}
 
-				return bindingsData.layout.layoutGPU;
+		// if not, assing one
 
-			}
+		const entries = this._createLayoutEntries( bindGroup );
+		const bindGroupLayoutKey = hashString( JSON.stringify( entries ) );
 
-		}
+		// try to find an existing layout in the cache
 
-		// create or reuse a bind group layout from the cache
+		let bindGroupLayout = this._bindGroupLayoutCache.get( bindGroupLayoutKey );
 
-		let bindGroupLayout = this._bindGroupLayoutCache.get( bindGroupLayoutHash );
+		// if not create a new one
 
 		if ( bindGroupLayout === undefined ) {
 
 			bindGroupLayout = new BindGroupLayout( device.createBindGroupLayout( { entries } ) );
-			this._bindGroupLayoutCache.set( bindGroupLayoutHash, bindGroupLayout );
+			this._bindGroupLayoutCache.set( bindGroupLayoutKey, bindGroupLayout );
 
 		}
 
 		bindGroupLayout.usedTimes ++;
 
 		bindingsData.layout = bindGroupLayout;
-		bindingsData.layoutHash = bindGroupLayoutHash;
-
-		// if layout changed, recreate the GPU bind group with the new layout
-
-		if ( layoutChanged ) {
-
-			bindingsData.group = this.createBindGroup( bindGroup, bindGroupLayout.layoutGPU );
-
-		}
+		bindingsData.layoutKey = bindGroupLayoutKey;
 
 		return bindGroupLayout.layoutGPU;
 
@@ -77109,12 +77109,12 @@ class WebGPUBindingUtils {
 
 			if ( bindingsData.layout.usedTimes === 0 ) {
 
-				this._bindGroupLayoutCache.delete( bindingsData.layoutHash );
+				this._bindGroupLayoutCache.delete( bindingsData.layoutKey );
 
 			}
 
 			bindingsData.layout = undefined;
-			bindingsData.layoutHash = undefined;
+			bindingsData.layoutKey = undefined;
 
 		}
 
@@ -77219,8 +77219,8 @@ class WebGPUPipelineUtils {
 
 		for ( const bindGroup of renderObject.getBindings() ) {
 
-			// ensure layout is up to date (visibility may have changed due to shared bind groups)
-			const layoutGPU = backend.bindingUtils.createBindingsLayout( bindGroup );
+			const bindingsData = backend.get( bindGroup );
+			const { layoutGPU } = bindingsData.layout;
 
 			bindGroupLayouts.push( layoutGPU );
 
@@ -77364,7 +77364,7 @@ class WebGPUPipelineUtils {
 			if ( renderStencil === true ) {
 
 				depthStencil.stencilFront = stencilFront;
-				depthStencil.stencilBack = {}; // three.js does not provide an API to configure the back function (gl.stencilFuncSeparate() was never used)
+				depthStencil.stencilBack = stencilFront; // apply the same stencil ops to both faces, matching gl.stencilOp() which is not face-separated
 				depthStencil.stencilReadMask = material.stencilFuncMask;
 				depthStencil.stencilWriteMask = material.stencilWriteMask;
 
@@ -77480,8 +77480,8 @@ class WebGPUPipelineUtils {
 
 		for ( const bindingsGroup of bindings ) {
 
-			// ensure layout is up to date (visibility may have changed due to shared bind groups)
-			const layoutGPU = backend.bindingUtils.createBindingsLayout( bindingsGroup );
+			const bindingsData = backend.get( bindingsGroup );
+			const { layoutGPU } = bindingsData.layout;
 
 			bindGroupLayouts.push( layoutGPU );
 
@@ -77904,7 +77904,7 @@ class WebGPUPipelineUtils {
 
 		if ( material.depthTest === false ) {
 
-			depthCompare = ( this.backend.parameters.reversedDepthBuffer ) ? GPUCompareFunction.Never : GPUCompareFunction.Always;
+			depthCompare = GPUCompareFunction.Always;
 
 		} else {
 

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
build/three.webgpu.min.js


+ 60 - 60
build/three.webgpu.nodes.js

@@ -19554,7 +19554,7 @@ class ViewportDepthNode extends Node {
 
 			if ( value !== null ) {
 
- 				node = depthBase().assign( value );
+				node = depthBase().assign( value );
 
 			}
 
@@ -19618,6 +19618,18 @@ ViewportDepthNode.LINEAR_DEPTH = 'linearDepth';
  */
 const viewZToOrthographicDepth = ( viewZ, near, far ) => viewZ.add( near ).div( near.sub( far ) );
 
+/**
+ * TSL function for converting a viewZ value to a reversed orthographic depth value.
+ *
+ * @tsl
+ * @function
+ * @param {Node<float>} viewZ - The viewZ node.
+ * @param {Node<float>} near - The camera's near value.
+ * @param {Node<float>} far - The camera's far value.
+ * @returns {Node<float>}
+ */
+const viewZToReversedOrthographicDepth = ( viewZ, near, far ) => viewZ.add( far ).div( far.sub( near ) );
+
 /**
  * TSL function for converting an orthographic depth value to a viewZ value.
  *
@@ -19628,7 +19640,19 @@ const viewZToOrthographicDepth = ( viewZ, near, far ) => viewZ.add( near ).div(
  * @param {Node<float>} far - The camera's far value.
  * @returns {Node<float>}
  */
-const orthographicDepthToViewZ = ( depth, near, far ) => near.sub( far ).mul( depth ).sub( near );
+const orthographicDepthToViewZ = /*@__PURE__*/ Fn( ( [ depth, near, far ], builder ) => {
+
+	if ( builder.renderer.reversedDepthBuffer === true ) {
+
+		return far.sub( near ).mul( depth ).sub( far );
+
+	} else {
+
+		return near.sub( far ).mul( depth ).sub( near );
+
+	}
+
+} );
 
 /**
  * TSL function for converting a viewZ value to a perspective depth value.
@@ -19666,7 +19690,19 @@ const viewZToReversedPerspectiveDepth = ( viewZ, near, far ) => near.mul( viewZ.
  * @param {Node<float>} far - The camera's far value.
  * @returns {Node<float>}
  */
-const perspectiveDepthToViewZ = ( depth, near, far ) => near.mul( far ).div( far.sub( near ).mul( depth ).sub( far ) );
+const perspectiveDepthToViewZ = /*@__PURE__*/ Fn( ( [ depth, near, far ], builder ) => {
+
+	if ( builder.renderer.reversedDepthBuffer === true ) {
+
+		return near.mul( far ).div( near.sub( far ).mul( depth ).sub( near ) );
+
+	} else {
+
+		return near.mul( far ).div( far.sub( near ).mul( depth ).sub( far ) );
+
+	}
+
+} );
 
 /**
  * TSL function for converting a viewZ value to a logarithmic depth value.
@@ -47173,6 +47209,7 @@ var TSL = /*#__PURE__*/Object.freeze({
 	viewZToLogarithmicDepth: viewZToLogarithmicDepth,
 	viewZToOrthographicDepth: viewZToOrthographicDepth,
 	viewZToPerspectiveDepth: viewZToPerspectiveDepth,
+	viewZToReversedOrthographicDepth: viewZToReversedOrthographicDepth,
 	viewZToReversedPerspectiveDepth: viewZToReversedPerspectiveDepth,
 	viewport: viewport,
 	viewportCoordinate: viewportCoordinate,
@@ -49367,14 +49404,6 @@ class NodeBuilder {
 
 				bindingGroupsCache.set( cacheKey, bindGroup );
 
-			} else {
-
-				for ( let i = 0; i < bindings.length; i ++ ) {
-
-					bindGroup.bindings[ i ].visibility |= bindings[ i ].visibility;
-
-				}
-
 			}
 
 		} else {
@@ -74455,14 +74484,12 @@ class WGSLNodeBuilder extends NodeBuilder {
 				if ( uniformsGroup === undefined ) {
 
 					uniformsGroup = new NodeUniformsGroup( groupName, group );
+					uniformsGroup.setVisibility( GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE );
 
 					this.uniformGroups[ groupName ] = uniformsGroup;
 
 				}
 
-				// Update visibility to include this shader stage (bitwise OR)
-				uniformsGroup.setVisibility( uniformsGroup.getVisibility() | gpuShaderStageLib[ shaderStage ] );
-
 				// Add to bindings for this stage if not already present
 				if ( bindings.indexOf( uniformsGroup ) === -1 ) {
 
@@ -76554,63 +76581,36 @@ class WebGPUBindingUtils {
 
 		const bindingsData = backend.get( bindGroup );
 
-		const entries = this._createLayoutEntries( bindGroup );
-		const bindGroupLayoutHash = hashString( JSON.stringify( entries ) );
-
-		let layoutChanged = false;
-
-		// check if the bind group already has a layout and if it's still valid
+		// check if the the bind group already has a layout
 
 		if ( bindingsData.layout ) {
 
-			// if the layout hash changed (e.g. visibility was updated), invalidate the old layout
-
-			if ( bindingsData.layoutHash !== bindGroupLayoutHash ) {
-
-				bindingsData.layout.usedTimes --;
-
-				if ( bindingsData.layout.usedTimes === 0 ) {
-
-					this._bindGroupLayoutCache.delete( bindingsData.layoutHash );
+			return bindingsData.layout.layoutGPU;
 
-				}
-
-				bindingsData.layout = undefined;
-				bindingsData.layoutHash = undefined;
-
-				layoutChanged = true;
-
-			} else {
+		}
 
-				return bindingsData.layout.layoutGPU;
+		// if not, assing one
 
-			}
+		const entries = this._createLayoutEntries( bindGroup );
+		const bindGroupLayoutKey = hashString( JSON.stringify( entries ) );
 
-		}
+		// try to find an existing layout in the cache
 
-		// create or reuse a bind group layout from the cache
+		let bindGroupLayout = this._bindGroupLayoutCache.get( bindGroupLayoutKey );
 
-		let bindGroupLayout = this._bindGroupLayoutCache.get( bindGroupLayoutHash );
+		// if not create a new one
 
 		if ( bindGroupLayout === undefined ) {
 
 			bindGroupLayout = new BindGroupLayout( device.createBindGroupLayout( { entries } ) );
-			this._bindGroupLayoutCache.set( bindGroupLayoutHash, bindGroupLayout );
+			this._bindGroupLayoutCache.set( bindGroupLayoutKey, bindGroupLayout );
 
 		}
 
 		bindGroupLayout.usedTimes ++;
 
 		bindingsData.layout = bindGroupLayout;
-		bindingsData.layoutHash = bindGroupLayoutHash;
-
-		// if layout changed, recreate the GPU bind group with the new layout
-
-		if ( layoutChanged ) {
-
-			bindingsData.group = this.createBindGroup( bindGroup, bindGroupLayout.layoutGPU );
-
-		}
+		bindingsData.layoutKey = bindGroupLayoutKey;
 
 		return bindGroupLayout.layoutGPU;
 
@@ -77109,12 +77109,12 @@ class WebGPUBindingUtils {
 
 			if ( bindingsData.layout.usedTimes === 0 ) {
 
-				this._bindGroupLayoutCache.delete( bindingsData.layoutHash );
+				this._bindGroupLayoutCache.delete( bindingsData.layoutKey );
 
 			}
 
 			bindingsData.layout = undefined;
-			bindingsData.layoutHash = undefined;
+			bindingsData.layoutKey = undefined;
 
 		}
 
@@ -77219,8 +77219,8 @@ class WebGPUPipelineUtils {
 
 		for ( const bindGroup of renderObject.getBindings() ) {
 
-			// ensure layout is up to date (visibility may have changed due to shared bind groups)
-			const layoutGPU = backend.bindingUtils.createBindingsLayout( bindGroup );
+			const bindingsData = backend.get( bindGroup );
+			const { layoutGPU } = bindingsData.layout;
 
 			bindGroupLayouts.push( layoutGPU );
 
@@ -77364,7 +77364,7 @@ class WebGPUPipelineUtils {
 			if ( renderStencil === true ) {
 
 				depthStencil.stencilFront = stencilFront;
-				depthStencil.stencilBack = {}; // three.js does not provide an API to configure the back function (gl.stencilFuncSeparate() was never used)
+				depthStencil.stencilBack = stencilFront; // apply the same stencil ops to both faces, matching gl.stencilOp() which is not face-separated
 				depthStencil.stencilReadMask = material.stencilFuncMask;
 				depthStencil.stencilWriteMask = material.stencilWriteMask;
 
@@ -77480,8 +77480,8 @@ class WebGPUPipelineUtils {
 
 		for ( const bindingsGroup of bindings ) {
 
-			// ensure layout is up to date (visibility may have changed due to shared bind groups)
-			const layoutGPU = backend.bindingUtils.createBindingsLayout( bindingsGroup );
+			const bindingsData = backend.get( bindingsGroup );
+			const { layoutGPU } = bindingsData.layout;
 
 			bindGroupLayouts.push( layoutGPU );
 
@@ -77904,7 +77904,7 @@ class WebGPUPipelineUtils {
 
 		if ( material.depthTest === false ) {
 
-			depthCompare = ( this.backend.parameters.reversedDepthBuffer ) ? GPUCompareFunction.Never : GPUCompareFunction.Always;
+			depthCompare = GPUCompareFunction.Always;
 
 		} else {
 

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
build/three.webgpu.nodes.min.js


Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff

粤ICP备19079148号