Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebIDL: use FLoat32Array #30990

Merged
merged 18 commits into from Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
103 changes: 29 additions & 74 deletions components/script/dom/audiobuffer.rs
Expand Up @@ -3,13 +3,10 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use std::cmp::min;
use std::ptr::{self, NonNull};

use dom_struct::dom_struct;
use js::jsapi::{Heap, JSObject, JS_GetArrayBufferViewBuffer};
use js::rust::wrappers::DetachArrayBuffer;
use js::rust::{CustomAutoRooterGuard, HandleObject};
use js::typedarray::{CreateWith, Float32Array};
use js::typedarray::Float32Array;
use servo_media::audio::buffer_source_node::AudioBuffer as ServoMediaAudioBuffer;

use crate::dom::audionode::MAX_CHANNEL_COUNT;
Expand All @@ -21,6 +18,7 @@ use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::typedarrays::HeapFloat32Array;
use crate::dom::globalscope::GlobalScope;
use crate::dom::window::Window;
use crate::realms::enter_realm;
Expand All @@ -31,8 +29,6 @@ use crate::script_runtime::JSContext;
pub const MIN_SAMPLE_RATE: f32 = 8000.;
pub const MAX_SAMPLE_RATE: f32 = 192000.;

type JSAudioChannel = Heap<*mut JSObject>;

/// The AudioBuffer keeps its data either in js_channels
/// or in shared_channels if js_channels buffers are detached.
///
Expand All @@ -46,7 +42,7 @@ pub struct AudioBuffer {
reflector_: Reflector,
/// Float32Arrays returned by calls to GetChannelData.
#[ignore_malloc_size_of = "mozjs"]
js_channels: DomRefCell<Vec<JSAudioChannel>>,
js_channels: DomRefCell<Vec<HeapFloat32Array>>,
/// Aggregates the data from js_channels.
/// This is Some<T> iff the buffers in js_channels are detached.
#[ignore_malloc_size_of = "servo_media"]
Expand All @@ -66,7 +62,9 @@ impl AudioBuffer {
#[allow(crown::unrooted_must_root)]
#[allow(unsafe_code)]
pub fn new_inherited(number_of_channels: u32, length: u32, sample_rate: f32) -> AudioBuffer {
let vec = (0..number_of_channels).map(|_| Heap::default()).collect();
let vec = (0..number_of_channels)
.map(|_| HeapFloat32Array::default())
.collect();
AudioBuffer {
reflector_: Reflector::new(),
js_channels: DomRefCell::new(vec),
Expand Down Expand Up @@ -152,34 +150,23 @@ impl AudioBuffer {
*self.shared_channels.borrow_mut() = Some(channels);
}

#[allow(unsafe_code)]
fn restore_js_channel_data(&self, cx: JSContext) -> bool {
let _ac = enter_realm(&*self);
for (i, channel) in self.js_channels.borrow_mut().iter().enumerate() {
if !channel.get().is_null() {
if channel.is_initialized() {
// Already have data in JS array.
continue;
}

rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
if let Some(ref shared_channels) = *self.shared_channels.borrow() {
// Step 4. of
// https://webaudio.github.io/web-audio-api/#acquire-the-content
// "Attach ArrayBuffers containing copies of the data to the AudioBuffer,
// to be returned by the next call to getChannelData()".
unsafe {
if Float32Array::create(
*cx,
CreateWith::Slice(&shared_channels.buffers[i]),
array.handle_mut(),
)
.is_err()
{
return false;
}
if channel.set_data(cx, &shared_channels.buffers[i]).is_err() {
return false;
}
}
channel.set(array.get());
}

*self.shared_channels.borrow_mut() = None;
Expand All @@ -188,7 +175,6 @@ impl AudioBuffer {
}

// https://webaudio.github.io/web-audio-api/#acquire-the-content
#[allow(unsafe_code)]
fn acquire_contents(&self) -> Option<ServoMediaAudioBuffer> {
let mut result = ServoMediaAudioBuffer::new(
self.number_of_channels as u8,
Expand All @@ -198,31 +184,12 @@ impl AudioBuffer {
let cx = GlobalScope::get_cx();
for (i, channel) in self.js_channels.borrow_mut().iter().enumerate() {
// Step 1.
if channel.get().is_null() {
if !channel.is_initialized() {
return None;
}

// Step 2.
let channel_data = unsafe {
typedarray!(in(*cx) let array: Float32Array = channel.get());
if let Ok(array) = array {
let data = array.to_vec();
let mut is_shared = false;
rooted!(in (*cx) let view_buffer =
JS_GetArrayBufferViewBuffer(*cx, channel.handle(), &mut is_shared));
// This buffer is always created unshared
debug_assert!(!is_shared);
let _ = DetachArrayBuffer(*cx, view_buffer.handle());
data
} else {
return None;
}
};

channel.set(ptr::null_mut());

// Step 3.
result.buffers[i] = channel_data;
result.buffers[i] = channel.acquire_data(cx).ok()?;
}

Some(result)
Expand Down Expand Up @@ -261,20 +228,18 @@ impl AudioBufferMethods for AudioBuffer {
}

// https://webaudio.github.io/web-audio-api/#dom-audiobuffer-getchanneldata
#[allow(unsafe_code)]
fn GetChannelData(&self, cx: JSContext, channel: u32) -> Fallible<NonNull<JSObject>> {
fn GetChannelData(&self, cx: JSContext, channel: u32) -> Fallible<Float32Array> {
if channel >= self.number_of_channels {
return Err(Error::IndexSize);
}

if !self.restore_js_channel_data(cx) {
return Err(Error::JSFailed);
}
unsafe {
Ok(NonNull::new_unchecked(
self.js_channels.borrow()[channel as usize].get(),
))
}

self.js_channels.borrow()[channel as usize]
.get_internal()
.map_err(|_| Error::JSFailed)
}

// https://webaudio.github.io/web-audio-api/#dom-audiobuffer-copyfromchannel
Expand All @@ -300,12 +265,13 @@ impl AudioBufferMethods for AudioBuffer {
let mut dest = Vec::with_capacity(destination.len());

// We either copy form js_channels or shared_channels.
let js_channel = self.js_channels.borrow()[channel_number].get();
if !js_channel.is_null() {
typedarray!(in(*cx) let array: Float32Array = js_channel);
if let Ok(array) = array {
let data = unsafe { array.as_slice() };
dest.extend_from_slice(&data[offset..offset + bytes_to_copy]);
let js_channel = &self.js_channels.borrow()[channel_number];
if js_channel.is_initialized() {
if js_channel
.copy_data_to(cx, &mut dest, offset, offset + bytes_to_copy)
.is_err()
{
return Err(Error::IndexSize);
}
} else if let Some(ref shared_channels) = *self.shared_channels.borrow() {
if let Some(shared_channel) = shared_channels.buffers.get(channel_number) {
Expand All @@ -321,7 +287,6 @@ impl AudioBufferMethods for AudioBuffer {
}

// https://webaudio.github.io/web-audio-api/#dom-audiobuffer-copytochannel
#[allow(unsafe_code)]
fn CopyToChannel(
&self,
source: CustomAutoRooterGuard<Float32Array>,
Expand All @@ -341,25 +306,15 @@ impl AudioBufferMethods for AudioBuffer {
return Err(Error::JSFailed);
}

let js_channel = self.js_channels.borrow()[channel_number as usize].get();
if js_channel.is_null() {
let js_channel = &self.js_channels.borrow()[channel_number as usize];
if !js_channel.is_initialized() {
// The array buffer was detached.
return Err(Error::IndexSize);
}

typedarray!(in(*cx) let js_channel: Float32Array = js_channel);
if let Ok(mut js_channel) = js_channel {
let bytes_to_copy = min(self.length - start_in_channel, source.len() as u32) as usize;
let js_channel_data = unsafe { js_channel.as_mut_slice() };
let (_, js_channel_data) = js_channel_data.split_at_mut(start_in_channel as usize);
unsafe {
js_channel_data[0..bytes_to_copy]
.copy_from_slice(&source.as_slice()[0..bytes_to_copy])
};
} else {
return Err(Error::IndexSize);
}

Ok(())
let bytes_to_copy = min(self.length - start_in_channel, source.len() as u32) as usize;
js_channel
.copy_data_from(cx, source, start_in_channel as usize, bytes_to_copy)
.map_err(|_| Error::IndexSize)
}
}
22 changes: 17 additions & 5 deletions components/script/dom/bindings/codegen/CodegenRust.py
Expand Up @@ -119,7 +119,8 @@ def wrapInNativeContainerType(type, inner):
IDLType.Tags.unrestricted_float: 'f32',
IDLType.Tags.float: 'Finite<f32>',
IDLType.Tags.unrestricted_double: 'f64',
IDLType.Tags.double: 'Finite<f64>'
IDLType.Tags.double: 'Finite<f64>',
IDLType.Tags.float32array: 'Float32Array'
}

numericTags = [
Expand Down Expand Up @@ -1459,16 +1460,26 @@ def getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs):
return "()"


def todo_switch_float_32(des):
return des.interface.identifier.name in ['XRView', 'XRRigidTransform', 'XRRay', 'GamepadPose']


def builtin_return_type(returnType):
result = CGGeneric(builtinNames[returnType.tag()])
if returnType.nullable():
result = CGWrapper(result, pre="Option<", post=">")
return result


# Returns a CGThing containing the type of the return value.
def getRetvalDeclarationForType(returnType, descriptorProvider):
if returnType is None or returnType.isUndefined():
# Nothing to declare
return CGGeneric("()")
if returnType.isPrimitive() and returnType.tag() in builtinNames:
result = CGGeneric(builtinNames[returnType.tag()])
if returnType.nullable():
result = CGWrapper(result, pre="Option<", post=">")
return result
return builtin_return_type(returnType)
if returnType.isTypedArray() and returnType.tag() in builtinNames and not todo_switch_float_32(descriptorProvider):
return builtin_return_type(returnType)
if returnType.isDOMString():
result = CGGeneric("DOMString")
if returnType.nullable():
Expand Down Expand Up @@ -6494,6 +6505,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'js::rust::define_properties',
'js::rust::get_object_class',
'js::typedarray',
'js::typedarray::Float32Array',
'crate::dom',
'crate::dom::bindings',
'crate::dom::bindings::codegen::InterfaceObjectMap',
Expand Down
1 change: 1 addition & 0 deletions components/script/dom/bindings/mod.rs
Expand Up @@ -158,6 +158,7 @@ pub mod str;
pub mod structuredclone;
pub mod trace;
pub mod transferable;
pub mod typedarrays;
pub mod utils;
pub mod weakref;
pub mod xmlname;
Expand Down
105 changes: 105 additions & 0 deletions components/script/dom/bindings/typedarrays.rs
@@ -0,0 +1,105 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

#![allow(unsafe_code)]

use std::ptr;

use js::jsapi::{Heap, JSObject, JS_GetArrayBufferViewBuffer};
use js::rust::wrappers::DetachArrayBuffer;
use js::rust::{CustomAutoRooterGuard, MutableHandleObject};
use js::typedarray::{CreateWith, Float32Array};

use crate::script_runtime::JSContext;

pub fn create_float32_array(
cx: JSContext,
data: &[f32],
dest: MutableHandleObject,
) -> Result<Float32Array, ()> {
let res = unsafe { Float32Array::create(*cx, CreateWith::Slice(data), dest) };
if res.is_err() {
Err(())
} else {
Float32Array::from(dest.get())
}
}

#[derive(Default, JSTraceable)]
pub struct HeapFloat32Array {
internal: Box<Heap<*mut JSObject>>,
}

impl HeapFloat32Array {
pub fn set_data(&self, cx: JSContext, data: &[f32]) -> Result<(), ()> {
rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
let _ = create_float32_array(cx, data, array.handle_mut())?;
self.internal.set(*array);
Ok(())
}

pub fn acquire_data(&self, cx: JSContext) -> Result<Vec<f32>, ()> {
assert!(self.is_initialized());
typedarray!(in(*cx) let array: Float32Array = self.internal.get());
let data = if let Ok(array) = array {
let data = array.to_vec();
let mut is_shared = false;
unsafe {
rooted!(in (*cx) let view_buffer =
JS_GetArrayBufferViewBuffer(*cx, self.internal.handle(), &mut is_shared));
// This buffer is always created unshared
debug_assert!(!is_shared);
let _ = DetachArrayBuffer(*cx, view_buffer.handle());
}
Ok(data)
} else {
Err(())
};
self.internal.set(ptr::null_mut());
data
}

pub fn copy_data_to(
&self,
cx: JSContext,
dest: &mut [f32],
source_start: usize,
length: usize,
) -> Result<(), ()> {
assert!(self.is_initialized());
typedarray!(in(*cx) let array: Float32Array = self.internal.get());
let Ok(array) = array else { return Err(()) };
unsafe {
let slice = (*array).as_slice();
dest.copy_from_slice(&slice[source_start..length]);
}
Ok(())
}

pub fn copy_data_from(
&self,
cx: JSContext,
source: CustomAutoRooterGuard<Float32Array>,
dest_start: usize,
length: usize,
) -> Result<(), ()> {
assert!(self.is_initialized());
typedarray!(in(*cx) let mut array: Float32Array = self.internal.get());
let Ok(mut array) = array else { return Err(()) };
unsafe {
let slice = (*array).as_mut_slice();
let (_, dest) = slice.split_at_mut(dest_start);
dest[0..length].copy_from_slice(&source.as_slice()[0..length])
}
Ok(())
}

pub fn is_initialized(&self) -> bool {
!self.internal.get().is_null()
}

pub fn get_internal(&self) -> Result<Float32Array, ()> {
Float32Array::from(self.internal.get())
}
}