Jelajahi Sumber

Merge pull request #107948 from adamscott/reintroduce-sample-position-worklet-pooling

[Web] Fix Webkit leak caused by the position reporting audio worklets
Rémi Verschelde 2 bulan lalu
induk
melakukan
30456ba095

+ 7 - 16
platform/web/js/libs/audio.position.worklet.js

@@ -28,40 +28,31 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 /**************************************************************************/
 
-const POST_THRESHOLD_S = 0.1;
-
 class GodotPositionReportingProcessor extends AudioWorkletProcessor {
 	constructor(...args) {
 		super(...args);
-		this.lastPostTime = currentTime;
 		this.position = 0;
-		this.ended = false;
 
 		this.port.onmessage = (event) => {
-			if (event?.data?.type === 'ended') {
-				this.ended = true;
+			switch (event?.data?.type) {
+			case 'reset':
+				this.position = 0;
+				break;
+			default:
+				// Do nothing.
 			}
 		};
 	}
 
 	process(inputs, _outputs, _parameters) {
-		if (this.ended) {
-			return false;
-		}
-
 		if (inputs.length > 0) {
 			const input = inputs[0];
 			if (input.length > 0) {
 				this.position += input[0].length;
+				this.port.postMessage({ type: 'position', data: this.position });
 			}
 		}
 
-		// Posting messages is expensive. Let's limit the number of posts.
-		if (currentTime - this.lastPostTime > POST_THRESHOLD_S) {
-			this.lastPostTime = currentTime;
-			this.port.postMessage({ type: 'position', data: this.position });
-		}
-
 		return true;
 	}
 }

+ 13 - 5
platform/web/js/libs/library_godot_audio.js

@@ -635,10 +635,14 @@ class SampleNode {
 		if (this._positionWorklet != null) {
 			return this._positionWorklet;
 		}
-		this._positionWorklet = new AudioWorkletNode(
-			GodotAudio.ctx,
-			'godot-position-reporting-processor'
-		);
+		if (GodotAudio.audioPositionWorkletNodes.length > 0) {
+			this._positionWorklet = GodotAudio.audioPositionWorkletNodes.pop();
+		} else {
+			this._positionWorklet = new AudioWorkletNode(
+				GodotAudio.ctx,
+				'godot-position-reporting-processor'
+			);
+		}
 		this._positionWorklet.port.onmessage = (event) => {
 			switch (event.data['type']) {
 			case 'position':
@@ -648,6 +652,7 @@ class SampleNode {
 				// Do nothing.
 			}
 		};
+		this._positionWorklet.port.postMessage('reset');
 		return this._positionWorklet;
 	}
 
@@ -678,7 +683,7 @@ class SampleNode {
 		if (this._positionWorklet) {
 			this._positionWorklet.disconnect();
 			this._positionWorklet.port.onmessage = null;
-			this._positionWorklet.port.postMessage({ type: 'ended' });
+			GodotAudio.audioPositionWorkletNodes.push(this._positionWorklet);
 			this._positionWorklet = null;
 		}
 
@@ -1198,6 +1203,8 @@ const _GodotAudio = {
 
 		/** @type {Promise} */
 		audioPositionWorkletPromise: null,
+		/** @type {Array<AudioWorkletNode>} */
+		audioPositionWorkletNodes: null,
 
 		/**
 		 * Converts linear volume to Db.
@@ -1224,6 +1231,7 @@ const _GodotAudio = {
 			GodotAudio.sampleNodes = new Map();
 			GodotAudio.buses = [];
 			GodotAudio.busSolo = null;
+			GodotAudio.audioPositionWorkletNodes = [];
 
 			const opts = {};
 			// If mix_rate is 0, let the browser choose.