Browse Source

chore: cherry-pick 3 changes from 0-M134 (#46010)

Co-authored-by: Charles Kerr <[email protected]>
Pedro Pontes 1 month ago
parent
commit
4ef9f87d29

+ 3 - 0
patches/chromium/.patches

@@ -146,3 +146,6 @@ reland_lzma_sdk_update_to_24_09.patch
 cherry-pick-521faebc8a7c.patch
 cherry-pick-9dacf5694dfd.patch
 cherry-pick-0adceb6159fb.patch
+add_a_flag_to_enable_strict_js_compliance_in_audioworklet.patch
+remove_denormalenabler_from_scriptprocessornode.patch
+allow_denormal_flushing_to_outlive_scoped_object.patch

+ 333 - 0
patches/chromium/add_a_flag_to_enable_strict_js_compliance_in_audioworklet.patch

@@ -0,0 +1,333 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Wilson <[email protected]>
+Date: Thu, 12 Dec 2024 08:45:53 -0800
+Subject: Add a flag to enable strict JS compliance in AudioWorklet
+
+AudioWorklet and ScriptProcessorNode are not strictly JavaScript spec
+compliant because we disable denormal numbers for performance reasons.
+
+This CL adds a flag to allow experimenting with enabling denormal
+numbers in AudioWorklet and ScriptProcessorNode, so that we can
+quantify the actual performance impact.
+
+The flag can also be used as a server-side switch.
+
+Bug: 382005099
+Change-Id: Ib41253cc42dd2f16c262036817cf3db4697f986f
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6077677
+Reviewed-by: Kentaro Hara <[email protected]>
+Commit-Queue: Michael Wilson <[email protected]>
+Reviewed-by: Hongchan Choi <[email protected]>
+Reviewed-by: Leszek Swirski <[email protected]>
+Cr-Commit-Position: refs/heads/main@{#1395444}
+
+diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
+index 6024875954b389d43e53e03ebafa4a5016d56416..98740bfbe45bcffdd30c5afc0ebafde6cd61a13d 100644
+--- a/third_party/blink/common/features.cc
++++ b/third_party/blink/common/features.cc
+@@ -2545,6 +2545,12 @@ BASE_FEATURE(kWebAppManifestLockScreen,
+              "WebAppManifestLockScreen",
+              base::FEATURE_DISABLED_BY_DEFAULT);
+ 
++// Allow denormals in AudioWorklet and ScriptProcessorNode, to enable strict
++// JavaScript denormal compliance.  See https://crbug.com/382005099.
++BASE_FEATURE(kWebAudioAllowDenormalInProcessing,
++             "WebAudioAllowDenormalInProcessing",
++             base::FEATURE_DISABLED_BY_DEFAULT);
++
+ // Enabling this flag bypasses additional buffering when using the Web Audio
+ // API, which may reduce audio output latency but may also increase the
+ // probability of an audio glitch.
+diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
+index d3f22b63862cacad8079f11725173d9b7c599610..bf2dcbc3c28d6d0317bc1668f16c25a6233d836f 100644
+--- a/third_party/blink/public/common/features.h
++++ b/third_party/blink/public/common/features.h
+@@ -1719,6 +1719,7 @@ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebAppEnableScopeExtensions);
+ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebAppEnableUrlHandlers);
+ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebAppManifestLockScreen);
+ 
++BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebAudioAllowDenormalInProcessing);
+ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebAudioBypassOutputBuffering);
+ BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(
+     kWebAudioContextConstructorEchoCancellation);
+diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_handler.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_handler.cc
+index 0382f578a4f98cbac422d5f927c73a6b922c01b8..9a662e7730d3e01dcf8e69f66c4eafa9dd7dd031 100644
+--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_handler.cc
++++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_handler.cc
+@@ -29,6 +29,7 @@
+ #include "third_party/blink/renderer/modules/webaudio/cross_thread_audio_worklet_processor_info.h"
+ #include "third_party/blink/renderer/platform/audio/audio_bus.h"
+ #include "third_party/blink/renderer/platform/audio/audio_utilities.h"
++#include "third_party/blink/renderer/platform/audio/denormal_disabler.h"
+ #include "third_party/blink/renderer/platform/bindings/exception_messages.h"
+ #include "third_party/blink/renderer/platform/heap/persistent.h"
+ #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+@@ -52,7 +53,9 @@ AudioWorkletHandler::AudioWorkletHandler(
+     const AudioWorkletNodeOptions* options)
+     : AudioHandler(kNodeTypeAudioWorklet, node, sample_rate),
+       name_(name),
+-      param_handler_map_(param_handler_map) {
++      param_handler_map_(param_handler_map),
++      allow_denormal_in_processing_(base::FeatureList::IsEnabled(
++          features::kWebAudioAllowDenormalInProcessing)) {
+   DCHECK(IsMainThread());
+ 
+   for (const auto& param_name : param_handler_map_.Keys()) {
+@@ -112,7 +115,7 @@ scoped_refptr<AudioWorkletHandler> AudioWorkletHandler::Create(
+                                                 param_handler_map, options));
+ }
+ 
+-void AudioWorkletHandler::Process(uint32_t frames_to_process) {
++void AudioWorkletHandler::ProcessInternal(uint32_t frames_to_process) {
+   DCHECK(Context()->IsAudioThread());
+ 
+   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("webaudio.audionode"),
+@@ -175,6 +178,15 @@ void AudioWorkletHandler::Process(uint32_t frames_to_process) {
+   }
+ }
+ 
++void AudioWorkletHandler::Process(uint32_t frames_to_process) {
++  if (allow_denormal_in_processing_) {
++    DenormalEnabler denormal_enabler;
++    ProcessInternal(frames_to_process);
++  } else {
++    ProcessInternal(frames_to_process);
++  }
++}
++
+ void AudioWorkletHandler::CheckNumberOfChannelsForInput(AudioNodeInput* input) {
+   DCHECK(Context()->IsAudioThread());
+   Context()->AssertGraphOwner();
+diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_handler.h b/third_party/blink/renderer/modules/webaudio/audio_worklet_handler.h
+index e6291f5e9e25433281646965f048a7f2abfc8c01..3ec80cd49a87a76ac03df105b37f1ae17437a328 100644
+--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_handler.h
++++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_handler.h
+@@ -68,6 +68,10 @@ class AudioWorkletHandler final : public AudioHandler {
+       HashMap<String, scoped_refptr<AudioParamHandler>> param_handler_map,
+       const AudioWorkletNodeOptions*);
+ 
++  // Used to avoid code duplication when using scoped objects that affect
++  // `Process`.
++  void ProcessInternal(uint32_t frames_to_process);
++
+   String name_;
+ 
+   double tail_time_ = std::numeric_limits<double>::infinity();
+@@ -102,6 +106,9 @@ class AudioWorkletHandler final : public AudioHandler {
+   // when a processor stops invoking the user-defined `process()` callback.
+   bool is_processor_active_ = true;
+ 
++  // Cached feature flag value
++  const bool allow_denormal_in_processing_;
++
+   base::WeakPtrFactory<AudioWorkletHandler> weak_ptr_factory_{this};
+ };
+ 
+diff --git a/third_party/blink/renderer/modules/webaudio/script_processor_handler.cc b/third_party/blink/renderer/modules/webaudio/script_processor_handler.cc
+index fd6ead021f9f656331d838fb6733cb0fb5220b12..8f9641bebb1a97d2963f8858e58a61cf2434770b 100644
+--- a/third_party/blink/renderer/modules/webaudio/script_processor_handler.cc
++++ b/third_party/blink/renderer/modules/webaudio/script_processor_handler.cc
+@@ -26,6 +26,7 @@
+ #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
+ #include "third_party/blink/renderer/modules/webaudio/realtime_audio_destination_node.h"
+ #include "third_party/blink/renderer/modules/webaudio/script_processor_node.h"
++#include "third_party/blink/renderer/platform/audio/denormal_disabler.h"
+ #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+ #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+ #include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h"
+@@ -48,7 +49,9 @@ ScriptProcessorHandler::ScriptProcessorHandler(
+       internal_input_bus_(AudioBus::Create(
+           number_of_input_channels,
+           node.context()->GetDeferredTaskHandler().RenderQuantumFrames(),
+-          false)) {
++          false)),
++      allow_denormal_in_processing_(base::FeatureList::IsEnabled(
++          features::kWebAudioAllowDenormalInProcessing)) {
+   DCHECK_GE(buffer_size_,
+             node.context()->GetDeferredTaskHandler().RenderQuantumFrames());
+   DCHECK_LE(number_of_input_channels, BaseAudioContext::MaxNumberOfChannels());
+@@ -109,7 +112,7 @@ void ScriptProcessorHandler::Initialize() {
+   AudioHandler::Initialize();
+ }
+ 
+-void ScriptProcessorHandler::Process(uint32_t frames_to_process) {
++void ScriptProcessorHandler::ProcessInternal(uint32_t frames_to_process) {
+   TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("webaudio.audionode"),
+                      "ScriptProcessorHandler::Process");
+ 
+@@ -238,6 +241,15 @@ void ScriptProcessorHandler::Process(uint32_t frames_to_process) {
+                    "ScriptProcessorHandler::Process");
+ }
+ 
++void ScriptProcessorHandler::Process(uint32_t frames_to_process) {
++  if (allow_denormal_in_processing_) {
++    DenormalEnabler denormal_enabler;
++    ProcessInternal(frames_to_process);
++  } else {
++    ProcessInternal(frames_to_process);
++  }
++}
++
+ void ScriptProcessorHandler::FireProcessEvent(uint32_t double_buffer_index) {
+   DCHECK(IsMainThread());
+ 
+diff --git a/third_party/blink/renderer/modules/webaudio/script_processor_handler.h b/third_party/blink/renderer/modules/webaudio/script_processor_handler.h
+index 006881fbef2fc74bde5bf8aadc9716367451b122..a960426678a5da72071f6defa046a4517fcb1cf7 100644
+--- a/third_party/blink/renderer/modules/webaudio/script_processor_handler.h
++++ b/third_party/blink/renderer/modules/webaudio/script_processor_handler.h
+@@ -65,6 +65,11 @@ class ScriptProcessorHandler final : public AudioHandler {
+                          uint32_t number_of_output_channels,
+                          const HeapVector<Member<AudioBuffer>>& input_buffers,
+                          const HeapVector<Member<AudioBuffer>>& output_buffers);
++
++  // Used to avoid code duplication when using scoped objects that affect
++  // `Process`.
++  void ProcessInternal(uint32_t frames_to_process);
++
+   double TailTime() const override;
+   double LatencyTime() const override;
+   bool RequiresTailProcessing() const final;
+@@ -92,6 +97,9 @@ class ScriptProcessorHandler final : public AudioHandler {
+ 
+   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ 
++  // Cached feature flag value
++  const bool allow_denormal_in_processing_;
++
+   base::WeakPtrFactory<ScriptProcessorHandler> weak_ptr_factory_{this};
+ 
+   FRIEND_TEST_ALL_PREFIXES(ScriptProcessorNodeTest, BufferLifetime);
+diff --git a/third_party/blink/renderer/platform/audio/denormal_disabler.h b/third_party/blink/renderer/platform/audio/denormal_disabler.h
+index e8fadf60eea81b017dc29b39c2d1cfe8c102999b..ac1cdfa026aa1f845a892e96200fd9de46a45c92 100644
+--- a/third_party/blink/renderer/platform/audio/denormal_disabler.h
++++ b/third_party/blink/renderer/platform/audio/denormal_disabler.h
+@@ -52,28 +52,28 @@ namespace blink {
+ #endif
+ 
+ #if defined(HAVE_DENORMAL)
+-class DenormalDisabler {
+-  DISALLOW_NEW();
+-
++class DenormalModifier {
+  public:
+-  DenormalDisabler() { DisableDenormals(); }
+-
+-  ~DenormalDisabler() { RestoreState(); }
+-
+-  // This is a nop if we can flush denormals to zero in hardware.
+-  static inline float FlushDenormalFloatToZero(float f) { return f; }
++  virtual ~DenormalModifier() = default;
+ 
+  private:
+   unsigned saved_csr_ = 0;
+ 
+ #if defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
++ protected:
+   inline void DisableDenormals() {
+     saved_csr_ = GetCSR();
+     SetCSR(saved_csr_ | 0x8040);
+   }
+ 
++  inline void EnableDenormals() {
++    saved_csr_ = GetCSR();
++    SetCSR(saved_csr_ & (~0x8040));
++  }
++
+   inline void RestoreState() { SetCSR(saved_csr_); }
+ 
++ private:
+   inline int GetCSR() {
+     int result;
+     asm volatile("stmxcsr %0" : "=m"(result));
+@@ -86,6 +86,7 @@ class DenormalDisabler {
+   }
+ 
+ #elif BUILDFLAG(IS_WIN) && defined(COMPILER_MSVC)
++ protected:
+   inline void DisableDenormals() {
+     // Save the current state, and set mode to flush denormals.
+     //
+@@ -95,11 +96,18 @@ class DenormalDisabler {
+     _controlfp_s(&unused, _DN_FLUSH, _MCW_DN);
+   }
+ 
++  inline void EnableDenormals() {
++    _controlfp_s(&saved_csr_, 0, 0);
++    unsigned unused;
++    _controlfp_s(&unused, _DN_SAVE, _MCW_DN);
++  }
++
+   inline void RestoreState() {
+     unsigned unused;
+     _controlfp_s(&unused, saved_csr_, _MCW_DN);
+   }
+ #elif defined(ARCH_CPU_ARM_FAMILY)
++ protected:
+   inline void DisableDenormals() {
+     saved_csr_ = GetStatusWord();
+     // Bit 24 is the flush-to-zero mode control bit. Setting it to 1 flushes
+@@ -107,8 +115,14 @@ class DenormalDisabler {
+     SetStatusWord(saved_csr_ | (1 << 24));
+   }
+ 
++  inline void EnableDenormals() {
++    saved_csr_ = GetStatusWord();
++    SetStatusWord(saved_csr_ & (~(1 << 24)));
++  }
++
+   inline void RestoreState() { SetStatusWord(saved_csr_); }
+ 
++ private:
+   inline int GetStatusWord() {
+     int result;
+ #if defined(ARCH_CPU_ARM64)
+@@ -130,13 +144,33 @@ class DenormalDisabler {
+ #endif
+ };
+ 
++class DenormalDisabler final : public DenormalModifier {
++  DISALLOW_NEW();
++
++ public:
++  DenormalDisabler() { DisableDenormals(); }
++  ~DenormalDisabler() final { RestoreState(); }
++
++  // This is a nop if we can flush denormals to zero in hardware.
++  static inline float FlushDenormalFloatToZero(float f) { return f; }
++};
++
++class DenormalEnabler final : public DenormalModifier {
++  DISALLOW_NEW();
++
++ public:
++  DenormalEnabler() { EnableDenormals(); }
++  ~DenormalEnabler() final { RestoreState(); }
++};
++
+ #else
+ // FIXME: add implementations for other architectures and compilers
+ class DenormalDisabler {
+   STACK_ALLOCATED();
+ 
+  public:
+-  DenormalDisabler() {}
++  DenormalDisabler() = default;
++  ~DenormalDisabler() = default;
+ 
+   // Assume the worst case that other architectures and compilers
+   // need to flush denormals to zero manually.
+@@ -145,6 +179,14 @@ class DenormalDisabler {
+   }
+ };
+ 
++class DenormalEnabler {
++  STACK_ALLOCATED();
++
++ public:
++  DenormalEnabler() = default;
++  ~DenormalEnabler() = default;
++};
++
+ #endif
+ 
+ }  // namespace blink

+ 293 - 0
patches/chromium/allow_denormal_flushing_to_outlive_scoped_object.patch

@@ -0,0 +1,293 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Wilson <[email protected]>
+Date: Fri, 7 Feb 2025 13:33:40 -0800
+Subject: Allow denormal flushing to outlive scoped object
+
+After this refactor we can disable or enable denormals for longer than
+a scoped object.
+
+Use this new functionality in audio_worklet_global_scope.cc.
+
+(cherry picked from commit 93c4f6fb0a0f10562ef9a637449605caae9200e6)
+
+Bug: 382005099
+Change-Id: I54f4810a4ec035f639d50275e14dae03b726b876
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6226252
+Reviewed-by: Hongchan Choi <[email protected]>
+Reviewed-by: Kentaro Hara <[email protected]>
+Commit-Queue: Michael Wilson <[email protected]>
+Cr-Original-Commit-Position: refs/heads/main@{#1415886}
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6242822
+Reviewed-by: Dave Tapuska <[email protected]>
+Cr-Commit-Position: refs/branch-heads/6998@{#221}
+Cr-Branched-From: de9c6fafd8ae5c6ea0438764076ca7d04a0b165d-refs/heads/main@{#1415337}
+
+diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc
+index c9bd1e8934d7058cb4c8044aa5618033ec975cec..09de112b96b6062f702d57e6181dd39e681e99a1 100644
+--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc
++++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc
+@@ -23,6 +23,7 @@
+ #include "third_party/blink/renderer/modules/webaudio/audio_worklet_processor.h"
+ #include "third_party/blink/renderer/modules/webaudio/audio_worklet_processor_definition.h"
+ #include "third_party/blink/renderer/modules/webaudio/cross_thread_audio_worklet_processor_info.h"
++#include "third_party/blink/renderer/platform/audio/denormal_disabler.h"
+ #include "third_party/blink/renderer/platform/bindings/callback_method_retriever.h"
+ #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+ 
+@@ -34,6 +35,9 @@ AudioWorkletGlobalScope::AudioWorkletGlobalScope(
+     : WorkletGlobalScope(std::move(creation_params),
+                          thread->GetWorkerReportingProxy(),
+                          thread) {
++  // Disable denormals for performance.
++  DenormalModifier::DisableDenormals();
++
+   // Audio is prone to jank introduced by e.g. the garbage collector. Workers
+   // are generally put in a background mode (as they are non-visible). Audio is
+   // an exception here, requiring low-latency behavior similar to any visible
+diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
+index 12da20fba1cceefd7f0960dd6411aef328f70832..73caf67339ad22cc3033c2869c91402dac460609 100644
+--- a/third_party/blink/renderer/platform/BUILD.gn
++++ b/third_party/blink/renderer/platform/BUILD.gn
+@@ -2129,6 +2129,7 @@ source_set("blink_platform_unittests_sources") {
+     "animation/timing_function_test.cc",
+     "audio/audio_destination_test.cc",
+     "audio/audio_frame_stats_accumulator_test.cc",
++    "audio/denormal_disabler_test.cc",
+     "audio/push_pull_fifo_multithread_test.cc",
+     "audio/push_pull_fifo_test.cc",
+     "audio/vector_math_test.cc",
+diff --git a/third_party/blink/renderer/platform/audio/denormal_disabler.h b/third_party/blink/renderer/platform/audio/denormal_disabler.h
+index ac1cdfa026aa1f845a892e96200fd9de46a45c92..a50d7b884e8fdc65f4c1fbe6b5cab7a7801a3b62 100644
+--- a/third_party/blink/renderer/platform/audio/denormal_disabler.h
++++ b/third_party/blink/renderer/platform/audio/denormal_disabler.h
+@@ -56,74 +56,65 @@ class DenormalModifier {
+  public:
+   virtual ~DenormalModifier() = default;
+ 
+- private:
+-  unsigned saved_csr_ = 0;
+-
+ #if defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
+- protected:
+-  inline void DisableDenormals() {
+-    saved_csr_ = GetCSR();
+-    SetCSR(saved_csr_ | 0x8040);
++ public:
++  static void DisableDenormals() {
++    unsigned old_csr = GetCsr();
++    SetCsr(old_csr | 0x8040);
+   }
+ 
+-  inline void EnableDenormals() {
+-    saved_csr_ = GetCSR();
+-    SetCSR(saved_csr_ & (~0x8040));
++  static void EnableDenormals() {
++    unsigned old_csr = GetCsr();
++    SetCsr(old_csr & (~0x8040));
+   }
+ 
+-  inline void RestoreState() { SetCSR(saved_csr_); }
+-
+- private:
+-  inline int GetCSR() {
++ protected:
++  static inline unsigned GetCsr() {
+     int result;
+     asm volatile("stmxcsr %0" : "=m"(result));
+     return result;
+   }
+ 
+-  inline void SetCSR(int a) {
++  static inline void SetCsr(int a) {
+     int temp = a;
+     asm volatile("ldmxcsr %0" : : "m"(temp));
+   }
+ 
+ #elif BUILDFLAG(IS_WIN) && defined(COMPILER_MSVC)
++ public:
++  static void DisableDenormals() { SetCsr(_DN_FLUSH); }
++
++  static void EnableDenormals() { SetCsr(_DN_SAVE); }
++
+  protected:
+-  inline void DisableDenormals() {
+-    // Save the current state, and set mode to flush denormals.
+-    //
+-    // http://stackoverflow.com/questions/637175/possible-bug-in-controlfp-s-may-not-restore-control-word-correctly
+-    _controlfp_s(&saved_csr_, 0, 0);
+-    unsigned unused;
+-    _controlfp_s(&unused, _DN_FLUSH, _MCW_DN);
++  static inline unsigned GetCsr() {
++    unsigned result;
++    _controlfp_s(&result, 0, 0);
++    return result;
+   }
+ 
+-  inline void EnableDenormals() {
+-    _controlfp_s(&saved_csr_, 0, 0);
++  static inline void SetCsr(unsigned a) {
++    // http://stackoverflow.com/questions/637175/possible-bug-in-controlfp-s-may-not-restore-control-word-correctly
+     unsigned unused;
+-    _controlfp_s(&unused, _DN_SAVE, _MCW_DN);
++    _controlfp_s(&unused, a, _MCW_DN);
+   }
+ 
+-  inline void RestoreState() {
+-    unsigned unused;
+-    _controlfp_s(&unused, saved_csr_, _MCW_DN);
+-  }
+ #elif defined(ARCH_CPU_ARM_FAMILY)
+- protected:
+-  inline void DisableDenormals() {
+-    saved_csr_ = GetStatusWord();
++ public:
++  static void DisableDenormals() {
++    unsigned old_csr = GetCsr();
+     // Bit 24 is the flush-to-zero mode control bit. Setting it to 1 flushes
+     // denormals to 0.
+-    SetStatusWord(saved_csr_ | (1 << 24));
++    SetCsr(old_csr | (1 << 24));
+   }
+ 
+-  inline void EnableDenormals() {
+-    saved_csr_ = GetStatusWord();
+-    SetStatusWord(saved_csr_ & (~(1 << 24)));
++  static void EnableDenormals() {
++    unsigned old_csr = GetCsr();
++    SetCsr(old_csr & (~(1 << 24)));
+   }
+ 
+-  inline void RestoreState() { SetStatusWord(saved_csr_); }
+-
+- private:
+-  inline int GetStatusWord() {
++ protected:
++  static inline unsigned GetCsr() {
+     int result;
+ #if defined(ARCH_CPU_ARM64)
+     asm volatile("mrs %x[result], FPCR" : [result] "=r"(result));
+@@ -133,7 +124,7 @@ class DenormalModifier {
+     return result;
+   }
+ 
+-  inline void SetStatusWord(int a) {
++  static inline void SetCsr(int a) {
+ #if defined(ARCH_CPU_ARM64)
+     asm volatile("msr FPCR, %x[src]" : : [src] "r"(a));
+ #else
+@@ -148,24 +139,44 @@ class DenormalDisabler final : public DenormalModifier {
+   DISALLOW_NEW();
+ 
+  public:
+-  DenormalDisabler() { DisableDenormals(); }
+-  ~DenormalDisabler() final { RestoreState(); }
++  DenormalDisabler() {
++    // Save the current state, and set mode to flush denormals.
++    saved_csr_ = GetCsr();
++    DisableDenormals();
++  }
++  ~DenormalDisabler() final { SetCsr(saved_csr_); }
+ 
+   // This is a nop if we can flush denormals to zero in hardware.
+   static inline float FlushDenormalFloatToZero(float f) { return f; }
++
++ private:
++  unsigned saved_csr_ = 0;
+ };
+ 
+ class DenormalEnabler final : public DenormalModifier {
+   DISALLOW_NEW();
+ 
+  public:
+-  DenormalEnabler() { EnableDenormals(); }
+-  ~DenormalEnabler() final { RestoreState(); }
++  DenormalEnabler() {
++    saved_csr_ = GetCsr();
++    EnableDenormals();
++  }
++  ~DenormalEnabler() final { SetCsr(saved_csr_); }
++
++ private:
++  unsigned saved_csr_ = 0;
+ };
+ 
+ #else
+ // FIXME: add implementations for other architectures and compilers
+-class DenormalDisabler {
++class DenormalModifier final {
++ public:
++  virtual ~DenormalModifier() = default;
++  static void DisableDenormals() {}
++  static void EnableDenormals() {}
++};
++
++class DenormalDisabler final {
+   STACK_ALLOCATED();
+ 
+  public:
+@@ -179,7 +190,7 @@ class DenormalDisabler {
+   }
+ };
+ 
+-class DenormalEnabler {
++class DenormalEnabler final {
+   STACK_ALLOCATED();
+ 
+  public:
+diff --git a/third_party/blink/renderer/platform/audio/denormal_disabler_test.cc b/third_party/blink/renderer/platform/audio/denormal_disabler_test.cc
+new file mode 100644
+index 0000000000000000000000000000000000000000..5083bbf2da9d4e0e12f1a4608d5e14e4ca910297
+--- /dev/null
++++ b/third_party/blink/renderer/platform/audio/denormal_disabler_test.cc
+@@ -0,0 +1,51 @@
++// Copyright 2025 The Chromium Authors
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "third_party/blink/renderer/platform/audio/denormal_disabler.h"
++
++#include "testing/gtest/include/gtest/gtest.h"
++
++namespace blink {
++
++namespace {
++
++bool DenormalsAreFlushedToZero() {
++  volatile double denorm = 2.225e-308;
++  return !((denorm / 2.0) > 0.0);
++}
++
++TEST(DenormalDisablerTest, DisableScoped) {
++  const bool already_flushed = DenormalsAreFlushedToZero();
++  if (!already_flushed) {
++    DenormalDisabler scoped_disabler;
++    EXPECT_TRUE(DenormalsAreFlushedToZero());
++  }
++}
++
++TEST(DenormalDisablerTest, EnableScoped) {
++  const bool already_flushed = DenormalsAreFlushedToZero();
++  if (!already_flushed) {
++    DenormalDisabler scoped_disabler;
++    EXPECT_TRUE(DenormalsAreFlushedToZero());
++    {
++      DenormalEnabler scoped_enabler;
++      EXPECT_FALSE(DenormalsAreFlushedToZero());
++    }
++    EXPECT_TRUE(DenormalsAreFlushedToZero());
++  }
++}
++
++TEST(DenormalDisablerTest, ModifyUnscoped) {
++  const bool already_flushed = DenormalsAreFlushedToZero();
++  if (!already_flushed) {
++    DenormalModifier::DisableDenormals();
++    EXPECT_TRUE(DenormalsAreFlushedToZero());
++    DenormalModifier::EnableDenormals();
++    EXPECT_FALSE(DenormalsAreFlushedToZero());
++  }
++}
++
++}  // namespace
++
++}  // namespace blink

+ 90 - 0
patches/chromium/remove_denormalenabler_from_scriptprocessornode.patch

@@ -0,0 +1,90 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Michael Wilson <[email protected]>
+Date: Thu, 30 Jan 2025 14:09:57 -0800
+Subject: Remove DenormalEnabler from ScriptProcessorNode
+
+This is a follow-up to https://crrev.com/c/6077677
+
+After experimenting, ScriptProcessorNode JavaScript is already running
+in a complaint mode so the DenormalEnabler is not necessary.
+
+Bug: 382005099
+Change-Id: If9774e60640446c567270a8f065500beecc8a40b
+Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6219685
+Commit-Queue: Michael Wilson <[email protected]>
+Reviewed-by: Alvin Ji <[email protected]>
+Cr-Commit-Position: refs/heads/main@{#1413754}
+
+diff --git a/third_party/blink/renderer/modules/webaudio/script_processor_handler.cc b/third_party/blink/renderer/modules/webaudio/script_processor_handler.cc
+index 8f9641bebb1a97d2963f8858e58a61cf2434770b..fd6ead021f9f656331d838fb6733cb0fb5220b12 100644
+--- a/third_party/blink/renderer/modules/webaudio/script_processor_handler.cc
++++ b/third_party/blink/renderer/modules/webaudio/script_processor_handler.cc
+@@ -26,7 +26,6 @@
+ #include "third_party/blink/renderer/modules/webaudio/base_audio_context.h"
+ #include "third_party/blink/renderer/modules/webaudio/realtime_audio_destination_node.h"
+ #include "third_party/blink/renderer/modules/webaudio/script_processor_node.h"
+-#include "third_party/blink/renderer/platform/audio/denormal_disabler.h"
+ #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+ #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+ #include "third_party/blink/renderer/platform/wtf/cross_thread_copier_base.h"
+@@ -49,9 +48,7 @@ ScriptProcessorHandler::ScriptProcessorHandler(
+       internal_input_bus_(AudioBus::Create(
+           number_of_input_channels,
+           node.context()->GetDeferredTaskHandler().RenderQuantumFrames(),
+-          false)),
+-      allow_denormal_in_processing_(base::FeatureList::IsEnabled(
+-          features::kWebAudioAllowDenormalInProcessing)) {
++          false)) {
+   DCHECK_GE(buffer_size_,
+             node.context()->GetDeferredTaskHandler().RenderQuantumFrames());
+   DCHECK_LE(number_of_input_channels, BaseAudioContext::MaxNumberOfChannels());
+@@ -112,7 +109,7 @@ void ScriptProcessorHandler::Initialize() {
+   AudioHandler::Initialize();
+ }
+ 
+-void ScriptProcessorHandler::ProcessInternal(uint32_t frames_to_process) {
++void ScriptProcessorHandler::Process(uint32_t frames_to_process) {
+   TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("webaudio.audionode"),
+                      "ScriptProcessorHandler::Process");
+ 
+@@ -241,15 +238,6 @@ void ScriptProcessorHandler::ProcessInternal(uint32_t frames_to_process) {
+                    "ScriptProcessorHandler::Process");
+ }
+ 
+-void ScriptProcessorHandler::Process(uint32_t frames_to_process) {
+-  if (allow_denormal_in_processing_) {
+-    DenormalEnabler denormal_enabler;
+-    ProcessInternal(frames_to_process);
+-  } else {
+-    ProcessInternal(frames_to_process);
+-  }
+-}
+-
+ void ScriptProcessorHandler::FireProcessEvent(uint32_t double_buffer_index) {
+   DCHECK(IsMainThread());
+ 
+diff --git a/third_party/blink/renderer/modules/webaudio/script_processor_handler.h b/third_party/blink/renderer/modules/webaudio/script_processor_handler.h
+index a960426678a5da72071f6defa046a4517fcb1cf7..308a9ba845ab3413462540a70564ad85091cf180 100644
+--- a/third_party/blink/renderer/modules/webaudio/script_processor_handler.h
++++ b/third_party/blink/renderer/modules/webaudio/script_processor_handler.h
+@@ -66,10 +66,6 @@ class ScriptProcessorHandler final : public AudioHandler {
+                          const HeapVector<Member<AudioBuffer>>& input_buffers,
+                          const HeapVector<Member<AudioBuffer>>& output_buffers);
+ 
+-  // Used to avoid code duplication when using scoped objects that affect
+-  // `Process`.
+-  void ProcessInternal(uint32_t frames_to_process);
+-
+   double TailTime() const override;
+   double LatencyTime() const override;
+   bool RequiresTailProcessing() const final;
+@@ -97,9 +93,6 @@ class ScriptProcessorHandler final : public AudioHandler {
+ 
+   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ 
+-  // Cached feature flag value
+-  const bool allow_denormal_in_processing_;
+-
+   base::WeakPtrFactory<ScriptProcessorHandler> weak_ptr_factory_{this};
+ 
+   FRIEND_TEST_ALL_PREFIXES(ScriptProcessorNodeTest, BufferLifetime);