<commit_before>#![allow(dead_code)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]

include!(concat!(env!("OUT_DIR"), "/aom.rs"));

use super::*;

use rand::{ChaChaRng, Rng, SeedableRng};
use std::collections::VecDeque;
use std::mem;
use std::sync::Arc;

    fn fill_frame(ra: &mut ChaChaRng, frame: &mut Frame) {
        for plane in frame.planes.iter_mut() {
            let stride = plane.cfg.stride;
            for row in plane.data.chunks_mut(stride) {
                for mut pixel in row {
                    let v: u8 = ra.gen();
                    *pixel = v as u16;
                }
            }
        }
    }

    struct AomDecoder {
        dec: aom_codec_ctx,
    }

    fn setup_decoder(w: usize, h: usize) -> AomDecoder {
        unsafe {
            let interface = aom_codec_av1_dx();
            let mut dec: AomDecoder = mem::uninitialized();
            let cfg = aom_codec_dec_cfg_t  {
                threads: 1,
                w: w as u32,
                h: h as u32,
                allow_lowbitdepth: 1,
                cfg: cfg_options { ext_partition: 1 }
            };

            let ret = aom_codec_dec_init_ver(&mut dec.dec, interface, &cfg, 0, AOM_DECODER_ABI_VERSION as i32);
            if ret != 0 {
                panic!("Cannot instantiate the decoder {}", ret);
            }

            dec
        }
    }

    impl Drop for AomDecoder {
        fn drop(&mut self) {
            unsafe { aom_codec_destroy(&mut self.dec) };
        }

    }

    fn setup_encoder(w: usize, h: usize, speed: usize, quantizer: usize,
        bit_depth: usize, chroma_sampling: ChromaSampling) -> (FrameInvariants, Sequence) {
        unsafe {
            av1_rtcd();
            aom_dsp_rtcd();
        }

        let config = EncoderConfig {
            quantizer: quantizer,
            speed: speed,
            ..Default::default()
        };
        let mut fi = FrameInvariants::new(w, h, config);

        fi.use_reduced_tx_set = true;
        // fi.min_partition_size =
        let seq = Sequence::new(w, h, bit_depth, chroma_sampling);

        (fi, seq)
    }

    // TODO: support non-multiple-of-16 dimensions
    static DIMENSION_OFFSETS: &[(usize, usize)] = &[(0, 0), (4, 4), (8, 8), (16, 16)];

    #[test]
    #[ignore]
    fn speed() {
        let quantizer = 100;
        let limit = 5;
        let w = 64;
        let h = 80;

        for b in DIMENSION_OFFSETS.iter() {
            for s in 0 .. 10 {
                encode_decode(w + b.0, h + b.1, s, quantizer, limit, 8);
            }
        }
    }

    static DIMENSIONS: &[(usize, usize)] = &[(8, 8), (16, 16), (32, 32), (64, 64),
        (128, 128), (256, 256), (512, 512), (1024, 1024), (2048, 2048),
        (258, 258), (260, 260), (262, 262), (264, 264), (265, 265)];

    #[test]
    #[ignore]
    fn dimensions() {
        let quantizer = 100;
        let limit = 1;
        let speed = 4;

        for (w, h) in DIMENSIONS.iter() {
            encode_decode(*w, *h, speed, quantizer, limit, 8);
        }
    }

    #[test]
    #[ignore]
    fn quantizer() {
        let limit = 5;
        let w = 64;
        let h = 80;
        let speed = 4;

        for b in DIMENSION_OFFSETS.iter() {
            for &q in [80, 100, 120].iter() {
                encode_decode(w + b.0, h + b.1, speed, q, limit, 8);
            }
        }
    }

    #[test]
    #[ignore]
    fn odd_size_frame_with_full_rdo() {
        let limit = 3;
        let w = 512 + 32 + 16 + 5;
        let h = 512 + 16 + 5;
        let speed = 0;
        let qindex = 100;

        encode_decode(w, h, speed, qindex, limit, 8);
    }

    #[test]
    #[ignore]
    fn high_bd() {
        let quantizer = 100;
        let limit = 3; // Include inter frames
        let speed = 0; // Test as many tools as possible
        let w = 64;
        let h = 80;

        // 10-bit
        encode_decode(w, h, speed, quantizer, limit, 10);

        // 12-bit
        encode_decode(w, h, speed, quantizer, limit, 12);
    }

    fn compare_plane<T: Ord + std::fmt::Debug>(rec: &[T], rec_stride: usize,
                     dec: &[T], dec_stride: usize,
                     width: usize, height: usize) {
        for line in rec.chunks(rec_stride)
            .zip(dec.chunks(dec_stride)).take(height) {
            assert_eq!(&line.0[..width], &line.1[..width]);
        }
    }

    fn compare_img(img: *const aom_image_t, frame: &Frame, bit_depth: usize) {
        use std::slice;
        let img = unsafe { *img };
        let img_iter = img.planes.iter().zip(img.stride.iter());

        for (img_plane, frame_plane) in img_iter.zip(frame.planes.iter()) {
            let w = frame_plane.cfg.width;
            let h = frame_plane.cfg.height;
            let rec_stride = frame_plane.cfg.stride;

            if bit_depth > 8 {
                let dec_stride = *img_plane.1 as usize / 2;

                let dec = unsafe {
                    let data = *img_plane.0 as *const u16;
                    let size = dec_stride * h;

                    slice::from_raw_parts(data, size)
                };

                let rec: Vec<u16> = frame_plane.data_origin().iter().map(|&v| v).collect();

                compare_plane::<u16>(&rec[..], rec_stride, dec, dec_stride, w, h);
            } else {
                let dec_stride = *img_plane.1 as usize;

                let dec = unsafe {
                    let data = *img_plane.0 as *const u8;
                    let size = dec_stride * h;

                    slice::from_raw_parts(data, size)
                };

                let rec: Vec<u8> = frame_plane.data_origin().iter().map(|&v| v as u8).collect();

                compare_plane::<u8>(&rec[..], rec_stride, dec, dec_stride, w, h);
            }
        }
    }

    fn encode_decode(w: usize, h: usize, speed: usize, quantizer: usize, limit: usize, bit_depth: usize) {
        use std::ptr;
        let mut ra = ChaChaRng::from_seed([0; 32]);

        let mut dec = setup_decoder(w, h);
        let (mut fi, mut seq) = setup_encoder(w, h, speed, quantizer, bit_depth, ChromaSampling::Cs420);

        println!("Encoding {}x{} speed {} quantizer {}", w, h, speed, quantizer);

        let mut iter: aom_codec_iter_t = ptr::null_mut();

        let mut rec_fifo = VecDeque::new();

        for _ in 0 .. limit {
            let mut fs = fi.new_frame_state();
            fill_frame(&mut ra, Arc::get_mut(&mut fs.input).unwrap());

            fi.frame_type = if fi.number % 30 == 0 { FrameType::KEY } else { FrameType::INTER };
            fi.refresh_frame_flags = if fi.frame_type == FrameType::KEY { ALL_REF_FRAMES_MASK } else { 1 };

            fi.intra_only = fi.frame_type == FrameType::KEY || fi.frame_type == FrameType::INTRA_ONLY;
            fi.use_prev_frame_mvs = !(fi.intra_only || fi.error_resilient);
            println!("Encoding frame {}", fi.number);
            let packet = encode_frame(&mut seq, &mut fi, &mut fs);
            println!("Encoded.");

            fs.rec.pad();

            rec_fifo.push_back(fs.rec.clone());

            update_rec_buffer(&mut fi, fs);

            let mut corrupted_count = 0;
            unsafe {
                println!("Decoding frame {}", fi.number);
                let ret = aom_codec_decode(&mut dec.dec, packet.as_ptr(), packet.len(), ptr::null_mut());
                println!("Decoded. -> {}", ret);
                if ret != 0 {
                    use std::ffi::CStr;
                    let error_msg = aom_codec_error(&mut dec.dec);
                    println!("  Decode codec_decode failed: {}", CStr::from_ptr(error_msg).to_string_lossy());
                    let detail = aom_codec_error_detail(&mut dec.dec);
                    if !detail.is_null() {
                        println!("  Decode codec_decode failed {}", CStr::from_ptr(detail).to_string_lossy());
                    }

                    corrupted_count += 1;
                }

                if ret == 0 {
                    loop {
                        println!("Retrieving frame");
                        let img = aom_codec_get_frame(&mut dec.dec, &mut iter);
                        println!("Retrieved.");
                        if img.is_null() {
                            break;
                        }
                        let mut corrupted = 0;
                        let ret = aom_codec_control_(&mut dec.dec, aom_dec_control_id_AOMD_GET_FRAME_CORRUPTED as i32, &mut corrupted);
                        if ret != 0 {
                            use std::ffi::CStr;
                            let detail = aom_codec_error_detail(&mut dec.dec);
                            panic!("Decode codec_control failed {}", CStr::from_ptr(detail).to_string_lossy());
                        }
                        corrupted_count += corrupted;

                        let rec = rec_fifo.pop_front().unwrap();
                        compare_img(img, &rec, bit_depth);
                    }
                }
            }

            assert_eq!(corrupted_count, 0);

            fi.number += 1;
        }
    }
<commit_msg>Update the decode tests to the current user api<commit_after>#![allow(dead_code)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]

include!(concat!(env!("OUT_DIR"), "/aom.rs"));

use super::*;

use rand::{ChaChaRng, Rng, SeedableRng};
use std::collections::VecDeque;
use std::mem;
use std::sync::Arc;

    fn fill_frame(ra: &mut ChaChaRng, frame: &mut Frame) {
        for plane in frame.planes.iter_mut() {
            let stride = plane.cfg.stride;
            for row in plane.data.chunks_mut(stride) {
                for mut pixel in row {
                    let v: u8 = ra.gen();
                    *pixel = v as u16;
                }
            }
        }
    }

    struct AomDecoder {
        dec: aom_codec_ctx,
    }

    fn setup_decoder(w: usize, h: usize) -> AomDecoder {
        unsafe {
            let interface = aom_codec_av1_dx();
            let mut dec: AomDecoder = mem::uninitialized();
            let cfg = aom_codec_dec_cfg_t  {
                threads: 1,
                w: w as u32,
                h: h as u32,
                allow_lowbitdepth: 1,
                cfg: cfg_options { ext_partition: 1 }
            };

            let ret = aom_codec_dec_init_ver(&mut dec.dec, interface, &cfg, 0, AOM_DECODER_ABI_VERSION as i32);
            if ret != 0 {
                panic!("Cannot instantiate the decoder {}", ret);
            }

            dec
        }
    }

    impl Drop for AomDecoder {
        fn drop(&mut self) {
            unsafe { aom_codec_destroy(&mut self.dec) };
        }

    }

    fn setup_encoder(w: usize, h: usize, speed: usize, quantizer: usize,
        bit_depth: usize, chroma_sampling: ChromaSampling) -> Context {
        unsafe {
            av1_rtcd();
            aom_dsp_rtcd();
        }

        let enc = EncoderConfig {
            quantizer: quantizer,
            speed: speed,
            ..Default::default()
        };

        let cfg = Config {
            width: w,
            height: h,
            bit_depth,
            chroma_sampling,
            timebase: Ratio::new(1, 1000),
            enc,
        };

        cfg.new_context()
    }

    // TODO: support non-multiple-of-16 dimensions
    static DIMENSION_OFFSETS: &[(usize, usize)] = &[(0, 0), (4, 4), (8, 8), (16, 16)];

    #[test]
    #[ignore]
    fn speed() {
        let quantizer = 100;
        let limit = 5;
        let w = 64;
        let h = 80;

        for b in DIMENSION_OFFSETS.iter() {
            for s in 0 .. 10 {
                encode_decode(w + b.0, h + b.1, s, quantizer, limit, 8);
            }
        }
    }

    static DIMENSIONS: &[(usize, usize)] = &[(8, 8), (16, 16), (32, 32), (64, 64),
        (128, 128), (256, 256), (512, 512), (1024, 1024), (2048, 2048),
        (258, 258), (260, 260), (262, 262), (264, 264), (265, 265)];

    #[test]
    #[ignore]
    fn dimensions() {
        let quantizer = 100;
        let limit = 1;
        let speed = 4;

        for (w, h) in DIMENSIONS.iter() {
            encode_decode(*w, *h, speed, quantizer, limit, 8);
        }
    }

    #[test]
    #[ignore]
    fn quantizer() {
        let limit = 5;
        let w = 64;
        let h = 80;
        let speed = 4;

        for b in DIMENSION_OFFSETS.iter() {
            for &q in [80, 100, 120].iter() {
                encode_decode(w + b.0, h + b.1, speed, q, limit, 8);
            }
        }
    }

    #[test]
    #[ignore]
    fn odd_size_frame_with_full_rdo() {
        let limit = 3;
        let w = 512 + 32 + 16 + 5;
        let h = 512 + 16 + 5;
        let speed = 0;
        let qindex = 100;

        encode_decode(w, h, speed, qindex, limit, 8);
    }

    #[test]
    #[ignore]
    fn high_bd() {
        let quantizer = 100;
        let limit = 3; // Include inter frames
        let speed = 0; // Test as many tools as possible
        let w = 64;
        let h = 80;

        // 10-bit
        encode_decode(w, h, speed, quantizer, limit, 10);

        // 12-bit
        encode_decode(w, h, speed, quantizer, limit, 12);
    }

    fn compare_plane<T: Ord + std::fmt::Debug>(rec: &[T], rec_stride: usize,
                     dec: &[T], dec_stride: usize,
                     width: usize, height: usize) {
        for line in rec.chunks(rec_stride)
            .zip(dec.chunks(dec_stride)).take(height) {
            assert_eq!(&line.0[..width], &line.1[..width]);
        }
    }

    fn compare_img(img: *const aom_image_t, frame: &Frame, bit_depth: usize) {
        use std::slice;
        let img = unsafe { *img };
        let img_iter = img.planes.iter().zip(img.stride.iter());

        for (img_plane, frame_plane) in img_iter.zip(frame.planes.iter()) {
            let w = frame_plane.cfg.width;
            let h = frame_plane.cfg.height;
            let rec_stride = frame_plane.cfg.stride;

            if bit_depth > 8 {
                let dec_stride = *img_plane.1 as usize / 2;

                let dec = unsafe {
                    let data = *img_plane.0 as *const u16;
                    let size = dec_stride * h;

                    slice::from_raw_parts(data, size)
                };

                let rec: Vec<u16> = frame_plane.data_origin().iter().map(|&v| v).collect();

                compare_plane::<u16>(&rec[..], rec_stride, dec, dec_stride, w, h);
            } else {
                let dec_stride = *img_plane.1 as usize;

                let dec = unsafe {
                    let data = *img_plane.0 as *const u8;
                    let size = dec_stride * h;

                    slice::from_raw_parts(data, size)
                };

                let rec: Vec<u8> = frame_plane.data_origin().iter().map(|&v| v as u8).collect();

                compare_plane::<u8>(&rec[..], rec_stride, dec, dec_stride, w, h);
            }
        }
    }

    fn encode_decode(w: usize, h: usize, speed: usize, quantizer: usize, limit: usize, bit_depth: usize) {
        use std::ptr;
        let mut ra = ChaChaRng::from_seed([0; 32]);

        let mut dec = setup_decoder(w, h);
        let mut ctx = setup_encoder(w, h, speed, quantizer, bit_depth, ChromaSampling::Cs420);

        println!("Encoding {}x{} speed {} quantizer {}", w, h, speed, quantizer);

        let mut iter: aom_codec_iter_t = ptr::null_mut();

        let mut rec_fifo = VecDeque::new();

        for _ in 0 .. limit {
            let mut input = ctx.new_frame();
            fill_frame(&mut ra, Arc::get_mut(&mut input).unwrap());

            let _ = ctx.send_frame(input);
            let pkt = ctx.receive_packet().unwrap();
            println!("Encoded packet {}", pkt.number);

            rec_fifo.push_back(pkt.rec.clone());

            let packet = pkt.data;

            let mut corrupted_count = 0;
            unsafe {
                println!("Decoding frame {}", pkt.number);
                let ret = aom_codec_decode(&mut dec.dec, packet.as_ptr(), packet.len(), ptr::null_mut());
                println!("Decoded. -> {}", ret);
                if ret != 0 {
                    use std::ffi::CStr;
                    let error_msg = aom_codec_error(&mut dec.dec);
                    println!("  Decode codec_decode failed: {}", CStr::from_ptr(error_msg).to_string_lossy());
                    let detail = aom_codec_error_detail(&mut dec.dec);
                    if !detail.is_null() {
                        println!("  Decode codec_decode failed {}", CStr::from_ptr(detail).to_string_lossy());
                    }

                    corrupted_count += 1;
                }

                if ret == 0 {
                    loop {
                        println!("Retrieving frame");
                        let img = aom_codec_get_frame(&mut dec.dec, &mut iter);
                        println!("Retrieved.");
                        if img.is_null() {
                            break;
                        }
                        let mut corrupted = 0;
                        let ret = aom_codec_control_(&mut dec.dec, aom_dec_control_id_AOMD_GET_FRAME_CORRUPTED as i32, &mut corrupted);
                        if ret != 0 {
                            use std::ffi::CStr;
                            let detail = aom_codec_error_detail(&mut dec.dec);
                            panic!("Decode codec_control failed {}", CStr::from_ptr(detail).to_string_lossy());
                        }
                        corrupted_count += corrupted;

                        let rec = rec_fifo.pop_front().unwrap();
                        compare_img(img, &rec, bit_depth);
                    }
                }
            }

            assert_eq!(corrupted_count, 0);
        }
    }
<|endoftext|>


<commit_before>import comm::*;
import libc::*;
import js = jsapi::bindgen;
import jsapi::*;
import ptr::null;
import void = c_void;

mod jsrust {
	/* Additional features. */
    fn JSRust_NewContext(rt : *JSRuntime, stackChunkSize : size_t)
        -> *JSContext { fail }
    fn JSRust_SetErrorChannel(cx : *JSContext, chan : chan<error_report>)
	-> bool { fail }
    fn JSRust_SetLogChannel(cx : *JSContext, object : *JSObject, chan : chan<log_message>)
	-> bool { fail }
    fn JSRust_InitRustLibrary(cx : *JSContext, object : *JSObject) -> bool { fail }
    fn JSRust_SetDataOnObject(cx : *JSContext, object : *JSObject, val : *c_char, vallen: u32) { fail }
    fn JSRust_GetThreadRuntime(maxbytes : u32) -> *JSRuntime { fail }
    fn JSRust_Exit(code : c_int) { fail }
}

type error_report = {
	message: str,
	filename: str,
	lineno: u32,
	flags: u32
};

type log_message = {
	message: str,
	level: u32,
        tag: u32,
        timeout: u32
};

enum object { object_priv(*JSObject) }
enum principals { principals_priv(*JSPrincipals) }
enum script { script_priv(*JSScript) }
enum string { string_priv(*JSString) }

resource runtime(_rt : *JSRuntime) {
    // because there is one runtime per thread, raii does not
    // work. one task will finish but there may be other tasks
    // on the same os thread.
    //js::JS_Finish(rt);
}

resource context(_cx : *JSContext) {
    //js::JS_DestroyContext(cx);
}

fn begin_request(cx : *JSContext) {
    js::JS_BeginRequest(cx);
}

fn end_request(cx : *JSContext) {
    js::JS_EndRequest(cx);
}

resource request(cx : *JSContext) {
    js::JS_EndRequest(cx);
}

/* Runtimes */

fn new_runtime(maxbytes : u32) -> runtime {
    ret runtime(js::JS_Init(maxbytes));
}

fn get_thread_runtime(maxbytes : u32) -> runtime {
    ret runtime(jsrust::JSRust_GetThreadRuntime(maxbytes));
}

fn shut_down() {
    js::JS_ShutDown();
}

/* Contexts */

fn new_context(rt : runtime, stack_chunk_size : size_t) -> context {
    ret context(jsrust::JSRust_NewContext(*rt, stack_chunk_size));
}

/* Options */

fn get_options(cx : context) -> u32 {
    ret js::JS_GetOptions(*cx);
}

fn set_options(cx : context, options : u32) {
    let _ = js::JS_SetOptions(*cx, options);
}

fn set_version(cx : context, version : JSVersion) {
    let _ = js::JS_SetVersion(*cx, version);
}

/* Objects */

fn new_compartment_and_global_object(cx : context, clas : @class,
                                     principals : principals) -> object {
    let jsclass = ptr::addr_of(clas.jsclass);
    let jsobj = js::JS_NewCompartmentAndGlobalObject(*cx, jsclass,
                                                     *principals);
    if jsobj == null() { fail; }
    ret object_priv(jsobj);
}

/* Principals */

fn null_principals() -> principals {
    ret principals_priv(null());
}

/* Classes */

type class_spec = {
    name: str,
    flags: u32
    /* TODO: More to add here. */
};

type class = {
    name: @str,
    jsclass: JSClass
};

fn new_class(spec : class_spec) -> @class unsafe {
    // Root the name separately, and make the JSClass name point into it.
    let name = @spec.name;
    let x : *void = ptr::null();
    ret @{
        name: name,
        jsclass: {
            name: str::as_c_str(*name, { |b| b }),
            flags: spec.flags,

            addProperty: crust::JS_PropertyStub,
            delProperty: crust::JS_PropertyStub,
            getProperty: crust::JS_PropertyStub,
            setProperty: crust::JS_StrictPropertyStub,
            enumerate: crust::JS_EnumerateStub,
            resolve: crust::JS_ResolveStub,
            convert: crust::JS_ConvertStub,
            finalize: crust::JS_FinalizeStub,

            reserved0: unsafe::reinterpret_cast(0),
            checkAccess: unsafe::reinterpret_cast(0),
            call: unsafe::reinterpret_cast(0),
            construct: unsafe::reinterpret_cast(0),
            xdrObject: unsafe::reinterpret_cast(0),
            hasInstance: unsafe::reinterpret_cast(0),
            trace: unsafe::reinterpret_cast(0),

            reserved1: unsafe::reinterpret_cast(0),
            reserved: (x,x,x,x,x,x,x,x, x,x,x,x,x,x,x,x,    /* 16 */
                       x,x,x,x,x,x,x,x, x,x,x,x,x,x,x,x,    /* 32 */
                       x,x,x,x,x,x,x,x)

        }
    };
}

/* Standard classes */

fn init_standard_classes(cx : context, object : object) {
    if js::JS_InitStandardClasses(*cx, *object) == 0 as JSBool { fail; }
}

/* Script compilation */

fn compile_script(cx : context, object : object, src : [u8], filename : str,
                  lineno : uint) -> script unsafe {
    let jsscript = str::as_c_str(filename, { |buf|
        js::JS_CompileScript(*cx, *object,
                             unsafe::reinterpret_cast(vec::unsafe::to_ptr(src)),
                             vec::len(src) as size_t, buf, lineno as c_uint)
    });
    if jsscript == ptr::null() {
        fail;   // TODO: this is antisocial
    }
    ret script_priv(jsscript);
}

/* Script execution */

fn execute_script(cx : context, object : object, script : script)
        -> option<jsval> unsafe {
    let rv : jsval = unsafe::reinterpret_cast(0);
    if js::JS_ExecuteScript(*cx, *object, *script, ptr::addr_of(rv)) == 0 as JSBool {
        ret none;
    }
    ret some(rv);
}

/* Value conversion */

fn value_to_source(cx : context, v : jsval) -> string {
    ret string_priv(js::JS_ValueToSource(*cx, v));
}

/* String conversion */

fn get_string_bytes(cx : context, jsstr : string) -> [u16] unsafe {
    // FIXME: leaks, probably
    let size = 0 as size_t;
    let bytes = js::JS_GetStringCharsZAndLength(*cx, *jsstr,
                                                ptr::addr_of(size));
    ret vec::unsafe::from_buf(bytes, ((size + (1 as size_t)) * (2 as size_t)));
}

fn get_string(cx : context, jsstr : string) -> str unsafe {
    let bytes = get_string_bytes(cx, jsstr);

    // Make a sizing call.
    let len = 0 as size_t;
    if js::JS_EncodeCharacters(*cx, vec::unsafe::to_ptr(bytes),
                               (vec::len(bytes) / 2u) as size_t, ptr::null(),
                               ptr::addr_of(len)) == 0 as JSBool {
        fail;
    }

    let buf = vec::from_elem(len + 1u, 0 as libc::c_char);
    if js::JS_EncodeCharacters(*cx, vec::unsafe::to_ptr(bytes),
                               (vec::len(bytes) / 2u) as size_t,
                               vec::unsafe::to_ptr(buf),
                               ptr::addr_of(len)) == 0 as JSBool {
        fail;
    }

    ret vec::as_buf(buf) {|buf| str::unsafe::from_c_str_len(buf, len as uint) };
}

fn get_int(cx : context, num : jsval) -> i32 unsafe {
    let oparam : i32 = 0i32;
    js::JS_ValueToInt32(*cx, num, ptr::addr_of(oparam));
    ret oparam;
}

fn set_data_property(cx : context, obj : object, value : str) {
    ret str::as_c_str(value) {|buf|
        jsrust::JSRust_SetDataOnObject(*cx, *obj, buf, value.len() as u32);
    }
}

/** Rust extensions to the JavaScript language bindings. */
mod ext {
	fn set_error_channel(cx : context, chan : chan<error_report>) {
		if !jsrust::JSRust_SetErrorChannel(*cx, chan) { fail; }
	}

	fn set_log_channel(cx : context, object : object, chan : chan<log_message>) {
		if !jsrust::JSRust_SetLogChannel(*cx, *object, chan) { fail; }
	}

	fn init_rust_library(cx : context, object : object) {
		if !jsrust::JSRust_InitRustLibrary(*cx, *object) { fail; }
	}

	fn rust_exit_now(code : int) {
		jsrust::JSRust_Exit(code as c_int);
	}
}

<commit_msg>Get rid of old rust-spidermonkey API's I don't have implementations for<commit_after>import comm::*;
import libc::*;
import js = jsapi::bindgen;
import jsapi::*;
import ptr::null;
import void = c_void;

type error_report = {
	message: str,
	filename: str,
	lineno: u32,
	flags: u32
};

type log_message = {
	message: str,
	level: u32,
        tag: u32,
        timeout: u32
};

enum object { object_priv(*JSObject) }
enum principals { principals_priv(*JSPrincipals) }
enum script { script_priv(*JSScript) }
enum string { string_priv(*JSString) }

resource runtime(_rt : *JSRuntime) {
    // because there is one runtime per thread, raii does not
    // work. one task will finish but there may be other tasks
    // on the same os thread.
    //js::JS_Finish(rt);
}

resource context(_cx : *JSContext) {
    //js::JS_DestroyContext(cx);
}

fn begin_request(cx : *JSContext) {
    js::JS_BeginRequest(cx);
}

fn end_request(cx : *JSContext) {
    js::JS_EndRequest(cx);
}

resource request(cx : *JSContext) {
    js::JS_EndRequest(cx);
}

/* Runtimes */

fn new_runtime(maxbytes : u32) -> runtime {
    ret runtime(js::JS_Init(maxbytes));
}

fn shut_down() {
    js::JS_ShutDown();
}

/* Contexts */

fn new_context(rt : runtime, stack_chunk_size : size_t) -> context {
    ret context(js::JS_NewContext(*rt, stack_chunk_size));
}

/* Options */

fn get_options(cx : context) -> u32 {
    ret js::JS_GetOptions(*cx);
}

fn set_options(cx : context, options : u32) {
    let _ = js::JS_SetOptions(*cx, options);
}

fn set_version(cx : context, version : JSVersion) {
    let _ = js::JS_SetVersion(*cx, version);
}

/* Objects */

fn new_compartment_and_global_object(cx : context, clas : @class,
                                     principals : principals) -> object {
    let jsclass = ptr::addr_of(clas.jsclass);
    let jsobj = js::JS_NewCompartmentAndGlobalObject(*cx, jsclass,
                                                     *principals);
    if jsobj == null() { fail; }
    ret object_priv(jsobj);
}

/* Principals */

fn null_principals() -> principals {
    ret principals_priv(null());
}

/* Classes */

type class_spec = {
    name: str,
    flags: u32
    /* TODO: More to add here. */
};

type class = {
    name: @str,
    jsclass: JSClass
};

fn new_class(spec : class_spec) -> @class unsafe {
    // Root the name separately, and make the JSClass name point into it.
    let name = @spec.name;
    let x : *void = ptr::null();
    ret @{
        name: name,
        jsclass: {
            name: str::as_c_str(*name, { |b| b }),
            flags: spec.flags,

            addProperty: crust::JS_PropertyStub,
            delProperty: crust::JS_PropertyStub,
            getProperty: crust::JS_PropertyStub,
            setProperty: crust::JS_StrictPropertyStub,
            enumerate: crust::JS_EnumerateStub,
            resolve: crust::JS_ResolveStub,
            convert: crust::JS_ConvertStub,
            finalize: crust::JS_FinalizeStub,

            reserved0: unsafe::reinterpret_cast(0),
            checkAccess: unsafe::reinterpret_cast(0),
            call: unsafe::reinterpret_cast(0),
            construct: unsafe::reinterpret_cast(0),
            xdrObject: unsafe::reinterpret_cast(0),
            hasInstance: unsafe::reinterpret_cast(0),
            trace: unsafe::reinterpret_cast(0),

            reserved1: unsafe::reinterpret_cast(0),
            reserved: (x,x,x,x,x,x,x,x, x,x,x,x,x,x,x,x,    /* 16 */
                       x,x,x,x,x,x,x,x, x,x,x,x,x,x,x,x,    /* 32 */
                       x,x,x,x,x,x,x,x)

        }
    };
}

/* Standard classes */

fn init_standard_classes(cx : context, object : object) {
    if js::JS_InitStandardClasses(*cx, *object) == 0 as JSBool { fail; }
}

/* Script compilation */

fn compile_script(cx : context, object : object, src : [u8], filename : str,
                  lineno : uint) -> script unsafe {
    let jsscript = str::as_c_str(filename, { |buf|
        js::JS_CompileScript(*cx, *object,
                             unsafe::reinterpret_cast(vec::unsafe::to_ptr(src)),
                             vec::len(src) as size_t, buf, lineno as c_uint)
    });
    if jsscript == ptr::null() {
        fail;   // TODO: this is antisocial
    }
    ret script_priv(jsscript);
}

/* Script execution */

fn execute_script(cx : context, object : object, script : script)
        -> option<jsval> unsafe {
    let rv : jsval = unsafe::reinterpret_cast(0);
    if js::JS_ExecuteScript(*cx, *object, *script, ptr::addr_of(rv)) == 0 as JSBool {
        ret none;
    }
    ret some(rv);
}

/* Value conversion */

fn value_to_source(cx : context, v : jsval) -> string {
    ret string_priv(js::JS_ValueToSource(*cx, v));
}

/* String conversion */

fn get_string_bytes(cx : context, jsstr : string) -> [u16] unsafe {
    // FIXME: leaks, probably
    let size = 0 as size_t;
    let bytes = js::JS_GetStringCharsZAndLength(*cx, *jsstr,
                                                ptr::addr_of(size));
    ret vec::unsafe::from_buf(bytes, ((size + (1 as size_t)) * (2 as size_t)));
}

fn get_string(cx : context, jsstr : string) -> str unsafe {
    let bytes = get_string_bytes(cx, jsstr);

    // Make a sizing call.
    let len = 0 as size_t;
    if js::JS_EncodeCharacters(*cx, vec::unsafe::to_ptr(bytes),
                               (vec::len(bytes) / 2u) as size_t, ptr::null(),
                               ptr::addr_of(len)) == 0 as JSBool {
        fail;
    }

    let buf = vec::from_elem(len + 1u, 0 as libc::c_char);
    if js::JS_EncodeCharacters(*cx, vec::unsafe::to_ptr(bytes),
                               (vec::len(bytes) / 2u) as size_t,
                               vec::unsafe::to_ptr(buf),
                               ptr::addr_of(len)) == 0 as JSBool {
        fail;
    }

    ret vec::as_buf(buf) {|buf| str::unsafe::from_c_str_len(buf, len as uint) };
}

fn get_int(cx : context, num : jsval) -> i32 unsafe {
    let oparam : i32 = 0i32;
    js::JS_ValueToInt32(*cx, num, ptr::addr_of(oparam));
    ret oparam;
}
<|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 gleam::gl;
use std::mem;
use std::rc::Rc;

use device::FrameId;

pub trait NamedTag {
    fn get_label(&self) -> &str;
}

#[derive(Debug, Clone)]
pub struct GpuTimer<T> {
    pub tag: T,
    pub time_ns: u64,
}

#[derive(Debug, Clone)]
pub struct GpuSampler<T> {
    pub tag: T,
    pub count: u64,
}

pub struct QuerySet<T> {
    set: Vec<gl::GLuint>,
    data: Vec<T>,
    pending: gl::GLuint,
}

impl<T> QuerySet<T> {
    fn new() -> Self {
        QuerySet {
            set: Vec::new(),
            data: Vec::new(),
            pending: 0,
        }
    }

    fn reset(&mut self) {
        self.data.clear();
        self.pending = 0;
    }

    fn add(&mut self, value: T) -> Option<gl::GLuint> {
        assert_eq!(self.pending, 0);
        self.set.get(self.data.len()).cloned().map(|query_id| {
            self.data.push(value);
            self.pending = query_id;
            query_id
        })
    }

    fn take<F: Fn(&mut T, gl::GLuint)>(&mut self, fun: F) -> Vec<T> {
        let mut data = mem::replace(&mut self.data, Vec::new());
        for (value, &query) in data.iter_mut().zip(self.set.iter()) {
            fun(value, query)
        }
        data
    }
}

pub struct GpuFrameProfile<T> {
    gl: Rc<gl::Gl>,
    timers: QuerySet<GpuTimer<T>>,
    samplers: QuerySet<GpuSampler<T>>,
    frame_id: FrameId,
    inside_frame: bool,
}

impl<T> GpuFrameProfile<T> {
    fn new(gl: Rc<gl::Gl>) -> Self {
        assert_eq!(gl.get_type(), gl::GlType::Gl);

        GpuFrameProfile {
            gl,
            timers: QuerySet::new(),
            samplers: QuerySet::new(),
            frame_id: FrameId::new(0),
            inside_frame: false,
        }
    }

    fn enable_timers(&mut self, count: i32) {
        self.timers.set = self.gl.gen_queries(count);
    }

    fn disable_timers(&mut self) {
        if !self.timers.set.is_empty() {
            self.gl.delete_queries(&self.timers.set);
        }
        self.timers.set = Vec::new();
    }

    fn enable_samplers(&mut self, count: i32) {
        self.samplers.set = self.gl.gen_queries(count);
    }

    fn disable_samplers(&mut self) {
        if !self.samplers.set.is_empty() {
            self.gl.delete_queries(&self.samplers.set);
        }
        self.samplers.set = Vec::new();
    }

    fn begin_frame(&mut self, frame_id: FrameId) {
        self.frame_id = frame_id;
        self.timers.reset();
        self.samplers.reset();
        self.inside_frame = true;
    }

    fn end_frame(&mut self) {
        self.finish_timer();
        self.finish_sampler();
        self.inside_frame = false;
    }

    fn finish_timer(&mut self) {
        debug_assert!(self.inside_frame);
        if self.timers.pending != 0 {
            self.gl.end_query(gl::TIME_ELAPSED);
            self.timers.pending = 0;
        }
    }

    fn finish_sampler(&mut self) {
        debug_assert!(self.inside_frame);
        if self.samplers.pending != 0 {
            self.gl.end_query(gl::SAMPLES_PASSED);
            self.samplers.pending = 0;
        }
    }
}

impl<T: NamedTag> GpuFrameProfile<T> {
    fn start_timer(&mut self, tag: T) -> GpuTimeQuery {
        self.finish_timer();

        let marker = GpuMarker::new(&self.gl, tag.get_label());

        if let Some(query) = self.timers.add(GpuTimer { tag, time_ns: 0 }) {
            self.gl.begin_query(gl::TIME_ELAPSED, query);
        }

        GpuTimeQuery(marker)
    }

    fn start_sampler(&mut self, tag: T) -> GpuSampleQuery {
        self.finish_sampler();

        if let Some(query) = self.samplers.add(GpuSampler { tag, count: 0 }) {
            self.gl.begin_query(gl::SAMPLES_PASSED, query);
        }

        GpuSampleQuery
    }

    fn build_samples(&mut self) -> (FrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>) {
        debug_assert!(!self.inside_frame);
        let gl = &self.gl;

        (
            self.frame_id,
            self.timers.take(|timer, query| {
                timer.time_ns = gl.get_query_object_ui64v(query, gl::QUERY_RESULT)
            }),
            self.samplers.take(|sampler, query| {
                sampler.count = gl.get_query_object_ui64v(query, gl::QUERY_RESULT)
            }),
        )
    }
}

impl<T> Drop for GpuFrameProfile<T> {
    fn drop(&mut self) {
        self.disable_timers();
        self.disable_samplers();
    }
}

pub struct GpuProfiler<T> {
    gl: Rc<gl::Gl>,
    frames: Vec<GpuFrameProfile<T>>,
    next_frame: usize,
}

impl<T> GpuProfiler<T> {
    pub fn new(gl: Rc<gl::Gl>) -> Self {
        const MAX_PROFILE_FRAMES: usize = 4;
        let frames = (0 .. MAX_PROFILE_FRAMES)
            .map(|_| GpuFrameProfile::new(Rc::clone(&gl)))
            .collect();

        GpuProfiler {
            gl,
            next_frame: 0,
            frames,
        }
    }

    pub fn enable_timers(&mut self) {
        const MAX_TIMERS_PER_FRAME: i32 = 256;

        for frame in &mut self.frames {
            frame.enable_timers(MAX_TIMERS_PER_FRAME);
        }
    }

    pub fn disable_timers(&mut self) {
        for frame in &mut self.frames {
            frame.disable_timers();
        }
    }

    pub fn toggle_timers_enabled(&mut self) {
        if self.frames[0].timers.set.is_empty() {
            self.enable_timers();
        } else {
            self.disable_timers();
        }
    }

    pub fn enable_samplers(&mut self) {
        const MAX_SAMPLERS_PER_FRAME: i32 = 16;
        if cfg!(target_os = "macos") {
            warn!("Expect OSX driver bugs related to sample queries")
        }

        for frame in &mut self.frames {
            frame.enable_samplers(MAX_SAMPLERS_PER_FRAME);
        }
    }

    pub fn disable_samplers(&mut self) {
        for frame in &mut self.frames {
            frame.disable_samplers();
        }
    }

    pub fn toggle_samplers_enabled(&mut self) {
        if self.frames[0].samplers.set.is_empty() {
            self.enable_samplers();
        } else {
            self.disable_samplers();
        }
    }
}

impl<T: NamedTag> GpuProfiler<T> {
    pub fn build_samples(&mut self) -> (FrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>) {
        self.frames[self.next_frame].build_samples()
    }

    pub fn begin_frame(&mut self, frame_id: FrameId) {
        self.frames[self.next_frame].begin_frame(frame_id);
    }

    pub fn end_frame(&mut self) {
        self.frames[self.next_frame].end_frame();
        self.next_frame = (self.next_frame + 1) % self.frames.len();
    }

    pub fn start_timer(&mut self, tag: T) -> GpuTimeQuery {
        self.frames[self.next_frame].start_timer(tag)
    }

    pub fn start_sampler(&mut self, tag: T) -> GpuSampleQuery {
        self.frames[self.next_frame].start_sampler(tag)
    }

    pub fn finish_sampler(&mut self, _sampler: GpuSampleQuery) {
        self.frames[self.next_frame].finish_sampler()
    }

    pub fn start_marker(&mut self, label: &str) -> GpuMarker {
        GpuMarker::new(&self.gl, label)
    }

    pub fn place_marker(&mut self, label: &str) {
        GpuMarker::fire(&self.gl, label)
    }
}

#[must_use]
pub struct GpuMarker {
    gl: Option<Rc<gl::Gl>>,
}

impl GpuMarker {
    fn new(gl: &Rc<gl::Gl>, message: &str) -> Self {
        if gl.get_type() == gl::GlType::Gl {
            gl.push_group_marker_ext(message);
            GpuMarker { gl: Some(Rc::clone(gl)) }
        } else {
            GpuMarker { gl: None }
        }
    }

    fn fire(gl: &Rc<gl::Gl>, message: &str) {
        if gl.get_type() == gl::GlType::Gl {
            gl.insert_event_marker_ext(message);
        }
    }
}

impl Drop for GpuMarker {
    fn drop(&mut self) {
        if let Some(ref gl) = self.gl {
            gl.pop_group_marker_ext();
        }
    }
}

#[must_use]
pub struct GpuTimeQuery(GpuMarker);
#[must_use]
pub struct GpuSampleQuery;
<commit_msg>Auto merge of #2049 - servo:kvark-query-win, r=jrmuizel<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 gleam::gl;
use std::mem;
use std::rc::Rc;

use device::FrameId;

pub trait NamedTag {
    fn get_label(&self) -> &str;
}

#[derive(Debug, Clone)]
pub struct GpuTimer<T> {
    pub tag: T,
    pub time_ns: u64,
}

#[derive(Debug, Clone)]
pub struct GpuSampler<T> {
    pub tag: T,
    pub count: u64,
}

pub struct QuerySet<T> {
    set: Vec<gl::GLuint>,
    data: Vec<T>,
    pending: gl::GLuint,
}

impl<T> QuerySet<T> {
    fn new() -> Self {
        QuerySet {
            set: Vec::new(),
            data: Vec::new(),
            pending: 0,
        }
    }

    fn reset(&mut self) {
        self.data.clear();
        self.pending = 0;
    }

    fn add(&mut self, value: T) -> Option<gl::GLuint> {
        assert_eq!(self.pending, 0);
        self.set.get(self.data.len()).cloned().map(|query_id| {
            self.data.push(value);
            self.pending = query_id;
            query_id
        })
    }

    fn take<F: Fn(&mut T, gl::GLuint)>(&mut self, fun: F) -> Vec<T> {
        let mut data = mem::replace(&mut self.data, Vec::new());
        for (value, &query) in data.iter_mut().zip(self.set.iter()) {
            fun(value, query)
        }
        data
    }
}

pub struct GpuFrameProfile<T> {
    gl: Rc<gl::Gl>,
    timers: QuerySet<GpuTimer<T>>,
    samplers: QuerySet<GpuSampler<T>>,
    frame_id: FrameId,
    inside_frame: bool,
}

impl<T> GpuFrameProfile<T> {
    fn new(gl: Rc<gl::Gl>) -> Self {
        GpuFrameProfile {
            gl,
            timers: QuerySet::new(),
            samplers: QuerySet::new(),
            frame_id: FrameId::new(0),
            inside_frame: false,
        }
    }

    fn enable_timers(&mut self, count: i32) {
        self.timers.set = self.gl.gen_queries(count);
    }

    fn disable_timers(&mut self) {
        if !self.timers.set.is_empty() {
            self.gl.delete_queries(&self.timers.set);
        }
        self.timers.set = Vec::new();
    }

    fn enable_samplers(&mut self, count: i32) {
        self.samplers.set = self.gl.gen_queries(count);
    }

    fn disable_samplers(&mut self) {
        if !self.samplers.set.is_empty() {
            self.gl.delete_queries(&self.samplers.set);
        }
        self.samplers.set = Vec::new();
    }

    fn begin_frame(&mut self, frame_id: FrameId) {
        self.frame_id = frame_id;
        self.timers.reset();
        self.samplers.reset();
        self.inside_frame = true;
    }

    fn end_frame(&mut self) {
        self.finish_timer();
        self.finish_sampler();
        self.inside_frame = false;
    }

    fn finish_timer(&mut self) {
        debug_assert!(self.inside_frame);
        if self.timers.pending != 0 {
            self.gl.end_query(gl::TIME_ELAPSED);
            self.timers.pending = 0;
        }
    }

    fn finish_sampler(&mut self) {
        debug_assert!(self.inside_frame);
        if self.samplers.pending != 0 {
            self.gl.end_query(gl::SAMPLES_PASSED);
            self.samplers.pending = 0;
        }
    }
}

impl<T: NamedTag> GpuFrameProfile<T> {
    fn start_timer(&mut self, tag: T) -> GpuTimeQuery {
        self.finish_timer();

        let marker = GpuMarker::new(&self.gl, tag.get_label());

        if let Some(query) = self.timers.add(GpuTimer { tag, time_ns: 0 }) {
            self.gl.begin_query(gl::TIME_ELAPSED, query);
        }

        GpuTimeQuery(marker)
    }

    fn start_sampler(&mut self, tag: T) -> GpuSampleQuery {
        self.finish_sampler();

        if let Some(query) = self.samplers.add(GpuSampler { tag, count: 0 }) {
            self.gl.begin_query(gl::SAMPLES_PASSED, query);
        }

        GpuSampleQuery
    }

    fn build_samples(&mut self) -> (FrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>) {
        debug_assert!(!self.inside_frame);
        let gl = &self.gl;

        (
            self.frame_id,
            self.timers.take(|timer, query| {
                timer.time_ns = gl.get_query_object_ui64v(query, gl::QUERY_RESULT)
            }),
            self.samplers.take(|sampler, query| {
                sampler.count = gl.get_query_object_ui64v(query, gl::QUERY_RESULT)
            }),
        )
    }
}

impl<T> Drop for GpuFrameProfile<T> {
    fn drop(&mut self) {
        self.disable_timers();
        self.disable_samplers();
    }
}

pub struct GpuProfiler<T> {
    gl: Rc<gl::Gl>,
    frames: Vec<GpuFrameProfile<T>>,
    next_frame: usize,
}

impl<T> GpuProfiler<T> {
    pub fn new(gl: Rc<gl::Gl>) -> Self {
        const MAX_PROFILE_FRAMES: usize = 4;
        let frames = (0 .. MAX_PROFILE_FRAMES)
            .map(|_| GpuFrameProfile::new(Rc::clone(&gl)))
            .collect();

        GpuProfiler {
            gl,
            next_frame: 0,
            frames,
        }
    }

    pub fn enable_timers(&mut self) {
        const MAX_TIMERS_PER_FRAME: i32 = 256;

        for frame in &mut self.frames {
            frame.enable_timers(MAX_TIMERS_PER_FRAME);
        }
    }

    pub fn disable_timers(&mut self) {
        for frame in &mut self.frames {
            frame.disable_timers();
        }
    }

    pub fn toggle_timers_enabled(&mut self) {
        if self.frames[0].timers.set.is_empty() {
            self.enable_timers();
        } else {
            self.disable_timers();
        }
    }

    pub fn enable_samplers(&mut self) {
        const MAX_SAMPLERS_PER_FRAME: i32 = 16;
        if cfg!(target_os = "macos") {
            warn!("Expect OSX driver bugs related to sample queries")
        }

        for frame in &mut self.frames {
            frame.enable_samplers(MAX_SAMPLERS_PER_FRAME);
        }
    }

    pub fn disable_samplers(&mut self) {
        for frame in &mut self.frames {
            frame.disable_samplers();
        }
    }

    pub fn toggle_samplers_enabled(&mut self) {
        if self.frames[0].samplers.set.is_empty() {
            self.enable_samplers();
        } else {
            self.disable_samplers();
        }
    }
}

impl<T: NamedTag> GpuProfiler<T> {
    pub fn build_samples(&mut self) -> (FrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>) {
        self.frames[self.next_frame].build_samples()
    }

    pub fn begin_frame(&mut self, frame_id: FrameId) {
        self.frames[self.next_frame].begin_frame(frame_id);
    }

    pub fn end_frame(&mut self) {
        self.frames[self.next_frame].end_frame();
        self.next_frame = (self.next_frame + 1) % self.frames.len();
    }

    pub fn start_timer(&mut self, tag: T) -> GpuTimeQuery {
        self.frames[self.next_frame].start_timer(tag)
    }

    pub fn start_sampler(&mut self, tag: T) -> GpuSampleQuery {
        self.frames[self.next_frame].start_sampler(tag)
    }

    pub fn finish_sampler(&mut self, _sampler: GpuSampleQuery) {
        self.frames[self.next_frame].finish_sampler()
    }

    pub fn start_marker(&mut self, label: &str) -> GpuMarker {
        GpuMarker::new(&self.gl, label)
    }

    pub fn place_marker(&mut self, label: &str) {
        GpuMarker::fire(&self.gl, label)
    }
}

#[must_use]
pub struct GpuMarker {
    gl: Option<Rc<gl::Gl>>,
}

impl GpuMarker {
    fn new(gl: &Rc<gl::Gl>, message: &str) -> Self {
        if gl.get_type() == gl::GlType::Gl {
            gl.push_group_marker_ext(message);
            GpuMarker { gl: Some(Rc::clone(gl)) }
        } else {
            GpuMarker { gl: None }
        }
    }

    fn fire(gl: &Rc<gl::Gl>, message: &str) {
        if gl.get_type() == gl::GlType::Gl {
            gl.insert_event_marker_ext(message);
        }
    }
}

impl Drop for GpuMarker {
    fn drop(&mut self) {
        if let Some(ref gl) = self.gl {
            gl.pop_group_marker_ext();
        }
    }
}

#[must_use]
pub struct GpuTimeQuery(GpuMarker);
#[must_use]
pub struct GpuSampleQuery;
<|endoftext|>


<commit_before>extern crate itertools;

extern crate structopt;
#[macro_use]
extern crate structopt_derive;

use itertools::Itertools;
use structopt::StructOpt;
use std::fs::File;
use std::io::Read;

fn part1(input: &str) -> u64 {
    input
        .lines()
        .map(|line| {
            let (min, max) = line.split_whitespace()
                .filter_map(|entry| entry.parse::<u64>().ok())
                .minmax()
                .into_option()
                .expect(&format!("Could not get min / max for {}", line));
            max - min
        })
        .sum()
}

fn main() {
    let opt = Opt::from_args();
    let mut contents = String::new();
    if opt.input == "-" {
        std::io::stdin()
            .read_to_string(&mut contents)
            .expect("could not read stdin");
    } else {
        let mut file = File::open(&opt.input)
            .expect(&format!("file {} not found", opt.input));
        file.read_to_string(&mut contents)
            .expect(&format!("could not read file {}", opt.input));
    }
    println!("Part 1: {}", part1(&contents));
}

#[derive(StructOpt, Debug)]
#[structopt(name = "day01", about = "Advent of code 2017 Day 01")]
struct Opt {
    #[structopt(help = "Input file")] input: String,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn part1_test() {
        assert_eq!(part1("5 1 9 5\n7 5 3\n2 4 6 8"), 18)
    }
}
<commit_msg>2017 day 02 part 02<commit_after>extern crate itertools;

extern crate structopt;
#[macro_use]
extern crate structopt_derive;

use itertools::Itertools;
use structopt::StructOpt;
use std::fs::File;
use std::io::Read;

fn part1(input: &str) -> u64 {
    input
        .lines()
        .map(|line| {
            let (min, max) = line.split_whitespace()
                .filter_map(|entry| entry.parse::<u64>().ok())
                .minmax()
                .into_option()
                .expect(&format!("Could not get min / max for {}", line));
            max - min
        })
        .sum()
}

fn part2(input: &str) -> u64 {
    input
        .lines()
        .map(|line| {
            let values: Vec<_> = line.split_whitespace()
                .filter_map(|entry| entry.parse::<u64>().ok())
                .collect();
            let mut result = None;
            'outer: for a in &values {
                for b in &values {
                    if a != b && a % b == 0 {
                        result = Some(a / b);
                        break 'outer;
                    }
                }
            }
            result
        })
        .filter_map(|x| x)
        .sum()
}

fn main() {
    let opt = Opt::from_args();
    let mut contents = String::new();
    if opt.input == "-" {
        std::io::stdin()
            .read_to_string(&mut contents)
            .expect("could not read stdin");
    } else {
        let mut file = File::open(&opt.input)
            .expect(&format!("file {} not found", opt.input));
        file.read_to_string(&mut contents)
            .expect(&format!("could not read file {}", opt.input));
    }
    println!("Part 1: {}", part1(&contents));
    println!("Part 2: {}", part2(&contents));
}

#[derive(StructOpt, Debug)]
#[structopt(name = "day02", about = "Advent of code 2017 day 02")]
struct Opt {
    #[structopt(help = "Input file")] input: String,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn part1_test() {
        assert_eq!(part1("5 1 9 5\n7 5 3\n2 4 6 8"), 18)
    }

    #[test]
    fn part2_test() {
        assert_eq!(part2("5 9 2 8\n9 4 7 3\n3 8 6 5"), 9)
    }
}
<|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 std::path::{Path, PathBuf};

use bindgen::cargo::cargo_expand;
use bindgen::cargo::cargo_lock::{self, Lock};
use bindgen::cargo::cargo_metadata::{self, Metadata};
use bindgen::cargo::cargo_toml;

/// Parse a dependency string used in Cargo.lock
fn parse_dep_string(dep_string: &str) -> (&str, &str) {
    let split: Vec<&str> = dep_string.split_whitespace().collect();

    (split[0], split[1])
}

/// A reference to a package including it's name and the specific version.
pub(crate) struct PackageRef {
    pub name: String,
    pub version: String,
}

/// A collection of metadata for a library from cargo.
#[derive(Clone, Debug)]
pub(crate) struct Cargo {
    manifest_path: PathBuf,
    binding_crate_name: String,

    lock: Option<Lock>,
    metadata: Metadata,
}

impl Cargo {
    /// Gather metadata from cargo for a specific library and binding crate
    /// name. If dependency finding isn't needed then Cargo.lock files don't
    /// need to be parsed.
    pub(crate) fn load(
        crate_dir: &Path,
        binding_crate_name: Option<&str>,
        use_cargo_lock: bool,
    ) -> Result<Cargo, String> {
        let toml_path = crate_dir.join("Cargo.toml");
        let lock_path = crate_dir.join("Cargo.lock");

        let lock = if use_cargo_lock {
            match cargo_lock::lock(&lock_path) {
                Ok(lock) => Some(lock),
                Err(x) => {
                    warn!("couldn't load lock file {:?}: {:?}", lock_path, x);
                    None
                }
            }
        } else {
            None
        };
        let metadata = cargo_metadata::metadata(&toml_path).map_err(|_| {
            format!(
                "couldn't execute `cargo metadata` with manifest {:?}.",
                toml_path
            )
        })?;

        // Use the specified binding crate name or infer it from the manifest
        let manifest = cargo_toml::manifest(&toml_path)
            .map_err(|_| format!("couldn't load {:?}.", toml_path))?;

        let binding_crate_name =
            binding_crate_name.map_or(manifest.package.name.clone(), |x| x.to_owned());

        Ok(Cargo {
            manifest_path: toml_path,
            binding_crate_name: binding_crate_name,
            lock: lock,
            metadata: metadata,
        })
    }

    pub(crate) fn binding_crate_name(&self) -> &str {
        &self.binding_crate_name
    }

    pub(crate) fn binding_crate_ref(&self) -> PackageRef {
        self.find_pkg_ref(&self.binding_crate_name).unwrap()
    }

    /// Finds the package reference in `cargo metadata` that has `package_name`
    /// ignoring the version.
    fn find_pkg_ref(&self, package_name: &str) -> Option<PackageRef> {
        for package in &self.metadata.packages {
            if package.name == package_name {
                return Some(PackageRef {
                    name: package_name.to_owned(),
                    version: package.version.clone(),
                });
            }
        }
        None
    }

    /// Finds the package reference for a dependency of a crate using
    /// `Cargo.lock`.
    pub(crate) fn find_dep_ref(
        &self,
        package: &PackageRef,
        dependency_name: &str,
    ) -> Option<PackageRef> {
        if self.lock.is_none() {
            return None;
        }
        let lock = self.lock.as_ref().unwrap();

        // the name in Cargo.lock could use '-' instead of '_', so we need to
        // look for that too.
        let replaced_name = dependency_name.replace("_", "-");

        if let &Some(ref root) = &lock.root {
            if root.name == package.name && root.version == package.version {
                if let Some(ref deps) = root.dependencies {
                    for dep in deps {
                        let (name, version) = parse_dep_string(dep);

                        if name == dependency_name || name == &replaced_name {
                            return Some(PackageRef {
                                name: name.to_owned(),
                                version: version.to_owned(),
                            });
                        }
                    }
                }
                return None;
            }
        }

        if let &Some(ref lock_packages) = &lock.package {
            for lock_package in lock_packages {
                if lock_package.name == package.name && lock_package.version == package.version {
                    if let Some(ref deps) = lock_package.dependencies {
                        for dep in deps {
                            let (name, version) = parse_dep_string(dep);

                            if name == dependency_name || name == &replaced_name {
                                return Some(PackageRef {
                                    name: name.to_owned(),
                                    version: version.to_owned(),
                                });
                            }
                        }
                    }
                    return None;
                }
            }
        }
        None
    }

    /// Finds the directory for a specified package reference.
    #[allow(unused)]
    pub(crate) fn find_crate_dir(&self, package: &PackageRef) -> Option<PathBuf> {
        for meta_package in &self.metadata.packages {
            if meta_package.name == package.name && meta_package.version == package.version {
                return Path::new(&meta_package.manifest_path)
                    .parent()
                    .map(|x| x.to_owned());
            }
        }
        None
    }

    /// Finds `src/lib.rs` for a specified package reference.
    pub(crate) fn find_crate_src(&self, package: &PackageRef) -> Option<PathBuf> {
        let kind_lib = String::from("lib");
        let kind_staticlib = String::from("staticlib");
        let kind_rlib = String::from("rlib");
        let kind_cdylib = String::from("cdylib");

        for meta_package in &self.metadata.packages {
            if meta_package.name == package.name && meta_package.version == package.version {
                for target in &meta_package.targets {
                    if target.kind.contains(&kind_lib) || target.kind.contains(&kind_staticlib)
                        || target.kind.contains(&kind_rlib)
                        || target.kind.contains(&kind_cdylib)
                    {
                        return Some(PathBuf::from(&target.src_path));
                    }
                }
                break;
            }
        }
        None
    }

    pub(crate) fn expand_crate(&self, package: &PackageRef) -> Result<String, String> {
        cargo_expand::expand(&self.manifest_path, &package.name, &package.version)
    }
}
<commit_msg>Include cargo-metadata error in own error<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 std::path::{Path, PathBuf};

use bindgen::cargo::cargo_expand;
use bindgen::cargo::cargo_lock::{self, Lock};
use bindgen::cargo::cargo_metadata::{self, Metadata};
use bindgen::cargo::cargo_toml;

/// Parse a dependency string used in Cargo.lock
fn parse_dep_string(dep_string: &str) -> (&str, &str) {
    let split: Vec<&str> = dep_string.split_whitespace().collect();

    (split[0], split[1])
}

/// A reference to a package including it's name and the specific version.
pub(crate) struct PackageRef {
    pub name: String,
    pub version: String,
}

/// A collection of metadata for a library from cargo.
#[derive(Clone, Debug)]
pub(crate) struct Cargo {
    manifest_path: PathBuf,
    binding_crate_name: String,

    lock: Option<Lock>,
    metadata: Metadata,
}

impl Cargo {
    /// Gather metadata from cargo for a specific library and binding crate
    /// name. If dependency finding isn't needed then Cargo.lock files don't
    /// need to be parsed.
    pub(crate) fn load(
        crate_dir: &Path,
        binding_crate_name: Option<&str>,
        use_cargo_lock: bool,
    ) -> Result<Cargo, String> {
        let toml_path = crate_dir.join("Cargo.toml");
        let lock_path = crate_dir.join("Cargo.lock");

        let lock = if use_cargo_lock {
            match cargo_lock::lock(&lock_path) {
                Ok(lock) => Some(lock),
                Err(x) => {
                    warn!("couldn't load lock file {:?}: {:?}", lock_path, x);
                    None
                }
            }
        } else {
            None
        };
        let metadata = cargo_metadata::metadata(&toml_path).map_err(|x| {
            format!(
                "couldn't execute `cargo metadata` with manifest {:?}: {:?}",
                toml_path,
                x
            )
        })?;

        // Use the specified binding crate name or infer it from the manifest
        let manifest = cargo_toml::manifest(&toml_path)
            .map_err(|_| format!("couldn't load {:?}.", toml_path))?;

        let binding_crate_name =
            binding_crate_name.map_or(manifest.package.name.clone(), |x| x.to_owned());

        Ok(Cargo {
            manifest_path: toml_path,
            binding_crate_name: binding_crate_name,
            lock: lock,
            metadata: metadata,
        })
    }

    pub(crate) fn binding_crate_name(&self) -> &str {
        &self.binding_crate_name
    }

    pub(crate) fn binding_crate_ref(&self) -> PackageRef {
        self.find_pkg_ref(&self.binding_crate_name).unwrap()
    }

    /// Finds the package reference in `cargo metadata` that has `package_name`
    /// ignoring the version.
    fn find_pkg_ref(&self, package_name: &str) -> Option<PackageRef> {
        for package in &self.metadata.packages {
            if package.name == package_name {
                return Some(PackageRef {
                    name: package_name.to_owned(),
                    version: package.version.clone(),
                });
            }
        }
        None
    }

    /// Finds the package reference for a dependency of a crate using
    /// `Cargo.lock`.
    pub(crate) fn find_dep_ref(
        &self,
        package: &PackageRef,
        dependency_name: &str,
    ) -> Option<PackageRef> {
        if self.lock.is_none() {
            return None;
        }
        let lock = self.lock.as_ref().unwrap();

        // the name in Cargo.lock could use '-' instead of '_', so we need to
        // look for that too.
        let replaced_name = dependency_name.replace("_", "-");

        if let &Some(ref root) = &lock.root {
            if root.name == package.name && root.version == package.version {
                if let Some(ref deps) = root.dependencies {
                    for dep in deps {
                        let (name, version) = parse_dep_string(dep);

                        if name == dependency_name || name == &replaced_name {
                            return Some(PackageRef {
                                name: name.to_owned(),
                                version: version.to_owned(),
                            });
                        }
                    }
                }
                return None;
            }
        }

        if let &Some(ref lock_packages) = &lock.package {
            for lock_package in lock_packages {
                if lock_package.name == package.name && lock_package.version == package.version {
                    if let Some(ref deps) = lock_package.dependencies {
                        for dep in deps {
                            let (name, version) = parse_dep_string(dep);

                            if name == dependency_name || name == &replaced_name {
                                return Some(PackageRef {
                                    name: name.to_owned(),
                                    version: version.to_owned(),
                                });
                            }
                        }
                    }
                    return None;
                }
            }
        }
        None
    }

    /// Finds the directory for a specified package reference.
    #[allow(unused)]
    pub(crate) fn find_crate_dir(&self, package: &PackageRef) -> Option<PathBuf> {
        for meta_package in &self.metadata.packages {
            if meta_package.name == package.name && meta_package.version == package.version {
                return Path::new(&meta_package.manifest_path)
                    .parent()
                    .map(|x| x.to_owned());
            }
        }
        None
    }

    /// Finds `src/lib.rs` for a specified package reference.
    pub(crate) fn find_crate_src(&self, package: &PackageRef) -> Option<PathBuf> {
        let kind_lib = String::from("lib");
        let kind_staticlib = String::from("staticlib");
        let kind_rlib = String::from("rlib");
        let kind_cdylib = String::from("cdylib");

        for meta_package in &self.metadata.packages {
            if meta_package.name == package.name && meta_package.version == package.version {
                for target in &meta_package.targets {
                    if target.kind.contains(&kind_lib) || target.kind.contains(&kind_staticlib)
                        || target.kind.contains(&kind_rlib)
                        || target.kind.contains(&kind_cdylib)
                    {
                        return Some(PathBuf::from(&target.src_path));
                    }
                }
                break;
            }
        }
        None
    }

    pub(crate) fn expand_crate(&self, package: &PackageRef) -> Result<String, String> {
        cargo_expand::expand(&self.manifest_path, &package.name, &package.version)
    }
}
<|endoftext|>


<commit_before><commit_msg>Use the provided NamedTempFile::persist method<commit_after><|endoftext|>


<commit_before>use std::collections::HashMap;
use std::path::{Path, PathBuf};

use crate::parser::{Expr, Macro, Node};
use crate::Config;

pub struct Heritage<'a> {
    pub root: &'a Context<'a>,
    pub blocks: BlockAncestry<'a>,
}

impl<'a> Heritage<'a> {
    pub fn new<'n>(
        mut ctx: &'n Context<'n>,
        contexts: &'n HashMap<&'n PathBuf, Context<'n>>,
    ) -> Heritage<'n> {
        let mut blocks: BlockAncestry<'n> = ctx
            .blocks
            .iter()
            .map(|(name, def)| (*name, vec![(ctx, *def)]))
            .collect();

        while let Some(ref path) = ctx.extends {
            ctx = &contexts[&path];
            for (name, def) in &ctx.blocks {
                blocks.entry(name).or_insert_with(Vec::new).push((ctx, def));
            }
        }

        Heritage { root: ctx, blocks }
    }
}

type BlockAncestry<'a> = HashMap<&'a str, Vec<(&'a Context<'a>, &'a Node<'a>)>>;

pub struct Context<'a> {
    pub nodes: &'a [Node<'a>],
    pub extends: Option<PathBuf>,
    pub blocks: HashMap<&'a str, &'a Node<'a>>,
    pub macros: HashMap<&'a str, &'a Macro<'a>>,
    pub imports: HashMap<&'a str, PathBuf>,
}

impl<'a> Context<'a> {
    pub fn new<'n>(config: &Config, path: &Path, nodes: &'n [Node<'n>]) -> Context<'n> {
        let mut extends = None;
        let mut blocks = Vec::new();
        let mut macros = HashMap::new();
        let mut imports = HashMap::new();

        for n in nodes {
            match n {
                Node::Extends(Expr::StrLit(extends_path)) => match extends {
                    Some(_) => panic!("multiple extend blocks found"),
                    None => {
                        extends = Some(config.find_template(extends_path, Some(path)));
                    }
                },
                def @ Node::BlockDef(_, _, _, _) => {
                    blocks.push(def);
                }
                Node::Macro(name, m) => {
                    macros.insert(*name, m);
                }
                Node::Import(_, import_path, scope) => {
                    let path = config.find_template(import_path, Some(path));
                    imports.insert(*scope, path);
                }
                _ => {}
            }
        }

        let mut check_nested = 0;
        let mut nested_blocks = Vec::new();
        while check_nested < blocks.len() {
            if let Node::BlockDef(_, _, ref nodes, _) = blocks[check_nested] {
                for n in nodes {
                    if let def @ Node::BlockDef(_, _, _, _) = n {
                        nested_blocks.push(def);
                    }
                }
            } else {
                panic!("non block found in list of blocks");
            }
            blocks.append(&mut nested_blocks);
            check_nested += 1;
        }

        let blocks: HashMap<_, _> = blocks
            .iter()
            .map(|def| {
                if let Node::BlockDef(_, name, _, _) = def {
                    (*name, *def)
                } else {
                    unreachable!()
                }
            })
            .collect();

        Context {
            nodes,
            extends,
            blocks,
            macros,
            imports,
        }
    }
}
<commit_msg>Refactor code for finding nested blocks<commit_after>use std::collections::HashMap;
use std::path::{Path, PathBuf};

use crate::parser::{Expr, Macro, Node};
use crate::Config;

pub struct Heritage<'a> {
    pub root: &'a Context<'a>,
    pub blocks: BlockAncestry<'a>,
}

impl<'a> Heritage<'a> {
    pub fn new<'n>(
        mut ctx: &'n Context<'n>,
        contexts: &'n HashMap<&'n PathBuf, Context<'n>>,
    ) -> Heritage<'n> {
        let mut blocks: BlockAncestry<'n> = ctx
            .blocks
            .iter()
            .map(|(name, def)| (*name, vec![(ctx, *def)]))
            .collect();

        while let Some(ref path) = ctx.extends {
            ctx = &contexts[&path];
            for (name, def) in &ctx.blocks {
                blocks.entry(name).or_insert_with(Vec::new).push((ctx, def));
            }
        }

        Heritage { root: ctx, blocks }
    }
}

type BlockAncestry<'a> = HashMap<&'a str, Vec<(&'a Context<'a>, &'a Node<'a>)>>;

pub struct Context<'a> {
    pub nodes: &'a [Node<'a>],
    pub extends: Option<PathBuf>,
    pub blocks: HashMap<&'a str, &'a Node<'a>>,
    pub macros: HashMap<&'a str, &'a Macro<'a>>,
    pub imports: HashMap<&'a str, PathBuf>,
}

impl<'a> Context<'a> {
    pub fn new<'n>(config: &Config, path: &Path, nodes: &'n [Node<'n>]) -> Context<'n> {
        let mut extends = None;
        let mut blocks = Vec::new();
        let mut macros = HashMap::new();
        let mut imports = HashMap::new();
        let mut nested = vec![nodes];
        let mut top = true;

        while let Some(nodes) = nested.pop() {
            for n in nodes {
                match n {
                    Node::Extends(Expr::StrLit(extends_path)) if top => match extends {
                        Some(_) => panic!("multiple extend blocks found"),
                        None => {
                            extends = Some(config.find_template(extends_path, Some(path)));
                        }
                    },
                    Node::Macro(name, m) if top => {
                        macros.insert(*name, m);
                    }
                    Node::Import(_, import_path, scope) if top => {
                        let path = config.find_template(import_path, Some(path));
                        imports.insert(*scope, path);
                    }
                    Node::Extends(_) | Node::Macro(_, _) | Node::Import(_, _, _) if !top => {
                        panic!("extends, macro or import blocks not allowed below top level");
                    }
                    def @ Node::BlockDef(_, _, _, _) => {
                        blocks.push(def);
                        if let Node::BlockDef(_, _, nodes, _) = def {
                            nested.push(nodes);
                        }
                    }
                    _ => {}
                }
            }
            top = false;
        }

        let blocks: HashMap<_, _> = blocks
            .iter()
            .map(|def| {
                if let Node::BlockDef(_, name, _, _) = def {
                    (*name, *def)
                } else {
                    unreachable!()
                }
            })
            .collect();

        Context {
            nodes,
            extends,
            blocks,
            macros,
            imports,
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Add read::coff::SectionTable::max_section_file_offset (#344)<commit_after><|endoftext|>


<commit_before>use camera::Camera;
use event::{Action, Key, MouseButton, WindowEvent};
use na::{self, Isometry3, Matrix4, Perspective3, Point3, Translation3, Vector2, Vector3};
use num::Zero;
use resource::ShaderUniform;
use std::f32;
use window::Canvas;

/// First-person camera mode.
///
///   * Left button press + drag - look around
///   * Right button press + drag - translates the camera position on the plane orthogonal to the
///   view direction
///   * Scroll in/out - zoom in/out
#[derive(Debug, Clone)]
pub struct FirstPerson {
    eye: Point3<f32>,
    yaw: f32,
    pitch: f32,

    yaw_step: f32,
    pitch_step: f32,
    move_step: f32,
    rotate_button: Option<MouseButton>,
    drag_button: Option<MouseButton>,
    up_key: Option<Key>,
    down_key: Option<Key>,
    left_key: Option<Key>,
    right_key: Option<Key>,

    projection: Perspective3<f32>,
    proj: Matrix4<f32>,
    view: Matrix4<f32>,
    proj_view: Matrix4<f32>,
    inverse_proj_view: Matrix4<f32>,
    last_cursor_pos: Vector2<f32>,
    up_axis: Vector3<f32>,
}

impl FirstPerson {
    /// Creates a first person camera with default sensitivity values.
    pub fn new(eye: Point3<f32>, at: Point3<f32>) -> FirstPerson {
        FirstPerson::new_with_frustrum(f32::consts::PI / 4.0, 0.1, 1024.0, eye, at)
    }

    /// Creates a new first person camera with default sensitivity values.
    pub fn new_with_frustrum(
        fov: f32,
        znear: f32,
        zfar: f32,
        eye: Point3<f32>,
        at: Point3<f32>,
    ) -> FirstPerson {
        let mut res = FirstPerson {
            eye: Point3::new(0.0, 0.0, 0.0),
            yaw: 0.0,
            pitch: 0.0,
            yaw_step: 0.005,
            pitch_step: 0.005,
            move_step: 0.5,
            rotate_button: Some(MouseButton::Button1),
            drag_button: Some(MouseButton::Button2),
            up_key: Some(Key::Up),
            down_key: Some(Key::Down),
            left_key: Some(Key::Left),
            right_key: Some(Key::Right),
            projection: Perspective3::new(800.0 / 600.0, fov, znear, zfar),
            proj: na::zero(),
            view: na::zero(),
            proj_view: na::zero(),
            inverse_proj_view: na::zero(),
            last_cursor_pos: na::zero(),
            up_axis: Vector3::y(),
        };

        res.look_at(eye, at);

        res
    }

    /// Sets the translational increment per arrow press.
    ///
    /// The default value is 0.5.
    #[inline]
    pub fn set_move_step(&mut self, step: f32) {
        self.move_step = step;
    }

    /// Sets the pitch increment per mouse movement.
    ///
    /// The default value is 0.005.
    #[inline]
    pub fn set_pitch_step(&mut self, step: f32) {
        self.pitch_step = step;
    }

    /// Sets the yaw increment per mouse movement.
    ///
    /// The default value is 0.005.
    #[inline]
    pub fn set_yaw_step(&mut self, step: f32) {
        self.yaw_step = step;
    }

    /// Gets the translational increment per arrow press.
    #[inline]
    pub fn move_step(&self) -> f32 {
        self.move_step
    }

    /// Gets the pitch increment per mouse movement.
    #[inline]
    pub fn pitch_step(&self) -> f32 {
        self.pitch_step
    }

    /// Gets the yaw  increment per mouse movement.
    #[inline]
    pub fn yaw_step(&self) -> f32 {
        self.yaw_step
    }

    /// Changes the orientation and position of the camera to look at the specified point.
    pub fn look_at(&mut self, eye: Point3<f32>, at: Point3<f32>) {
        let dist = (eye - at).norm();

        let pitch = ((at.y - eye.y) / dist).acos();
        let yaw = (at.z - eye.z).atan2(at.x - eye.x);

        self.eye = eye;
        self.yaw = yaw;
        self.pitch = pitch;
        self.update_projviews();
    }

    /// The point the camera is looking at.
    pub fn at(&self) -> Point3<f32> {
        let ax = self.eye.x + self.yaw.cos() * self.pitch.sin();
        let ay = self.eye.y + self.pitch.cos();
        let az = self.eye.z + self.yaw.sin() * self.pitch.sin();

        Point3::new(ax, ay, az)
    }

    fn update_restrictions(&mut self) {
        if self.pitch <= 0.01 {
            self.pitch = 0.01
        }

        let _pi: f32 = f32::consts::PI;
        if self.pitch > _pi - 0.01 {
            self.pitch = _pi - 0.01
        }
    }

    /// The button used to rotate the FirstPerson camera.
    pub fn rotate_button(&self) -> Option<MouseButton> {
        self.rotate_button
    }

    /// Set the button used to rotate the FirstPerson camera.
    /// Use None to disable rotation.
    pub fn rebind_rotate_button(&mut self, new_button: Option<MouseButton>) {
        self.rotate_button = new_button;
    }

    /// The button used to drag the FirstPerson camera.
    pub fn drag_button(&self) -> Option<MouseButton> {
        self.drag_button
    }

    /// Set the button used to drag the FirstPerson camera.
    /// Use None to disable dragging.
    pub fn rebind_drag_button(&mut self, new_button: Option<MouseButton>) {
        self.drag_button = new_button;
    }

    /// The movement button for up.
    pub fn up_key(&self) -> Option<Key> {
        self.up_key
    }

    /// The movement button for down.
    pub fn down_key(&self) -> Option<Key> {
        self.down_key
    }

    /// The movement button for left.
    pub fn left_key(&self) -> Option<Key> {
        self.left_key
    }

    /// The movement button for right.
    pub fn right_key(&self) -> Option<Key> {
        self.right_key
    }

    /// Set the movement button for up.
    /// Use None to disable movement in this direction.
    pub fn rebind_up_key(&mut self, new_key: Option<Key>) {
        self.up_key = new_key;
    }

    /// Set the movement button for down.
    /// Use None to disable movement in this direction.
    pub fn rebind_down_key(&mut self, new_key: Option<Key>) {
        self.down_key = new_key;
    }

    /// Set the movement button for left.
    /// Use None to disable movement in this direction.
    pub fn rebind_left_key(&mut self, new_key: Option<Key>) {
        self.left_key = new_key;
    }

    /// Set the movement button for right.
    /// Use None to disable movement in this direction.
    pub fn rebind_right_key(&mut self, new_key: Option<Key>) {
        self.right_key = new_key;
    }

    /// Disable the movement buttons for up, down, left and right.
    pub fn unbind_movement_keys(&mut self) {
        self.up_key = None;
        self.down_key = None;
        self.left_key = None;
        self.right_key = None;
    }

    #[doc(hidden)]
    pub fn handle_left_button_displacement(&mut self, dpos: &Vector2<f32>) {
        self.yaw = self.yaw + dpos.x * self.yaw_step;
        self.pitch = self.pitch + dpos.y * self.pitch_step;

        self.update_restrictions();
        self.update_projviews();
    }

    #[doc(hidden)]
    pub fn handle_right_button_displacement(&mut self, dpos: &Vector2<f32>) {
        let at = self.at();
        let dir = (at - self.eye).normalize();
        let tangent = Vector3::y().cross(&dir).normalize();
        let bitangent = dir.cross(&tangent);

        self.eye = self.eye + tangent * (0.01 * dpos.x / 10.0) + bitangent * (0.01 * dpos.y / 10.0);
        self.update_restrictions();
        self.update_projviews();
    }

    #[doc(hidden)]
    pub fn handle_scroll(&mut self, yoff: f32) {
        let front = self.observer_frame() * Vector3::z();

        self.eye = self.eye + front * (self.move_step * yoff);

        self.update_restrictions();
        self.update_projviews();
    }

    fn update_projviews(&mut self) {
        self.view = self.view_transform().to_homogeneous();
        self.proj = *self.projection.as_matrix();
        self.proj_view = self.proj * self.view;
        let _ = self
            .proj_view
            .try_inverse()
            .map(|inverse_proj| self.inverse_proj_view = inverse_proj);
    }

    /// The direction this camera is looking at.
    pub fn eye_dir(&self) -> Vector3<f32> {
        (self.at() - self.eye).normalize()
    }

    /// The direction this camera is being moved by the keyboard keys for a given set of key states.
    pub fn move_dir(&self, up: bool, down: bool, right: bool, left: bool) -> Vector3<f32> {
        let t = self.observer_frame();
        let frontv = t * Vector3::z();
        let rightv = t * Vector3::x();

        let mut movement = na::zero::<Vector3<f32>>();

        if up {
            movement = movement + frontv
        }

        if down {
            movement = movement - frontv
        }

        if right {
            movement = movement - rightv
        }

        if left {
            movement = movement + rightv
        }

        if movement.is_zero() {
            movement
        } else {
            movement.normalize()
        }
    }

    /// Translates in-place this camera by `t`.
    #[inline]
    pub fn translate_mut(&mut self, t: &Translation3<f32>) {
        let new_eye = t * self.eye;

        self.set_eye(new_eye);
    }

    /// Translates this camera by `t`.
    #[inline]
    pub fn translate(&self, t: &Translation3<f32>) -> FirstPerson {
        let mut res = self.clone();
        res.translate_mut(t);
        res
    }

    /// Sets the eye of this camera to `eye`.
    #[inline]
    fn set_eye(&mut self, eye: Point3<f32>) {
        self.eye = eye;
        self.update_restrictions();
        self.update_projviews();
    }

    /// Sets the Up vector of this camera.
    #[inline]
    pub fn set_up_axis(&mut self, up_axis: Vector3<f32>) {
        self.up_axis = up_axis;
    }

    /// The camera observer local frame.
    fn observer_frame(&self) -> Isometry3<f32> {
        Isometry3::face_towards(&self.eye, &self.at(), &Vector3::y())
    }
}

impl Camera for FirstPerson {
    fn clip_planes(&self) -> (f32, f32) {
        (self.projection.znear(), self.projection.zfar())
    }

    /// The camera view transformation (i-e transformation without projection).
    fn view_transform(&self) -> Isometry3<f32> {
        Isometry3::look_at_rh(&self.eye, &self.at(), &self.up_axis)
    }

    fn handle_event(&mut self, canvas: &Canvas, event: &WindowEvent) {
        match *event {
            WindowEvent::CursorPos(x, y, _) => {
                let curr_pos = Vector2::new(x as f32, y as f32);

                if let Some(rotate_button) = self.rotate_button {
                    if canvas.get_mouse_button(rotate_button) == Action::Press {
                        let dpos = curr_pos - self.last_cursor_pos;
                        self.handle_left_button_displacement(&dpos)
                    }
                }

                if let Some(drag_button) = self.drag_button {
                    if canvas.get_mouse_button(drag_button) == Action::Press {
                        let dpos = curr_pos - self.last_cursor_pos;
                        self.handle_right_button_displacement(&dpos)
                    }
                }

                self.last_cursor_pos = curr_pos;
            }
            WindowEvent::Scroll(_, off, _) => self.handle_scroll(off as f32),
            WindowEvent::FramebufferSize(w, h) => {
                self.projection.set_aspect(w as f32 / h as f32);
                self.update_projviews();
            }
            _ => {}
        }
    }

    fn eye(&self) -> Point3<f32> {
        self.eye
    }

    fn transformation(&self) -> Matrix4<f32> {
        self.proj_view
    }

    fn inverse_transformation(&self) -> Matrix4<f32> {
        self.inverse_proj_view
    }

    #[inline]
    fn upload(
        &self,
        _: usize,
        proj: &mut ShaderUniform<Matrix4<f32>>,
        view: &mut ShaderUniform<Matrix4<f32>>,
    ) {
        proj.upload(&self.proj);
        view.upload(&self.view);
    }

    fn update(&mut self, canvas: &Canvas) {
        let up = check_optional_key_state(canvas, self.up_key, Action::Press);
        let down = check_optional_key_state(canvas, self.down_key, Action::Press);
        let right = check_optional_key_state(canvas, self.right_key, Action::Press);
        let left = check_optional_key_state(canvas, self.left_key, Action::Press);
        let dir = self.move_dir(up, down, right, left);

        let move_amount = dir * self.move_step;
        self.translate_mut(&Translation3::from(move_amount));
    }
}

fn check_optional_key_state(canvas: &Canvas, key: Option<Key>, key_state: Action) -> bool {
    if let Some(actual_key) = key {
        canvas.get_key(actual_key) == key_state
    } else {
        false
    }
}
<commit_msg>Fix First Person camera controls for Z-up<commit_after>use camera::Camera;
use event::{Action, Key, MouseButton, WindowEvent};
use na::{self, Isometry3, Matrix4, Perspective3, Point3, Translation3, Vector2, Vector3};
use num::Zero;
use resource::ShaderUniform;
use std::f32;
use window::Canvas;

/// First-person camera mode.
///
///   * Left button press + drag - look around
///   * Right button press + drag - translates the camera position on the plane orthogonal to the
///   view direction
///   * Scroll in/out - zoom in/out
#[derive(Debug, Clone)]
pub struct FirstPerson {
    eye: Point3<f32>,
    yaw: f32,
    pitch: f32,

    yaw_step: f32,
    pitch_step: f32,
    move_step: f32,
    rotate_button: Option<MouseButton>,
    drag_button: Option<MouseButton>,
    up_key: Option<Key>,
    down_key: Option<Key>,
    left_key: Option<Key>,
    right_key: Option<Key>,

    projection: Perspective3<f32>,
    proj: Matrix4<f32>,
    view: Matrix4<f32>,
    proj_view: Matrix4<f32>,
    inverse_proj_view: Matrix4<f32>,
    last_cursor_pos: Vector2<f32>,
    coord_system: CoordSystem,
}

impl FirstPerson {
    /// Creates a first person camera with default sensitivity values.
    pub fn new(eye: Point3<f32>, at: Point3<f32>) -> FirstPerson {
        FirstPerson::new_with_frustrum(f32::consts::PI / 4.0, 0.1, 1024.0, eye, at)
    }

    /// Creates a new first person camera with default sensitivity values.
    pub fn new_with_frustrum(
        fov: f32,
        znear: f32,
        zfar: f32,
        eye: Point3<f32>,
        at: Point3<f32>,
    ) -> FirstPerson {
        let mut res = FirstPerson {
            eye: Point3::new(0.0, 0.0, 0.0),
            yaw: 0.0,
            pitch: 0.0,
            yaw_step: 0.005,
            pitch_step: 0.005,
            move_step: 0.5,
            rotate_button: Some(MouseButton::Button1),
            drag_button: Some(MouseButton::Button2),
            up_key: Some(Key::Up),
            down_key: Some(Key::Down),
            left_key: Some(Key::Left),
            right_key: Some(Key::Right),
            projection: Perspective3::new(800.0 / 600.0, fov, znear, zfar),
            proj: na::zero(),
            view: na::zero(),
            proj_view: na::zero(),
            inverse_proj_view: na::zero(),
            last_cursor_pos: na::zero(),
            coord_system: CoordSystem::RightHandYUp,
        };

        res.look_at(eye, at);

        res
    }

    /// Sets the translational increment per arrow press.
    ///
    /// The default value is 0.5.
    #[inline]
    pub fn set_move_step(&mut self, step: f32) {
        self.move_step = step;
    }

    /// Sets the pitch increment per mouse movement.
    ///
    /// The default value is 0.005.
    #[inline]
    pub fn set_pitch_step(&mut self, step: f32) {
        self.pitch_step = step;
    }

    /// Sets the yaw increment per mouse movement.
    ///
    /// The default value is 0.005.
    #[inline]
    pub fn set_yaw_step(&mut self, step: f32) {
        self.yaw_step = step;
    }

    /// Gets the translational increment per arrow press.
    #[inline]
    pub fn move_step(&self) -> f32 {
        self.move_step
    }

    /// Gets the pitch increment per mouse movement.
    #[inline]
    pub fn pitch_step(&self) -> f32 {
        self.pitch_step
    }

    /// Gets the yaw  increment per mouse movement.
    #[inline]
    pub fn yaw_step(&self) -> f32 {
        self.yaw_step
    }

    /// Changes the orientation and position of the camera to look at the specified point.
    pub fn look_at(&mut self, eye: Point3<f32>, at: Point3<f32>) {
        let dist = (eye - at).norm();

        let pitch;
        let yaw;
        match self.coord_system {
            CoordSystem::RightHandYUp => {
                pitch = ((at.y - eye.y) / dist).acos();
                yaw = (at.z - eye.z).atan2(at.x - eye.x);
            }
            CoordSystem::RightHandZUp => {
                pitch = ((at.z - eye.z) / dist).acos();
                yaw = (eye.y - at.y).atan2(at.x - eye.x);
            }
        }

        self.eye = eye;
        self.yaw = yaw;
        self.pitch = pitch;
        self.update_projviews();
    }

    /// The point the camera is looking at.
    pub fn at(&self) -> Point3<f32> {
        let ax = self.eye.x + self.yaw.cos() * self.pitch.sin();
        let ay;
        let az;
        match self.coord_system {
            CoordSystem::RightHandYUp => {
                ay = self.eye.y + self.pitch.cos();
                az = self.eye.z + self.yaw.sin() * self.pitch.sin();
            }
            CoordSystem::RightHandZUp => {
                ay = self.eye.y - self.yaw.sin() * self.pitch.sin();
                az = self.eye.z + self.pitch.cos();
            }
        }

        Point3::new(ax, ay, az)
    }

    fn update_restrictions(&mut self) {
        if self.pitch <= 0.01 {
            self.pitch = 0.01
        }

        let _pi: f32 = f32::consts::PI;
        if self.pitch > _pi - 0.01 {
            self.pitch = _pi - 0.01
        }
    }

    /// The button used to rotate the FirstPerson camera.
    pub fn rotate_button(&self) -> Option<MouseButton> {
        self.rotate_button
    }

    /// Set the button used to rotate the FirstPerson camera.
    /// Use None to disable rotation.
    pub fn rebind_rotate_button(&mut self, new_button: Option<MouseButton>) {
        self.rotate_button = new_button;
    }

    /// The button used to drag the FirstPerson camera.
    pub fn drag_button(&self) -> Option<MouseButton> {
        self.drag_button
    }

    /// Set the button used to drag the FirstPerson camera.
    /// Use None to disable dragging.
    pub fn rebind_drag_button(&mut self, new_button: Option<MouseButton>) {
        self.drag_button = new_button;
    }

    /// The movement button for up.
    pub fn up_key(&self) -> Option<Key> {
        self.up_key
    }

    /// The movement button for down.
    pub fn down_key(&self) -> Option<Key> {
        self.down_key
    }

    /// The movement button for left.
    pub fn left_key(&self) -> Option<Key> {
        self.left_key
    }

    /// The movement button for right.
    pub fn right_key(&self) -> Option<Key> {
        self.right_key
    }

    /// Set the movement button for up.
    /// Use None to disable movement in this direction.
    pub fn rebind_up_key(&mut self, new_key: Option<Key>) {
        self.up_key = new_key;
    }

    /// Set the movement button for down.
    /// Use None to disable movement in this direction.
    pub fn rebind_down_key(&mut self, new_key: Option<Key>) {
        self.down_key = new_key;
    }

    /// Set the movement button for left.
    /// Use None to disable movement in this direction.
    pub fn rebind_left_key(&mut self, new_key: Option<Key>) {
        self.left_key = new_key;
    }

    /// Set the movement button for right.
    /// Use None to disable movement in this direction.
    pub fn rebind_right_key(&mut self, new_key: Option<Key>) {
        self.right_key = new_key;
    }

    /// Disable the movement buttons for up, down, left and right.
    pub fn unbind_movement_keys(&mut self) {
        self.up_key = None;
        self.down_key = None;
        self.left_key = None;
        self.right_key = None;
    }

    #[doc(hidden)]
    pub fn handle_left_button_displacement(&mut self, dpos: &Vector2<f32>) {
        self.yaw = self.yaw + dpos.x * self.yaw_step;
        self.pitch = self.pitch + dpos.y * self.pitch_step;

        self.update_restrictions();
        self.update_projviews();
    }

    #[doc(hidden)]
    pub fn handle_right_button_displacement(&mut self, dpos: &Vector2<f32>) {
        let at = self.at();
        let dir = (at - self.eye).normalize();
        let tangent = self.coord_system.up_axis().cross(&dir).normalize();
        let bitangent = dir.cross(&tangent);

        self.eye = self.eye + tangent * (0.01 * dpos.x / 10.0) + bitangent * (0.01 * dpos.y / 10.0);
        self.update_restrictions();
        self.update_projviews();
    }

    #[doc(hidden)]
    pub fn handle_scroll(&mut self, yoff: f32) {
        let front = self.observer_frame() * Vector3::z();

        self.eye = self.eye + front * (self.move_step * yoff);

        self.update_restrictions();
        self.update_projviews();
    }

    fn update_projviews(&mut self) {
        self.view = self.view_transform().to_homogeneous();
        self.proj = *self.projection.as_matrix();
        self.proj_view = self.proj * self.view;
        let _ = self
            .proj_view
            .try_inverse()
            .map(|inverse_proj| self.inverse_proj_view = inverse_proj);
    }

    /// The direction this camera is looking at.
    pub fn eye_dir(&self) -> Vector3<f32> {
        (self.at() - self.eye).normalize()
    }

    /// The direction this camera is being moved by the keyboard keys for a given set of key states.
    pub fn move_dir(&self, up: bool, down: bool, right: bool, left: bool) -> Vector3<f32> {
        let t = self.observer_frame();
        let frontv = t * Vector3::z();
        let rightv = t * Vector3::x();

        let mut movement = na::zero::<Vector3<f32>>();

        if up {
            movement = movement + frontv
        }

        if down {
            movement = movement - frontv
        }

        if right {
            movement = movement - rightv
        }

        if left {
            movement = movement + rightv
        }

        if movement.is_zero() {
            movement
        } else {
            movement.normalize()
        }
    }

    /// Translates in-place this camera by `t`.
    #[inline]
    pub fn translate_mut(&mut self, t: &Translation3<f32>) {
        let new_eye = t * self.eye;

        self.set_eye(new_eye);
    }

    /// Translates this camera by `t`.
    #[inline]
    pub fn translate(&self, t: &Translation3<f32>) -> FirstPerson {
        let mut res = self.clone();
        res.translate_mut(t);
        res
    }

    /// Sets the eye of this camera to `eye`.
    #[inline]
    fn set_eye(&mut self, eye: Point3<f32>) {
        self.eye = eye;
        self.update_restrictions();
        self.update_projviews();
    }

    /// Sets the Up vector of this camera.
    #[inline]
    pub fn set_up_axis(&mut self, up_axis: Vector3<f32>) {
        let new_coord_system;
        if up_axis == Vector3::y() {
            new_coord_system = CoordSystem::RightHandYUp;
        } else if up_axis == Vector3::z() {
            new_coord_system = CoordSystem::RightHandZUp;
        } else {
            panic!("This up_axis is not supported: {:?}", up_axis);
        }
        if self.coord_system != new_coord_system {
            // Since setting the up axis changes the meaning of pitch and yaw
            // angles, we need to recalculate them in order to preserve the eye
            // position.
            let old_at = self.at();
            self.coord_system = new_coord_system;
            self.look_at(self.eye, old_at);
        }
    }

    /// The camera observer local frame.
    fn observer_frame(&self) -> Isometry3<f32> {
        Isometry3::face_towards(&self.eye, &self.at(), &self.coord_system.up_axis())
    }
}

impl Camera for FirstPerson {
    fn clip_planes(&self) -> (f32, f32) {
        (self.projection.znear(), self.projection.zfar())
    }

    /// The camera view transformation (i-e transformation without projection).
    fn view_transform(&self) -> Isometry3<f32> {
        Isometry3::look_at_rh(&self.eye, &self.at(), &self.coord_system.up_axis())
    }

    fn handle_event(&mut self, canvas: &Canvas, event: &WindowEvent) {
        match *event {
            WindowEvent::CursorPos(x, y, _) => {
                let curr_pos = Vector2::new(x as f32, y as f32);

                if let Some(rotate_button) = self.rotate_button {
                    if canvas.get_mouse_button(rotate_button) == Action::Press {
                        let dpos = curr_pos - self.last_cursor_pos;
                        self.handle_left_button_displacement(&dpos)
                    }
                }

                if let Some(drag_button) = self.drag_button {
                    if canvas.get_mouse_button(drag_button) == Action::Press {
                        let dpos = curr_pos - self.last_cursor_pos;
                        self.handle_right_button_displacement(&dpos)
                    }
                }

                self.last_cursor_pos = curr_pos;
            }
            WindowEvent::Scroll(_, off, _) => self.handle_scroll(off as f32),
            WindowEvent::FramebufferSize(w, h) => {
                self.projection.set_aspect(w as f32 / h as f32);
                self.update_projviews();
            }
            _ => {}
        }
    }

    fn eye(&self) -> Point3<f32> {
        self.eye
    }

    fn transformation(&self) -> Matrix4<f32> {
        self.proj_view
    }

    fn inverse_transformation(&self) -> Matrix4<f32> {
        self.inverse_proj_view
    }

    #[inline]
    fn upload(
        &self,
        _: usize,
        proj: &mut ShaderUniform<Matrix4<f32>>,
        view: &mut ShaderUniform<Matrix4<f32>>,
    ) {
        proj.upload(&self.proj);
        view.upload(&self.view);
    }

    fn update(&mut self, canvas: &Canvas) {
        let up = check_optional_key_state(canvas, self.up_key, Action::Press);
        let down = check_optional_key_state(canvas, self.down_key, Action::Press);
        let right = check_optional_key_state(canvas, self.right_key, Action::Press);
        let left = check_optional_key_state(canvas, self.left_key, Action::Press);
        let dir = self.move_dir(up, down, right, left);

        let move_amount = dir * self.move_step;
        self.translate_mut(&Translation3::from(move_amount));
    }
}

fn check_optional_key_state(canvas: &Canvas, key: Option<Key>, key_state: Action) -> bool {
    if let Some(actual_key) = key {
        canvas.get_key(actual_key) == key_state
    } else {
        false
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum CoordSystem {
    RightHandYUp,
    RightHandZUp,
}

impl CoordSystem {
    #[inline]
    fn up_axis(self) -> Vector3<f32> {
        match self {
            CoordSystem::RightHandYUp => Vector3::y(),
            CoordSystem::RightHandZUp => Vector3::z(),
        }
    }
}
<|endoftext|>