<commit_before>//! Decoding of OpenEXR (.exr) Images
//!
//! OpenEXR is an image format that is widely used, especially in VFX,
//! because it supports lossless and lossy compression for float data.
//!
//! # Related Links
//! * <https://www.openexr.com/documentation.html> - The OpenEXR reference.

// ROADMAP: See https://github.com/image-rs/image/pull/1475

extern crate exr;
use exr::prelude::*;

use crate::{ImageDecoder, ImageResult, ColorType, Progress, image, ImageError, ImageFormat};
use std::io::{Write, Seek, BufRead, Cursor};
use crate::error::{DecodingError, ImageFormatHint};

/// An OpenEXR decoder
#[derive(Debug)]
pub struct ExrDecoder<R> {
    exr_reader: exr::block::reader::Reader<R>,

    // select a header that is rgb and not deep
    header_index: usize,
}

impl<R: BufRead + Seek> ExrDecoder<R> {

    /// Create a decoder. Consumes the first few bytes of the source to extract image dimensions.
    pub fn read(source: R) -> ImageResult<Self> {

        // read meta data, then wait for further instructions, keeping the file open and ready
        let exr_reader = exr::block::read(source, false)?;

        let header_index = exr_reader.headers().into_iter()
            .position(|header|{
                let has_rgb = ["R","G","B"].iter().all( // alpha will be optional
                    // check if r/g/b exists in the channels
                    |required| header.channels.list.iter()
                        .find(|chan| chan.name.eq(required)).is_some() // TODO eq_lowercase only if exrs supports it
                );

                !header.deep && has_rgb
            })
            .ok_or_else(|| ImageError::Decoding(DecodingError::new(
                ImageFormatHint::Exact(ImageFormat::Exr),
                "image does not contain non-deep rgb channels"
            )))?;

        Ok(Self { exr_reader, header_index })
    }
}

impl<'a, R: 'a + BufRead + Seek> ImageDecoder<'a> for ExrDecoder<R> {
    type Reader = Cursor<Vec<u8>>;

    fn dimensions(&self) -> (u32, u32) {
        let size = self.exr_reader.headers()[self.header_index].layer_size;
        (size.width() as u32, size.height() as u32)
    }

    // TODO fn original_color_type(&self) -> ExtendedColorType {}

    fn color_type(&self) -> ColorType {
        // TODO adapt to actual exr color type
        // let _channels = &self.exr_reader.headers()[self.header_index].channels;
        ColorType::Rgba32F
    }

    /// Use `read_image` if possible,
    /// as this method creates a whole new buffer to contain the entire image.
    fn into_reader(self) -> ImageResult<Self::Reader> {
        Ok(Cursor::new(image::decoder_to_vec(self)?)) // TODO no vec?
    }

    fn scanline_bytes(&self) -> u64 {
        // we cannot always read individual scan lines for every file,
        // as the tiles or lines in the file could be in random or reversed order.
        // therefore we currently read all lines at once
        // Todo: optimize for specific exr.line_order?
       self.total_bytes()
    }

    fn read_image_with_progress<F: Fn(Progress)>(self, target: &mut [u8], progress_callback: F) -> ImageResult<()> {
        let blocks_in_header = self.exr_reader.headers()[self.header_index].chunk_count as u64;
        let height = self.dimensions().1 as usize;

        let result = read()
            .no_deep_data().largest_resolution_level()
            .rgba_channels(
                |size, _channels|
                    (size, vec![0_f32; size.area()*4])
                ,

                move |(size, buffer), mut position, (r,g,b,a_or_1): (f32,f32,f32,f32)| {
                    // TODO white point chromaticities + srgb/linear conversion?
                    position.1 = height - (position.1 + 1); // openexr stores +y upwards, but we need +y downwards
                    let first_f32_index = position.flat_index_for_size(*size);
                    buffer[first_f32_index*4 .. (first_f32_index + 1)*4].copy_from_slice(&[r, g, b, a_or_1]);
                }
            )
            .first_valid_layer() // TODO select exact layer by self.header_index?
            .all_attributes()
            .on_progress(|progress| {
                progress_callback(Progress::new(
                    (progress*blocks_in_header as f64) as u64, blocks_in_header) // TODO precision errors?
                );
            })
            .from_chunks(self.exr_reader)?;

        // TODO this copy is strictly not necessary, but the exr api is a little too simple for reading into a borrowed target slice
        target.copy_from_slice(bytemuck::cast_slice(result.layer_data.channel_data.pixels.1.as_slice()));

        // TODO keep meta data?
        Ok(())
    }
}

pub fn write_image(mut write: impl Write/* + Seek*/, bytes: &[u8], width: u32, height: u32, color_type: ColorType) -> ImageResult<()> {
    let width = width as usize;
    let height = height as usize;

    let mut seekable_write = Cursor::new(Vec::with_capacity(width*height*3*4)); // TODO remove

    match color_type {
        ColorType::Rgb32F => {
            let pixels: &[f32] = bytemuck::try_cast_slice(bytes)
                .expect("image byte buffer must be aligned to f32");

            exr::prelude::Image
                ::from_channels(
                    (width, height),
                    SpecificChannels::rgb(|mut pixel: Vec2<usize>| {
                        debug_assert_ne!(height, 0);
                        pixel.1 = height - 1 - pixel.1; // openexr stores +y upwards, but we need +y downwards

                        let pixel_index = 3 * pixel.flat_index_for_size(Vec2(width, height));
                        (pixels[pixel_index], pixels[pixel_index+1], pixels[pixel_index+2])
                    })
                )
                .write()
                // .on_progress(|progress| todo!())
                .to_buffered(&mut seekable_write)?; // TODO BufWrite::new()?
        }

        ColorType::Rgba32F => {
            let pixels: &[f32] = bytemuck::try_cast_slice(bytes)
                .expect("image byte buffer must be aligned to f32");

            exr::prelude::Image
                ::from_channels(
                    (width, height),
                    SpecificChannels::rgba(|mut pixel: Vec2<usize>| {
                        debug_assert_ne!(height, 0);
                        pixel.1 = height - 1 - pixel.1; // openexr stores +y upwards, but we need +y downwards

                        let pixel_index = 4 * pixel.flat_index_for_size(Vec2(width, height));
                        (pixels[pixel_index], pixels[pixel_index+1], pixels[pixel_index+2], pixels[pixel_index+3])
                    })
                )
                .write()
                // .on_progress(|progress| todo!())
                .to_buffered(&mut seekable_write)?; // TODO BufWrite::new()?
        }

        _ => todo!()
    }

    write.write_all(seekable_write.into_inner().as_slice())?;
    Ok(())
}

impl From<Error> for ImageError {
    fn from(exr_error: Error) -> Self {
        ImageError::Decoding(DecodingError::new(
            ImageFormatHint::Exact(ImageFormat::Exr),
            exr_error
        ))
    }
}

<commit_msg>add tests to openexr module, load alpha only if present in file or requested, add some convenience functions to read and write f32 buffers using exr<commit_after>//! Decoding of OpenEXR (.exr) Images
//!
//! OpenEXR is an image format that is widely used, especially in VFX,
//! because it supports lossless and lossy compression for float data.
//!
//! This decoder only supports RGB and RGBA images.
//! If an image does not contain alpha information,
//! it is defaulted to `1.0`, which means no transparency.
//!
//! # Related Links
//! * <https://www.openexr.com/documentation.html> - The OpenEXR reference.

// ROADMAP: See https://github.com/image-rs/image/pull/1475

extern crate exr;
use exr::prelude::*;

use crate::{ImageDecoder, ImageResult, ColorType, Progress, ImageError, ImageFormat, ImageBuffer, Rgba, Rgb};
use std::io::{Write, Seek, BufRead, Cursor, BufReader, BufWriter};
use crate::error::{DecodingError, ImageFormatHint, LimitError, LimitErrorKind, EncodingError};
use crate::image::decoder_to_vec;
use std::path::Path;

/// An image buffer for 32-bit float RGB pixels,
/// where the backing container is a flattened vector of floats.
pub type RgbF32Buffer = ImageBuffer<Rgb<f32>, Vec<f32>>;

/// An image buffer for 32-bit float RGBA pixels,
/// where the backing container is a flattened vector of floats.
pub type RgbaF32Buffer = ImageBuffer<Rgba<f32>, Vec<f32>>;

// TODO this could be a generic function that works for any format
/// Read the file from the specified path into an `RgbaF32Buffer`.
// TODO progress? load rect?
pub fn read_as_rgba_image_from_file(path: impl AsRef<Path>) -> ImageResult<RgbaF32Buffer> {
    read_as_rgba_image(BufReader::new(std::fs::File::open(path)?))
}

// TODO this could be a generic function that works for any format
/// Read an `RgbaF32Buffer`.
/// Assumes the reader is buffered. In most cases,
/// you should wrap your reader in a `BufReader` for best performance.
// TODO progress? load rect?
pub fn read_as_rgba_image(read: impl BufRead + Seek) -> ImageResult<RgbaF32Buffer> {
    ExrDecoder::read(read)?.read_as_rgba_image()
}

// TODO this could be a generic function that works for any format
/// Read the file from the specified path into an `RgbF32Buffer`.
// TODO progress? load rect?
pub fn read_as_rgb_image_from_file(path: impl AsRef<Path>) -> ImageResult<RgbF32Buffer> {
    read_as_rgb_image(BufReader::new(std::fs::File::open(path)?))
}

// TODO this could be a generic function that works for any format
/// Read an `RgbF32Buffer`.
/// Assumes the reader is buffered. In most cases,
/// you should wrap your reader in a `BufReader` for best performance.
// TODO progress? load rect?
pub fn read_as_rgb_image(read: impl BufRead + Seek) -> ImageResult<RgbF32Buffer> {
    ExrDecoder::read(read)?.read_as_rgb_image()
}

// TODO this could be a generic function that works for any format
/// Write an `RgbF32Buffer` to the specified file path, replacing any existing file.
// TODO progress? load rect?
pub fn write_rgb_image_to_file(path: impl AsRef<Path>, image: &RgbF32Buffer) -> ImageResult<()> {
    write_rgb_image(BufWriter::new(std::fs::File::create(path)?), image)
}

// TODO this could be a generic function that works for any format
/// Write an `RgbF32Buffer`.
/// Assumes the writer is buffered. In most cases,
/// you should wrap your writer in a `BufWriter` for best performance.
// TODO progress? load rect?
pub fn write_rgb_image(write: impl Write/* + Seek*/, image: &RgbF32Buffer) -> ImageResult<()> {
    write_buffer(
        write,
        bytemuck::cast_slice(image.as_raw().as_slice()),
        image.width(), image.height(),
        ColorType::Rgb32F
    )
}

// TODO this could be a generic function that works for any format
/// Write an `RgbaF32Buffer` to the specified file path, replacing any existing file.
// TODO progress? load rect?
pub fn write_rgba_image_to_file(path: impl AsRef<Path>, image: &RgbaF32Buffer) -> ImageResult<()> {
    write_rgba_image(BufWriter::new(std::fs::File::create(path)?), image)
}

// TODO this could be a generic function that works for any format
/// Write an `RgbaF32Buffer`.
/// Assumes the writer is buffered. In most cases,
/// you should wrap your writer in a `BufWriter` for best performance.
// TODO progress? load rect?
pub fn write_rgba_image(write: impl Write/* + Seek*/, image: &RgbaF32Buffer) -> ImageResult<()> {
    write_buffer(
        write,
        bytemuck::cast_slice(image.as_raw().as_slice()),
        image.width(), image.height(),
        ColorType::Rgba32F
    )
}

/// An OpenEXR decoder. Immediately reads the meta data from the file.
#[derive(Debug)]
pub struct ExrDecoder<R> {
    exr_reader: exr::block::reader::Reader<R>,

    // select a header that is rgb and not deep
    header_index: usize,

    // decode either rgb or rgba.
    // by default, only activated if image contains alpha.
    // can be changed to include or discard alpha channels.
    insert_alpha: bool,
}

impl<R: BufRead + Seek> ExrDecoder<R> {

    /// Create a decoder. Consumes the first few bytes of the source to extract image dimensions.
    /// Assumes the reader is buffered. In most cases,
    /// you should wrap your reader in a `BufReader` for best performance.
    pub fn read(source: R) -> ImageResult<Self> {

        // read meta data, then wait for further instructions, keeping the file open and ready
        let exr_reader = exr::block::read(source, false)?;

        let header_index = exr_reader.headers().into_iter()
            .position(|header|{
                let has_rgb = ["R","G","B"].iter().all( // alpha will be optional
                    // check if r/g/b exists in the channels
                    // TODO binary search, channels are sorted
                    |required| header.channels.list.iter()
                        .find(|chan| chan.name.eq(required)).is_some() // TODO eq_lowercase only if exrs supports it
                );

                // we currently dont support deep images, or images with other color spaces than rgb
                !header.deep && has_rgb
            })
            .ok_or_else(|| ImageError::Decoding(DecodingError::new(
                ImageFormatHint::Exact(ImageFormat::Exr),
                "image does not contain non-deep rgb channels"
            )))?;

        // TODO binary search, channels are sorted
        let has_alpha = exr_reader.headers()[header_index]
            .channels.list
            //.binary_search_by_key(b"A", |chan| chan.name.bytes())
            .iter()
            .find(|chan| chan.name.eq("A")) // TODO eq_lowercase only if exrs supports it
            .is_some();

        Ok(Self { exr_reader, header_index, insert_alpha: has_alpha })
    }

    // do not leak exrs-specific meta data into public api, just do it for this module
    fn selected_exr_header(&self) -> &exr::meta::header::Header {
        &self.exr_reader.meta_data().headers[self.header_index]
    }

    // TODO does using Rgba<f32> with Vec<f32> automagically un-flatten the vector??
    /// Read this OpenEXR file as an `RgbaF32Buffer`,
    /// including the alpha channel. A default value of `1.0` is used
    /// if there is no alpha channel in the image.
    /// Any calls to `color_type` or `total_bytes`
    /// might not agree with the result of this function.
    // TODO progress? load rect?
    pub fn read_as_rgba_image(mut self) -> ImageResult<RgbaF32Buffer> {
        let (width, height) = self.dimensions();

        self.insert_alpha = true;
        let buffer: Vec<f32> = decoder_to_vec(self)?;

        ImageBuffer::from_raw(width, height, buffer)

            // this should be the only reason for the "from raw" call to fail,
            // even though such a large allocation would probably cause an error much earlier
            .ok_or_else(|| ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory)))
    }

    // TODO does using Rgba<f32> with Vec<f32> automagically un-flatten the vector??
    /// Read this OpenEXR file as an `RgbF32Buffer`, discarding alpha.
    /// Any calls to `color_type` or `total_bytes`
    /// might not agree with the result of this function.
    // TODO progress? load rect?
    pub fn read_as_rgb_image(mut self) -> ImageResult<RgbF32Buffer> {
        let (width, height) = self.dimensions();

        self.insert_alpha = false;
        let buffer: Vec<f32> = decoder_to_vec(self)?;

        ImageBuffer::from_raw(width, height, buffer)

            // this should be the only reason for the "from raw" call to fail,
            // even though such a large allocation would probably cause an error much earlier
            .ok_or_else(|| ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory)))
    }
}

impl<'a, R: 'a + BufRead + Seek> ImageDecoder<'a> for ExrDecoder<R> {
    type Reader = Cursor<Vec<u8>>;

    fn dimensions(&self) -> (u32, u32) {
        let size = self.selected_exr_header().layer_size;
        (size.width() as u32, size.height() as u32)
    }

    // TODO fn original_color_type(&self) -> ExtendedColorType {}

    fn color_type(&self) -> ColorType {
        // TODO adapt to actual exr color type
        // let _channels = &self.exr_reader.headers()[self.header_index].channels;
        if self.insert_alpha { ColorType::Rgba32F } else { ColorType::Rgb32F }
    }

    /// Use `read_image` if possible,
    /// as this method creates a whole new buffer to contain the entire image.
    fn into_reader(self) -> ImageResult<Self::Reader> {
        Ok(Cursor::new(decoder_to_vec(self)?)) // TODO no vec?
    }

    fn scanline_bytes(&self) -> u64 {
        // we cannot always read individual scan lines for every file,
        // as the tiles or lines in the file could be in random or reversed order.
        // therefore we currently read all lines at once
        // Todo: optimize for specific exr.line_order?
       self.total_bytes()
    }

    // reads with or without alpha, depending on `self.should_include_alpha`
    fn read_image_with_progress<F: Fn(Progress)>(self, target: &mut [u8], progress_callback: F) -> ImageResult<()> {
        let blocks_in_header = self.selected_exr_header().chunk_count as u64;

        let with_alpha = self.insert_alpha;
        let channel_count = if with_alpha { 4 } else { 3 };

        let result = read()
            .no_deep_data().largest_resolution_level()
            .rgba_channels(
                |size, _channels| {
                    // TODO debug_assert_eq!(self.header_index, header_index);
                    (size, vec![0_f32; size.area() * channel_count])
                },

                move |(size, buffer), position, (r,g,b,a_or_1): (f32,f32,f32,f32)| {
                    let first_f32_index = position.flat_index_for_size(*size);

                    buffer[first_f32_index * channel_count .. (first_f32_index + 1) * channel_count]
                        .copy_from_slice(&[r, g, b, a_or_1][0 .. channel_count]);

                    // TODO white point chromaticities + srgb/linear conversion?
                }
            )
            .first_valid_layer() // TODO select exact layer by self.header_index?
            .all_attributes()
            .on_progress(|progress| {
                progress_callback(Progress::new(
                    (progress*blocks_in_header as f64) as u64, blocks_in_header) // TODO precision errors?
                );
            })
            .from_chunks(self.exr_reader)?;

        // TODO this copy is strictly not necessary, but the exr api is a little too simple for reading into a borrowed target slice
        target.copy_from_slice(bytemuck::cast_slice(result.layer_data.channel_data.pixels.1.as_slice()));
        Ok(())
    }
}

/// Write a raw byte buffer of pixels,
/// panicking if the buffer is not aligned to `f32`
/// or if it has an invalid length.
///
/// Assumes the writer is buffered. In most cases,
/// you should wrap your writer in a `BufWriter` for best performance.
pub fn write_buffer(
    mut buffered_write: impl Write/* + Seek*/, bytes: &[u8],
    width: u32, height: u32, color_type: ColorType
) -> ImageResult<()>
{
    let width = width as usize;
    let height = height as usize;

    let mut seekable_write = Cursor::new(Vec::with_capacity(width*height*3*4)); // TODO remove

    match color_type {
        ColorType::Rgb32F => {
            let pixels: &[f32] = bytemuck::try_cast_slice(bytes)
                .expect("image byte buffer must be aligned to f32");

            debug_assert_eq!(pixels.len(), width * height * 3, "invalid dimensions for byte buffer length");

            exr::prelude::Image
                ::from_channels(
                    (width, height),
                    SpecificChannels::rgb(|pixel: Vec2<usize>| {
                        let pixel_index = 3 * pixel.flat_index_for_size(Vec2(width, height));
                        (pixels[pixel_index], pixels[pixel_index+1], pixels[pixel_index+2])
                    })
                )
                .write()
                // .on_progress(|progress| todo!())
                .to_buffered(&mut seekable_write)?; // TODO BufWrite::new()?
        }

        ColorType::Rgba32F => {
            let pixels: &[f32] = bytemuck::try_cast_slice(bytes)
                .expect("image byte buffer must be aligned to f32");

            debug_assert_eq!(pixels.len(), width * height * 4, "invalid dimensions for byte buffer length");

            exr::prelude::Image
                ::from_channels(
                    (width, height),
                    SpecificChannels::rgba(|pixel: Vec2<usize>| {
                        let pixel_index = 4 * pixel.flat_index_for_size(Vec2(width, height));
                        (pixels[pixel_index], pixels[pixel_index+1], pixels[pixel_index+2], pixels[pixel_index+3])
                    })
                )
                .write()
                // .on_progress(|progress| todo!())
                .to_buffered(&mut seekable_write)?; // TODO BufWrite::new()?
        }

        _ => return Err(ImageError::Encoding(EncodingError::new(
            ImageFormatHint::Exact(ImageFormat::Exr),
            format!("color type {:?} not yet supported", color_type)
        )))
    }

    buffered_write.write_all(seekable_write.into_inner().as_slice())?;
    Ok(())
}

impl From<Error> for ImageError {
    fn from(exr_error: Error) -> Self {
        ImageError::Decoding(DecodingError::new(
            ImageFormatHint::Exact(ImageFormat::Exr),
            exr_error
        ))
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::path::PathBuf;

    const BASE_PATH: &[&str] = &[".", "tests", "images", "exr"];

    #[test]
    fn check_exr() {
        assert!(cfg!(feature = "hdr"), "to run all the openexr tests, activate the hdr feature flag");

        #[cfg(feature = "hdr")]
        {
            let folder = BASE_PATH.iter().collect::<PathBuf>();
            let reference_path = folder.clone().join("overexposed gradient.hdr");
            let exr_path = folder.clone().join("overexposed gradient - data window equals display window.exr");

            let hdr: Vec<Rgb<f32>> = crate::codecs::hdr::HdrDecoder::new(
                std::io::BufReader::new(std::fs::File::open(&reference_path).unwrap())
            ).unwrap().read_image_hdr().unwrap();

            let exr_pixels: RgbF32Buffer = read_as_rgb_image_from_file(exr_path).unwrap();

            // TODO let hdr = ImageBuffer::from_raw(___)
            assert_eq!(exr_pixels.dimensions().0 * exr_pixels.dimensions().1, hdr.len() as u32);

            for (expected, found) in hdr.iter().zip(exr_pixels.pixels()){
                for (expected, found) in expected.0.iter().zip(found.0.iter()) {
                    // the large tolerance seems to be caused by
                    // the RGBE u8x4 pixel quantization of the hdr image format
                    assert!((expected - found).abs() < 0.1, "expected {}, found {}", expected, found);
                }
            }
        }
    }

    #[test]
    fn roundtrip_rgba_exr() {
        let mut next_random = vec![ 1.0, 0.0, -1.0, -3.14, 27.0, 11.0, 31.0 ].into_iter().cycle();
        let mut next_random = move || next_random.next().unwrap();

        let generated_image: RgbaF32Buffer = ImageBuffer::from_fn(9, 31, |_x, _y|{
            Rgba([next_random(), next_random(), next_random(), next_random()])
        });

        let mut bytes = vec![];
        write_rgba_image(Cursor::new(&mut bytes), &generated_image).unwrap();
        let decoded_image = read_as_rgba_image(Cursor::new(bytes)).unwrap();

        debug_assert_eq!(generated_image, decoded_image);
    }

    #[test]
    fn roundtrip_rgb_exr() {
        let mut next_random = vec![ 1.0, 0.0, -1.0, -3.14, 27.0, 11.0, 31.0 ].into_iter().cycle();
        let mut next_random = move || next_random.next().unwrap();

        let generated_image: RgbF32Buffer = ImageBuffer::from_fn(9, 31, |_x, _y|{
            Rgb([next_random(), next_random(), next_random()])
        });

        let mut bytes = vec![];
        write_rgb_image(Cursor::new(&mut bytes), &generated_image).unwrap();
        let decoded_image = read_as_rgb_image(Cursor::new(bytes)).unwrap();

        debug_assert_eq!(generated_image, decoded_image);
    }

    #[test]
    fn compare_rgba_rgb_exr() {
        let exr_path = BASE_PATH.iter().collect::<PathBuf>()
            .join("overexposed gradient - data window equals display window.exr");

        let rgb: RgbF32Buffer = read_as_rgb_image_from_file(&exr_path).unwrap();
        let rgba: RgbaF32Buffer = read_as_rgba_image_from_file(&exr_path).unwrap();

        assert_eq!(rgba.dimensions(), rgb.dimensions());

        for (Rgb(rgb), Rgba(rgba)) in rgb.pixels().zip(rgba.pixels()) {
            assert_eq!(rgb, &rgba[..3]);
        }
    }
}<|endoftext|>


<commit_before>use cocoa::base::{id, nil};
use cocoa::foundation::NSString;
use command_queue::_get_raw_command_queue;
use device::_make_device_ref;
use std::borrow::Cow;
use std::convert::AsRef;
use std::ffi::CStr;
use sys::{MTLCommandBuffer, MTLCommandQueue, MTLCommandBufferStatus};
use {CommandQueue, DeviceRef};

pub type CommandBufferStatus = MTLCommandBufferStatus;

pub struct CommandBuffer(id);

impl CommandBuffer {
    pub fn new(command_queue: &mut CommandQueue) -> Self {
        let cmd_queue = unsafe { _get_raw_command_queue(command_queue) };
        let cmd_buf = unsafe { cmd_queue.commandBuffer() };
        debug_assert!(cmd_buf != nil);
        CommandBuffer(cmd_buf)
    }

    pub fn with_unretained_references(command_queue: &mut CommandQueue) -> Self {
        let cmd_queue = unsafe { _get_raw_command_queue(command_queue) };
        let cmd_buf_unretained = unsafe { cmd_queue.commandBufferWithUnretainedReferences() };
        debug_assert!(cmd_buf_unretained != nil);
        CommandBuffer(cmd_buf_unretained)
    }

    pub fn set_label<S: AsRef<str>>(&mut self, label: S) {
        unsafe { MTLCommandBuffer::setLabel(self.0, NSString::alloc(nil).init_str(label.as_ref())) }
    }

    pub fn get_label(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(MTLCommandBuffer::label(self.0).UTF8String()).to_string_lossy() }
    }

    pub fn get_device<'a>(&'a self) -> DeviceRef<'a> {
        let device = unsafe { MTLCommandBuffer::device(self.0) };
        debug_assert!(device != nil);
        unsafe { _make_device_ref(device) }
    }
}
<commit_msg>Implement some methods on CommandBuffer ; need to add more types such as CommandQueueRef and Drawable before I can call this finished<commit_after>#![allow(unused_variables)]

use cocoa::base::{id, nil, YES};
use cocoa::foundation::NSString;
use command_queue::_get_raw_command_queue;
use device::_make_device_ref;
use std::borrow::Cow;
use std::convert::AsRef;
use std::ffi::CStr;
use std::time::Duration;
use sys::{MTLCommandBuffer, MTLCommandQueue, MTLCommandBufferStatus};
use {CommandQueue, DeviceRef};

pub type CommandBufferStatus = MTLCommandBufferStatus;

pub struct CommandBuffer(id);

pub struct Drawable;
pub struct CommandQueueRef;

impl CommandBuffer {
    pub fn new(command_queue: &mut CommandQueue) -> Self {
        let cmd_queue = unsafe { _get_raw_command_queue(command_queue) };
        let cmd_buf = unsafe { cmd_queue.commandBuffer() };
        debug_assert!(cmd_buf != nil);
        CommandBuffer(cmd_buf)
    }

    pub fn with_unretained_references(command_queue: &mut CommandQueue) -> Self {
        let cmd_queue = unsafe { _get_raw_command_queue(command_queue) };
        let cmd_buf_unretained = unsafe { cmd_queue.commandBufferWithUnretainedReferences() };
        debug_assert!(cmd_buf_unretained != nil);
        CommandBuffer(cmd_buf_unretained)
    }

    pub fn enqueue(&mut self) {
        unsafe { self.0.enqueue() }
    }

    pub fn commit(&mut self) {
        unsafe { self.0.commit() }
    }

    pub fn present_drawable(&mut self, drawable: Drawable) {
        unimplemented!();
        //unsafe { self.0.presentDrawable() }
    }

    pub fn present_drawable_at_time(&mut self, drawable: Drawable, time: Duration) {
        unimplemented!();
    }

    pub fn wait_until_scheduled(&mut self) {
        unsafe { self.0.waitUntilScheduled() }
    }

    pub fn wait_until_completed(&mut self) {
        unsafe { self.0.waitUntilCompleted() }
    }

    pub fn get_status(&self) -> CommandBufferStatus {
        unsafe { self.0.status() }
    }

    pub fn get_error(&self) -> id {
        unsafe { self.0.error() }
    }

    pub fn has_retained_references(&self) -> bool {
        unsafe { self.0.retainedReferences() == YES }
    }

    pub fn get_device<'a>(&'a self) -> DeviceRef<'a> {
        let device = unsafe { MTLCommandBuffer::device(self.0) };
        debug_assert!(device != nil);
        unsafe { _make_device_ref(device) }
    }

    pub fn get_command_queue(&self) -> CommandQueueRef {
        unimplemented!();
    }

    pub fn set_label<S: AsRef<str>>(&mut self, label: S) {
        unsafe { MTLCommandBuffer::setLabel(self.0, NSString::alloc(nil).init_str(label.as_ref())) }
    }

    pub fn get_label(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(MTLCommandBuffer::label(self.0).UTF8String()).to_string_lossy() }
    }
}
<|endoftext|>


<commit_before>use serenity::model::Mentionable;

command!(join(ctx, msg) {

    let mut channel = None;

    if let Some(guild) = msg.guild() {
        for (_, vs) in guild.read().unwrap().voice_states.iter() {
            if msg.author.id == vs.user_id {
                channel = vs.channel_id;
            }
        }
    };

    let connect_to = match channel {
        Some(id) => id,
        None => {
            let _ = msg.channel_id.say("You need to join a voice channel first");
            return Ok(());
        }
    };

    let guild_id = match msg.guild_id() {
        Some(id) => id,
        None => {
            let _ = msg.channel_id.say("Groups and DMs not supported");
            return Ok(());
        }
    };

    let mut shard = ctx.shard.lock();
    shard.manager.join(guild_id, connect_to);

    let _ = msg.channel_id.say(&format!("Joined {}", connect_to.mention()));
});

command!(leave(ctx, msg) {
    let guild_id = match msg.guild_id() {
        Some(id) => id,
        None => {
            let _ = msg.channel_id.say("Groups and DMs not supported");
            return Ok(());
        }
    };

    let mut shard = ctx.shard.lock();
    let has_handler = shard.manager.get(guild_id).is_some();

    if has_handler {
        shard.manager.remove(guild_id);
        let _ = msg.channel_id.say("Left voice channel");
    } else {
        let _ = msg.channel_id.say("Not in a voice channel");
    }
});

use serenity::voice;

command!(play(ctx, msg) {
    let guild_id = match msg.guild_id() {
        Some(id) => id,
        None => {
            let _ = msg.channel_id.say("Error finding guild id");
            return Ok(());
        }
    };

    let airhorn_sound = ".\\audio\\airhorn.dca";

    if let Some(handler) = ctx.shard.lock().manager.get(guild_id) {
        let source = match voice::dca(airhorn_sound) {
            Ok(source) => source,
            Err(why) => {
                println!("Err starting source: {:?}", why);

                let _ = msg.channel_id.say("Couldn't find file");

                return Ok(());
            },
        };

        handler.play(source);

        let _ = msg.channel_id.say("Playing");
    } else {
        let _ = msg.channel_id.say("Not in a voice channel");
    }
});<commit_msg>Simplified finding user's channel<commit_after>use serenity::model::Mentionable;

command!(join(ctx, msg) {
    let channel = msg.guild().and_then(|guild| {
        let mut result = None;
        for (_, vs) in guild.read().unwrap().voice_states.iter() {
            if msg.author.id == vs.user_id {
                result = vs.channel_id;
            }
        }
        result
    });

    let connect_to = match channel {
        Some(id) => id,
        None => {
            let _ = msg.channel_id.say("You need to join a voice channel first");
            return Ok(());
        }
    };

    let guild_id = match msg.guild_id() {
        Some(id) => id,
        None => {
            let _ = msg.channel_id.say("Groups and DMs not supported");
            return Ok(());
        }
    };

    let mut shard = ctx.shard.lock();
    shard.manager.join(guild_id, connect_to);

    let _ = msg.channel_id.say(&format!("Joined {}", connect_to.mention()));
});

command!(leave(ctx, msg) {
    let guild_id = match msg.guild_id() {
        Some(id) => id,
        None => {
            let _ = msg.channel_id.say("Groups and DMs not supported");
            return Ok(());
        }
    };

    let mut shard = ctx.shard.lock();
    let has_handler = shard.manager.get(guild_id).is_some();

    if has_handler {
        shard.manager.remove(guild_id);
        let _ = msg.channel_id.say("Left voice channel");
    } else {
        let _ = msg.channel_id.say("Not in a voice channel");
    }
});

use serenity::voice;

command!(play(ctx, msg) {
    let guild_id = match msg.guild_id() {
        Some(id) => id,
        None => {
            let _ = msg.channel_id.say("Error finding guild id");
            return Ok(());
        }
    };

    let airhorn_sound = ".\\audio\\airhorn.dca";

    if let Some(handler) = ctx.shard.lock().manager.get(guild_id) {
        let source = match voice::dca(airhorn_sound) {
            Ok(source) => source,
            Err(why) => {
                println!("Err starting source: {:?}", why);

                let _ = msg.channel_id.say("Couldn't find file");

                return Ok(());
            },
        };

        handler.play(source);

        let _ = msg.channel_id.say("Playing");
    } else {
        let _ = msg.channel_id.say("Not in a voice channel");
    }
});<|endoftext|>


<commit_before>
extern crate gitlab_api as gitlab;

use std::env;
use gitlab::GitLab;
use gitlab::Pagination;

fn main() {
    let hostname = match env::var("GITLAB_HOSTNAME") {
        Ok(val) => val,
        Err(_) => {
            let default = String::from("gitlab.com");
            println!("Please set environment variable 'GITLAB_HOSTNAME'. Using default '{}'.",
                     default);
            default
        }
    };

    let token = match env::var("GITLAB_TOKEN") {
        Ok(val) => val,
        Err(_) => {
            panic!("Please set environment variable 'GITLAB_TOKEN'. Take it from \
                    http://{}/profile/account",
                   hostname);
        }
    };

    let mut gl = GitLab::new_https(&hostname, &token);
    // for i in 1..82 {
    //     gl.set_pagination(Pagination{page: i, per_page: 1});
    //     println!("projects: {:?}", gl.projects().unwrap());
    // }
    // gl.set_pagination(Pagination {
    //     page: 1,
    //     per_page: 100,
    // });
    // let _projects = gl.projects().unwrap();
    // FIXME: Project's members are private
    // for project in projects {
    //     println!("{:?}", project.path_with_namespace);
    // }
}
<commit_msg>Print some projects<commit_after>
extern crate gitlab_api as gitlab;

use std::env;
use gitlab::GitLab;
use gitlab::Pagination;

fn main() {
    let hostname = match env::var("GITLAB_HOSTNAME") {
        Ok(val) => val,
        Err(_) => {
            let default = String::from("gitlab.com");
            println!("Please set environment variable 'GITLAB_HOSTNAME'. Using default '{}'.",
                     default);
            default
        }
    };

    let token = match env::var("GITLAB_TOKEN") {
        Ok(val) => val,
        Err(_) => {
            panic!("Please set environment variable 'GITLAB_TOKEN'. Take it from \
                    http://{}/profile/account",
                   hostname);
        }
    };

    let mut gl = GitLab::new_https(&hostname, &token);
    // for i in 1..82 {
    //     gl.set_pagination(Pagination{page: i, per_page: 1});
    //     println!("projects: {:?}", gl.projects().unwrap());
    // }
    // gl.set_pagination(Pagination {
    //     page: 1,
    //     per_page: 100,
    // });
    let projects = gl.projects(gitlab::projects::Listing::new()).unwrap();
    println!("projects: {:?}", projects);
    // FIXME: Project's members are private
    // for project in projects {
    //     println!("{:?}", project.path_with_namespace);
    // }

    let projects = gl.projects_all(gitlab::projects::all::Listing::new()).unwrap();
    println!("projects: {:?}", projects);

    let projects = gl.projects_id(gitlab::projects::id::Listing::new(gitlab::projects::id::ListingId::Id(10))).unwrap();
    println!("projects: {:?}", projects);
}
<|endoftext|>


<commit_before>use memory::PAddr;

/// Represents an ELF section header
#[derive(Debug)]
#[repr(C)]
pub struct Header { name_offset: u32
                  , pub ty: Type
                  , pub flags: PAddr
                  , pub address: PAddr
                  , offset: PAddr
                  , pub length: PAddr
                  , link: u32
                  , info: u32
                  , address_align: u32
                  , entry_length: PAddr
                  }

/// Iterator over ELF64 sections
#[derive(Clone,Debug)]
pub struct Sections { curr: &'static Header
                    , remaining: u32
                    , size: u32
                    }

impl Iterator for Sections {
  type Item = &'static Header;

  fn next(&mut self) -> Option<&'static Header> {
      if self.remaining == 0 {
          None
      } else {
          let current = self.curr;
          self.curr = unsafe {
              &*(((self.curr as *const Header) as u32 + self.size)
                  as *const Header)
          };
          self.remaining -= 1;
          if current.ty == Type::Null {
              self.next()
          } else {
              Some(current)
          }
      }
  }
}

/// Enum representing an ELF file section type.
///
/// Refer to Figure 1-10: "Section Types, sh_type" in Section 1 of the
/// [ELF standard](http://www.sco.com/developers/gabi/latest/ch4.sheader.html)
/// for more information.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
#[repr(u32)]
pub enum Type {
    /// Section type 0: `SHT_NULL`
    ///
    /// This value marks the section header as inactive; it does not have an
    /// associated section. Other members of the section header have
    /// undefined values.
    Null         = 0
  , /// Section type 1: `SHT_PROGBITS`
    ///
    /// The section holds information defined by the program, whose format and
    /// meaning are determined solely by the program.
    ProgramBits = 1
  , /// Section type 2: `SHT_SYMTAB`
    ///
    /// Typically, `SHT_SYMTAB` provides symbols for link editing, though it
    /// may also be used for dynamic linking. As a complete symbol table, it
    /// may contain many symbols unneces- sary for dynamic linking.
    ///
    /// Consequently, an object file may also contain a `SHT_DYNSYM` section,
    /// which holds a minimal set of dynamic linking symbols, to save space.
    SymbolTable = 2
  , /// Section type 3: `SHT_STRTAB`
    ///
    /// The section holds a string table. An object file may have multiple
    /// string table sections.
    StringTable  = 3
  , /// Section type 4: `SHT_RELA`
    ///
    /// The section holds relocation entries with explicit addends, such as
    /// type `Elf32_Rela` for the 32-bit class of object files. An object file
    /// may have multiple relocation sections.
    Rela         = 4
  , /// Section type 5: `SHT_HASH`
    ///
    /// The section holds a symbol hash table. All objects participating in
    /// dynamic linking must contain a symbol hash table. Currently, an object
    /// file may have only one hash table, but this restriction may be relaxed
    /// in the future.
    HashTable   = 5
  , /// Section type 6: `SHT_DYNAMIC`
    ///
    /// The section holds information for dynamic linking. Currently, an object
    /// file may have only one dynamic section, but this restriction may be
    ///  relaxed in the future.
    Dynamic     = 6
  , /// Section type 7: `SHT_NOTE`
    ///
    /// The section holds information that marks the file in some way.
    Notes       = 7
  , /// Section type 8: `SHT_NOBITS`
    ///
    /// A section of this type occupies no space in the file but otherwise
    /// resembles `SHT_PROGBITS`. Although this section contains no bytes, the
    /// `sh_offset` member contains the conceptual file offset.
    NoBits      = 8
  , /// Section type 9: `SHT_REL`
    ///
    /// The section holds relocation entries without explicit addends, such as
    /// type `Elf32_Rel` for the 32-bit class of object files. An object file
    /// may have multiple reloca- tion sections.
    Rel         = 9
  , /// Section type 10: `SHT_SHLIB`
    ///
    /// This section type is reserved but has unspecified semantics. Programs
    /// that contain a section of this type do not conform to the ABI.
    Shlib       = 10
  , /// Section type 11: `SHT_DYNSYM`
    ///
    /// Typically, `SHT_SYMTAB` provides symbols for link editing, though it
    /// may also be used for dynamic linking. As a complete symbol table, it
    /// may contain many symbols unneces- sary for dynamic linking.
    ///
    /// Consequently, an object file may also contain a `SHT_DYNSYM` section,
    /// which holds a minimal set of dynamic linking symbols, to save space.
    DynSymTable = 11
    // The remainder is reserved
}

#[derive(Debug)]
#[repr(u32)]
pub enum Flags { Writable    = 0x1
               , Allocated   = 0x2
               , Executable  = 0x4
               }
<commit_msg>[kernel] ELF section iterator does need to live in multiboot module<commit_after>use memory::PAddr;

/// Represents an ELF section header
#[derive(Debug)]
#[repr(C)]
pub struct Header { name_offset: u32
                  , pub ty: Type
                  , pub flags: PAddr
                  , pub address: PAddr
                  , offset: PAddr
                  , pub length: PAddr
                  , link: u32
                  , info: u32
                  , address_align: u32
                  , entry_length: PAddr
                  }

/// Enum representing an ELF file section type.
///
/// Refer to Figure 1-10: "Section Types, sh_type" in Section 1 of the
/// [ELF standard](http://www.sco.com/developers/gabi/latest/ch4.sheader.html)
/// for more information.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
#[repr(u32)]
pub enum Type {
    /// Section type 0: `SHT_NULL`
    ///
    /// This value marks the section header as inactive; it does not have an
    /// associated section. Other members of the section header have
    /// undefined values.
    Null         = 0
  , /// Section type 1: `SHT_PROGBITS`
    ///
    /// The section holds information defined by the program, whose format and
    /// meaning are determined solely by the program.
    ProgramBits = 1
  , /// Section type 2: `SHT_SYMTAB`
    ///
    /// Typically, `SHT_SYMTAB` provides symbols for link editing, though it
    /// may also be used for dynamic linking. As a complete symbol table, it
    /// may contain many symbols unneces- sary for dynamic linking.
    ///
    /// Consequently, an object file may also contain a `SHT_DYNSYM` section,
    /// which holds a minimal set of dynamic linking symbols, to save space.
    SymbolTable = 2
  , /// Section type 3: `SHT_STRTAB`
    ///
    /// The section holds a string table. An object file may have multiple
    /// string table sections.
    StringTable  = 3
  , /// Section type 4: `SHT_RELA`
    ///
    /// The section holds relocation entries with explicit addends, such as
    /// type `Elf32_Rela` for the 32-bit class of object files. An object file
    /// may have multiple relocation sections.
    Rela         = 4
  , /// Section type 5: `SHT_HASH`
    ///
    /// The section holds a symbol hash table. All objects participating in
    /// dynamic linking must contain a symbol hash table. Currently, an object
    /// file may have only one hash table, but this restriction may be relaxed
    /// in the future.
    HashTable   = 5
  , /// Section type 6: `SHT_DYNAMIC`
    ///
    /// The section holds information for dynamic linking. Currently, an object
    /// file may have only one dynamic section, but this restriction may be
    ///  relaxed in the future.
    Dynamic     = 6
  , /// Section type 7: `SHT_NOTE`
    ///
    /// The section holds information that marks the file in some way.
    Notes       = 7
  , /// Section type 8: `SHT_NOBITS`
    ///
    /// A section of this type occupies no space in the file but otherwise
    /// resembles `SHT_PROGBITS`. Although this section contains no bytes, the
    /// `sh_offset` member contains the conceptual file offset.
    NoBits      = 8
  , /// Section type 9: `SHT_REL`
    ///
    /// The section holds relocation entries without explicit addends, such as
    /// type `Elf32_Rel` for the 32-bit class of object files. An object file
    /// may have multiple reloca- tion sections.
    Rel         = 9
  , /// Section type 10: `SHT_SHLIB`
    ///
    /// This section type is reserved but has unspecified semantics. Programs
    /// that contain a section of this type do not conform to the ABI.
    Shlib       = 10
  , /// Section type 11: `SHT_DYNSYM`
    ///
    /// Typically, `SHT_SYMTAB` provides symbols for link editing, though it
    /// may also be used for dynamic linking. As a complete symbol table, it
    /// may contain many symbols unneces- sary for dynamic linking.
    ///
    /// Consequently, an object file may also contain a `SHT_DYNSYM` section,
    /// which holds a minimal set of dynamic linking symbols, to save space.
    DynSymTable = 11
    // The remainder is reserved
}

#[derive(Debug)]
#[repr(u32)]
pub enum Flags { Writable    = 0x1
               , Allocated   = 0x2
               , Executable  = 0x4
               }
<|endoftext|>


<commit_before>/* 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 http://mozilla.org/MPL/2.0/. */

use dom::audiodestinationnode::AudioDestinationNode;
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::AudioNodeBinding::AudioNodeOptions;
use dom::bindings::codegen::Bindings::AudioNodeBinding::{ChannelCountMode, ChannelInterpretation};
use dom::bindings::codegen::Bindings::BaseAudioContextBinding::BaseAudioContextMethods;
use dom::bindings::codegen::Bindings::BaseAudioContextBinding::AudioContextState;
use dom::bindings::codegen::Bindings::OscillatorNodeBinding::OscillatorOptions;
use dom::bindings::inheritance::Castable;
use dom::bindings::num::Finite;
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::{DomObject, Reflector};
use dom::bindings::root::DomRoot;
use dom::globalscope::GlobalScope;
use dom::oscillatornode::OscillatorNode;
use dom::promise::Promise;
use dom::window::Window;
use dom_struct::dom_struct;
use servo_media::ServoMedia;
use servo_media::audio::graph::{AudioGraph, ProcessingState};
use servo_media::audio::graph::{OfflineAudioGraphOptions, RealTimeAudioGraphOptions};
use servo_media::audio::graph_impl::NodeId;
use servo_media::audio::node::AudioNodeType;
use std::rc::Rc;
use task_source::TaskSource;

pub enum BaseAudioContextOptions {
    AudioContext(RealTimeAudioGraphOptions),
    OfflineAudioContext(OfflineAudioGraphOptions),
}

#[dom_struct]
pub struct BaseAudioContext {
    reflector_: Reflector,
    #[ignore_malloc_size_of = "servo_media"]
    audio_graph: AudioGraph,
    /// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-destination
    destination: Option<DomRoot<AudioDestinationNode>>,
    /// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-samplerate
    sample_rate: f32,
    /// https://webaudio.github.io/web-audio-api/#pendingresumepromises
    #[ignore_malloc_size_of = "promises are hard"]
    pending_resume_promises: DomRefCell<Vec<Rc<Promise>>>,
}

impl BaseAudioContext {
    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    pub fn new_inherited(
        global: &GlobalScope,
        options: BaseAudioContextOptions,
        ) -> BaseAudioContext {
        let options = match options {
            BaseAudioContextOptions::AudioContext(options) => options,
            BaseAudioContextOptions::OfflineAudioContext(_) => unimplemented!(),
        };

        let sample_rate = options.sample_rate;

        let mut context = BaseAudioContext {
            reflector_: Reflector::new(),
            audio_graph: ServoMedia::get().unwrap().create_audio_graph(Some(options.into())),
            destination: None,
            sample_rate,
            pending_resume_promises: Default::default(),
        };

        let mut options = unsafe { AudioNodeOptions::empty(global.get_cx()) };
        options.channelCount = Some(2);
        options.channelCountMode = Some(ChannelCountMode::Explicit);
        options.channelInterpretation = Some(ChannelInterpretation::Speakers);

        context.destination = Some(AudioDestinationNode::new(global, &context, &options));

        context
    }

    pub fn create_node_engine(&self, node_type: AudioNodeType) -> NodeId {
        self.audio_graph.create_node(node_type)
    }

    // https://webaudio.github.io/web-audio-api/#allowed-to-start
    pub fn is_allowed_to_start(&self) -> bool {
        let state: AudioContextState = self.audio_graph.state().into();
        state == AudioContextState::Suspended
    }

    pub fn resume(&self) {
        let window = DomRoot::downcast::<Window>(self.global()).unwrap();
        let task_source = window.dom_manipulation_task_source();

        // Set the state attribute to `running` and start rendering audio.
        let this = Trusted::new(self);
        task_source.queue(task!(resume: move || {
            let this = this.root();
            this.audio_graph.resume();
        }), window.upcast()).unwrap();

        // Queue a task to fire a simple event named `statechange` at the AudioContext.
        task_source.queue_simple_event(
            self.upcast(),
            atom!("statechange"),
            &window,
            );
    }
}

impl BaseAudioContextMethods for BaseAudioContext {
    // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-samplerate
    fn SampleRate(&self) -> Finite<f32> {
        Finite::wrap(self.sample_rate)
    }

    // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-currenttime
    fn CurrentTime(&self) -> Finite<f64> {
        let current_time = self.audio_graph.current_time();
        Finite::wrap(current_time)
    }

    // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-state
    fn State(&self) -> AudioContextState {
        self.audio_graph.state().into()
    }

    // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-resume
    #[allow(unrooted_must_root)]
    fn Resume(&self) -> Rc<Promise> {
        // TODO
        Promise::new(&self.global())
    }

    // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-destination
    fn Destination(&self) -> DomRoot<AudioDestinationNode> {
        DomRoot::from_ref(self.destination.as_ref().unwrap())
    }

    // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-onstatechange
    event_handler!(statechange, GetOnstatechange, SetOnstatechange);

    #[allow(unsafe_code)]
    fn CreateOscillator(&self) -> DomRoot<OscillatorNode> {
        let global = self.global();
        let window = global.as_window();
        let options = unsafe { OscillatorOptions::empty(window.get_cx()) };
        OscillatorNode::new(&window, &self, &options)
    }
}

impl From<ProcessingState> for AudioContextState {
    fn from(state: ProcessingState) -> Self {
        match state {
            ProcessingState::Suspended => AudioContextState::Suspended,
            ProcessingState::Running => AudioContextState::Running,
            ProcessingState::Closed => AudioContextState::Closed,
        }
    }
}
<commit_msg>AudioContext.resume() implementation<commit_after>/* 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 http://mozilla.org/MPL/2.0/. */

use dom::audiodestinationnode::AudioDestinationNode;
use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::AudioNodeBinding::AudioNodeOptions;
use dom::bindings::codegen::Bindings::AudioNodeBinding::{ChannelCountMode, ChannelInterpretation};
use dom::bindings::codegen::Bindings::BaseAudioContextBinding::BaseAudioContextMethods;
use dom::bindings::codegen::Bindings::BaseAudioContextBinding::AudioContextState;
use dom::bindings::codegen::Bindings::OscillatorNodeBinding::OscillatorOptions;
use dom::bindings::error::Error;
use dom::bindings::inheritance::Castable;
use dom::bindings::num::Finite;
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::{DomObject, Reflector};
use dom::bindings::root::DomRoot;
use dom::globalscope::GlobalScope;
use dom::oscillatornode::OscillatorNode;
use dom::promise::Promise;
use dom::window::Window;
use dom_struct::dom_struct;
use servo_media::ServoMedia;
use servo_media::audio::graph::{AudioGraph, ProcessingState};
use servo_media::audio::graph::{OfflineAudioGraphOptions, RealTimeAudioGraphOptions};
use servo_media::audio::graph_impl::NodeId;
use servo_media::audio::node::AudioNodeType;
use std::rc::Rc;
use task_source::TaskSource;

pub enum BaseAudioContextOptions {
    AudioContext(RealTimeAudioGraphOptions),
    OfflineAudioContext(OfflineAudioGraphOptions),
}

#[dom_struct]
pub struct BaseAudioContext {
    reflector_: Reflector,
    #[ignore_malloc_size_of = "servo_media"]
    audio_graph: AudioGraph,
    /// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-destination
    destination: Option<DomRoot<AudioDestinationNode>>,
    /// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-samplerate
    sample_rate: f32,
    /// https://webaudio.github.io/web-audio-api/#pendingresumepromises
    #[ignore_malloc_size_of = "promises are hard"]
    pending_resume_promises: DomRefCell<Vec<Rc<Promise>>>,
}

impl BaseAudioContext {
    #[allow(unrooted_must_root)]
    #[allow(unsafe_code)]
    pub fn new_inherited(
        global: &GlobalScope,
        options: BaseAudioContextOptions,
        ) -> BaseAudioContext {
        let options = match options {
            BaseAudioContextOptions::AudioContext(options) => options,
            BaseAudioContextOptions::OfflineAudioContext(_) => unimplemented!(),
        };

        let sample_rate = options.sample_rate;

        let mut context = BaseAudioContext {
            reflector_: Reflector::new(),
            audio_graph: ServoMedia::get().unwrap().create_audio_graph(Some(options.into())),
            destination: None,
            sample_rate,
            pending_resume_promises: Default::default(),
        };

        let mut options = unsafe { AudioNodeOptions::empty(global.get_cx()) };
        options.channelCount = Some(2);
        options.channelCountMode = Some(ChannelCountMode::Explicit);
        options.channelInterpretation = Some(ChannelInterpretation::Speakers);

        context.destination = Some(AudioDestinationNode::new(global, &context, &options));

        context
    }

    pub fn create_node_engine(&self, node_type: AudioNodeType) -> NodeId {
        self.audio_graph.create_node(node_type)
    }

    // https://webaudio.github.io/web-audio-api/#allowed-to-start
    pub fn is_allowed_to_start(&self) -> bool {
        self.audio_graph.state() == ProcessingState::Suspended
    }

    pub fn resume(&self) {
        let window = DomRoot::downcast::<Window>(self.global()).unwrap();
        let task_source = window.dom_manipulation_task_source();

        // Set the state attribute to `running` and start rendering audio.
        let this = Trusted::new(self);
        task_source.queue(task!(resume: move || {
            let this = this.root();
            this.audio_graph.resume();
        }), window.upcast()).unwrap();

        // Queue a task to fire a simple event named `statechange` at the AudioContext.
        task_source.queue_simple_event(
            self.upcast(),
            atom!("statechange"),
            &window,
            );
    }
}

impl BaseAudioContextMethods for BaseAudioContext {
    // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-samplerate
    fn SampleRate(&self) -> Finite<f32> {
        Finite::wrap(self.sample_rate)
    }

    // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-currenttime
    fn CurrentTime(&self) -> Finite<f64> {
        let current_time = self.audio_graph.current_time();
        Finite::wrap(current_time)
    }

    // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-state
    fn State(&self) -> AudioContextState {
        self.audio_graph.state().into()
    }

    // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-resume
    #[allow(unrooted_must_root)]
    fn Resume(&self) -> Rc<Promise> {
        // Step 1.
        let promise = Promise::new(&self.global());

        // Step 2.
        let state = self.audio_graph.state();
        if state == ProcessingState::Closed {
            promise.reject_error(Error::InvalidState);
            return promise;
        }

        // Step 3.
        if state == ProcessingState::Running {
            promise.resolve_native(&());
            return promise;
        }

        // Step 4.
        if !self.is_allowed_to_start() {
            self.pending_resume_promises.borrow_mut().push(promise.clone());
            return promise;
        }

        // Steps 5 and 6.
        self.resume();

        // Step 7.
        promise
    }

    // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-destination
    fn Destination(&self) -> DomRoot<AudioDestinationNode> {
        DomRoot::from_ref(self.destination.as_ref().unwrap())
    }

    // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-onstatechange
    event_handler!(statechange, GetOnstatechange, SetOnstatechange);

    #[allow(unsafe_code)]
    fn CreateOscillator(&self) -> DomRoot<OscillatorNode> {
        let global = self.global();
        let window = global.as_window();
        let options = unsafe { OscillatorOptions::empty(window.get_cx()) };
        OscillatorNode::new(&window, &self, &options)
    }
}

impl From<ProcessingState> for AudioContextState {
    fn from(state: ProcessingState) -> Self {
        match state {
            ProcessingState::Suspended => AudioContextState::Suspended,
            ProcessingState::Running => AudioContextState::Running,
            ProcessingState::Closed => AudioContextState::Closed,
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2022 TiKV Project Authors. Licensed under Apache-2.0.

use std::{
    sync::{
        atomic::{AtomicU64, Ordering},
        Arc,
    },
    time::Duration,
};

use cpu_time::ThreadTime;
use futures::compat::Future01CompatExt;
use online_config::{ConfigChange, ConfigManager};

use super::{
    config::{ReadableDuration, ReadableSize},
    time::Limiter,
    timer::GLOBAL_TIMER_HANDLE,
};

// TODO: This value is fixed based on experience of AWS 4vCPU TPC-C bench test.
// It's better to use a universal approach.
const CPU_LIMITER_REFILL_DURATION: Duration = Duration::from_millis(100);

// Quota limiter allows users to obtain stable performance by increasing the
// completion time of tasks through restrictions of different metrics.
#[derive(Debug)]
pub struct QuotaLimiter {
    cputime_limiter: Limiter,
    write_bandwidth_limiter: Limiter,
    read_bandwidth_limiter: Limiter,
    // max delay nano seconds
    max_delay_duration: AtomicU64,
}

// Throttle must be consumed in quota limiter.
pub struct Sample {
    read_bytes: usize,
    write_bytes: usize,
    cpu_time: Duration,
    enable_cpu_limit: bool,
}

impl<'a> Sample {
    pub fn add_read_bytes(&mut self, bytes: usize) {
        self.read_bytes += bytes;
    }

    pub fn add_write_bytes(&mut self, bytes: usize) {
        self.write_bytes += bytes;
    }

    // Record the cpu time in the lifetime. Use this function inside code block.
    // If `cputime_limiter` is not enabled, guard will do nothing when dropped.
    pub fn observe_cpu(&'a mut self) -> CpuObserveGuard<'a> {
        if self.enable_cpu_limit {
            CpuObserveGuard {
                timer: Some(ThreadTime::now()),
                sample: self,
            }
        } else {
            CpuObserveGuard {
                timer: None,
                sample: self,
            }
        }
    }

    fn add_cpu_time(&mut self, time: Duration) {
        self.cpu_time += time;
    }
}

pub struct CpuObserveGuard<'a> {
    timer: Option<ThreadTime>,
    sample: &'a mut Sample,
}

impl<'a> Drop for CpuObserveGuard<'a> {
    fn drop(&mut self) {
        if let Some(timer) = self.timer {
            self.sample.add_cpu_time(timer.elapsed());
        }
    }
}

impl Default for QuotaLimiter {
    fn default() -> Self {
        Self {
            cputime_limiter: Limiter::new(f64::INFINITY),
            write_bandwidth_limiter: Limiter::new(f64::INFINITY),
            read_bandwidth_limiter: Limiter::new(f64::INFINITY),
            max_delay_duration: AtomicU64::new(0),
        }
    }
}

impl QuotaLimiter {
    // 1000 millicpu equals to 1vCPU, 0 means unlimited
    pub fn new(
        cpu_quota: usize,
        write_bandwidth: ReadableSize,
        read_bandwidth: ReadableSize,
        max_delay_duration: ReadableDuration,
    ) -> Self {
        let cputime_limiter = Limiter::builder(Self::speed_limit(cpu_quota as f64 * 1000_f64))
            .refill(CPU_LIMITER_REFILL_DURATION)
            .build();

        let write_bandwidth_limiter = Limiter::new(Self::speed_limit(write_bandwidth.0 as f64));

        let read_bandwidth_limiter = Limiter::new(Self::speed_limit(read_bandwidth.0 as f64));

        let max_delay_duration = AtomicU64::new(max_delay_duration.0.as_nanos() as u64);

        Self {
            cputime_limiter,
            write_bandwidth_limiter,
            read_bandwidth_limiter,
            max_delay_duration,
        }
    }

    fn speed_limit(quota: f64) -> f64 {
        if quota < f64::EPSILON {
            f64::INFINITY
        } else {
            quota
        }
    }

    pub fn set_cpu_time_limit(&self, quota_limit: usize) {
        self.cputime_limiter
            .set_speed_limit(Self::speed_limit(quota_limit as f64 * 1000_f64));
    }

    pub fn set_write_bandwidth_limit(&self, write_bandwidth: ReadableSize) {
        self.write_bandwidth_limiter
            .set_speed_limit(write_bandwidth.0 as f64);
    }

    pub fn set_read_bandwidth_limit(&self, read_bandwidth: ReadableSize) {
        self.read_bandwidth_limiter
            .set_speed_limit(read_bandwidth.0 as f64);
    }

    pub fn set_max_delay_duration(&self, duration: ReadableDuration) {
        self.max_delay_duration
            .store(duration.0.as_nanos() as u64, Ordering::Relaxed);
    }

    fn max_delay_duration(&self) -> Duration {
        Duration::from_nanos(self.max_delay_duration.load(Ordering::Relaxed))
    }

    // To generate a sampler.
    pub fn new_sample(&self) -> Sample {
        Sample {
            read_bytes: 0,
            write_bytes: 0,
            cpu_time: Duration::ZERO,
            enable_cpu_limit: !self.cputime_limiter.speed_limit().is_infinite(),
        }
    }

    // To consume a sampler and return delayed duration.
    // If the sampler is null, the speed limiter will just return ZERO.
    pub async fn async_consume(&self, sample: Sample) -> Duration {
        let cpu_dur = if sample.cpu_time > Duration::ZERO {
            self.cputime_limiter
                .consume_duration(sample.cpu_time.as_micros() as usize)
        } else {
            Duration::ZERO
        };

        let w_bw_dur = if sample.write_bytes > 0 {
            self.write_bandwidth_limiter
                .consume_duration(sample.write_bytes)
        } else {
            Duration::ZERO
        };

        let r_bw_dur = if sample.read_bytes > 0 {
            self.read_bandwidth_limiter
                .consume_duration(sample.read_bytes)
        } else {
            Duration::ZERO
        };

        let mut exec_delay = std::cmp::max(cpu_dur, std::cmp::max(w_bw_dur, r_bw_dur));
        let delay_duration = self.max_delay_duration();
        if !delay_duration.is_zero() {
            exec_delay = std::cmp::min(delay_duration, exec_delay);
        };

        if !exec_delay.is_zero() {
            GLOBAL_TIMER_HANDLE
                .delay(std::time::Instant::now() + exec_delay)
                .compat()
                .await
                .unwrap();
        }

        exec_delay
    }
}

pub struct QuotaLimitConfigManager {
    limiter: Arc<QuotaLimiter>,
}

impl QuotaLimitConfigManager {
    pub fn new(limiter: Arc<QuotaLimiter>) -> Self {
        Self { limiter }
    }
}

impl ConfigManager for QuotaLimitConfigManager {
    fn dispatch(
        &mut self,
        change: ConfigChange,
    ) -> std::result::Result<(), Box<dyn std::error::Error>> {
        if let Some(cpu_limit) = change.get("foreground_cpu_time") {
            self.limiter.set_cpu_time_limit(cpu_limit.into());
        }
        if let Some(write_bandwidth) = change.get("foreground_write_bandwidth") {
            self.limiter
                .set_write_bandwidth_limit(write_bandwidth.clone().into())
        }
        if let Some(read_bandwidth) = change.get("foreground_read_bandwidth") {
            self.limiter
                .set_write_bandwidth_limit(read_bandwidth.clone().into());
        }
        if let Some(duration) = change.get("max_delay_duration") {
            let delay_dur: ReadableDuration = duration.clone().into();
            self.limiter
                .max_delay_duration
                .store(delay_dur.0.as_nanos() as u64, Ordering::Relaxed);
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use futures::executor::block_on;

    use super::*;

    #[test]
    fn test_quota_limiter() {
        // refill duration = 100ms
        // bucket capacity = 100
        let quota_limiter = QuotaLimiter::new(
            1000,
            ReadableSize::kb(1),
            ReadableSize::kb(1),
            ReadableDuration::millis(0),
        );

        let thread_start_time = ThreadTime::now();

        let check_duration = |actual: Duration, expected: Duration| {
            // time delay may be less than expected because the cpu time is keep-going.
            assert!(
                actual >= expected.saturating_sub(Duration::from_millis(5)) && actual <= expected
            );
        };

        let mut sample = quota_limiter.new_sample();
        sample.add_cpu_time(Duration::from_millis(60));
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::ZERO);

        let mut sample = quota_limiter.new_sample();
        sample.add_cpu_time(Duration::from_millis(50));
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::from_millis(110));

        std::thread::sleep(Duration::from_millis(10));

        let mut sample = quota_limiter.new_sample();
        sample.add_cpu_time(Duration::from_millis(20));
        let should_delay = block_on(quota_limiter.async_consume(sample));
        // should less 60+50+20
        assert!(should_delay < Duration::from_millis(130));

        let mut sample = quota_limiter.new_sample();
        sample.add_cpu_time(Duration::from_millis(200));
        sample.add_write_bytes(256);
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::from_millis(250));

        // ThreadTime elapsed time is not long.
        assert!(thread_start_time.elapsed() < Duration::from_millis(50));

        quota_limiter.set_cpu_time_limit(2000);
        let mut sample = quota_limiter.new_sample();
        sample.add_cpu_time(Duration::from_millis(200));
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::from_millis(100));

        quota_limiter.set_read_bandwidth_limit(ReadableSize(512));
        let mut sample = quota_limiter.new_sample();
        sample.add_read_bytes(128);
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::from_millis(250));

        quota_limiter.set_write_bandwidth_limit(ReadableSize::kb(2));
        let mut sample = quota_limiter.new_sample();
        sample.add_write_bytes(256);
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::from_millis(125));

        quota_limiter.set_max_delay_duration(ReadableDuration::millis(40));
        let mut sample = quota_limiter.new_sample();
        sample.add_read_bytes(256);
        sample.add_write_bytes(512);
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::from_millis(40));
    }
}
<commit_msg>quota-limiter: fix panic when set limiter value to 0 (#12623)<commit_after>// Copyright 2022 TiKV Project Authors. Licensed under Apache-2.0.

use std::{
    sync::{
        atomic::{AtomicU64, Ordering},
        Arc,
    },
    time::Duration,
};

use cpu_time::ThreadTime;
use futures::compat::Future01CompatExt;
use online_config::{ConfigChange, ConfigManager};

use super::{
    config::{ReadableDuration, ReadableSize},
    time::Limiter,
    timer::GLOBAL_TIMER_HANDLE,
};

// TODO: This value is fixed based on experience of AWS 4vCPU TPC-C bench test.
// It's better to use a universal approach.
const CPU_LIMITER_REFILL_DURATION: Duration = Duration::from_millis(100);

// Quota limiter allows users to obtain stable performance by increasing the
// completion time of tasks through restrictions of different metrics.
#[derive(Debug)]
pub struct QuotaLimiter {
    cputime_limiter: Limiter,
    write_bandwidth_limiter: Limiter,
    read_bandwidth_limiter: Limiter,
    // max delay nano seconds
    max_delay_duration: AtomicU64,
}

// Throttle must be consumed in quota limiter.
pub struct Sample {
    read_bytes: usize,
    write_bytes: usize,
    cpu_time: Duration,
    enable_cpu_limit: bool,
}

impl<'a> Sample {
    pub fn add_read_bytes(&mut self, bytes: usize) {
        self.read_bytes += bytes;
    }

    pub fn add_write_bytes(&mut self, bytes: usize) {
        self.write_bytes += bytes;
    }

    // Record the cpu time in the lifetime. Use this function inside code block.
    // If `cputime_limiter` is not enabled, guard will do nothing when dropped.
    pub fn observe_cpu(&'a mut self) -> CpuObserveGuard<'a> {
        if self.enable_cpu_limit {
            CpuObserveGuard {
                timer: Some(ThreadTime::now()),
                sample: self,
            }
        } else {
            CpuObserveGuard {
                timer: None,
                sample: self,
            }
        }
    }

    fn add_cpu_time(&mut self, time: Duration) {
        self.cpu_time += time;
    }
}

pub struct CpuObserveGuard<'a> {
    timer: Option<ThreadTime>,
    sample: &'a mut Sample,
}

impl<'a> Drop for CpuObserveGuard<'a> {
    fn drop(&mut self) {
        if let Some(timer) = self.timer {
            self.sample.add_cpu_time(timer.elapsed());
        }
    }
}

impl Default for QuotaLimiter {
    fn default() -> Self {
        Self {
            cputime_limiter: Limiter::new(f64::INFINITY),
            write_bandwidth_limiter: Limiter::new(f64::INFINITY),
            read_bandwidth_limiter: Limiter::new(f64::INFINITY),
            max_delay_duration: AtomicU64::new(0),
        }
    }
}

impl QuotaLimiter {
    // 1000 millicpu equals to 1vCPU, 0 means unlimited
    pub fn new(
        cpu_quota: usize,
        write_bandwidth: ReadableSize,
        read_bandwidth: ReadableSize,
        max_delay_duration: ReadableDuration,
    ) -> Self {
        let cputime_limiter = Limiter::builder(Self::speed_limit(cpu_quota as f64 * 1000_f64))
            .refill(CPU_LIMITER_REFILL_DURATION)
            .build();

        let write_bandwidth_limiter = Limiter::new(Self::speed_limit(write_bandwidth.0 as f64));

        let read_bandwidth_limiter = Limiter::new(Self::speed_limit(read_bandwidth.0 as f64));

        let max_delay_duration = AtomicU64::new(max_delay_duration.0.as_nanos() as u64);

        Self {
            cputime_limiter,
            write_bandwidth_limiter,
            read_bandwidth_limiter,
            max_delay_duration,
        }
    }

    fn speed_limit(quota: f64) -> f64 {
        if quota < f64::EPSILON {
            f64::INFINITY
        } else {
            quota
        }
    }

    pub fn set_cpu_time_limit(&self, quota_limit: usize) {
        self.cputime_limiter
            .set_speed_limit(Self::speed_limit(quota_limit as f64 * 1000_f64));
    }

    pub fn set_write_bandwidth_limit(&self, write_bandwidth: ReadableSize) {
        self.write_bandwidth_limiter
            .set_speed_limit(Self::speed_limit(write_bandwidth.0 as f64));
    }

    pub fn set_read_bandwidth_limit(&self, read_bandwidth: ReadableSize) {
        self.read_bandwidth_limiter
            .set_speed_limit(Self::speed_limit(read_bandwidth.0 as f64));
    }

    pub fn set_max_delay_duration(&self, duration: ReadableDuration) {
        self.max_delay_duration
            .store(duration.0.as_nanos() as u64, Ordering::Relaxed);
    }

    fn max_delay_duration(&self) -> Duration {
        Duration::from_nanos(self.max_delay_duration.load(Ordering::Relaxed))
    }

    // To generate a sampler.
    pub fn new_sample(&self) -> Sample {
        Sample {
            read_bytes: 0,
            write_bytes: 0,
            cpu_time: Duration::ZERO,
            enable_cpu_limit: !self.cputime_limiter.speed_limit().is_infinite(),
        }
    }

    // To consume a sampler and return delayed duration.
    // If the sampler is null, the speed limiter will just return ZERO.
    pub async fn async_consume(&self, sample: Sample) -> Duration {
        let cpu_dur = if sample.cpu_time > Duration::ZERO {
            self.cputime_limiter
                .consume_duration(sample.cpu_time.as_micros() as usize)
        } else {
            Duration::ZERO
        };

        let w_bw_dur = if sample.write_bytes > 0 {
            self.write_bandwidth_limiter
                .consume_duration(sample.write_bytes)
        } else {
            Duration::ZERO
        };

        let r_bw_dur = if sample.read_bytes > 0 {
            self.read_bandwidth_limiter
                .consume_duration(sample.read_bytes)
        } else {
            Duration::ZERO
        };

        let mut exec_delay = std::cmp::max(cpu_dur, std::cmp::max(w_bw_dur, r_bw_dur));
        let delay_duration = self.max_delay_duration();
        if !delay_duration.is_zero() {
            exec_delay = std::cmp::min(delay_duration, exec_delay);
        };

        if !exec_delay.is_zero() {
            GLOBAL_TIMER_HANDLE
                .delay(std::time::Instant::now() + exec_delay)
                .compat()
                .await
                .unwrap();
        }

        exec_delay
    }
}

pub struct QuotaLimitConfigManager {
    limiter: Arc<QuotaLimiter>,
}

impl QuotaLimitConfigManager {
    pub fn new(limiter: Arc<QuotaLimiter>) -> Self {
        Self { limiter }
    }
}

impl ConfigManager for QuotaLimitConfigManager {
    fn dispatch(
        &mut self,
        change: ConfigChange,
    ) -> std::result::Result<(), Box<dyn std::error::Error>> {
        if let Some(cpu_limit) = change.get("foreground_cpu_time") {
            self.limiter.set_cpu_time_limit(cpu_limit.into());
        }
        if let Some(write_bandwidth) = change.get("foreground_write_bandwidth") {
            self.limiter
                .set_write_bandwidth_limit(write_bandwidth.clone().into())
        }
        if let Some(read_bandwidth) = change.get("foreground_read_bandwidth") {
            self.limiter
                .set_write_bandwidth_limit(read_bandwidth.clone().into());
        }
        if let Some(duration) = change.get("max_delay_duration") {
            let delay_dur: ReadableDuration = duration.clone().into();
            self.limiter
                .max_delay_duration
                .store(delay_dur.0.as_nanos() as u64, Ordering::Relaxed);
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use futures::executor::block_on;

    use super::*;

    #[test]
    fn test_quota_limiter() {
        // refill duration = 100ms
        // bucket capacity = 100
        let quota_limiter = QuotaLimiter::new(
            1000,
            ReadableSize::kb(1),
            ReadableSize::kb(1),
            ReadableDuration::millis(0),
        );

        let thread_start_time = ThreadTime::now();

        let check_duration = |actual: Duration, expected: Duration| {
            // time delay may be less than expected because the cpu time is keep-going.
            assert!(
                actual >= expected.saturating_sub(Duration::from_millis(5)) && actual <= expected
            );
        };

        let mut sample = quota_limiter.new_sample();
        sample.add_cpu_time(Duration::from_millis(60));
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::ZERO);

        let mut sample = quota_limiter.new_sample();
        sample.add_cpu_time(Duration::from_millis(50));
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::from_millis(110));

        std::thread::sleep(Duration::from_millis(10));

        let mut sample = quota_limiter.new_sample();
        sample.add_cpu_time(Duration::from_millis(20));
        let should_delay = block_on(quota_limiter.async_consume(sample));
        // should less 60+50+20
        assert!(should_delay < Duration::from_millis(130));

        let mut sample = quota_limiter.new_sample();
        sample.add_cpu_time(Duration::from_millis(200));
        sample.add_write_bytes(256);
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::from_millis(250));

        // ThreadTime elapsed time is not long.
        assert!(thread_start_time.elapsed() < Duration::from_millis(50));

        quota_limiter.set_cpu_time_limit(2000);
        let mut sample = quota_limiter.new_sample();
        sample.add_cpu_time(Duration::from_millis(200));
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::from_millis(100));

        quota_limiter.set_read_bandwidth_limit(ReadableSize(512));
        let mut sample = quota_limiter.new_sample();
        sample.add_read_bytes(128);
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::from_millis(250));

        quota_limiter.set_write_bandwidth_limit(ReadableSize::kb(2));
        let mut sample = quota_limiter.new_sample();
        sample.add_write_bytes(256);
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::from_millis(125));

        quota_limiter.set_max_delay_duration(ReadableDuration::millis(40));
        let mut sample = quota_limiter.new_sample();
        sample.add_read_bytes(256);
        sample.add_write_bytes(512);
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::from_millis(40));

        // test change limiter to 0
        quota_limiter.set_cpu_time_limit(0);
        let mut sample = quota_limiter.new_sample();
        sample.add_cpu_time(Duration::from_millis(100));
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::ZERO);

        quota_limiter.set_write_bandwidth_limit(ReadableSize::kb(0));
        let mut sample = quota_limiter.new_sample();
        sample.add_write_bytes(256);
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::ZERO);

        quota_limiter.set_read_bandwidth_limit(ReadableSize::kb(0));
        let mut sample = quota_limiter.new_sample();
        sample.add_read_bytes(256);
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::ZERO);

        // set bandwidth back
        quota_limiter.set_write_bandwidth_limit(ReadableSize::kb(1));
        quota_limiter.set_max_delay_duration(ReadableDuration::millis(0));
        let mut sample = quota_limiter.new_sample();
        sample.add_write_bytes(128);
        let should_delay = block_on(quota_limiter.async_consume(sample));
        check_duration(should_delay, Duration::from_millis(125));
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

extern crate cgmath;
extern crate image;
extern crate winit;

#[macro_use]
extern crate vulkano;
extern crate vulkano_win;

use vulkano_win::VkSurfaceBuild;

use std::time::Duration;

fn main() {
    // The start of this example is exactly the same as `triangle`. You should read the
    // `triangle` example if you haven't done so yet.

    let extensions = vulkano_win::required_extensions();
    let instance = vulkano::instance::Instance::new(None, &extensions, &[]).expect("failed to create instance");

    let physical = vulkano::instance::PhysicalDevice::enumerate(&instance)
                            .next().expect("no device available");
    println!("Using device: {} (type: {:?})", physical.name(), physical.ty());

    let window = winit::WindowBuilder::new().build_vk_surface(&instance).unwrap();

    let queue = physical.queue_families().find(|q| q.supports_graphics() &&
                                                   window.surface().is_supported(q).unwrap_or(false))
                                                .expect("couldn't find a graphical queue family");

    let device_ext = vulkano::device::DeviceExtensions {
        khr_swapchain: true,
        .. vulkano::device::DeviceExtensions::none()
    };
    let (device, mut queues) = vulkano::device::Device::new(&physical, physical.supported_features(),
                                                            &device_ext, [(queue, 0.5)].iter().cloned())
                               .expect("failed to create device");
    let queue = queues.next().unwrap();

    let (swapchain, images) = {
        let caps = window.surface().get_capabilities(&physical).expect("failed to get surface capabilities");

        let dimensions = caps.current_extent.unwrap_or([1280, 1024]);
        let present = caps.present_modes.iter().next().unwrap();
        let usage = caps.supported_usage_flags;

        vulkano::swapchain::Swapchain::new(&device, &window.surface(), caps.min_image_count,
                                           vulkano::format::B8G8R8A8Srgb, dimensions, 1,
                                           &usage, &queue, vulkano::swapchain::SurfaceTransform::Identity,
                                           vulkano::swapchain::CompositeAlpha::Opaque,
                                           present, true, None).expect("failed to create swapchain")
    };

    #[derive(Debug, Clone)]
    struct Vertex { position: [f32; 2] }
    impl_vertex!(Vertex, position);

    let vertex_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer::<[Vertex]>
                               ::from_iter(&device, &vulkano::buffer::BufferUsage::all(),
                                       Some(queue.family()), [
                                           Vertex { position: [-0.5, -0.5 ] },
                                           Vertex { position: [-0.5,  0.5 ] },
                                           Vertex { position: [ 0.5, -0.5 ] },
                                           Vertex { position: [ 0.5,  0.5 ] },
                                       ].iter().cloned()).expect("failed to create buffer");

    mod vs { include!{concat!(env!("OUT_DIR"), "/shaders/src/bin/image_vs.glsl")} }
    let vs = vs::Shader::load(&device).expect("failed to create shader module");
    mod fs { include!{concat!(env!("OUT_DIR"), "/shaders/src/bin/image_fs.glsl")} }
    let fs = fs::Shader::load(&device).expect("failed to create shader module");

    mod renderpass {
        single_pass_renderpass!{
            attachments: {
                color: {
                    load: Clear,
                    store: Store,
                    format: ::vulkano::format::B8G8R8A8Srgb,
                }
            },
            pass: {
                color: [color],
                depth_stencil: {}
            }
        }
    }

    let renderpass = renderpass::CustomRenderPass::new(&device, &renderpass::Formats {
        color: (vulkano::format::B8G8R8A8Srgb, 1)
    }).unwrap();

    let texture = vulkano::image::immutable::ImmutableImage::new(&device, vulkano::image::Dimensions::Dim2d { width: 93, height: 93 },
                                                                 vulkano::format::R8G8B8A8Unorm, Some(queue.family())).unwrap();

    let pixel_buffer = {
        let image = image::load_from_memory_with_format(include_bytes!("image_img.png"),
                                                        image::ImageFormat::PNG).unwrap().to_rgba();
        let image_data = image.into_raw().clone();

        let image_data_chunks = image_data.chunks(4).map(|c| [c[0], c[1], c[2], c[3]]);

        // TODO: staging buffer instead
        vulkano::buffer::cpu_access::CpuAccessibleBuffer::<[[u8; 4]]>
            ::from_iter(&device, &vulkano::buffer::BufferUsage::all(),
                        Some(queue.family()), image_data_chunks)
                        .expect("failed to create buffer")
    };

    let sampler = vulkano::sampler::Sampler::new(&device, vulkano::sampler::Filter::Linear,
                                                 vulkano::sampler::Filter::Linear, vulkano::sampler::MipmapMode::Nearest,
                                                 vulkano::sampler::SamplerAddressMode::Repeat,
                                                 vulkano::sampler::SamplerAddressMode::Repeat,
                                                 vulkano::sampler::SamplerAddressMode::Repeat,
                                                 0.0, 1.0, 0.0, 0.0).unwrap();

    let descriptor_pool = vulkano::descriptor::descriptor_set::DescriptorPool::new(&device);
    mod pipeline_layout {
        pipeline_layout!{
            set0: {
                tex: CombinedImageSampler
            }
        }
    }

    let pipeline_layout = pipeline_layout::CustomPipeline::new(&device).unwrap();
    let set = pipeline_layout::set0::Set::new(&descriptor_pool, &pipeline_layout, &pipeline_layout::set0::Descriptors {
        tex: (&sampler, &texture)
    });

    let pipeline = vulkano::pipeline::GraphicsPipeline::new(&device, vulkano::pipeline::GraphicsPipelineParams {
        vertex_input: vulkano::pipeline::vertex::SingleBufferDefinition::new(),
        vertex_shader: vs.main_entry_point(),
        input_assembly: vulkano::pipeline::input_assembly::InputAssembly {
            topology: vulkano::pipeline::input_assembly::PrimitiveTopology::TriangleStrip,
            primitive_restart_enable: false,
        },
        tessellation: None,
        geometry_shader: None,
        viewport: vulkano::pipeline::viewport::ViewportsState::Fixed {
            data: vec![(
                vulkano::pipeline::viewport::Viewport {
                    origin: [0.0, 0.0],
                    depth_range: 0.0 .. 1.0,
                    dimensions: [images[0].dimensions()[0] as f32, images[0].dimensions()[1] as f32],
                },
                vulkano::pipeline::viewport::Scissor::irrelevant()
            )],
        },
        raster: Default::default(),
        multisample: vulkano::pipeline::multisample::Multisample::disabled(),
        fragment_shader: fs.main_entry_point(),
        depth_stencil: vulkano::pipeline::depth_stencil::DepthStencil::disabled(),
        blend: vulkano::pipeline::blend::Blend::pass_through(),
        layout: &pipeline_layout,
        render_pass: vulkano::framebuffer::Subpass::from(&renderpass, 0).unwrap(),
    }).unwrap();

    let framebuffers = images.iter().map(|image| {
        let attachments = renderpass::AList {
            color: &image,
        };

        vulkano::framebuffer::Framebuffer::new(&renderpass, [images[0].dimensions()[0], images[0].dimensions()[1], 1], attachments).unwrap()
    }).collect::<Vec<_>>();

    let command_buffers = framebuffers.iter().map(|framebuffer| {
        vulkano::command_buffer::PrimaryCommandBufferBuilder::new(&device, queue.family())
            .copy_buffer_to_color_image(&pixel_buffer, &texture, 0, 0 .. 1, [0, 0, 0],
                                        [texture.dimensions().width(), texture.dimensions().height(), 1])
            //.clear_color_image(&texture, [0.0, 1.0, 0.0, 1.0])
            .draw_inline(&renderpass, &framebuffer, renderpass::ClearValues {
                color: [0.0, 0.0, 1.0, 1.0]
            })
            .draw(&pipeline, &vertex_buffer, &vulkano::command_buffer::DynamicState::none(),
                  &set, &())
            .draw_end()
            .build()
    }).collect::<Vec<_>>();

    loop {
        let image_num = swapchain.acquire_next_image(Duration::new(10, 0)).unwrap();
        vulkano::command_buffer::submit(&command_buffers[image_num], &queue).unwrap();
        swapchain.present(&queue, image_num).unwrap();

        for ev in window.window().poll_events() {
            match ev {
                winit::Event::Closed => return,
                _ => ()
            }
        }
    }
}
<commit_msg>Fix image example<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

extern crate cgmath;
extern crate image;
extern crate winit;

#[macro_use]
extern crate vulkano;
extern crate vulkano_win;

use vulkano_win::VkSurfaceBuild;
use vulkano::command_buffer::CommandBufferBuilder;
use vulkano::sync::GpuFuture;

use std::sync::Arc;
use std::time::Duration;

fn main() {
    // The start of this example is exactly the same as `triangle`. You should read the
    // `triangle` example if you haven't done so yet.

    let extensions = vulkano_win::required_extensions();
    let instance = vulkano::instance::Instance::new(None, &extensions, &[]).expect("failed to create instance");

    let physical = vulkano::instance::PhysicalDevice::enumerate(&instance)
                            .next().expect("no device available");
    println!("Using device: {} (type: {:?})", physical.name(), physical.ty());

    let window = winit::WindowBuilder::new().build_vk_surface(&instance).unwrap();

    let queue = physical.queue_families().find(|q| q.supports_graphics() &&
                                                   window.surface().is_supported(q).unwrap_or(false))
                                                .expect("couldn't find a graphical queue family");

    let device_ext = vulkano::device::DeviceExtensions {
        khr_swapchain: true,
        .. vulkano::device::DeviceExtensions::none()
    };
    let (device, mut queues) = vulkano::device::Device::new(&physical, physical.supported_features(),
                                                            &device_ext, [(queue, 0.5)].iter().cloned())
                               .expect("failed to create device");
    let queue = queues.next().unwrap();

    let (swapchain, images) = {
        let caps = window.surface().get_capabilities(&physical).expect("failed to get surface capabilities");

        let dimensions = caps.current_extent.unwrap_or([1280, 1024]);
        let present = caps.present_modes.iter().next().unwrap();
        let usage = caps.supported_usage_flags;

        vulkano::swapchain::Swapchain::new(&device, &window.surface(), caps.min_image_count,
                                           vulkano::format::B8G8R8A8Srgb, dimensions, 1,
                                           &usage, &queue, vulkano::swapchain::SurfaceTransform::Identity,
                                           vulkano::swapchain::CompositeAlpha::Opaque,
                                           present, true, None).expect("failed to create swapchain")
    };

    #[derive(Debug, Clone)]
    struct Vertex { position: [f32; 2] }
    impl_vertex!(Vertex, position);

    let vertex_buffer = vulkano::buffer::cpu_access::CpuAccessibleBuffer::<[Vertex]>
                               ::from_iter(&device, &vulkano::buffer::BufferUsage::all(),
                                       Some(queue.family()), [
                                           Vertex { position: [-0.5, -0.5 ] },
                                           Vertex { position: [-0.5,  0.5 ] },
                                           Vertex { position: [ 0.5, -0.5 ] },
                                           Vertex { position: [ 0.5,  0.5 ] },
                                       ].iter().cloned()).expect("failed to create buffer");

    mod vs { include!{concat!(env!("OUT_DIR"), "/shaders/src/bin/image_vs.glsl")} }
    let vs = vs::Shader::load(&device).expect("failed to create shader module");
    mod fs { include!{concat!(env!("OUT_DIR"), "/shaders/src/bin/image_fs.glsl")} }
    let fs = fs::Shader::load(&device).expect("failed to create shader module");

    let renderpass = Arc::new(
        single_pass_renderpass!(device.clone(),
            attachments: {
                color: {
                    load: Clear,
                    store: Store,
                    format: images[0].format(),
                    samples: 1,
                }
            },
            pass: {
                color: [color],
                depth_stencil: {}
            }
        ).unwrap()
    );

    let texture = vulkano::image::immutable::ImmutableImage::new(&device, vulkano::image::Dimensions::Dim2d { width: 93, height: 93 },
                                                                 vulkano::format::R8G8B8A8Unorm, Some(queue.family())).unwrap();

    let pixel_buffer = {
        let image = image::load_from_memory_with_format(include_bytes!("image_img.png"),
                                                        image::ImageFormat::PNG).unwrap().to_rgba();
        let image_data = image.into_raw().clone();

        let image_data_chunks = image_data.chunks(4).map(|c| [c[0], c[1], c[2], c[3]]);

        // TODO: staging buffer instead
        vulkano::buffer::cpu_access::CpuAccessibleBuffer::<[[u8; 4]]>
            ::from_iter(&device, &vulkano::buffer::BufferUsage::all(),
                        Some(queue.family()), image_data_chunks)
                        .expect("failed to create buffer")
    };

    let sampler = vulkano::sampler::Sampler::new(&device, vulkano::sampler::Filter::Linear,
                                                 vulkano::sampler::Filter::Linear, vulkano::sampler::MipmapMode::Nearest,
                                                 vulkano::sampler::SamplerAddressMode::Repeat,
                                                 vulkano::sampler::SamplerAddressMode::Repeat,
                                                 vulkano::sampler::SamplerAddressMode::Repeat,
                                                 0.0, 1.0, 0.0, 0.0).unwrap();

    let pipeline = Arc::new(vulkano::pipeline::GraphicsPipeline::new(&device, vulkano::pipeline::GraphicsPipelineParams {
        vertex_input: vulkano::pipeline::vertex::SingleBufferDefinition::new(),
        vertex_shader: vs.main_entry_point(),
        input_assembly: vulkano::pipeline::input_assembly::InputAssembly {
            topology: vulkano::pipeline::input_assembly::PrimitiveTopology::TriangleStrip,
            primitive_restart_enable: false,
        },
        tessellation: None,
        geometry_shader: None,
        viewport: vulkano::pipeline::viewport::ViewportsState::Fixed {
            data: vec![(
                vulkano::pipeline::viewport::Viewport {
                    origin: [0.0, 0.0],
                    depth_range: 0.0 .. 1.0,
                    dimensions: [images[0].dimensions()[0] as f32, images[0].dimensions()[1] as f32],
                },
                vulkano::pipeline::viewport::Scissor::irrelevant()
            )],
        },
        raster: Default::default(),
        multisample: vulkano::pipeline::multisample::Multisample::disabled(),
        fragment_shader: fs.main_entry_point(),
        depth_stencil: vulkano::pipeline::depth_stencil::DepthStencil::disabled(),
        blend: vulkano::pipeline::blend::Blend::pass_through(),
        render_pass: vulkano::framebuffer::Subpass::from(renderpass.clone(), 0).unwrap(),
    }).unwrap());

    let set = Arc::new(simple_descriptor_set!(pipeline.clone(), 0, {
        tex: (texture.clone(), sampler.clone())
    }));

    let framebuffers = images.iter().map(|image| {
        let attachments = renderpass.desc().start_attachments()
            .color(image.clone());
        let dimensions = [image.dimensions()[0], image.dimensions()[1], 1];

        vulkano::framebuffer::Framebuffer::new(renderpass.clone(), dimensions, attachments).unwrap()
    }).collect::<Vec<_>>();

    let mut submissions: Vec<Box<GpuFuture>> = Vec::new();

    loop {
        while submissions.len() >= 4 {
            submissions.remove(0);
        }

        let (image_num, future) = swapchain.acquire_next_image(Duration::new(10, 0)).unwrap();

        let cb = vulkano::command_buffer::AutoCommandBufferBuilder::new(device.clone(), queue.family())
            .unwrap()
            .copy_buffer_to_image(pixel_buffer.clone(), texture.clone())
            .unwrap()
            //.clear_color_image(&texture, [0.0, 1.0, 0.0, 1.0])
            .begin_render_pass(
                framebuffers[image_num].clone(), false,
                renderpass.desc().start_clear_values()
                    .color([0.0, 0.0, 1.0, 1.0]))
            .draw(pipeline.clone(), vulkano::command_buffer::DynamicState::none(), vertex_buffer.clone(),
                  set.clone(), ())
            .end_render_pass()
            .build();

        let future = future
            .then_execute(queue.clone(), cb)
            .then_swapchain_present(queue.clone(), swapchain.clone(), image_num)
            .then_signal_fence();
        future.flush().unwrap();
        submissions.push(Box::new(future) as Box<_>);

        for ev in window.window().poll_events() {
            match ev {
                winit::Event::Closed => return,
                _ => ()
            }
        }
    }
}
<|endoftext|>