bug 1198656 delay AudioBuffer allocation until required r=padenot
authorKarl Tomlinson <karlt+@karlt.net>
Mon, 24 Aug 2015 19:30:36 +1200
changeset 294338 e4fb01e4e3cd2eed2668c0efc85c35f8a8d42224
parent 294337 d325ca7fa94cccbd0a3f30ce7bf5c22618a7054d
child 294339 1dbf68298734ad14579849f3534fa07c6e897059
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs1198656
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1198656 delay AudioBuffer allocation until required r=padenot This saves an allocation and zeroing for buffers generated by AudioNodes and avoids allocation altogether for empty buffers. Incidentally, RestoreJSChannelData() now avoids unnecessary recreation of Float32Arrays if they already exist after a previous call failed.
dom/media/webaudio/AudioBuffer.cpp
--- a/dom/media/webaudio/AudioBuffer.cpp
+++ b/dom/media/webaudio/AudioBuffer.cpp
@@ -83,62 +83,53 @@ AudioBuffer::Create(AudioContext* aConte
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return nullptr;
   }
 
   nsRefPtr<AudioBuffer> buffer =
     new AudioBuffer(aContext, aNumberOfChannels, aLength, aSampleRate,
                     Move(aInitialContents));
 
-  if (buffer->mSharedChannels) {
-    return buffer.forget();
-  }
-
-  for (uint32_t i = 0; i < aNumberOfChannels; ++i) {
-    JS::Rooted<JSObject*> array(aJSContext,
-                                JS_NewFloat32Array(aJSContext, aLength));
-    if (!array) {
-      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return nullptr;
-    }
-    buffer->mJSChannels[i] = array;
-  }
-
   return buffer.forget();
 }
 
 JSObject*
 AudioBuffer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return AudioBufferBinding::Wrap(aCx, this, aGivenProto);
 }
 
 bool
 AudioBuffer::RestoreJSChannelData(JSContext* aJSContext)
 {
-  // "4. Attach ArrayBuffers containing copies of the data to the AudioBuffer,
-  // to be returned by the next call to getChannelData."
-  if (mSharedChannels) {
-    for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
+  for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
+    if (mJSChannels[i]) {
+      // Already have data in JS array.
+      continue;
+    }
+
+    // The following code first zeroes the array and then copies our data
+    // into it. We could avoid this with additional JS APIs to construct
+    // an array (or ArrayBuffer) containing initial data.
+    JS::Rooted<JSObject*> array(aJSContext,
+                                JS_NewFloat32Array(aJSContext, mLength));
+    if (!array) {
+      return false;
+    }
+    if (mSharedChannels) {
+      // "4. Attach ArrayBuffers containing copies of the data to the
+      // AudioBuffer, to be returned by the next call to getChannelData."
       const float* data = mSharedChannels->GetData(i);
-      // The following code first zeroes the array and then copies our data
-      // into it. We could avoid this with additional JS APIs to construct
-      // an array (or ArrayBuffer) containing initial data.
-      JS::Rooted<JSObject*> array(aJSContext,
-                                  JS_NewFloat32Array(aJSContext, mLength));
-      if (!array) {
-        return false;
-      }
       JS::AutoCheckCannotGC nogc;
       mozilla::PodCopy(JS_GetFloat32ArrayData(array, nogc), data, mLength);
-      mJSChannels[i] = array;
     }
+    mJSChannels[i] = array;
+  }
 
-    mSharedChannels = nullptr;
-  }
+  mSharedChannels = nullptr;
 
   return true;
 }
 
 void
 AudioBuffer::CopyFromChannel(const Float32Array& aDestination, uint32_t aChannelNumber,
                              uint32_t aStartInChannel, ErrorResult& aRv)
 {
@@ -148,27 +139,36 @@ AudioBuffer::CopyFromChannel(const Float
   CheckedInt<uint32_t> end = aStartInChannel;
   end += length;
   if (aChannelNumber >= NumberOfChannels() ||
       !end.isValid() || end.value() > mLength) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
-  if (!mSharedChannels && JS_GetTypedArrayLength(mJSChannels[aChannelNumber]) != mLength) {
-    // The array was probably neutered
-    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return;
+  JS::AutoCheckCannotGC nogc;
+  JSObject* channelArray = mJSChannels[aChannelNumber];
+  const float* sourceData = nullptr;
+  if (channelArray) {
+    if (JS_GetTypedArrayLength(channelArray) != mLength) {
+      // The array was probably neutered
+      aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+      return;
+    }
+
+    sourceData = JS_GetFloat32ArrayData(channelArray, nogc);
+  } else if (mSharedChannels) {
+    sourceData = mSharedChannels->GetData(aChannelNumber);
   }
 
-  JS::AutoCheckCannotGC nogc;
-  const float* sourceData = mSharedChannels ?
-    mSharedChannels->GetData(aChannelNumber) :
-    JS_GetFloat32ArrayData(mJSChannels[aChannelNumber], nogc);
-  PodMove(aDestination.Data(), sourceData + aStartInChannel, length);
+  if (sourceData) {
+    PodMove(aDestination.Data(), sourceData + aStartInChannel, length);
+  } else {
+    PodZero(aDestination.Data(), length);
+  }
 }
 
 void
 AudioBuffer::CopyToChannel(JSContext* aJSContext, const Float32Array& aSource,
                            uint32_t aChannelNumber, uint32_t aStartInChannel,
                            ErrorResult& aRv)
 {
   aSource.ComputeLengthAndData();
@@ -177,29 +177,30 @@ AudioBuffer::CopyToChannel(JSContext* aJ
   CheckedInt<uint32_t> end = aStartInChannel;
   end += length;
   if (aChannelNumber >= NumberOfChannels() ||
       !end.isValid() || end.value() > mLength) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
-  if (!mSharedChannels && JS_GetTypedArrayLength(mJSChannels[aChannelNumber]) != mLength) {
-    // The array was probably neutered
-    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return;
-  }
-
   if (!RestoreJSChannelData(aJSContext)) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
   JS::AutoCheckCannotGC nogc;
-  PodMove(JS_GetFloat32ArrayData(mJSChannels[aChannelNumber], nogc) + aStartInChannel,
+  JSObject* channelArray = mJSChannels[aChannelNumber];
+  if (JS_GetTypedArrayLength(channelArray) != mLength) {
+    // The array was probably neutered
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
+  }
+
+  PodMove(JS_GetFloat32ArrayData(channelArray, nogc) + aStartInChannel,
           aSource.Data(), length);
 }
 
 void
 AudioBuffer::GetChannelData(JSContext* aJSContext, uint32_t aChannel,
                             JS::MutableHandle<JSObject*> aRetval,
                             ErrorResult& aRv)
 {
@@ -221,18 +222,19 @@ AudioBuffer::GetChannelData(JSContext* a
 
 already_AddRefed<ThreadSharedFloatArrayBufferList>
 AudioBuffer::StealJSArrayDataIntoSharedChannels(JSContext* aJSContext)
 {
   // "1. If any of the AudioBuffer's ArrayBuffer have been neutered, abort
   // these steps, and return a zero-length channel data buffers to the
   // invoker."
   for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
-    if (mLength != JS_GetTypedArrayLength(mJSChannels[i])) {
-      // Probably one of the arrays was neutered
+    JSObject* channelArray = mJSChannels[i];
+    if (!channelArray || mLength != JS_GetTypedArrayLength(channelArray)) {
+      // Either empty buffer or one of the arrays was probably neutered
       return nullptr;
     }
   }
 
   // "2. Neuter all ArrayBuffers for arrays previously returned by
   // getChannelData on this AudioBuffer."
   // "3. Retain the underlying data buffers from those ArrayBuffers and return
   // references to them to the invoker."