<commit_before>use goblin::archive::*;
use scroll::Pread;
use std::path::Path;
use std::fs::File;

#[test]
fn parse_file_header() {
    let file_header: [u8; SIZEOF_HEADER] = [0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
                                            0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
                                            0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20,
                                            0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
                                            0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30,
                                            0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20,
                                            0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38,
                                            0x32, 0x34, 0x34, 0x20, 0x20, 0x20, 0x20,
                                            0x20, 0x20, 0x60, 0x0a];
    let buffer = &file_header[..];
    match buffer.pread::<MemberHeader>(0) {
        Err(e) => panic!("could not read the buffer: {:?}", e),
        Ok(file_header2) => {
            let file_header = MemberHeader {
                identifier: [0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,],
                timestamp: [48, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32],
                owner_id: [48, 32, 32, 32, 32, 32],
                group_id: [48, 32, 32, 32, 32, 32],
                mode: [48, 32, 32, 32, 32, 32, 32, 32],
                file_size: [56, 50, 52, 52, 32, 32, 32, 32, 32, 32],
                terminator: [96, 10] };
            assert_eq!(file_header, file_header2)
        }
    }
}

#[test]
fn parse_archive() {
    let crt1a: Vec<u8> = include!("../etc/crt1a.rs");
    const START: &str = "_start";
    match Archive::parse(&crt1a) {
        Ok(archive) => {
            assert_eq!(archive.member_of_symbol(START), Some("crt1.o"));
            if let Some(member) = archive.get("crt1.o") {
                assert_eq!(member.offset, 194);
                assert_eq!(member.size(), 1928)
            } else {
                panic!("could not get crt1.o");
            }
        },
        Err(err) => panic!("could not parse archive: {:?}", err),
    };
}

#[test]
fn parse_self() {
    use std::fs;
    use std::io::Read;
    let mut path = Path::new("target").join("debug").join("libgoblin.rlib");
    // https://github.com/m4b/goblin/issues/63
    if fs::metadata(&path).is_err() {
        path = Path::new("target").join("release").join("libgoblin.rlib");
    }
    let buffer = {
        let mut fd = File::open(path).expect("open file");
        let mut v = Vec::new();
        fd.read_to_end(&mut v).expect("read file");
        v
    };

    let archive = Archive::parse(&buffer).expect("parse rlib");

    // check that the archive has a useful symbol table by counting the total number of symbols
    let symbol_count: usize = archive.summarize().into_iter()
        .map(|(_member_name, _member_index, ref symbols)| symbols.len())
        .sum();
    assert!(symbol_count > 500);

    let goblin_object_name = archive.members()
        .into_iter()
        .find(|member| {
            println!("member: {:?}", member);
            member.ends_with("goblin-archive.o")    // < 1.18
                || (member.starts_with("goblin") && member.ends_with("0.o")) // >= 1.18 && < 1.22
                || (member.starts_with("goblin") && member.ends_with("rust-cgu.o")) // = 1.22
                || (member.starts_with("goblin") && member.ends_with("rcgu.o")) // >= nightly 1.23
        })
        .expect("goblin-<hash>.0.o not found");

    let bytes = archive.extract(goblin_object_name, &buffer).expect("extract goblin object");
    match goblin::Object::parse(&bytes).expect("parse object") {
        goblin::Object::Elf(elf) => {
            assert!(elf.entry == 0);
        }
        goblin::Object::Mach(goblin::mach::Mach::Binary(macho)) => {
            assert_eq!(macho.header.filetype, goblin::mach::header::MH_OBJECT);
            assert_eq!(macho.entry, 0);
        }
        other => {
            panic!("unexpected Object::parse result: {:?}", other);
        }
    }
}
<commit_msg>tests: update expect message for parse_self archive<commit_after>use goblin::archive::*;
use scroll::Pread;
use std::path::Path;
use std::fs::File;

#[test]
fn parse_file_header() {
    let file_header: [u8; SIZEOF_HEADER] = [0x2f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
                                            0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
                                            0x20, 0x20, 0x30, 0x20, 0x20, 0x20, 0x20,
                                            0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
                                            0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30,
                                            0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x20,
                                            0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38,
                                            0x32, 0x34, 0x34, 0x20, 0x20, 0x20, 0x20,
                                            0x20, 0x20, 0x60, 0x0a];
    let buffer = &file_header[..];
    match buffer.pread::<MemberHeader>(0) {
        Err(e) => panic!("could not read the buffer: {:?}", e),
        Ok(file_header2) => {
            let file_header = MemberHeader {
                identifier: [0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,],
                timestamp: [48, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32],
                owner_id: [48, 32, 32, 32, 32, 32],
                group_id: [48, 32, 32, 32, 32, 32],
                mode: [48, 32, 32, 32, 32, 32, 32, 32],
                file_size: [56, 50, 52, 52, 32, 32, 32, 32, 32, 32],
                terminator: [96, 10] };
            assert_eq!(file_header, file_header2)
        }
    }
}

#[test]
fn parse_archive() {
    let crt1a: Vec<u8> = include!("../etc/crt1a.rs");
    const START: &str = "_start";
    match Archive::parse(&crt1a) {
        Ok(archive) => {
            assert_eq!(archive.member_of_symbol(START), Some("crt1.o"));
            if let Some(member) = archive.get("crt1.o") {
                assert_eq!(member.offset, 194);
                assert_eq!(member.size(), 1928)
            } else {
                panic!("could not get crt1.o");
            }
        },
        Err(err) => panic!("could not parse archive: {:?}", err),
    };
}

#[test]
fn parse_self() {
    use std::fs;
    use std::io::Read;
    let mut path = Path::new("target").join("debug").join("libgoblin.rlib");
    // https://github.com/m4b/goblin/issues/63
    if fs::metadata(&path).is_err() {
        path = Path::new("target").join("release").join("libgoblin.rlib");
    }
    let buffer = {
        let mut fd = File::open(path).expect("can open file; did you run cargo build first?");
        let mut v = Vec::new();
        fd.read_to_end(&mut v).expect("read file");
        v
    };

    let archive = Archive::parse(&buffer).expect("parse rlib");

    // check that the archive has a useful symbol table by counting the total number of symbols
    let symbol_count: usize = archive.summarize().into_iter()
        .map(|(_member_name, _member_index, ref symbols)| symbols.len())
        .sum();
    assert!(symbol_count > 500);

    let goblin_object_name = archive.members()
        .into_iter()
        .find(|member| {
            println!("member: {:?}", member);
            member.ends_with("goblin-archive.o")    // < 1.18
                || (member.starts_with("goblin") && member.ends_with("0.o")) // >= 1.18 && < 1.22
                || (member.starts_with("goblin") && member.ends_with("rust-cgu.o")) // = 1.22
                || (member.starts_with("goblin") && member.ends_with("rcgu.o")) // >= nightly 1.23
        })
        .expect("goblin-<hash>.0.o not found");

    let bytes = archive.extract(goblin_object_name, &buffer).expect("extract goblin object");
    match goblin::Object::parse(&bytes).expect("parse object") {
        goblin::Object::Elf(elf) => {
            assert!(elf.entry == 0);
        }
        goblin::Object::Mach(goblin::mach::Mach::Binary(macho)) => {
            assert_eq!(macho.header.filetype, goblin::mach::header::MH_OBJECT);
            assert_eq!(macho.entry, 0);
        }
        other => {
            panic!("unexpected Object::parse result: {:?}", other);
        }
    }
}
<|endoftext|>


<commit_before>use base::math::*;
use world::WorldView;
use glium::Surface;
use super::{Camera, GameContext};
use view::Sun;
use view::SkyView;
use std::rc::Rc;
use std::error::Error;
use std::env;
use super::weather::Weather;
use glium::texture::texture2d::Texture2d;
use glium::texture::{DepthFormat, DepthTexture2d, MipmapsOption, UncompressedFloatFormat};
use glium::framebuffer::SimpleFrameBuffer;
use glium::{IndexBuffer, Program, VertexBuffer};
use glium::index::PrimitiveType;
use glium::backend::Facade;
use glium::framebuffer::ToColorAttachment;

const SHADOW_MAP_SIZE: u32 = 1024;
const SHADOW_ORTHO_WIDTH: f32 = 300.0;
const SHADOW_ORTHO_HEIGHT: f32 = 300.0;
const SHADOW_ORTHO_NEAR: f32 = 100.0;
const SHADOW_ORTHO_FAR: f32 = 400.0;

pub struct Renderer {
    context: Rc<GameContext>,
    /// Screen-sized texture the scene is rendered into and then post-processed.
    quad_tex: Texture2d,
    /// Depth texture used by the normal render.
    depth_texture: DepthTexture2d,
    /// Depth texture rendered to from sun perspective.
    shadow_map: DepthTexture2d,
    /// Render the shadow map to the screen instead of the world.
    shadow_debug: bool,
    shadow_debug_program: Program,
    quad_vertex_buffer: VertexBuffer<Vertex>,
    quad_index_buffer: IndexBuffer<u16>,
    resolution: (u32, u32),
    bloom_filter_tex: Texture2d,
    bloom_horz_tex: Texture2d,
    bloom_vert_tex: Texture2d,
    bloom_blend_tex: Texture2d,
    tonemapping_program: Program,
    bloom_filter_program: Program,
    bloom_blur_program: Program,
    bloom_blend_program: Program,
}

impl Renderer {
    pub fn new(context: Rc<GameContext>) -> Self {

        // FIXME The index buffer is useless, switch to using `NoIndices` and
        // `TriangleStrip`
        let ibuf = IndexBuffer::new(context.get_facade(),
                                    PrimitiveType::TrianglesList,
                                    &[0u16, 1, 2, 0, 2, 3])
            .unwrap();

        let shadow_map = DepthTexture2d::empty_with_format(context.get_facade(),
                                                           DepthFormat::I16,
                                                           MipmapsOption::NoMipmap,
                                                           SHADOW_MAP_SIZE,
                                                           SHADOW_MAP_SIZE)
            .unwrap();

        let tonemapping_program = context.load_program("tonemapping").unwrap();
        let bloom_filter_program = context.load_program("bloom_filter").unwrap();
        let bloom_blur_program = context.load_program("bloom_blur").unwrap();
        let bloom_blend_program = context.load_program("bloom_blending").unwrap();
        let shadow_debug_program = context.load_program("shadow_debug").unwrap();

        let mut this = Renderer {
            context: context.clone(),
            quad_tex: Texture2d::empty(context.get_facade(), 1, 1).unwrap(),
            tonemapping_program: tonemapping_program,
            depth_texture: DepthTexture2d::empty(context.get_facade(), 1, 1).unwrap(),
            shadow_map: shadow_map,
            shadow_debug: env::var("SHADOW_DEBUG").is_ok(),
            shadow_debug_program: shadow_debug_program,
            quad_vertex_buffer: Renderer::create_vertex_buf(context.get_facade()),
            quad_index_buffer: ibuf,
            resolution: (0, 0),
            bloom_filter_tex: Texture2d::empty(context.get_facade(), 1, 1).unwrap(),
            bloom_horz_tex: Texture2d::empty(context.get_facade(), 1, 1).unwrap(),
            bloom_vert_tex: Texture2d::empty(context.get_facade(), 1, 1).unwrap(),
            bloom_blend_tex: Texture2d::empty(context.get_facade(), 1, 1).unwrap(),
            bloom_filter_program: bloom_filter_program,
            bloom_blur_program: bloom_blur_program,
            bloom_blend_program: bloom_blend_program,
        };

        // Create all textures with correct screen size
        this.render_update();
        this
    }

    /// Renders `world_view` into the renderer's shadow map from the
    /// perspective of the sun, whose position is `sun_pos`.
    ///
    /// Returns the MVP matrix used.
    fn render_shadow_map(&mut self,
                         world_view: &WorldView,
                         sun_pos: Point3f,
                         camera: Point3f)
                         -> Result<Matrix4<f32>, Box<Error>> {
        let mut shadow_target = try!(SimpleFrameBuffer::depth_only(self.context
                                                                       .get_facade(),
                                                                   &self.shadow_map));
        shadow_target.clear_depth(1.0);

        // Render the world from the perspective of the sun.
        let mut cam_pos = camera.to_vec();
        cam_pos.z = 0.0;
        let mut sun_cam = Camera::new_from_vector(sun_pos + cam_pos,
                                                  -sun_pos.to_vec(),
                                                  SHADOW_ORTHO_WIDTH / SHADOW_ORTHO_HEIGHT);
        // Set an orthographic projection matrix.
        sun_cam.set_proj_matrix(ortho(-SHADOW_ORTHO_WIDTH / 2.0,
                                      SHADOW_ORTHO_WIDTH / 2.0,
                                      -SHADOW_ORTHO_HEIGHT / 2.0,
                                      SHADOW_ORTHO_HEIGHT / 2.0,
                                      SHADOW_ORTHO_NEAR,
                                      SHADOW_ORTHO_FAR));
        world_view.draw_shadow(&mut shadow_target, &sun_cam);

        Ok(sun_cam.proj_matrix() * sun_cam.view_matrix())
    }

    /// Is called once every main loop iteration
    pub fn render(&mut self,
                  world_view: &WorldView,
                  camera: &Camera,
                  sun: &Sun,
                  weather: &mut Weather,
                  sky_view: &SkyView)
                  -> Result<(), Box<Error>> {
        // ===================================================================
        // check dimensions
        // ===================================================================
        let new_res = self.context.get_facade().get_framebuffer_dimensions();
        if self.resolution != new_res {
            self.render_update();
        }

        // ===================================================================
        // Creating shadow map
        // ===================================================================
        let depth_mvp = try!(self.render_shadow_map(world_view, sun.position(), camera.position));

        // ===================================================================
        // Rendering into HDR framebuffer
        // ===================================================================
        let mut hdr_buffer = try!(SimpleFrameBuffer::with_depth_buffer(self.context.get_facade(),
                                                                       &self.quad_tex,
                                                                       &self.depth_texture));
        hdr_buffer.clear_color_and_depth((0.0, 0.0, 1.0, 1.0), 1.0);

        if self.shadow_debug {
            let mut target = self.context.get_facade().draw();
            target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
            try!(target.draw(&self.quad_vertex_buffer,
                             &self.quad_index_buffer,
                             &self.shadow_debug_program,
                             &uniform!{
                          decal_texture: &self.shadow_map,
                      },
                             &Default::default()));
            try!(target.finish());
            return Ok(());
        }

        world_view.draw(&mut hdr_buffer, camera, &self.shadow_map, &depth_mvp);
        sky_view.draw_skydome(&mut hdr_buffer, camera);
        sun.draw_sun(&mut hdr_buffer, camera);
        weather.draw(&mut hdr_buffer, camera);

        // ===================================================================
        // Creating the Bloom framebuffer
        // ===================================================================

        // =======================  light texture  ===========================

        // Bloom state
        // 0: Disable Bloom
        // 1: Enable Bloom
        // 2: Show only Bloom Map
        let bloom_state = 1;

        let uniforms = uniform! {
            decal_texture: &self.quad_tex,
            exposure: 1.0f32
        };

        let mut bloom_buffer = try!(SimpleFrameBuffer::new(self.context.get_facade(),
                                                           self.bloom_filter_tex
                                                               .to_color_attachment()));

        bloom_buffer.clear_color(0.0, 0.0, 0.0, 1.0);

        try!(bloom_buffer.draw(&self.quad_vertex_buffer,
                               &self.quad_index_buffer,
                               &self.bloom_filter_program,
                               &uniforms,
                               &Default::default()));

        // ============================  blur  ===============================

        let mut bloom_blur_horz_buffer = try!(SimpleFrameBuffer::new(self.context.get_facade(),
                                                                     self.bloom_horz_tex
                                                                         .to_color_attachment()));

        bloom_blur_horz_buffer.clear_color(0.0, 0.0, 0.0, 1.0);

        let mut bloom_blur_vert_buffer = try!(SimpleFrameBuffer::new(self.context.get_facade(),
                                                                     self.bloom_vert_tex
                                                                         .to_color_attachment()));
        bloom_blur_vert_buffer.clear_color(0.0, 0.0, 0.0, 1.0);

        let mut uniforms_horz_blur = uniform! {
            //for the first iteration: Use the bloom quad texture, from second iteration on this
            // will change to bloom_vert_tex
            image: &self.bloom_filter_tex,
            horizontal: true,
        };
        let uniforms_vert_blur = uniform! {
            image: &self.bloom_horz_tex,
            horizontal: false,
        };

        let mut first_iteration = true; //to know when we need to switch uniforms_horz_blur
        let mut horizontal = true;      //to switch between horizontal and vertical blur

        for _ in 0..10 {
            if horizontal {
                try!(bloom_blur_horz_buffer.draw(&self.quad_vertex_buffer,
                                                 &self.quad_index_buffer,
                                                 &self.bloom_blur_program,
                                                 &uniforms_horz_blur,
                                                 &Default::default()));
            } else {
                try!(bloom_blur_vert_buffer.draw(&self.quad_vertex_buffer,
                                                 &self.quad_index_buffer,
                                                 &self.bloom_blur_program,
                                                 &uniforms_vert_blur,
                                                 &Default::default()));
            }
            if first_iteration {
                uniforms_horz_blur = uniform! {
                    image: &self.bloom_vert_tex,
                    horizontal: true,
                };
                first_iteration = false;
            }
            horizontal = !horizontal;
        }

        // ==========================  blending  =============================

        let mut bloom_blend_buffer = try!(SimpleFrameBuffer::new(self.context.get_facade(),
                                                                 self.bloom_blend_tex
                                                                     .to_color_attachment()));

        bloom_blend_buffer.clear_color(0.0, 0.0, 0.0, 1.0);

        let u_blend_tex =
            if horizontal { &self.bloom_vert_tex } else { &self.bloom_horz_tex };

        let uniforms = uniform! {
            bloom_tex: u_blend_tex,
            world_tex: &self.quad_tex,
        };

        try!(bloom_blend_buffer.draw(&self.quad_vertex_buffer,
                                     &self.quad_index_buffer,
                                     &self.bloom_blend_program,
                                     &uniforms,
                                     &Default::default()));

        // ===================================================================
        // Tonemapping
        // ===================================================================

        let decal_texture = match bloom_state {
            0 => &self.quad_tex,
            2 => u_blend_tex,
            _ => &self.bloom_blend_tex,
        };

        let uniforms = uniform! {
            decal_texture: decal_texture,
            exposure: 1.0f32,
        };

        let mut target = self.context.get_facade().draw();
        target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);

        try!(target.draw(&self.quad_vertex_buffer,
                         &self.quad_index_buffer,
                         &self.tonemapping_program,
                         &uniforms,
                         &Default::default()));

        try!(target.finish());

        Ok(())
    }

    fn create_vertex_buf<F: Facade>(facade: &F) -> VertexBuffer<Vertex> {

        VertexBuffer::new(facade,
                          &[Vertex {
                                in_position: [-1.0, -1.0, 0.0, 1.0],
                                in_texcoord: [0.0, 0.0],
                            },
                            Vertex {
                                in_position: [1.0, -1.0, 0.0, 1.0],
                                in_texcoord: [1.0, 0.0],
                            },
                            Vertex {
                                in_position: [1.0, 1.0, 0.0, 1.0],
                                in_texcoord: [1.0, 1.0],
                            },
                            Vertex {
                                in_position: [-1.0, 1.0, 0.0, 1.0],
                                in_texcoord: [0.0, 1.0],
                            }])
            .unwrap()
    }

    // render update is called when a dimensions resize of the facade occures
    // recreates the 2D textures
    fn render_update(&mut self) {
        self.resolution = self.context.get_facade().get_framebuffer_dimensions();
        info!("time to resize framebuffer with dimensions {},{}",
              self.resolution.0,
              self.resolution.1);

        self.quad_tex = Texture2d::empty_with_format(self.context.get_facade(),
                                                     UncompressedFloatFormat::F32F32F32F32,
                                                     MipmapsOption::NoMipmap,
                                                     self.resolution.0,
                                                     self.resolution.1)
            .unwrap();

        self.depth_texture = DepthTexture2d::empty_with_format(self.context.get_facade(),
                                                               DepthFormat::F32,
                                                               MipmapsOption::NoMipmap,
                                                               self.resolution.0,
                                                               self.resolution.1)
            .unwrap();

        self.bloom_filter_tex = Texture2d::empty_with_format(self.context.get_facade(),
                                                             UncompressedFloatFormat::F32F32F32F32,
                                                             MipmapsOption::NoMipmap,
                                                             self.resolution.0,
                                                             self.resolution.1)
            .unwrap();

        self.bloom_horz_tex = Texture2d::empty_with_format(self.context.get_facade(),
                                                           UncompressedFloatFormat::F32F32F32F32,
                                                           MipmapsOption::NoMipmap,
                                                           self.resolution.0,
                                                           self.resolution.1)
            .unwrap();

        self.bloom_vert_tex = Texture2d::empty_with_format(self.context.get_facade(),
                                                           UncompressedFloatFormat::F32F32F32F32,
                                                           MipmapsOption::NoMipmap,
                                                           self.resolution.0,
                                                           self.resolution.1)
            .unwrap();

        self.bloom_blend_tex = Texture2d::empty_with_format(self.context.get_facade(),
                                                            UncompressedFloatFormat::F32F32F32F32,
                                                            MipmapsOption::NoMipmap,
                                                            self.resolution.0,
                                                            self.resolution.1)
            .unwrap();
    }
}

#[derive(Copy, Clone)]
struct Vertex {
    in_position: [f32; 4],
    in_texcoord: [f32; 2],
}

implement_vertex!(Vertex, in_position, in_texcoord);
<commit_msg>Increase shadow projection far plane<commit_after>use base::math::*;
use world::WorldView;
use glium::Surface;
use super::{Camera, GameContext};
use view::Sun;
use view::SkyView;
use std::rc::Rc;
use std::error::Error;
use std::env;
use super::weather::Weather;
use glium::texture::texture2d::Texture2d;
use glium::texture::{DepthFormat, DepthTexture2d, MipmapsOption, UncompressedFloatFormat};
use glium::framebuffer::SimpleFrameBuffer;
use glium::{IndexBuffer, Program, VertexBuffer};
use glium::index::PrimitiveType;
use glium::backend::Facade;
use glium::framebuffer::ToColorAttachment;

const SHADOW_MAP_SIZE: u32 = 1024;
const SHADOW_ORTHO_WIDTH: f32 = 300.0;
const SHADOW_ORTHO_HEIGHT: f32 = 300.0;
const SHADOW_ORTHO_NEAR: f32 = 100.0;
const SHADOW_ORTHO_FAR: f32 = 600.0;

pub struct Renderer {
    context: Rc<GameContext>,
    /// Screen-sized texture the scene is rendered into and then post-processed.
    quad_tex: Texture2d,
    /// Depth texture used by the normal render.
    depth_texture: DepthTexture2d,
    /// Depth texture rendered to from sun perspective.
    shadow_map: DepthTexture2d,
    /// Render the shadow map to the screen instead of the world.
    shadow_debug: bool,
    shadow_debug_program: Program,
    quad_vertex_buffer: VertexBuffer<Vertex>,
    quad_index_buffer: IndexBuffer<u16>,
    resolution: (u32, u32),
    bloom_filter_tex: Texture2d,
    bloom_horz_tex: Texture2d,
    bloom_vert_tex: Texture2d,
    bloom_blend_tex: Texture2d,
    tonemapping_program: Program,
    bloom_filter_program: Program,
    bloom_blur_program: Program,
    bloom_blend_program: Program,
}

impl Renderer {
    pub fn new(context: Rc<GameContext>) -> Self {

        // FIXME The index buffer is useless, switch to using `NoIndices` and
        // `TriangleStrip`
        let ibuf = IndexBuffer::new(context.get_facade(),
                                    PrimitiveType::TrianglesList,
                                    &[0u16, 1, 2, 0, 2, 3])
            .unwrap();

        let shadow_map = DepthTexture2d::empty_with_format(context.get_facade(),
                                                           DepthFormat::I16,
                                                           MipmapsOption::NoMipmap,
                                                           SHADOW_MAP_SIZE,
                                                           SHADOW_MAP_SIZE)
            .unwrap();

        let tonemapping_program = context.load_program("tonemapping").unwrap();
        let bloom_filter_program = context.load_program("bloom_filter").unwrap();
        let bloom_blur_program = context.load_program("bloom_blur").unwrap();
        let bloom_blend_program = context.load_program("bloom_blending").unwrap();
        let shadow_debug_program = context.load_program("shadow_debug").unwrap();

        let mut this = Renderer {
            context: context.clone(),
            quad_tex: Texture2d::empty(context.get_facade(), 1, 1).unwrap(),
            tonemapping_program: tonemapping_program,
            depth_texture: DepthTexture2d::empty(context.get_facade(), 1, 1).unwrap(),
            shadow_map: shadow_map,
            shadow_debug: env::var("SHADOW_DEBUG").is_ok(),
            shadow_debug_program: shadow_debug_program,
            quad_vertex_buffer: Renderer::create_vertex_buf(context.get_facade()),
            quad_index_buffer: ibuf,
            resolution: (0, 0),
            bloom_filter_tex: Texture2d::empty(context.get_facade(), 1, 1).unwrap(),
            bloom_horz_tex: Texture2d::empty(context.get_facade(), 1, 1).unwrap(),
            bloom_vert_tex: Texture2d::empty(context.get_facade(), 1, 1).unwrap(),
            bloom_blend_tex: Texture2d::empty(context.get_facade(), 1, 1).unwrap(),
            bloom_filter_program: bloom_filter_program,
            bloom_blur_program: bloom_blur_program,
            bloom_blend_program: bloom_blend_program,
        };

        // Create all textures with correct screen size
        this.render_update();
        this
    }

    /// Renders `world_view` into the renderer's shadow map from the
    /// perspective of the sun, whose position is `sun_pos`.
    ///
    /// Returns the MVP matrix used.
    fn render_shadow_map(&mut self,
                         world_view: &WorldView,
                         sun_pos: Point3f,
                         camera: Point3f)
                         -> Result<Matrix4<f32>, Box<Error>> {
        let mut shadow_target = try!(SimpleFrameBuffer::depth_only(self.context
                                                                       .get_facade(),
                                                                   &self.shadow_map));
        shadow_target.clear_depth(1.0);

        // Render the world from the perspective of the sun.
        let mut cam_pos = camera.to_vec();
        cam_pos.z = 0.0;
        let mut sun_cam = Camera::new_from_vector(sun_pos + cam_pos,
                                                  -sun_pos.to_vec(),
                                                  SHADOW_ORTHO_WIDTH / SHADOW_ORTHO_HEIGHT);
        // Set an orthographic projection matrix.
        sun_cam.set_proj_matrix(ortho(-SHADOW_ORTHO_WIDTH / 2.0,
                                      SHADOW_ORTHO_WIDTH / 2.0,
                                      -SHADOW_ORTHO_HEIGHT / 2.0,
                                      SHADOW_ORTHO_HEIGHT / 2.0,
                                      SHADOW_ORTHO_NEAR,
                                      SHADOW_ORTHO_FAR));
        world_view.draw_shadow(&mut shadow_target, &sun_cam);

        Ok(sun_cam.proj_matrix() * sun_cam.view_matrix())
    }

    /// Is called once every main loop iteration
    pub fn render(&mut self,
                  world_view: &WorldView,
                  camera: &Camera,
                  sun: &Sun,
                  weather: &mut Weather,
                  sky_view: &SkyView)
                  -> Result<(), Box<Error>> {
        // ===================================================================
        // check dimensions
        // ===================================================================
        let new_res = self.context.get_facade().get_framebuffer_dimensions();
        if self.resolution != new_res {
            self.render_update();
        }

        // ===================================================================
        // Creating shadow map
        // ===================================================================
        let depth_mvp = try!(self.render_shadow_map(world_view, sun.position(), camera.position));

        // ===================================================================
        // Rendering into HDR framebuffer
        // ===================================================================
        let mut hdr_buffer = try!(SimpleFrameBuffer::with_depth_buffer(self.context.get_facade(),
                                                                       &self.quad_tex,
                                                                       &self.depth_texture));
        hdr_buffer.clear_color_and_depth((0.0, 0.0, 1.0, 1.0), 1.0);

        if self.shadow_debug {
            let mut target = self.context.get_facade().draw();
            target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
            try!(target.draw(&self.quad_vertex_buffer,
                             &self.quad_index_buffer,
                             &self.shadow_debug_program,
                             &uniform!{
                          decal_texture: &self.shadow_map,
                      },
                             &Default::default()));
            try!(target.finish());
            return Ok(());
        }

        world_view.draw(&mut hdr_buffer, camera, &self.shadow_map, &depth_mvp);
        sky_view.draw_skydome(&mut hdr_buffer, camera);
        sun.draw_sun(&mut hdr_buffer, camera);
        weather.draw(&mut hdr_buffer, camera);

        // ===================================================================
        // Creating the Bloom framebuffer
        // ===================================================================

        // =======================  light texture  ===========================

        // Bloom state
        // 0: Disable Bloom
        // 1: Enable Bloom
        // 2: Show only Bloom Map
        let bloom_state = 1;

        let uniforms = uniform! {
            decal_texture: &self.quad_tex,
            exposure: 1.0f32
        };

        let mut bloom_buffer = try!(SimpleFrameBuffer::new(self.context.get_facade(),
                                                           self.bloom_filter_tex
                                                               .to_color_attachment()));

        bloom_buffer.clear_color(0.0, 0.0, 0.0, 1.0);

        try!(bloom_buffer.draw(&self.quad_vertex_buffer,
                               &self.quad_index_buffer,
                               &self.bloom_filter_program,
                               &uniforms,
                               &Default::default()));

        // ============================  blur  ===============================

        let mut bloom_blur_horz_buffer = try!(SimpleFrameBuffer::new(self.context.get_facade(),
                                                                     self.bloom_horz_tex
                                                                         .to_color_attachment()));

        bloom_blur_horz_buffer.clear_color(0.0, 0.0, 0.0, 1.0);

        let mut bloom_blur_vert_buffer = try!(SimpleFrameBuffer::new(self.context.get_facade(),
                                                                     self.bloom_vert_tex
                                                                         .to_color_attachment()));
        bloom_blur_vert_buffer.clear_color(0.0, 0.0, 0.0, 1.0);

        let mut uniforms_horz_blur = uniform! {
            //for the first iteration: Use the bloom quad texture, from second iteration on this
            // will change to bloom_vert_tex
            image: &self.bloom_filter_tex,
            horizontal: true,
        };
        let uniforms_vert_blur = uniform! {
            image: &self.bloom_horz_tex,
            horizontal: false,
        };

        let mut first_iteration = true; //to know when we need to switch uniforms_horz_blur
        let mut horizontal = true;      //to switch between horizontal and vertical blur

        for _ in 0..10 {
            if horizontal {
                try!(bloom_blur_horz_buffer.draw(&self.quad_vertex_buffer,
                                                 &self.quad_index_buffer,
                                                 &self.bloom_blur_program,
                                                 &uniforms_horz_blur,
                                                 &Default::default()));
            } else {
                try!(bloom_blur_vert_buffer.draw(&self.quad_vertex_buffer,
                                                 &self.quad_index_buffer,
                                                 &self.bloom_blur_program,
                                                 &uniforms_vert_blur,
                                                 &Default::default()));
            }
            if first_iteration {
                uniforms_horz_blur = uniform! {
                    image: &self.bloom_vert_tex,
                    horizontal: true,
                };
                first_iteration = false;
            }
            horizontal = !horizontal;
        }

        // ==========================  blending  =============================

        let mut bloom_blend_buffer = try!(SimpleFrameBuffer::new(self.context.get_facade(),
                                                                 self.bloom_blend_tex
                                                                     .to_color_attachment()));

        bloom_blend_buffer.clear_color(0.0, 0.0, 0.0, 1.0);

        let u_blend_tex =
            if horizontal { &self.bloom_vert_tex } else { &self.bloom_horz_tex };

        let uniforms = uniform! {
            bloom_tex: u_blend_tex,
            world_tex: &self.quad_tex,
        };

        try!(bloom_blend_buffer.draw(&self.quad_vertex_buffer,
                                     &self.quad_index_buffer,
                                     &self.bloom_blend_program,
                                     &uniforms,
                                     &Default::default()));

        // ===================================================================
        // Tonemapping
        // ===================================================================

        let decal_texture = match bloom_state {
            0 => &self.quad_tex,
            2 => u_blend_tex,
            _ => &self.bloom_blend_tex,
        };

        let uniforms = uniform! {
            decal_texture: decal_texture,
            exposure: 1.0f32,
        };

        let mut target = self.context.get_facade().draw();
        target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);

        try!(target.draw(&self.quad_vertex_buffer,
                         &self.quad_index_buffer,
                         &self.tonemapping_program,
                         &uniforms,
                         &Default::default()));

        try!(target.finish());

        Ok(())
    }

    fn create_vertex_buf<F: Facade>(facade: &F) -> VertexBuffer<Vertex> {

        VertexBuffer::new(facade,
                          &[Vertex {
                                in_position: [-1.0, -1.0, 0.0, 1.0],
                                in_texcoord: [0.0, 0.0],
                            },
                            Vertex {
                                in_position: [1.0, -1.0, 0.0, 1.0],
                                in_texcoord: [1.0, 0.0],
                            },
                            Vertex {
                                in_position: [1.0, 1.0, 0.0, 1.0],
                                in_texcoord: [1.0, 1.0],
                            },
                            Vertex {
                                in_position: [-1.0, 1.0, 0.0, 1.0],
                                in_texcoord: [0.0, 1.0],
                            }])
            .unwrap()
    }

    // render update is called when a dimensions resize of the facade occures
    // recreates the 2D textures
    fn render_update(&mut self) {
        self.resolution = self.context.get_facade().get_framebuffer_dimensions();
        info!("time to resize framebuffer with dimensions {},{}",
              self.resolution.0,
              self.resolution.1);

        self.quad_tex = Texture2d::empty_with_format(self.context.get_facade(),
                                                     UncompressedFloatFormat::F32F32F32F32,
                                                     MipmapsOption::NoMipmap,
                                                     self.resolution.0,
                                                     self.resolution.1)
            .unwrap();

        self.depth_texture = DepthTexture2d::empty_with_format(self.context.get_facade(),
                                                               DepthFormat::F32,
                                                               MipmapsOption::NoMipmap,
                                                               self.resolution.0,
                                                               self.resolution.1)
            .unwrap();

        self.bloom_filter_tex = Texture2d::empty_with_format(self.context.get_facade(),
                                                             UncompressedFloatFormat::F32F32F32F32,
                                                             MipmapsOption::NoMipmap,
                                                             self.resolution.0,
                                                             self.resolution.1)
            .unwrap();

        self.bloom_horz_tex = Texture2d::empty_with_format(self.context.get_facade(),
                                                           UncompressedFloatFormat::F32F32F32F32,
                                                           MipmapsOption::NoMipmap,
                                                           self.resolution.0,
                                                           self.resolution.1)
            .unwrap();

        self.bloom_vert_tex = Texture2d::empty_with_format(self.context.get_facade(),
                                                           UncompressedFloatFormat::F32F32F32F32,
                                                           MipmapsOption::NoMipmap,
                                                           self.resolution.0,
                                                           self.resolution.1)
            .unwrap();

        self.bloom_blend_tex = Texture2d::empty_with_format(self.context.get_facade(),
                                                            UncompressedFloatFormat::F32F32F32F32,
                                                            MipmapsOption::NoMipmap,
                                                            self.resolution.0,
                                                            self.resolution.1)
            .unwrap();
    }
}

#[derive(Copy, Clone)]
struct Vertex {
    in_position: [f32; 4],
    in_texcoord: [f32; 2],
}

implement_vertex!(Vertex, in_position, in_texcoord);
<|endoftext|>


<commit_before>// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate clap;
extern crate clippy_dev;
extern crate regex;

use clap::{App, Arg, SubCommand};
use clippy_dev::*;

fn main() {
    let matches = App::new("Clippy developer tooling")
        .subcommand(
            SubCommand::with_name("update_lints")
                .about("Update the lint list")
                .arg(
                    Arg::with_name("print-only")
                        .long("print-only")
                        .short("p")
                        .help("Print a table of lints to STDOUT. Does not modify any files."),
                )
        )
        .get_matches();

    if let Some(matches) = matches.subcommand_matches("update_lints") {
        if matches.is_present("print-only") {
            print_lints();
        }
    }
}

fn print_lints() {
    let lint_list = gather_all().collect::<Vec<Lint>>();
    let grouped_by_lint_group = Lint::by_lint_group(&lint_list);

    for (lint_group, mut lints) in grouped_by_lint_group {
        if lint_group == "Deprecated" { continue; }
        println!("\n## {}", lint_group);

        lints.sort_by(|a, b| a.name.cmp(&b.name));

        for lint in lints {
            println!("* [{}]({}#{}) ({})", lint.name, clippy_dev::DOCS_LINK.clone(), lint.name, lint.desc);
        }
    }

    println!("there are {} lints", Lint::active_lints(lint_list.into_iter()).count());
}
<commit_msg>sort_by -> sort_by_key<commit_after>// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate clap;
extern crate clippy_dev;
extern crate regex;

use clap::{App, Arg, SubCommand};
use clippy_dev::*;

fn main() {
    let matches = App::new("Clippy developer tooling")
        .subcommand(
            SubCommand::with_name("update_lints")
                .about("Update the lint list")
                .arg(
                    Arg::with_name("print-only")
                        .long("print-only")
                        .short("p")
                        .help("Print a table of lints to STDOUT. Does not modify any files."),
                )
        )
        .get_matches();

    if let Some(matches) = matches.subcommand_matches("update_lints") {
        if matches.is_present("print-only") {
            print_lints();
        }
    }
}

fn print_lints() {
    let lint_list = gather_all().collect::<Vec<Lint>>();
    let grouped_by_lint_group = Lint::by_lint_group(&lint_list);

    for (lint_group, mut lints) in grouped_by_lint_group {
        if lint_group == "Deprecated" { continue; }
        println!("\n## {}", lint_group);

        lints.sort_by_key(|l| l.name.clone());

        for lint in lints {
            println!("* [{}]({}#{}) ({})", lint.name, clippy_dev::DOCS_LINK.clone(), lint.name, lint.desc);
        }
    }

    println!("there are {} lints", Lint::active_lints(lint_list.into_iter()).count());
}
<|endoftext|>


<commit_before>use specs::*;
use templating::*;

use handlebars::{self, Handlebars, HelperDef};
use itertools::Itertools;
use std::collections::BTreeMap;

pub struct CodeGenerator {
    specs:      AMQProtocolDefinition,
    handlebars: Handlebars,
}

impl CodeGenerator {
    pub fn new(specs: AMQProtocolDefinition, templates: AMQPTemplates) -> CodeGenerator {
        let mut handlebars = Handlebars::new();

        handlebars.register_escape_fn(handlebars::no_escape);

        handlebars.register_helper("camel", Box::new(camel_helper));
        handlebars.register_helper("snake", Box::new(snake_helper));

        handlebars.register_template_string("main",     &templates.main).expect("Failed to register main template");
        handlebars.register_template_string("domain",   &templates.domain).expect("Failed to register domain template");
        handlebars.register_template_string("constant", &templates.constant).expect("Failed to register constant template");
        handlebars.register_template_string("class",    &templates.klass).expect("Failed to register class template");
        handlebars.register_template_string("method",   &templates.method).expect("Failed to register method template");
        handlebars.register_template_string("argument", &templates.argument).expect("Failed to register argument template");
        handlebars.register_template_string("property", &templates.property).expect("Failed to register property template");

        CodeGenerator {
            specs:      specs,
            handlebars: handlebars,
        }
    }

    pub fn with_helper(mut self, name: &str, helper: Box<HelperDef + 'static>) -> CodeGenerator {
        self.handlebars.register_helper(name, helper);
        self
    }

    pub fn generate(&self) -> String {
        self.handlebars.render("main", &AMQProtocolDefinitionWrapper {
            protocol:  &self.specs,
            copyright: self.specs.copyright.iter().join(""),
            domains:   self.specs.domains.iter().map(|domain| domain.codegen(&self.handlebars)).join("\n"),
            constants: self.specs.constants.iter().map(|constant| constant.codegen(&self.handlebars)).join("\n"),
            classes:   self.specs.classes.iter().map(|klass| klass.codegen(&self.handlebars)).join("\n"),
        }).expect("Failed to render main template")
    }
}

#[derive(Debug, Serialize)]
struct AMQProtocolDefinitionWrapper<'a> {
    protocol : &'a AMQProtocolDefinition,
    copyright: String,
    domains:   String,
    constants: String,
    classes:   String,
}

pub trait Codegen {
    fn codegen(&self, handlebars: &Handlebars) -> String;
}

trait BasicGetData {
    fn get_data(&self, handlebars: &Handlebars) -> BTreeMap<String, &Self>;
}

trait ClassGetData {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPClassWrapper;
}

trait MethodGetData {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPMethodWrapper;
}

macro_rules! get_data {
    ($t:ty, $key:expr) => {
        impl BasicGetData for $t {
            #[allow(unused_variables)]
            fn get_data(&self, handlebars: &Handlebars) -> BTreeMap<String, &Self> {
                let mut data = BTreeMap::new();
                data.insert($key.to_string(), self);
                data
            }
        }
    }
}

#[derive(Debug, Serialize)]
struct AMQPClassWrapper<'a> {
    #[serde(rename="class")]
    klass:      &'a AMQPClass,
    methods:    String,
    properties: Option<String>,
}

impl ClassGetData for AMQPClass {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPClassWrapper {
        AMQPClassWrapper {
            klass:      self,
            methods:    self.methods.iter().map(|method| method.codegen(&handlebars)).join("\n"),
            properties: match self.properties {
                Some(ref properties) => Some(properties.iter().map(|method| method.codegen(&handlebars)).join("\n")),
                None                 => None,
            },
        }
    }
}

#[derive(Debug, Serialize)]
struct AMQPMethodWrapper<'a> {
    method:    &'a AMQPMethod,
    arguments: String,
}

impl MethodGetData for AMQPMethod {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPMethodWrapper {
        AMQPMethodWrapper {
            method:    self,
            arguments: self.arguments.iter().map(|arg| arg.codegen(&handlebars)).join("\n"),
        }
    }
}

get_data!(AMQPDomain,   "domain");
get_data!(AMQPConstant, "constant");
get_data!(AMQPArgument, "argument");
get_data!(AMQPProperty, "property");

macro_rules! codegen {
    ($t:ty, $name:expr) => {
        impl Codegen for $t {
            fn codegen (&self, handlebars: &Handlebars) -> String {
                handlebars.render($name, &self.get_data(handlebars)).expect(&format!("Failed to render {} template", $name))
            }
        }
    };
}

codegen!(AMQPDomain,   "domain");
codegen!(AMQPConstant, "constant");
codegen!(AMQPClass,    "class");
codegen!(AMQPMethod,   "method");
codegen!(AMQPArgument, "argument");
codegen!(AMQPProperty, "property");

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

    use amq_protocol_types::*;
    use serde_json::Value;
    use templating::AMQPTemplates;

    fn specs() -> AMQProtocolDefinition {
        AMQProtocolDefinition {
            name:          "AMQP".to_string(),
            major_version: 0,
            minor_version: 9,
            revision:      1,
            port:          5672,
            copyright:     vec!["Copyright 1\n".to_string(), "Copyright 2".to_string()],
            domains:       vec![
                AMQPDomain{
                    name:      "domain1".to_string(),
                    amqp_type: AMQPType::ShortInt
                }
            ],
            constants:     vec![
                AMQPConstant {
                    name:  "constant1".to_string(),
                    value: 128,
                    klass: Some("class1".to_string()),
                }
            ],
            classes:       vec![
                AMQPClass {
                    id:         42,
                    methods:    vec![
                        AMQPMethod {
                            id:          64,
                            arguments:   vec![
                                AMQPArgument {
                                    amqp_type:     Some(AMQPType::LongString),
                                    name:          "argument1".to_string(),
                                    default_value: Some(Value::String("value1".to_string())),
                                    domain:        Some("domain1".to_string()),
                                }
                            ],
                            name:        "method1".to_string(),
                            synchronous: Some(true),
                        }
                    ],
                    name:       "class1".to_string(),
                    properties: Some(vec![
                        AMQPProperty {
                            amqp_type: AMQPType::LongString,
                            name:      "property1".to_string(),
                        }
                    ]),
                }
            ],
        }
    }

    fn templates() -> AMQPTemplates {
        AMQPTemplates {
            main:     r#"
{{protocol.name}} - {{protocol.major_version}}.{{protocol.minor_version}}.{{protocol.revision}}
{{copyright}}
port {{protocol.port}}
{{domains}}
{{constants}}
{{classes}}
"#.to_string(),
            domain:   "{{domain.name}}: {{domain.type}}".to_string(),
            constant: "{{constant.name}}({{constant.class}}) = {{constant.value}}".to_string(),
            klass:    r#"
{{class.id}} - {{class.name}}
{{properties}}
{{methods}}
"#.to_string(),
            method:   r#"
{{method.id}} - {{method.name}}
synchronous: {{method.synchronous}}
{{arguments}}
"#.to_string(),
            argument: "{{argument.name}}({{argument.domain}}): {{argument.type}}".to_string(),
            property: "{{property.name}}: {{property.type}}".to_string(),
        }
    }

    #[test]
    fn main_template() {
        assert_eq!(specs().code_generator(templates()).generate(), r#"
AMQP - 0.9.1
Copyright 1
Copyright 2
port 5672
domain1: ShortInt
constant1(class1) = 128

42 - class1
property1: LongString

64 - method1
synchronous: true
argument1(domain1): LongString

"#);
    }
}
<commit_msg>codegen: allow registering custom templates<commit_after>use specs::*;
use templating::*;

use handlebars::{self, Handlebars, HelperDef};
use itertools::Itertools;
use std::collections::BTreeMap;

pub struct CodeGenerator {
    specs:      AMQProtocolDefinition,
    handlebars: Handlebars,
}

impl CodeGenerator {
    pub fn new(specs: AMQProtocolDefinition, templates: AMQPTemplates) -> CodeGenerator {
        let mut handlebars = Handlebars::new();

        handlebars.register_escape_fn(handlebars::no_escape);

        handlebars.register_helper("camel", Box::new(camel_helper));
        handlebars.register_helper("snake", Box::new(snake_helper));

        handlebars.register_template_string("main",     &templates.main).expect("Failed to register main template");
        handlebars.register_template_string("domain",   &templates.domain).expect("Failed to register domain template");
        handlebars.register_template_string("constant", &templates.constant).expect("Failed to register constant template");
        handlebars.register_template_string("class",    &templates.klass).expect("Failed to register class template");
        handlebars.register_template_string("method",   &templates.method).expect("Failed to register method template");
        handlebars.register_template_string("argument", &templates.argument).expect("Failed to register argument template");
        handlebars.register_template_string("property", &templates.property).expect("Failed to register property template");

        CodeGenerator {
            specs:      specs,
            handlebars: handlebars,
        }
    }

    pub fn with_helper(mut self, name: &str, helper: Box<HelperDef + 'static>) -> CodeGenerator {
        self.handlebars.register_helper(name, helper);
        self
    }

    pub fn with_template(mut self, name: &str, template: &str) -> CodeGenerator {
        self.handlebars.register_template_string(name, template).expect(&format!("Failed to register {} template", name));
        self
    }

    pub fn generate(&self) -> String {
        self.generate_from_template_name("main")
    }

    pub fn generate_from_template_name(&self, name: &str) -> String {
        self.handlebars.render(name, &AMQProtocolDefinitionWrapper {
            protocol:  &self.specs,
            copyright: self.specs.copyright.iter().join(""),
            domains:   self.specs.domains.iter().map(|domain| domain.codegen(&self.handlebars)).join("\n"),
            constants: self.specs.constants.iter().map(|constant| constant.codegen(&self.handlebars)).join("\n"),
            classes:   self.specs.classes.iter().map(|klass| klass.codegen(&self.handlebars)).join("\n"),
        }).expect(&format!("Failed to render {} template", name))
    }
}

#[derive(Debug, Serialize)]
struct AMQProtocolDefinitionWrapper<'a> {
    protocol : &'a AMQProtocolDefinition,
    copyright: String,
    domains:   String,
    constants: String,
    classes:   String,
}

pub trait Codegen {
    fn codegen(&self, handlebars: &Handlebars) -> String;
}

trait BasicGetData {
    fn get_data(&self, handlebars: &Handlebars) -> BTreeMap<String, &Self>;
}

trait ClassGetData {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPClassWrapper;
}

trait MethodGetData {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPMethodWrapper;
}

macro_rules! get_data {
    ($t:ty, $key:expr) => {
        impl BasicGetData for $t {
            #[allow(unused_variables)]
            fn get_data(&self, handlebars: &Handlebars) -> BTreeMap<String, &Self> {
                let mut data = BTreeMap::new();
                data.insert($key.to_string(), self);
                data
            }
        }
    }
}

#[derive(Debug, Serialize)]
struct AMQPClassWrapper<'a> {
    #[serde(rename="class")]
    klass:      &'a AMQPClass,
    methods:    String,
    properties: Option<String>,
}

impl ClassGetData for AMQPClass {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPClassWrapper {
        AMQPClassWrapper {
            klass:      self,
            methods:    self.methods.iter().map(|method| method.codegen(&handlebars)).join("\n"),
            properties: match self.properties {
                Some(ref properties) => Some(properties.iter().map(|method| method.codegen(&handlebars)).join("\n")),
                None                 => None,
            },
        }
    }
}

#[derive(Debug, Serialize)]
struct AMQPMethodWrapper<'a> {
    method:    &'a AMQPMethod,
    arguments: String,
}

impl MethodGetData for AMQPMethod {
    fn get_data(&self, handlebars: &Handlebars) -> AMQPMethodWrapper {
        AMQPMethodWrapper {
            method:    self,
            arguments: self.arguments.iter().map(|arg| arg.codegen(&handlebars)).join("\n"),
        }
    }
}

get_data!(AMQPDomain,   "domain");
get_data!(AMQPConstant, "constant");
get_data!(AMQPArgument, "argument");
get_data!(AMQPProperty, "property");

macro_rules! codegen {
    ($t:ty, $name:expr) => {
        impl Codegen for $t {
            fn codegen (&self, handlebars: &Handlebars) -> String {
                handlebars.render($name, &self.get_data(handlebars)).expect(&format!("Failed to render {} template", $name))
            }
        }
    };
}

codegen!(AMQPDomain,   "domain");
codegen!(AMQPConstant, "constant");
codegen!(AMQPClass,    "class");
codegen!(AMQPMethod,   "method");
codegen!(AMQPArgument, "argument");
codegen!(AMQPProperty, "property");

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

    use amq_protocol_types::*;
    use serde_json::Value;
    use templating::AMQPTemplates;

    fn specs() -> AMQProtocolDefinition {
        AMQProtocolDefinition {
            name:          "AMQP".to_string(),
            major_version: 0,
            minor_version: 9,
            revision:      1,
            port:          5672,
            copyright:     vec!["Copyright 1\n".to_string(), "Copyright 2".to_string()],
            domains:       vec![
                AMQPDomain{
                    name:      "domain1".to_string(),
                    amqp_type: AMQPType::ShortInt
                }
            ],
            constants:     vec![
                AMQPConstant {
                    name:  "constant1".to_string(),
                    value: 128,
                    klass: Some("class1".to_string()),
                }
            ],
            classes:       vec![
                AMQPClass {
                    id:         42,
                    methods:    vec![
                        AMQPMethod {
                            id:          64,
                            arguments:   vec![
                                AMQPArgument {
                                    amqp_type:     Some(AMQPType::LongString),
                                    name:          "argument1".to_string(),
                                    default_value: Some(Value::String("value1".to_string())),
                                    domain:        Some("domain1".to_string()),
                                }
                            ],
                            name:        "method1".to_string(),
                            synchronous: Some(true),
                        }
                    ],
                    name:       "class1".to_string(),
                    properties: Some(vec![
                        AMQPProperty {
                            amqp_type: AMQPType::LongString,
                            name:      "property1".to_string(),
                        }
                    ]),
                }
            ],
        }
    }

    fn templates() -> AMQPTemplates {
        AMQPTemplates {
            main:     r#"
{{protocol.name}} - {{protocol.major_version}}.{{protocol.minor_version}}.{{protocol.revision}}
{{copyright}}
port {{protocol.port}}
{{domains}}
{{constants}}
{{classes}}
"#.to_string(),
            domain:   "{{domain.name}}: {{domain.type}}".to_string(),
            constant: "{{constant.name}}({{constant.class}}) = {{constant.value}}".to_string(),
            klass:    r#"
{{class.id}} - {{class.name}}
{{properties}}
{{methods}}
"#.to_string(),
            method:   r#"
{{method.id}} - {{method.name}}
synchronous: {{method.synchronous}}
{{arguments}}
"#.to_string(),
            argument: "{{argument.name}}({{argument.domain}}): {{argument.type}}".to_string(),
            property: "{{property.name}}: {{property.type}}".to_string(),
        }
    }

    #[test]
    fn main_template() {
        assert_eq!(specs().code_generator(templates()).generate(), r#"
AMQP - 0.9.1
Copyright 1
Copyright 2
port 5672
domain1: ShortInt
constant1(class1) = 128

42 - class1
property1: LongString

64 - method1
synchronous: true
argument1(domain1): LongString

"#);
    }
}
<|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/. */

//! Module to handle Philips Hue bridges
//!
//! This module implements various aspects Philips Hue bridges (short: hubs).
//! It handles pairing and light enumeration. Detected lights are
//! reported to the adapter's main loop via IPC.
//!
//! The module spawns a management thread for every hub.

use serde_json;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use super::hub_api::HubApi;
use super::{ HueAction, PhilipsHueAdapter, structs };
use traits::Controller;
use uuid::Uuid;

pub struct Hub<C> {
    pub adapter: PhilipsHueAdapter<C>,
    pub id: String,
    pub ip: String,
    pub api: Arc<HubApi>,
}

impl<C: Controller> Hub<C> {
    pub fn new(adapter: PhilipsHueAdapter<C>, id: &str, ip: &str) -> Self {
        // Get API token from config store, default to a random UUID.
        // The API token is used like a password when pairing with a
        // Philips Hue bridge. Once paired, it is sent with every
        // API request for authentication purposes, so it is crucial
        // that it is not predictable.
        let token = adapter.controller.get_config().get_or_set_default(
            "philips_hue",
            &format!("token_{}", id),
            &Uuid::new_v4().to_simple_string());
        Hub {
            adapter: adapter,
            id: id.to_owned(),
            ip: ip.to_owned(),
            api: Arc::new(HubApi::new(id, ip, &token)),
        }
    }
    pub fn start(&self) {
        info!("Starting Hue Hub Service for {}", self.id);
        let adapter = self.adapter.clone();
        let id = self.id.clone();
        let api = self.api.clone();

        thread::spawn(move || {

            // The main Hub management loop
            loop {
                if !api.is_available() {
                    // Re-check availability every minute.
                    thread::sleep(Duration::from_millis(60*1000));
                    continue;
                }

                // If the Hub is not paired, try pairing.
                if !api.is_paired() {
                    debug!("Push pairing button on Philips Hue Bridge ID {}", id);

                    // Try pairing for 120 seconds.
                    for _ in 0..120 {
                        adapter.controller.adapter_notification(
                            json_value!({ adapter: "philips_hue",
                                message: "NeedsPairing", hub: id }));
                        if api.try_pairing() {
                            break;
                        }
                        thread::sleep(Duration::from_millis(1000));
                    }

                    if api.is_paired() {
                        info!("Paired with Philips Hue Bridge ID {}", id);
                        adapter.controller.adapter_notification(
                            json_value!({ adapter: "philips_hue", message: "PairingSuccess",
                                hub: id }));
                    } else {
                        warn!("Pairing timeout with Philips Hue Bridge ID {}", id);
                        adapter.controller.adapter_notification(
                            json_value!({ adapter: "philips_hue", message: "PairingTimeout",
                                hub: id }));
                        // Giving up for this Hub.
                        // Re-try pairing every hour.
                        thread::sleep(Duration::from_millis(60*60*1000));
                        continue;
                    }
                }

                // We have a paired Hub, instantiate the lights services.
                // Extract and log some info
                let setting = api.get_settings();
                let hs = structs::Settings::new(&setting).unwrap(); // TODO: no unwrap
                info!(
                    "Connected to Philips Hue bridge model {}, ID {}, software version {}, IP address {}",
                    hs.config.modelid, hs.config.bridgeid, hs.config.swversion,
                    hs.config.ipaddress);

                let light_ids = api.get_lights();
                for light_id in light_ids {
                    debug!("Found light {} on hub {}", light_id, id);
                    adapter.send(HueAction::AddLight(id.to_owned(), light_id.to_owned()));
                }

                loop { // Forever
                    // TODO: add hub monitoring (polling) here
                    thread::sleep(Duration::from_millis(60*1000));
                }
            }
        });
    }
    pub fn update_ip(&mut self, new_ip: &str) {
        debug!("Updating IP for {} to {}", self.id, new_ip);
        self.ip = new_ip.to_owned();
    }
    pub fn stop(&self) {
        debug!("Stopping Hue Hub Service for {}", self.id)
    }
}
<commit_msg>Warn the user about manually pairingx with Philips Hue<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/. */

//! Module to handle Philips Hue bridges
//!
//! This module implements various aspects Philips Hue bridges (short: hubs).
//! It handles pairing and light enumeration. Detected lights are
//! reported to the adapter's main loop via IPC.
//!
//! The module spawns a management thread for every hub.

use serde_json;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use super::hub_api::HubApi;
use super::{ HueAction, PhilipsHueAdapter, structs };
use traits::Controller;
use uuid::Uuid;

pub struct Hub<C> {
    pub adapter: PhilipsHueAdapter<C>,
    pub id: String,
    pub ip: String,
    pub api: Arc<HubApi>,
}

impl<C: Controller> Hub<C> {
    pub fn new(adapter: PhilipsHueAdapter<C>, id: &str, ip: &str) -> Self {
        // Get API token from config store, default to a random UUID.
        // The API token is used like a password when pairing with a
        // Philips Hue bridge. Once paired, it is sent with every
        // API request for authentication purposes, so it is crucial
        // that it is not predictable.
        let token = adapter.controller.get_config().get_or_set_default(
            "philips_hue",
            &format!("token_{}", id),
            &Uuid::new_v4().to_simple_string());
        Hub {
            adapter: adapter,
            id: id.to_owned(),
            ip: ip.to_owned(),
            api: Arc::new(HubApi::new(id, ip, &token)),
        }
    }
    pub fn start(&self) {
        info!("Starting Hue Hub Service for {}", self.id);
        let adapter = self.adapter.clone();
        let id = self.id.clone();
        let api = self.api.clone();

        thread::spawn(move || {

            // The main Hub management loop
            loop {
                if !api.is_available() {
                    // Re-check availability every minute.
                    thread::sleep(Duration::from_millis(60*1000));
                    continue;
                }

                // If the Hub is not paired, try pairing.
                if !api.is_paired() {
                    warn!("Philips Hue detected but not paired. Please, push pairing \
                           button on Philips Hue Bridge ID {} to start using it.", id);

                    // Try pairing for 120 seconds.
                    for _ in 0..120 {
                        adapter.controller.adapter_notification(
                            json_value!({ adapter: "philips_hue",
                                message: "NeedsPairing", hub: id }));
                        if api.try_pairing() {
                            break;
                        }
                        thread::sleep(Duration::from_millis(1000));
                    }

                    if api.is_paired() {
                        info!("Paired with Philips Hue Bridge ID {}", id);
                        adapter.controller.adapter_notification(
                            json_value!({ adapter: "philips_hue", message: "PairingSuccess",
                                hub: id }));
                    } else {
                        warn!("Pairing timeout with Philips Hue Bridge ID {}", id);
                        adapter.controller.adapter_notification(
                            json_value!({ adapter: "philips_hue", message: "PairingTimeout",
                                hub: id }));
                        // Giving up for this Hub.
                        // Re-try pairing every hour.
                        thread::sleep(Duration::from_millis(60*60*1000));
                        continue;
                    }
                }

                // We have a paired Hub, instantiate the lights services.
                // Extract and log some info
                let setting = api.get_settings();
                let hs = structs::Settings::new(&setting).unwrap(); // TODO: no unwrap
                info!(
                    "Connected to Philips Hue bridge model {}, ID {}, software version {}, IP address {}",
                    hs.config.modelid, hs.config.bridgeid, hs.config.swversion,
                    hs.config.ipaddress);

                let light_ids = api.get_lights();
                for light_id in light_ids {
                    debug!("Found light {} on hub {}", light_id, id);
                    adapter.send(HueAction::AddLight(id.to_owned(), light_id.to_owned()));
                }

                loop { // Forever
                    // TODO: add hub monitoring (polling) here
                    thread::sleep(Duration::from_millis(60*1000));
                }
            }
        });
    }
    pub fn update_ip(&mut self, new_ip: &str) {
        debug!("Updating IP for {} to {}", self.id, new_ip);
        self.ip = new_ip.to_owned();
    }
    pub fn stop(&self) {
        debug!("Stopping Hue Hub Service for {}", self.id)
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2020, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use num_derive::*;

use crate::partition::BlockSize;
use crate::serialize::{Deserialize, Serialize};

use std::fmt;

// NOTE: Add Structures at the end.
/// Contains the speed settings.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct SpeedSettings {
  /// Enables inter-frames to have multiple reference frames.
  ///
  /// Enabled is slower.
  pub multiref: bool,
  /// Enables fast deblocking filter.
  pub fast_deblock: bool,
  /// Enables reduced transform set.
  ///
  /// Enabled is faster.
  pub reduced_tx_set: bool,
  /// Enables using transform-domain distortion instead of pixel-domain.
  ///
  /// Enabled is faster.
  pub tx_domain_distortion: bool,
  /// Enables using transform-domain rate estimation.
  ///
  /// Enabled is faster.
  pub tx_domain_rate: bool,
  /// Enables bottom-up encoding, rather than top-down.
  ///
  /// Enabled is slower.
  pub encode_bottomup: bool,
  /// Enables searching transform size and type with RDO.
  ///
  /// Enabled is slower.
  pub rdo_tx_decision: bool,
  /// Prediction modes to search.
  ///
  /// Complex settings are slower.
  pub prediction_modes: PredictionModesSetting,
  /// Enables searching near motion vectors during RDO.
  ///
  /// Enabled is slower.
  pub include_near_mvs: bool,
  /// Disables scene-cut detection.
  ///
  /// Enabled is faster.
  pub no_scene_detection: bool,
  /// Fast scene detection mode, uses simple SAD instead of encoder cost estimates.
  pub fast_scene_detection: bool,
  /// Enables diamond motion vector search rather than full search.
  pub diamond_me: bool,
  /// Enables CDEF.
  pub cdef: bool,
  /// Enables LRF.
  pub lrf: bool,
  /// The amount of search done for self guided restoration.
  pub sgr_complexity: SGRComplexityLevel,
  /// Use SATD instead of SAD for subpixel search.
  ///
  /// Enabled is slower.
  pub use_satd_subpel: bool,
  /// Use non-square partition type everywhere
  ///
  /// Enabled is slower.
  pub non_square_partition: bool,

  /// Use segmentation.
  pub enable_segmentation: bool,

  /// Enable tx split for inter mode block.
  pub enable_inter_tx_split: bool,

  /// Use fine directional intra prediction
  pub fine_directional_intra: bool,

  // NOTE: put enums and basic type fields above
  /// Range of partition sizes that can be used. Larger ranges are slower.
  ///
  /// Must be based on square block sizes, so e.g. 8×4 isn't allowed here.
  pub partition_range: PartitionRange,
}

impl Default for SpeedSettings {
  /// This is currently used exclusively for feature testing and comparison.
  /// It is set to the slowest settings possible.
  fn default() -> Self {
    SpeedSettings {
      partition_range: PartitionRange::new(
        BlockSize::BLOCK_4X4,
        BlockSize::BLOCK_64X64,
      ),
      multiref: true,
      fast_deblock: false,
      reduced_tx_set: false,
      tx_domain_distortion: false,
      tx_domain_rate: false,
      encode_bottomup: true,
      rdo_tx_decision: true,
      prediction_modes: PredictionModesSetting::ComplexAll,
      include_near_mvs: true,
      no_scene_detection: false,
      fast_scene_detection: false,
      diamond_me: true,
      cdef: true,
      lrf: false,
      sgr_complexity: SGRComplexityLevel::Full,
      use_satd_subpel: true,
      non_square_partition: true,
      enable_segmentation: true,
      enable_inter_tx_split: false,
      fine_directional_intra: false,
    }
  }
}

impl SpeedSettings {
  /// Set the speed setting according to a numeric speed preset.
  ///
  /// The speed settings vary depending on speed value from 0 to 10.
  /// - 10 (fastest): min block size 64x64, reduced TX set, fast deblock, fast scenechange detection.
  /// - 9: min block size 32x32, reduced TX set, fast deblock.
  /// - 8: min block size 8x8, reduced TX set, fast deblock.
  /// - 7: min block size 8x8, reduced TX set.
  /// - 6 (default): min block size 8x8, reduced TX set, complex pred modes for keyframes.
  /// - 5: min block size 8x8, complex pred modes for keyframes, reduced TX set, RDO TX decision.
  /// - 4: min block size 8x8, complex pred modes for keyframes, RDO TX decision, full SGR search.
  /// - 3: min block size 8x8, complex pred modes for keyframes, RDO TX decision, include near MVs,
  ///        full SGR search.
  /// - 2: min block size 4x4, complex pred modes, RDO TX decision, include near MVs,
  ///        full SGR search.
  /// - 1: min block size 4x4, complex pred modes, RDO TX decision, include near MVs,
  ///        bottom-up encoding, full SGR search.
  /// - 0 (slowest): min block size 4x4, complex pred modes, RDO TX decision, include near MVs,
  ///        bottom-up encoding with non-square partitions everywhere, full SGR search.
  pub fn from_preset(speed: usize) -> Self {
    SpeedSettings {
      partition_range: Self::partition_range_preset(speed),
      multiref: Self::multiref_preset(speed),
      fast_deblock: Self::fast_deblock_preset(speed),
      reduced_tx_set: Self::reduced_tx_set_preset(speed),
      tx_domain_distortion: Self::tx_domain_distortion_preset(speed),
      tx_domain_rate: Self::tx_domain_rate_preset(speed),
      encode_bottomup: Self::encode_bottomup_preset(speed),
      rdo_tx_decision: Self::rdo_tx_decision_preset(speed),
      prediction_modes: Self::prediction_modes_preset(speed),
      include_near_mvs: Self::include_near_mvs_preset(speed),
      no_scene_detection: Self::no_scene_detection_preset(speed),
      fast_scene_detection: Self::fast_scene_detection_preset(speed),
      diamond_me: Self::diamond_me_preset(speed),
      cdef: Self::cdef_preset(speed),
      lrf: Self::lrf_preset(speed),
      sgr_complexity: Self::sgr_complexity_preset(speed),
      use_satd_subpel: Self::use_satd_subpel(speed),
      non_square_partition: Self::non_square_partition_preset(speed),
      enable_segmentation: Self::enable_segmentation_preset(speed),
      enable_inter_tx_split: Self::enable_inter_tx_split_preset(speed),
      fine_directional_intra: Self::fine_directional_intra_preset(speed),
    }
  }

  /// This preset is set this way because 8x8 with reduced TX set is faster but with equivalent
  /// or better quality compared to 16x16 (to which reduced TX set does not apply).
  fn partition_range_preset(speed: usize) -> PartitionRange {
    if speed <= 2 {
      PartitionRange::new(BlockSize::BLOCK_4X4, BlockSize::BLOCK_64X64)
    } else if speed <= 8 {
      PartitionRange::new(BlockSize::BLOCK_8X8, BlockSize::BLOCK_64X64)
    } else if speed <= 9 {
      PartitionRange::new(BlockSize::BLOCK_32X32, BlockSize::BLOCK_64X64)
    } else {
      PartitionRange::new(BlockSize::BLOCK_32X32, BlockSize::BLOCK_32X32)
    }
  }

  /// Multiref is enabled automatically if low_latency is false.
  ///
  /// If low_latency is true, enabling multiref allows using multiple
  /// backwards references. low_latency false enables both forward and
  /// backwards references.
  const fn multiref_preset(speed: usize) -> bool {
    speed <= 7
  }

  const fn fast_deblock_preset(speed: usize) -> bool {
    speed >= 8
  }

  const fn reduced_tx_set_preset(speed: usize) -> bool {
    speed >= 5
  }

  /// TX domain distortion is always faster, with no significant quality change
  const fn tx_domain_distortion_preset(_speed: usize) -> bool {
    true
  }

  const fn tx_domain_rate_preset(_speed: usize) -> bool {
    false
  }

  const fn encode_bottomup_preset(speed: usize) -> bool {
    speed <= 1
  }

  const fn rdo_tx_decision_preset(speed: usize) -> bool {
    speed <= 5
  }

  fn prediction_modes_preset(speed: usize) -> PredictionModesSetting {
    if speed <= 2 {
      PredictionModesSetting::ComplexAll
    } else if speed <= 6 {
      PredictionModesSetting::ComplexKeyframes
    } else {
      PredictionModesSetting::Simple
    }
  }

  const fn include_near_mvs_preset(speed: usize) -> bool {
    speed <= 3
  }

  const fn no_scene_detection_preset(_speed: usize) -> bool {
    false
  }

  const fn fast_scene_detection_preset(speed: usize) -> bool {
    speed == 10
  }

  /// Currently Diamond ME gives better quality than full search on most videos,
  /// in addition to being faster.
  // There are a few outliers, such as the Wikipedia test clip.
  // TODO: Revisit this setting if full search quality improves in the future.
  const fn diamond_me_preset(_speed: usize) -> bool {
    true
  }

  const fn cdef_preset(_speed: usize) -> bool {
    true
  }

  const fn lrf_preset(speed: usize) -> bool {
    speed <= 9
  }

  fn sgr_complexity_preset(speed: usize) -> SGRComplexityLevel {
    if speed <= 4 {
      SGRComplexityLevel::Full
    } else {
      SGRComplexityLevel::Reduced
    }
  }

  const fn use_satd_subpel(speed: usize) -> bool {
    speed <= 9
  }

  const fn non_square_partition_preset(speed: usize) -> bool {
    speed == 0
  }

  // FIXME: this is currently only enabled at speed 0 because choosing a segment
  // requires doing RDO, but once that is replaced by a less bruteforce
  // solution we should be able to enable segmentation at all speeds.
  const fn enable_segmentation_preset(speed: usize) -> bool {
    speed == 0
  }

  // FIXME: With unknown reasons, inter_tx_split does not work if reduced_tx_set is false
  const fn enable_inter_tx_split_preset(speed: usize) -> bool {
    speed >= 9
  }

  fn fine_directional_intra_preset(speed: usize) -> bool {
    speed <= 1 || speed >= 5
  }
}

/// Range of block sizes to use.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct PartitionRange {
  pub(crate) min: BlockSize,
  pub(crate) max: BlockSize,
}

impl PartitionRange {
  /// Creates a new partition range with min and max partition sizes.
  pub fn new(min: BlockSize, max: BlockSize) -> Self {
    assert!(max >= min);
    // Topdown search checks the min block size for PARTITION_SPLIT only, so
    // the min block size must be square.
    assert!(min.is_sqr());
    // Rectangular max partition sizes have not been tested.
    assert!(max.is_sqr());

    Self { min, max }
  }
}

/// Prediction modes to search.
#[derive(
  Clone,
  Copy,
  Debug,
  PartialOrd,
  PartialEq,
  FromPrimitive,
  Serialize,
  Deserialize,
)]
pub enum PredictionModesSetting {
  /// Only simple prediction modes.
  Simple,
  /// Search all prediction modes on key frames and simple modes on other
  /// frames.
  ComplexKeyframes,
  /// Search all prediction modes on all frames.
  ComplexAll,
}

impl fmt::Display for PredictionModesSetting {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    write!(
      f,
      "{}",
      match self {
        PredictionModesSetting::Simple => "Simple",
        PredictionModesSetting::ComplexKeyframes => "Complex-KFs",
        PredictionModesSetting::ComplexAll => "Complex-All",
      }
    )
  }
}

/// Search level for self guided restoration
#[derive(
  Clone,
  Copy,
  Debug,
  PartialOrd,
  PartialEq,
  FromPrimitive,
  Serialize,
  Deserialize,
)]
pub enum SGRComplexityLevel {
  /// Search all sgr parameters
  Full,
  /// Search a reduced set of sgr parameters
  Reduced,
}

impl fmt::Display for SGRComplexityLevel {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    write!(
      f,
      "{}",
      match self {
        SGRComplexityLevel::Full => "Full",
        SGRComplexityLevel::Reduced => "Reduced",
      }
    )
  }
}
<commit_msg>Use full transform set at speed 5<commit_after>// Copyright (c) 2020, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use num_derive::*;

use crate::partition::BlockSize;
use crate::serialize::{Deserialize, Serialize};

use std::fmt;

// NOTE: Add Structures at the end.
/// Contains the speed settings.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct SpeedSettings {
  /// Enables inter-frames to have multiple reference frames.
  ///
  /// Enabled is slower.
  pub multiref: bool,
  /// Enables fast deblocking filter.
  pub fast_deblock: bool,
  /// Enables reduced transform set.
  ///
  /// Enabled is faster.
  pub reduced_tx_set: bool,
  /// Enables using transform-domain distortion instead of pixel-domain.
  ///
  /// Enabled is faster.
  pub tx_domain_distortion: bool,
  /// Enables using transform-domain rate estimation.
  ///
  /// Enabled is faster.
  pub tx_domain_rate: bool,
  /// Enables bottom-up encoding, rather than top-down.
  ///
  /// Enabled is slower.
  pub encode_bottomup: bool,
  /// Enables searching transform size and type with RDO.
  ///
  /// Enabled is slower.
  pub rdo_tx_decision: bool,
  /// Prediction modes to search.
  ///
  /// Complex settings are slower.
  pub prediction_modes: PredictionModesSetting,
  /// Enables searching near motion vectors during RDO.
  ///
  /// Enabled is slower.
  pub include_near_mvs: bool,
  /// Disables scene-cut detection.
  ///
  /// Enabled is faster.
  pub no_scene_detection: bool,
  /// Fast scene detection mode, uses simple SAD instead of encoder cost estimates.
  pub fast_scene_detection: bool,
  /// Enables diamond motion vector search rather than full search.
  pub diamond_me: bool,
  /// Enables CDEF.
  pub cdef: bool,
  /// Enables LRF.
  pub lrf: bool,
  /// The amount of search done for self guided restoration.
  pub sgr_complexity: SGRComplexityLevel,
  /// Use SATD instead of SAD for subpixel search.
  ///
  /// Enabled is slower.
  pub use_satd_subpel: bool,
  /// Use non-square partition type everywhere
  ///
  /// Enabled is slower.
  pub non_square_partition: bool,

  /// Use segmentation.
  pub enable_segmentation: bool,

  /// Enable tx split for inter mode block.
  pub enable_inter_tx_split: bool,

  /// Use fine directional intra prediction
  pub fine_directional_intra: bool,

  // NOTE: put enums and basic type fields above
  /// Range of partition sizes that can be used. Larger ranges are slower.
  ///
  /// Must be based on square block sizes, so e.g. 8×4 isn't allowed here.
  pub partition_range: PartitionRange,
}

impl Default for SpeedSettings {
  /// This is currently used exclusively for feature testing and comparison.
  /// It is set to the slowest settings possible.
  fn default() -> Self {
    SpeedSettings {
      partition_range: PartitionRange::new(
        BlockSize::BLOCK_4X4,
        BlockSize::BLOCK_64X64,
      ),
      multiref: true,
      fast_deblock: false,
      reduced_tx_set: false,
      tx_domain_distortion: false,
      tx_domain_rate: false,
      encode_bottomup: true,
      rdo_tx_decision: true,
      prediction_modes: PredictionModesSetting::ComplexAll,
      include_near_mvs: true,
      no_scene_detection: false,
      fast_scene_detection: false,
      diamond_me: true,
      cdef: true,
      lrf: false,
      sgr_complexity: SGRComplexityLevel::Full,
      use_satd_subpel: true,
      non_square_partition: true,
      enable_segmentation: true,
      enable_inter_tx_split: false,
      fine_directional_intra: false,
    }
  }
}

impl SpeedSettings {
  /// Set the speed setting according to a numeric speed preset.
  ///
  /// The speed settings vary depending on speed value from 0 to 10.
  /// - 10 (fastest): min block size 64x64, reduced TX set, fast deblock, fast scenechange detection.
  /// - 9: min block size 32x32, reduced TX set, fast deblock.
  /// - 8: min block size 8x8, reduced TX set, fast deblock.
  /// - 7: min block size 8x8, reduced TX set.
  /// - 6 (default): min block size 8x8, reduced TX set, complex pred modes for keyframes.
  /// - 5: min block size 8x8, complex pred modes for keyframes, RDO TX decision.
  /// - 4: min block size 8x8, complex pred modes for keyframes, RDO TX decision, full SGR search.
  /// - 3: min block size 8x8, complex pred modes for keyframes, RDO TX decision, include near MVs,
  ///        full SGR search.
  /// - 2: min block size 4x4, complex pred modes, RDO TX decision, include near MVs,
  ///        full SGR search.
  /// - 1: min block size 4x4, complex pred modes, RDO TX decision, include near MVs,
  ///        bottom-up encoding, full SGR search.
  /// - 0 (slowest): min block size 4x4, complex pred modes, RDO TX decision, include near MVs,
  ///        bottom-up encoding with non-square partitions everywhere, full SGR search.
  pub fn from_preset(speed: usize) -> Self {
    SpeedSettings {
      partition_range: Self::partition_range_preset(speed),
      multiref: Self::multiref_preset(speed),
      fast_deblock: Self::fast_deblock_preset(speed),
      reduced_tx_set: Self::reduced_tx_set_preset(speed),
      tx_domain_distortion: Self::tx_domain_distortion_preset(speed),
      tx_domain_rate: Self::tx_domain_rate_preset(speed),
      encode_bottomup: Self::encode_bottomup_preset(speed),
      rdo_tx_decision: Self::rdo_tx_decision_preset(speed),
      prediction_modes: Self::prediction_modes_preset(speed),
      include_near_mvs: Self::include_near_mvs_preset(speed),
      no_scene_detection: Self::no_scene_detection_preset(speed),
      fast_scene_detection: Self::fast_scene_detection_preset(speed),
      diamond_me: Self::diamond_me_preset(speed),
      cdef: Self::cdef_preset(speed),
      lrf: Self::lrf_preset(speed),
      sgr_complexity: Self::sgr_complexity_preset(speed),
      use_satd_subpel: Self::use_satd_subpel(speed),
      non_square_partition: Self::non_square_partition_preset(speed),
      enable_segmentation: Self::enable_segmentation_preset(speed),
      enable_inter_tx_split: Self::enable_inter_tx_split_preset(speed),
      fine_directional_intra: Self::fine_directional_intra_preset(speed),
    }
  }

  /// This preset is set this way because 8x8 with reduced TX set is faster but with equivalent
  /// or better quality compared to 16x16 (to which reduced TX set does not apply).
  fn partition_range_preset(speed: usize) -> PartitionRange {
    if speed <= 2 {
      PartitionRange::new(BlockSize::BLOCK_4X4, BlockSize::BLOCK_64X64)
    } else if speed <= 8 {
      PartitionRange::new(BlockSize::BLOCK_8X8, BlockSize::BLOCK_64X64)
    } else if speed <= 9 {
      PartitionRange::new(BlockSize::BLOCK_32X32, BlockSize::BLOCK_64X64)
    } else {
      PartitionRange::new(BlockSize::BLOCK_32X32, BlockSize::BLOCK_32X32)
    }
  }

  /// Multiref is enabled automatically if low_latency is false.
  ///
  /// If low_latency is true, enabling multiref allows using multiple
  /// backwards references. low_latency false enables both forward and
  /// backwards references.
  const fn multiref_preset(speed: usize) -> bool {
    speed <= 7
  }

  const fn fast_deblock_preset(speed: usize) -> bool {
    speed >= 8
  }

  const fn reduced_tx_set_preset(speed: usize) -> bool {
    speed >= 6
  }

  /// TX domain distortion is always faster, with no significant quality change
  const fn tx_domain_distortion_preset(_speed: usize) -> bool {
    true
  }

  const fn tx_domain_rate_preset(_speed: usize) -> bool {
    false
  }

  const fn encode_bottomup_preset(speed: usize) -> bool {
    speed <= 1
  }

  const fn rdo_tx_decision_preset(speed: usize) -> bool {
    speed <= 5
  }

  fn prediction_modes_preset(speed: usize) -> PredictionModesSetting {
    if speed <= 2 {
      PredictionModesSetting::ComplexAll
    } else if speed <= 6 {
      PredictionModesSetting::ComplexKeyframes
    } else {
      PredictionModesSetting::Simple
    }
  }

  const fn include_near_mvs_preset(speed: usize) -> bool {
    speed <= 3
  }

  const fn no_scene_detection_preset(_speed: usize) -> bool {
    false
  }

  const fn fast_scene_detection_preset(speed: usize) -> bool {
    speed == 10
  }

  /// Currently Diamond ME gives better quality than full search on most videos,
  /// in addition to being faster.
  // There are a few outliers, such as the Wikipedia test clip.
  // TODO: Revisit this setting if full search quality improves in the future.
  const fn diamond_me_preset(_speed: usize) -> bool {
    true
  }

  const fn cdef_preset(_speed: usize) -> bool {
    true
  }

  const fn lrf_preset(speed: usize) -> bool {
    speed <= 9
  }

  fn sgr_complexity_preset(speed: usize) -> SGRComplexityLevel {
    if speed <= 4 {
      SGRComplexityLevel::Full
    } else {
      SGRComplexityLevel::Reduced
    }
  }

  const fn use_satd_subpel(speed: usize) -> bool {
    speed <= 9
  }

  const fn non_square_partition_preset(speed: usize) -> bool {
    speed == 0
  }

  // FIXME: this is currently only enabled at speed 0 because choosing a segment
  // requires doing RDO, but once that is replaced by a less bruteforce
  // solution we should be able to enable segmentation at all speeds.
  const fn enable_segmentation_preset(speed: usize) -> bool {
    speed == 0
  }

  // FIXME: With unknown reasons, inter_tx_split does not work if reduced_tx_set is false
  const fn enable_inter_tx_split_preset(speed: usize) -> bool {
    speed >= 9
  }

  fn fine_directional_intra_preset(speed: usize) -> bool {
    speed <= 1 || speed >= 5
  }
}

/// Range of block sizes to use.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct PartitionRange {
  pub(crate) min: BlockSize,
  pub(crate) max: BlockSize,
}

impl PartitionRange {
  /// Creates a new partition range with min and max partition sizes.
  pub fn new(min: BlockSize, max: BlockSize) -> Self {
    assert!(max >= min);
    // Topdown search checks the min block size for PARTITION_SPLIT only, so
    // the min block size must be square.
    assert!(min.is_sqr());
    // Rectangular max partition sizes have not been tested.
    assert!(max.is_sqr());

    Self { min, max }
  }
}

/// Prediction modes to search.
#[derive(
  Clone,
  Copy,
  Debug,
  PartialOrd,
  PartialEq,
  FromPrimitive,
  Serialize,
  Deserialize,
)]
pub enum PredictionModesSetting {
  /// Only simple prediction modes.
  Simple,
  /// Search all prediction modes on key frames and simple modes on other
  /// frames.
  ComplexKeyframes,
  /// Search all prediction modes on all frames.
  ComplexAll,
}

impl fmt::Display for PredictionModesSetting {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    write!(
      f,
      "{}",
      match self {
        PredictionModesSetting::Simple => "Simple",
        PredictionModesSetting::ComplexKeyframes => "Complex-KFs",
        PredictionModesSetting::ComplexAll => "Complex-All",
      }
    )
  }
}

/// Search level for self guided restoration
#[derive(
  Clone,
  Copy,
  Debug,
  PartialOrd,
  PartialEq,
  FromPrimitive,
  Serialize,
  Deserialize,
)]
pub enum SGRComplexityLevel {
  /// Search all sgr parameters
  Full,
  /// Search a reduced set of sgr parameters
  Reduced,
}

impl fmt::Display for SGRComplexityLevel {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    write!(
      f,
      "{}",
      match self {
        SGRComplexityLevel::Full => "Full",
        SGRComplexityLevel::Reduced => "Reduced",
      }
    )
  }
}
<|endoftext|>