<commit_before>#![feature(try_trait)]

extern crate pgn_reader;
extern crate memmap;
extern crate madvise;
extern crate btoi;
extern crate rand;
extern crate reqwest;

use std::env;
use std::mem;
use std::cmp::min;
use std::fs::File;
use std::option::NoneError;
use std::io::Read;

use memmap::Mmap;
use madvise::{AccessPattern, AdviseMemory};
use pgn_reader::{Visitor, Skip, Reader, San};
use btoi::ParseIntegerError;
use rand::{random, Closed01};

const BATCH_SIZE: usize = 50;

const MAX_PLIES: usize = 50;

#[derive(Debug)]
enum TimeControl {
    UltraBullet,
    Bullet,
    Blitz,
    Classical,
    Correspondence,
}

#[derive(Debug)]
struct TimeControlError;

impl From<NoneError> for TimeControlError {
    fn from(_: NoneError) -> TimeControlError {
        TimeControlError { }
    }
}

impl From<ParseIntegerError> for TimeControlError {
    fn from(_: ParseIntegerError) -> TimeControlError {
        TimeControlError { }
    }
}

impl TimeControl {
    fn from_seconds_and_increment(seconds: u64, increment: u64) -> TimeControl {
        let total = seconds + 40 * increment;

        if total < 30 {
            TimeControl::UltraBullet
        } else if total < 180 {
            TimeControl::Bullet
        } else if total < 480 {
            TimeControl::Blitz
        } else if total < 21600 {
            TimeControl::Classical
        } else {
            TimeControl::Correspondence
        }
    }

    fn from_bytes(bytes: &[u8]) -> Result<TimeControl, TimeControlError> {
        if bytes == b"-" {
            return Ok(TimeControl::Correspondence);
        }

        let mut parts = bytes.splitn(2, |ch| *ch == b'+');
        let seconds = btoi::btou(parts.next()?)?;
        let increment = btoi::btou(parts.next()?)?;
        Ok(TimeControl::from_seconds_and_increment(seconds, increment))
    }
}

struct Indexer {
    context: String,

    white_elo: i16,
    black_elo: i16,
    time_control: TimeControl,
    skip: bool,

    current_game: Vec<u8>,
    plies: usize,
    standard: bool,

    batch: Vec<u8>,
    batch_size: usize,
}

impl Indexer {
    fn new(context: &str) -> Indexer {
        Indexer {
            context: context.into(),

            white_elo: 0,
            black_elo: 0,
            time_control: TimeControl::Correspondence,
            skip: true,

            current_game: Vec::new(),
            plies: 0,
            standard: true,

            batch: Vec::new(),
            batch_size: 0,
        }
    }

    fn send(&mut self) {
        if self.batch_size > 0 {
            self.batch_size = 0;

            let mut res = reqwest::Client::new()
                .put("http://localhost:9000/import/lichess")
                .body(mem::replace(&mut self.batch, Vec::new()))
                .send().expect("send batch");

            let mut answer = String::new();
            res.read_to_string(&mut answer).expect("decode response");
            println!("{}: {}", self.context, answer);
            assert!(res.status().is_success());
        }
    }
}

impl<'pgn> Visitor<'pgn> for Indexer {
    type Result = ();

    fn begin_game(&mut self) {
        self.current_game.clear();
        self.plies = 0;
    }

    fn begin_headers(&mut self) {
        self.white_elo = 0;
        self.black_elo = 0;
        self.time_control = TimeControl::Correspondence;
        self.standard = true;
    }

    fn header(&mut self, key: &'pgn [u8], value: &'pgn [u8]) {
        if key == b"WhiteElo" {
            self.white_elo = if value == b"?" { 0 } else { btoi::btoi(value).expect("WhiteElo") };
        } else if key == b"BlackElo" {
            self.black_elo = if value == b"?" { 0 } else { btoi::btoi(value).expect("BlackElo") };
        } else if key == b"TimeControl" {
            self.time_control = TimeControl::from_bytes(value).expect("TimeControl");
        } else if key == b"Variant" {
            self.standard = value == b"Standard";
            if self.standard {
                return; // we add this unconditionally later
            }
        }

        let (key, value) = if key == b"Site" {
            (&b"LichessID"[..], value.rsplitn(2, |ch| *ch == b'/').next().expect("Site"))
        } else {
            (key, value)
        };

        self.current_game.push(b'[');
        self.current_game.extend(key);
        self.current_game.extend(b" \"");
        self.current_game.extend(value);
        self.current_game.extend(b"\"]\n");
    }

    fn end_headers(&mut self) -> Skip {
        let rating = (self.white_elo + self.black_elo) / 2;

        let probability = if self.standard {
            self.current_game.extend(b"[Variant \"Standard\"]\n");
            self.current_game.push(b'\n');

            match self.time_control {
                TimeControl::Correspondence => 1.0,
                TimeControl::Classical if rating >= 2000 => 1.0,
                TimeControl::Classical if rating >= 1800 => 2.0 / 5.0,
                TimeControl::Classical => 1.0 / 8.0,
                TimeControl::Blitz if rating >= 2000 => 1.0,
                TimeControl::Blitz if rating >= 1800 => 1.0 / 4.0,
                TimeControl::Blitz => 1.0 / 15.0,
                TimeControl::Bullet if rating >= 2300 => 1.0,
                TimeControl::Bullet if rating >= 2200 => 4.0 / 5.0,
                TimeControl::Bullet if rating >= 2000 => 1.0 / 4.0,
                TimeControl::Bullet if rating >= 1800 => 1.0 / 7.0,
                _ => 1.0 / 20.0,
            }
        } else {
            1.0 // variant game
        };

        let Closed01(rnd) = random::<Closed01<f64>>();
        let accept = min(self.white_elo, self.black_elo) >= 1500 && probability >= rnd;

        self.skip = !accept;
        Skip(self.skip)
    }

    fn san(&mut self, san: San) {
        if self.plies < MAX_PLIES {
            if self.plies > 0 {
                self.current_game.push(b' ');
            }

            self.current_game.extend(san.to_string().as_bytes());
            self.plies += 1;
        }
    }

    fn begin_variation(&mut self) -> Skip {
        Skip(true) // stay in the mainline
    }

    fn end_game(&mut self, _game: &'pgn [u8]) {
        if !self.skip && self.plies > 8 {
            if self.batch_size >= BATCH_SIZE {
                self.send();
            }

            if self.batch_size > 0 {
                self.batch.extend(b"\n\n\n");
            }

            self.batch.extend(&self.current_game);
            self.batch_size += 1;
        }
    }
}

fn main() {
    for arg in env::args().skip(1) {
        eprintln!("% indexing {} ...", arg);
        let file = File::open(&arg).expect("fopen");
        let pgn = unsafe { Mmap::map(&file).expect("mmap") };
        pgn.advise_memory_access(AccessPattern::Sequential).expect("madvise");

        let mut indexer = Indexer::new(&arg);
        Reader::new(&mut indexer, &pgn[..]).read_all();
        indexer.send(); // send last
    }
}
<commit_msg>fix line ending<commit_after>#![feature(try_trait)]

extern crate pgn_reader;
extern crate memmap;
extern crate madvise;
extern crate btoi;
extern crate rand;
extern crate reqwest;

use std::env;
use std::mem;
use std::cmp::min;
use std::fs::File;
use std::option::NoneError;
use std::io::Read;

use memmap::Mmap;
use madvise::{AccessPattern, AdviseMemory};
use pgn_reader::{Visitor, Skip, Reader, San};
use btoi::ParseIntegerError;
use rand::{random, Closed01};

const BATCH_SIZE: usize = 50;

const MAX_PLIES: usize = 50;

#[derive(Debug)]
enum TimeControl {
    UltraBullet,
    Bullet,
    Blitz,
    Classical,
    Correspondence,
}

#[derive(Debug)]
struct TimeControlError;

impl From<NoneError> for TimeControlError {
    fn from(_: NoneError) -> TimeControlError {
        TimeControlError { }
    }
}

impl From<ParseIntegerError> for TimeControlError {
    fn from(_: ParseIntegerError) -> TimeControlError {
        TimeControlError { }
    }
}

impl TimeControl {
    fn from_seconds_and_increment(seconds: u64, increment: u64) -> TimeControl {
        let total = seconds + 40 * increment;

        if total < 30 {
            TimeControl::UltraBullet
        } else if total < 180 {
            TimeControl::Bullet
        } else if total < 480 {
            TimeControl::Blitz
        } else if total < 21600 {
            TimeControl::Classical
        } else {
            TimeControl::Correspondence
        }
    }

    fn from_bytes(bytes: &[u8]) -> Result<TimeControl, TimeControlError> {
        if bytes == b"-" {
            return Ok(TimeControl::Correspondence);
        }

        let mut parts = bytes.splitn(2, |ch| *ch == b'+');
        let seconds = btoi::btou(parts.next()?)?;
        let increment = btoi::btou(parts.next()?)?;
        Ok(TimeControl::from_seconds_and_increment(seconds, increment))
    }
}

struct Indexer {
    context: String,

    white_elo: i16,
    black_elo: i16,
    time_control: TimeControl,
    skip: bool,

    current_game: Vec<u8>,
    plies: usize,
    standard: bool,

    batch: Vec<u8>,
    batch_size: usize,
}

impl Indexer {
    fn new(context: &str) -> Indexer {
        Indexer {
            context: context.into(),

            white_elo: 0,
            black_elo: 0,
            time_control: TimeControl::Correspondence,
            skip: true,

            current_game: Vec::new(),
            plies: 0,
            standard: true,

            batch: Vec::new(),
            batch_size: 0,
        }
    }

    fn send(&mut self) {
        if self.batch_size > 0 {
            self.batch_size = 0;

            let mut res = reqwest::Client::new()
                .put("http://localhost:9000/import/lichess")
                .body(mem::replace(&mut self.batch, Vec::new()))
                .send().expect("send batch");

            let mut answer = String::new();
            res.read_to_string(&mut answer).expect("decode response");
            println!("{}: {}", self.context, answer);
            assert!(res.status().is_success());
        }
    }
}

impl<'pgn> Visitor<'pgn> for Indexer {
    type Result = ();

    fn begin_game(&mut self) {
        self.current_game.clear();
        self.plies = 0;
    }

    fn begin_headers(&mut self) {
        self.white_elo = 0;
        self.black_elo = 0;
        self.time_control = TimeControl::Correspondence;
        self.standard = true;
    }

    fn header(&mut self, key: &'pgn [u8], value: &'pgn [u8]) {
        if key == b"WhiteElo" {
            self.white_elo = if value == b"?" { 0 } else { btoi::btoi(value).expect("WhiteElo") };
        } else if key == b"BlackElo" {
            self.black_elo = if value == b"?" { 0 } else { btoi::btoi(value).expect("BlackElo") };
        } else if key == b"TimeControl" {
            self.time_control = TimeControl::from_bytes(value).expect("TimeControl");
        } else if key == b"Variant" {
            self.standard = value == b"Standard";
            if self.standard {
                return; // we add this unconditionally later
            }
        }

        let (key, value) = if key == b"Site" {
            (&b"LichessID"[..], value.rsplitn(2, |ch| *ch == b'/').next().expect("Site"))
        } else {
            (key, value)
        };

        self.current_game.push(b'[');
        self.current_game.extend(key);
        self.current_game.extend(b" \"");
        self.current_game.extend(value);
        self.current_game.extend(b"\"]\n");
    }

    fn end_headers(&mut self) -> Skip {
        let rating = (self.white_elo + self.black_elo) / 2;

        let probability = if self.standard {
            self.current_game.extend(b"[Variant \"Standard\"]\n");

            match self.time_control {
                TimeControl::Correspondence => 1.0,
                TimeControl::Classical if rating >= 2000 => 1.0,
                TimeControl::Classical if rating >= 1800 => 2.0 / 5.0,
                TimeControl::Classical => 1.0 / 8.0,
                TimeControl::Blitz if rating >= 2000 => 1.0,
                TimeControl::Blitz if rating >= 1800 => 1.0 / 4.0,
                TimeControl::Blitz => 1.0 / 15.0,
                TimeControl::Bullet if rating >= 2300 => 1.0,
                TimeControl::Bullet if rating >= 2200 => 4.0 / 5.0,
                TimeControl::Bullet if rating >= 2000 => 1.0 / 4.0,
                TimeControl::Bullet if rating >= 1800 => 1.0 / 7.0,
                _ => 1.0 / 20.0,
            }
        } else {
            1.0 // variant game
        };

        self.current_game.push(b'\n');

        let Closed01(rnd) = random::<Closed01<f64>>();
        let accept = min(self.white_elo, self.black_elo) >= 1500 && probability >= rnd;

        self.skip = !accept;
        Skip(self.skip)
    }

    fn san(&mut self, san: San) {
        if self.plies < MAX_PLIES {
            if self.plies > 0 {
                self.current_game.push(b' ');
            }

            self.current_game.extend(san.to_string().as_bytes());
            self.plies += 1;
        }
    }

    fn begin_variation(&mut self) -> Skip {
        Skip(true) // stay in the mainline
    }

    fn end_game(&mut self, _game: &'pgn [u8]) {
        if !self.skip && self.plies > 8 {
            if self.batch_size >= BATCH_SIZE {
                self.send();
            }

            if self.batch_size > 0 {
                self.batch.extend(b"\n\n\n");
            }

            self.batch.extend(&self.current_game);
            self.batch_size += 1;
        }
    }
}

fn main() {
    for arg in env::args().skip(1) {
        eprintln!("% indexing {} ...", arg);
        let file = File::open(&arg).expect("fopen");
        let pgn = unsafe { Mmap::map(&file).expect("mmap") };
        pgn.advise_memory_access(AccessPattern::Sequential).expect("madvise");

        let mut indexer = Indexer::new(&arg);
        Reader::new(&mut indexer, &pgn[..]).read_all();
        indexer.send(); // send last
    }
}
<|endoftext|>


<commit_before>//! Support types for generated encoding tables.
//!
//! This module contains types and functions for working with the encoding tables generated by
//! `lib/cretonne/meta/gen_encoding.py`.
use ir::{Type, Opcode};
use isa::{Encoding, Legalize};
use constant_hash::{Table, probe};

/// Level 1 hash table entry.
///
/// One level 1 hash table is generated per CPU mode. This table is keyed by the controlling type
/// variable, using `VOID` for non-polymorphic instructions.
///
/// The hash table values are references to level 2 hash tables, encoded as an offset in `LEVEL2`
/// where the table begins, and the binary logarithm of its length. All the level 2 hash tables
/// have a power-of-two size.
///
/// Entries are generic over the offset type. It will typically be `u32` or `u16`, depending on the
/// size of the `LEVEL2` table. A `u16` offset allows entries to shrink to 32 bits each, but some
/// ISAs may have tables so large that `u32` offsets are needed.
///
/// Empty entries are encoded with a 0 `log2len`. This is on the assumption that no level 2 tables
/// have only a single entry.
pub struct Level1Entry<OffT: Into<u32> + Copy> {
    pub ty: Type,
    pub log2len: u8,
    pub offset: OffT,
}

impl<OffT: Into<u32> + Copy> Table<Type> for [Level1Entry<OffT>] {
    fn len(&self) -> usize {
        self.len()
    }

    fn key(&self, idx: usize) -> Option<Type> {
        if self[idx].log2len != 0 {
            Some(self[idx].ty)
        } else {
            None
        }
    }
}

/// Level 2 hash table entry.
///
/// The second level hash tables are keyed by `Opcode`, and contain an offset into the `ENCLISTS`
/// table where the encoding recipes for the instrution are stored.
///
/// Entries are generic over the offset type which depends on the size of `ENCLISTS`. A `u16`
/// offset allows the entries to be only 32 bits each. There is no benefit to dropping down to `u8`
/// for tiny ISAs. The entries won't shrink below 32 bits since the opcode is expected to be 16
/// bits.
///
/// Empty entries are encoded with a `NotAnOpcode` `opcode` field.
pub struct Level2Entry<OffT: Into<u32> + Copy> {
    pub opcode: Option<Opcode>,
    pub offset: OffT,
}

impl<OffT: Into<u32> + Copy> Table<Opcode> for [Level2Entry<OffT>] {
    fn len(&self) -> usize {
        self.len()
    }

    fn key(&self, idx: usize) -> Option<Opcode> {
        self[idx].opcode
    }
}

/// Two-level hash table lookup.
///
/// Given the controlling type variable and instruction opcode, find the corresponding encoding
/// list.
///
/// Returns an offset into the ISA's `ENCLIST` table, or `None` if the opcode/type combination is
/// not legal.
pub fn lookup_enclist<OffT1, OffT2>(ctrl_typevar: Type,
                                    opcode: Opcode,
                                    level1_table: &[Level1Entry<OffT1>],
                                    level2_table: &[Level2Entry<OffT2>])
                                    -> Result<usize, Legalize>
    where OffT1: Into<u32> + Copy,
          OffT2: Into<u32> + Copy
{
    // TODO: The choice of legalization actions here is naive. This needs to be configurable.
    probe(level1_table, ctrl_typevar, ctrl_typevar.index())
        .ok_or_else(|| if ctrl_typevar.lane_type().bits() > 32 {
                        Legalize::Narrow
                    } else {
                        Legalize::Expand
                    })
        .and_then(|l1idx| {
                      let l1ent = &level1_table[l1idx];
                      let l2off = l1ent.offset.into() as usize;
                      let l2tab = &level2_table[l2off..l2off + (1 << l1ent.log2len)];
                      probe(l2tab, opcode, opcode as usize)
                          .map(|l2idx| l2tab[l2idx].offset.into() as usize)
                          .ok_or(Legalize::Expand)
                  })
}

/// Encoding list entry.
///
/// Encoding lists are represented as sequences of u16 words.
pub type EncListEntry = u16;

/// Number of bits used to represent a predicate. c.f. `meta.gen_encoding.py`.
const PRED_BITS: u8 = 12;
const PRED_MASK: EncListEntry = (1 << PRED_BITS) - 1;

/// The match-always instruction predicate. c.f. `meta.gen_encoding.py`.
const CODE_ALWAYS: EncListEntry = PRED_MASK;

/// The encoding list terminator.
const CODE_FAIL: EncListEntry = 0xffff;

/// Find the most general encoding of `inst`.
///
/// Given an encoding list offset as returned by `lookup_enclist` above, search the encoding list
/// for the most general encoding that applies to `inst`. The encoding lists are laid out such that
/// this is the last valid entry in the list.
///
/// This function takes two closures that are used to evaluate predicates:
/// - `instp` is passed an instruction predicate number to be evaluated on the current instruction.
/// - `isap` is passed an ISA predicate number to evaluate.
///
/// Returns the corresponding encoding, or `None` if no list entries are satisfied by `inst`.
pub fn general_encoding<InstP, IsaP>(offset: usize,
                                     enclist: &[EncListEntry],
                                     instp: InstP,
                                     isap: IsaP)
                                     -> Option<Encoding>
    where InstP: Fn(EncListEntry) -> bool,
          IsaP: Fn(EncListEntry) -> bool
{
    let mut found = None;
    let mut pos = offset;
    while enclist[pos] != CODE_FAIL {
        let pred = enclist[pos];
        if pred <= CODE_ALWAYS {
            // This is an instruction predicate followed by recipe and encbits entries.
            if pred == CODE_ALWAYS || instp(pred) {
                found = Some(Encoding::new(enclist[pos + 1], enclist[pos + 2]))
            }
            pos += 3;
        } else {
            // This is an ISA predicate entry.
            pos += 1;
            if !isap(pred & PRED_MASK) {
                // ISA predicate failed, skip the next N entries.
                pos += 3 * (pred >> PRED_BITS) as usize;
            }
        }
    }
    found
}
<commit_msg>Return the first applicable encoding from general_encoding().<commit_after>//! Support types for generated encoding tables.
//!
//! This module contains types and functions for working with the encoding tables generated by
//! `lib/cretonne/meta/gen_encoding.py`.
use ir::{Type, Opcode};
use isa::{Encoding, Legalize};
use constant_hash::{Table, probe};

/// Level 1 hash table entry.
///
/// One level 1 hash table is generated per CPU mode. This table is keyed by the controlling type
/// variable, using `VOID` for non-polymorphic instructions.
///
/// The hash table values are references to level 2 hash tables, encoded as an offset in `LEVEL2`
/// where the table begins, and the binary logarithm of its length. All the level 2 hash tables
/// have a power-of-two size.
///
/// Entries are generic over the offset type. It will typically be `u32` or `u16`, depending on the
/// size of the `LEVEL2` table. A `u16` offset allows entries to shrink to 32 bits each, but some
/// ISAs may have tables so large that `u32` offsets are needed.
///
/// Empty entries are encoded with a 0 `log2len`. This is on the assumption that no level 2 tables
/// have only a single entry.
pub struct Level1Entry<OffT: Into<u32> + Copy> {
    pub ty: Type,
    pub log2len: u8,
    pub offset: OffT,
}

impl<OffT: Into<u32> + Copy> Table<Type> for [Level1Entry<OffT>] {
    fn len(&self) -> usize {
        self.len()
    }

    fn key(&self, idx: usize) -> Option<Type> {
        if self[idx].log2len != 0 {
            Some(self[idx].ty)
        } else {
            None
        }
    }
}

/// Level 2 hash table entry.
///
/// The second level hash tables are keyed by `Opcode`, and contain an offset into the `ENCLISTS`
/// table where the encoding recipes for the instrution are stored.
///
/// Entries are generic over the offset type which depends on the size of `ENCLISTS`. A `u16`
/// offset allows the entries to be only 32 bits each. There is no benefit to dropping down to `u8`
/// for tiny ISAs. The entries won't shrink below 32 bits since the opcode is expected to be 16
/// bits.
///
/// Empty entries are encoded with a `NotAnOpcode` `opcode` field.
pub struct Level2Entry<OffT: Into<u32> + Copy> {
    pub opcode: Option<Opcode>,
    pub offset: OffT,
}

impl<OffT: Into<u32> + Copy> Table<Opcode> for [Level2Entry<OffT>] {
    fn len(&self) -> usize {
        self.len()
    }

    fn key(&self, idx: usize) -> Option<Opcode> {
        self[idx].opcode
    }
}

/// Two-level hash table lookup.
///
/// Given the controlling type variable and instruction opcode, find the corresponding encoding
/// list.
///
/// Returns an offset into the ISA's `ENCLIST` table, or `None` if the opcode/type combination is
/// not legal.
pub fn lookup_enclist<OffT1, OffT2>(ctrl_typevar: Type,
                                    opcode: Opcode,
                                    level1_table: &[Level1Entry<OffT1>],
                                    level2_table: &[Level2Entry<OffT2>])
                                    -> Result<usize, Legalize>
    where OffT1: Into<u32> + Copy,
          OffT2: Into<u32> + Copy
{
    // TODO: The choice of legalization actions here is naive. This needs to be configurable.
    probe(level1_table, ctrl_typevar, ctrl_typevar.index())
        .ok_or_else(|| if ctrl_typevar.lane_type().bits() > 32 {
                        Legalize::Narrow
                    } else {
                        Legalize::Expand
                    })
        .and_then(|l1idx| {
                      let l1ent = &level1_table[l1idx];
                      let l2off = l1ent.offset.into() as usize;
                      let l2tab = &level2_table[l2off..l2off + (1 << l1ent.log2len)];
                      probe(l2tab, opcode, opcode as usize)
                          .map(|l2idx| l2tab[l2idx].offset.into() as usize)
                          .ok_or(Legalize::Expand)
                  })
}

/// Encoding list entry.
///
/// Encoding lists are represented as sequences of u16 words.
pub type EncListEntry = u16;

/// Number of bits used to represent a predicate. c.f. `meta.gen_encoding.py`.
const PRED_BITS: u8 = 12;
const PRED_MASK: EncListEntry = (1 << PRED_BITS) - 1;

/// The match-always instruction predicate. c.f. `meta.gen_encoding.py`.
const CODE_ALWAYS: EncListEntry = PRED_MASK;

/// The encoding list terminator.
const CODE_FAIL: EncListEntry = 0xffff;

/// Find the first applicable general encoding of `inst`.
///
/// Given an encoding list offset as returned by `lookup_enclist` above, search the encoding list
/// for the most first encoding that applies to `inst`. The encoding lists are laid out such that
/// this is the first valid entry in the list.
///
/// This function takes two closures that are used to evaluate predicates:
/// - `instp` is passed an instruction predicate number to be evaluated on the current instruction.
/// - `isap` is passed an ISA predicate number to evaluate.
///
/// Returns the corresponding encoding, or `None` if no list entries are satisfied by `inst`.
pub fn general_encoding<InstP, IsaP>(offset: usize,
                                     enclist: &[EncListEntry],
                                     instp: InstP,
                                     isap: IsaP)
                                     -> Option<Encoding>
    where InstP: Fn(EncListEntry) -> bool,
          IsaP: Fn(EncListEntry) -> bool
{
    let mut pos = offset;
    while enclist[pos] != CODE_FAIL {
        let pred = enclist[pos];
        if pred <= CODE_ALWAYS {
            // This is an instruction predicate followed by recipe and encbits entries.
            if pred == CODE_ALWAYS || instp(pred) {
                return Some(Encoding::new(enclist[pos + 1], enclist[pos + 2]));
            }
            pos += 3;
        } else {
            // This is an ISA predicate entry.
            pos += 1;
            if !isap(pred & PRED_MASK) {
                // ISA predicate failed, skip the next N entries.
                pos += 3 * (pred >> PRED_BITS) as usize;
            }
        }
    }

    None
}
<|endoftext|>


<commit_before>//! A stack type with heap-allocated contents.
//!
//! MinStacks have amortized `O(1)` push, pop and min (return the minimum value
//! in the stack).

use std::cmp::PartialEq;
use std::cmp::PartialOrd;

pub struct MinStack<T> {
    stack: Vec<T>,
    // Maintain a parallel stack of minimum values.
    // Whenever a value is pushed to `stack`, also push it to `min_stack` if
    // the value is <= the value at the top of `min_stack`.
    // Whenever a value is popped from `stack`, also pop `min_stack` if the
    // value == the value at the top of `min_stack`.
    min_stack: Vec<T>
}

impl<T : Copy + PartialEq + PartialOrd> MinStack<T> {
    /// Constructs a new, empty `MinStack<T>`.
    ///
    /// # Examples
    ///
    /// ```
    /// use minstack::MinStack;
    ///
    /// let mut stack: MinStack<i32> = MinStack::new();
    /// ```
    #[inline]
    pub fn new() -> MinStack<T> {
        MinStack {
            stack: Vec::new(),
            min_stack: Vec::new()
        }
    }

    /// Appends an element to end of the stack.
    ///
    /// # Panics
    ///
    /// Panics if the number of elements in the vector overflows a `usize`.
    ///
    /// # Examples
    ///
    /// ```
    /// use minstack::MinStack;
    ///
    /// let mut stack = MinStack::new();
    /// stack.push(3);
    /// assert_eq!(Some(3), stack.pop());
    /// ```
    #[inline]
    pub fn push(&mut self, value: T) {
        self.stack.push(value);
        match self.min_stack.last() {
            Some(min) if *min < value => { return; },
            _ => { }
        }
        self.min_stack.push(value);
    }

    #[inline]
    fn should_pop_min(&self, popped_value: &Option<T>) -> bool {
        match *popped_value {
            Some(v) => match self.min_stack.last() {
                Some(min) => *min == v,
                None => false
            },
            None => false
        }
    }

    /// Removes the last element from the stack and returns it, or `None` if it
    /// is empty.
    ///
    /// # Examples
    ///
    /// ```
    /// use minstack::MinStack;
    ///
    /// let mut stack = MinStack::new();
    /// stack.push(3);
    /// assert_eq!(Some(3), stack.pop());
    /// assert_eq!(None, stack.pop());
    /// ```
    #[inline]
    pub fn pop(&mut self) -> Option<T> {
        let value = self.stack.pop();

        if self.should_pop_min(&value) {
            self.min_stack.pop();
        }
        value
    }

    /// Returns the smallest element in the stack, or `None` if it is empty.
    ///
    /// # Examples
    ///
    /// ```
    /// use minstack::MinStack;
    ///
    /// let mut stack = MinStack::new();
    /// assert_eq!(None, stack.min());
    /// stack.push(3);
    /// stack.push(1);
    /// stack.push(83);
    /// assert_eq!(Some(&1), stack.min());
    /// ```
    #[inline]
    #[inline]
    pub fn min(&self) -> Option<&T> {
        return self.min_stack.last();
    }
}

#[test]
fn empty_stack() {
    let mut stack : MinStack<i32> = MinStack::new();
    assert_eq!(None, stack.min());
    assert_eq!(None, stack.pop());
}

#[test]
fn ascending_push() {
    let mut stack : MinStack<i32> = MinStack::new();
    stack.push(1);
    stack.push(2);
    stack.push(3);
    assert_eq!(Some(&1), stack.min());
    assert_eq!(Some(3), stack.pop());
    assert_eq!(Some(&1), stack.min());
    assert_eq!(Some(2), stack.pop());
    assert_eq!(Some(&1), stack.min());
    assert_eq!(Some(1), stack.pop());
    assert_eq!(None, stack.min());
    assert_eq!(None, stack.pop());
}

#[test]
fn decending_push() {
    let mut stack : MinStack<i32> = MinStack::new();
    stack.push(3);
    stack.push(2);
    stack.push(1);
    assert_eq!(Some(&1), stack.min());
    assert_eq!(Some(1), stack.pop());
    assert_eq!(Some(&2), stack.min());
    assert_eq!(Some(2), stack.pop());
    assert_eq!(Some(&3), stack.min());
    assert_eq!(Some(3), stack.pop());
    assert_eq!(None, stack.min());
    assert_eq!(None, stack.pop());
}

#[test]
fn duplicate_push() {
    let mut stack : MinStack<i32> = MinStack::new();
    stack.push(1);
    stack.push(2);
    stack.push(1);
    assert_eq!(Some(&1), stack.min());
    assert_eq!(Some(1), stack.pop());
    assert_eq!(Some(&1), stack.min());
    assert_eq!(Some(2), stack.pop());
    assert_eq!(Some(&1), stack.min());
    assert_eq!(Some(1), stack.pop());
    assert_eq!(None, stack.min());
    assert_eq!(None, stack.pop());
}
<commit_msg>Change minstack to not require Copy data<commit_after>//! A stack type with heap-allocated contents.
//!
//! MinStacks have amortized `O(1)` push, pop and min (return the minimum value
//! in the stack).

pub struct MinStack<T> {
    stack: Vec<T>,
    // Maintain a parallel stack of locations of minimum values.
    // Whenever a value is pushed to `stack`, also push its location to `min_stack` if
    // the value is <= the value at the top of `min_stack`.
    // Whenever a value is popped from `stack`, also pop `min_stack` if the
    // position == the value at the top of `min_stack`.
    min_stack: Vec<usize>
}

impl<T : Ord> MinStack<T> {
    /// Constructs a new, empty `MinStack<T>`.
    ///
    /// # Examples
    ///
    /// ```
    /// use minstack::MinStack;
    ///
    /// let mut stack: MinStack<i32> = MinStack::new();
    /// ```
    #[inline]
    pub fn new() -> MinStack<T> {
        MinStack {
            stack: Vec::new(),
            min_stack: Vec::new()
        }
    }

    /// Appends an element to end of the stack.
    ///
    /// # Panics
    ///
    /// Panics if the number of elements in the vector overflows a `usize`.
    ///
    /// # Examples
    ///
    /// ```
    /// use minstack::MinStack;
    ///
    /// let mut stack = MinStack::new();
    /// stack.push(3);
    /// assert_eq!(Some(3), stack.pop());
    /// ```
    #[inline]
    pub fn push(&mut self, value: T) {
        let idx = self.stack.len();

        match self.min_stack.last() {
            Some(&min) if value <= self.stack[min] =>
                self.min_stack.push(idx),
            None =>
                self.min_stack.push(idx),
            _ => { }
        }

        self.stack.push(value);
    }

    /// Removes the last element from the stack and returns it, or `None` if it
    /// is empty.
    ///
    /// # Examples
    ///
    /// ```
    /// use minstack::MinStack;
    ///
    /// let mut stack = MinStack::new();
    /// stack.push(3);
    /// assert_eq!(Some(3), stack.pop());
    /// assert_eq!(None, stack.pop());
    /// ```
    #[inline]
    pub fn pop(&mut self) -> Option<T> {
        let value = self.stack.pop();

        match self.min_stack.last() {
            Some(&min) if min == self.stack.len() => {
                self.min_stack.pop();
            }
            _ => { }
        }

        value
    }

    /// Returns the smallest element in the stack, or `None` if it is empty.
    ///
    /// # Examples
    ///
    /// ```
    /// use minstack::MinStack;
    ///
    /// let mut stack = MinStack::new();
    /// assert_eq!(None, stack.min());
    /// stack.push(3);
    /// stack.push(1);
    /// stack.push(83);
    /// assert_eq!(Some(&1), stack.min());
    /// ```
    #[inline]
    pub fn min(&self) -> Option<&T> {
        self.min_stack.last().map(|&n| &self.stack[n])
    }
}

#[test]
fn empty_stack() {
    let mut stack : MinStack<i32> = MinStack::new();
    assert_eq!(None, stack.min());
    assert_eq!(None, stack.pop());
}

#[test]
fn ascending_push() {
    let mut stack : MinStack<i32> = MinStack::new();
    stack.push(1);
    stack.push(2);
    stack.push(3);
    assert_eq!(Some(&1), stack.min());
    assert_eq!(Some(3), stack.pop());
    assert_eq!(Some(&1), stack.min());
    assert_eq!(Some(2), stack.pop());
    assert_eq!(Some(&1), stack.min());
    assert_eq!(Some(1), stack.pop());
    assert_eq!(None, stack.min());
    assert_eq!(None, stack.pop());
}

#[test]
fn decending_push() {
    let mut stack : MinStack<i32> = MinStack::new();
    stack.push(3);
    stack.push(2);
    stack.push(1);
    assert_eq!(Some(&1), stack.min());
    assert_eq!(Some(1), stack.pop());
    assert_eq!(Some(&2), stack.min());
    assert_eq!(Some(2), stack.pop());
    assert_eq!(Some(&3), stack.min());
    assert_eq!(Some(3), stack.pop());
    assert_eq!(None, stack.min());
    assert_eq!(None, stack.pop());
}

#[test]
fn duplicate_push() {
    let mut stack : MinStack<i32> = MinStack::new();
    stack.push(1);
    stack.push(2);
    stack.push(1);
    assert_eq!(Some(&1), stack.min());
    assert_eq!(Some(1), stack.pop());
    assert_eq!(Some(&1), stack.min());
    assert_eq!(Some(2), stack.pop());
    assert_eq!(Some(&1), stack.min());
    assert_eq!(Some(1), stack.pop());
    assert_eq!(None, stack.min());
    assert_eq!(None, stack.pop());
}
<|endoftext|>


<commit_before>use super::parse_module;

use core::semantic::ast::Node;

macro_rules! expr_test {
    ($name:ident, $code:expr) => {
        #[test]
        fn $name() {
            assert_eq!( parse_module($code)
                            .unwrap()[0]
                            .to_sexpr(0)
                      , $code)
        }
    }
}

expr_test!(test_basic_add, "(+ 1 2)");
<commit_msg>Starting on parser unit testing<commit_after>use super::parse_module;

use core::semantic::ast::Node;

macro_rules! expr_test {
    ($name:ident, $code:expr) => {
        #[test]
        fn $name() {
            assert_eq!( parse_module($code)
                            .unwrap()[0]
                            .to_sexpr(0)
                      , $code)
        }
    }
}

expr_test!(test_basic_add, "(+ 1 2)");
expr_test!(test_basic_sub, "(- 3 4)");
expr_test!(test_basic_div, "(/ 5 6)");
expr_test!(test_basic_mul, "(* 1 2)");
expr_test!(test_nested_arith_1, "(+ 1 (- 2 3))");
expr_test!(test_nested_arith_2, "(* (+ 1 2) 3 4)");
expr_test!(test_nested_arith_3, "(+ (/ 1 2) (* 3 4))");
<|endoftext|>


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

use std::error;
use std::ffi::CString;
use std::fmt;
use std::ptr;

use Error;
use OomError;
use instance::loader;
use instance::loader::LoadingError;
use vk;
use check_errors;

macro_rules! extensions {
    ($sname:ident, $($ext:ident => $s:expr,)*) => (
        /// List of extensions that are enabled or available.
        #[derive(Debug, Copy, Clone, PartialEq, Eq)]
        #[allow(missing_docs)]
        pub struct $sname {
            $(
                pub $ext: bool,
            )*

			/// Helper for future extensibility.
			pub _hidden: Hidden,
        }

        impl $sname {
            /// Returns an `Extensions` object with all members set to `false`.
            #[inline]
            pub fn none() -> $sname {
                $sname {
                    $($ext: false,)*
					_hidden: Hidden(())
                }
            }

            /// Builds a Vec containing the list of extensions.
            pub fn build_extensions_list(&self) -> Vec<CString> {
                let mut data = Vec::new();
                $(if self.$ext { data.push(CString::new(&$s[..]).unwrap()); })*
                data
            }

            /// Returns the intersection of this list and another list.
            #[inline]
            pub fn intersection(&self, other: &$sname) -> $sname {
                $sname {
                    $(
                        $ext: self.$ext && other.$ext,
                    )*
					_hidden: Hidden(())
                }
            }
        }
    );
}

macro_rules! instance_extensions {
    ($sname:ident, $($ext:ident => $s:expr,)*) => (
        extensions! {
            $sname,
            $( $ext => $s,)*
        }

        impl $sname {
            /// See the docs of supported_by_core().
            pub fn supported_by_core_raw() -> Result<$sname, SupportedExtensionsError> {
                let entry_points = try!(loader::entry_points());

                let properties: Vec<vk::ExtensionProperties> = unsafe {
                    let mut num = 0;
                    try!(check_errors(entry_points.EnumerateInstanceExtensionProperties(
                        ptr::null(), &mut num, ptr::null_mut())));

                    let mut properties = Vec::with_capacity(num as usize);
                    try!(check_errors(entry_points.EnumerateInstanceExtensionProperties(
                        ptr::null(), &mut num, properties.as_mut_ptr())));
                    properties.set_len(num as usize);
                    properties
                };

                let mut extensions = $sname::none();
                for property in properties {
                    let name = property.extensionName;
                    $(
                        // TODO: this is VERY inefficient
                        // TODO: Check specVersion?
                        let same = {
                            let mut i = 0;
                            while name[i] != 0 && $s[i] != 0 && name[i] as u8 == $s[i] && i < $s.len() { i += 1; }
                            name[i] == 0 && (i >= $s.len() || name[i] as u8 == $s[i])
                        };
                        if same {
                            extensions.$ext = true;
                        }
                    )*
                }

                Ok(extensions)
            }

            /// Returns an `Extensions` object with extensions supported by the core driver.
            pub fn supported_by_core() -> Result<$sname, LoadingError> {
                match $sname::supported_by_core_raw() {
                    Ok(l) => Ok(l),
                    Err(SupportedExtensionsError::LoadingError(e)) => Err(e),
                    Err(SupportedExtensionsError::OomError(e)) => panic!("{:?}", e),
                }
            }
        }
    );
}

instance_extensions! {
    InstanceExtensions,
    khr_surface => b"VK_KHR_surface",
    khr_display => b"VK_KHR_display",
    khr_xlib_surface => b"VK_KHR_xlib_surface",
    khr_xcb_surface => b"VK_KHR_xcb_surface",
    khr_wayland_surface => b"VK_KHR_wayland_surface",
    khr_mir_surface => b"VK_KHR_mir_surface",
    khr_android_surface => b"VK_KHR_android_surface",
    khr_win32_surface => b"VK_KHR_win32_surface",
    ext_debug_report => b"VK_EXT_debug_report",
}

extensions! {
    DeviceExtensions,
    khr_swapchain => b"VK_KHR_swapchain",
    khr_display_swapchain => b"VK_KHR_display_swapchain",
}

/// Error that can happen when loading the list of layers.
#[derive(Clone, Debug)]
pub enum SupportedExtensionsError {
    /// Failed to load the Vulkan shared library.
    LoadingError(LoadingError),
    /// Not enough memory.
    OomError(OomError),
}

impl error::Error for SupportedExtensionsError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            SupportedExtensionsError::LoadingError(_) => "failed to load the Vulkan shared library",
            SupportedExtensionsError::OomError(_) => "not enough memory available",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            SupportedExtensionsError::LoadingError(ref err) => Some(err),
            SupportedExtensionsError::OomError(ref err) => Some(err),
        }
    }
}

impl fmt::Display for SupportedExtensionsError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<OomError> for SupportedExtensionsError {
    #[inline]
    fn from(err: OomError) -> SupportedExtensionsError {
        SupportedExtensionsError::OomError(err)
    }
}

impl From<LoadingError> for SupportedExtensionsError {
    #[inline]
    fn from(err: LoadingError) -> SupportedExtensionsError {
        SupportedExtensionsError::LoadingError(err)
    }
}

impl From<Error> for SupportedExtensionsError {
    #[inline]
    fn from(err: Error) -> SupportedExtensionsError {
        match err {
            err @ Error::OutOfHostMemory => {
                SupportedExtensionsError::OomError(OomError::from(err))
            },
            err @ Error::OutOfDeviceMemory => {
                SupportedExtensionsError::OomError(OomError::from(err))
            },
            _ => panic!("unexpected error: {:?}", err)
        }
    }
}

/// Non-constructible helper for future extensibility of extension structs.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Hidden(());

#[cfg(test)]
mod tests {
    use instance::InstanceExtensions;
    use instance::DeviceExtensions;

    #[test]
    fn empty_extensions() {
        let i = InstanceExtensions::none().build_extensions_list();
        assert!(i.is_empty());

        let d = DeviceExtensions::none().build_extensions_list();
        assert!(d.is_empty());
    }
}
<commit_msg>indentation; documentation; renamed helper; hidden docs<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error;
use std::ffi::CString;
use std::fmt;
use std::ptr;

use Error;
use OomError;
use instance::loader;
use instance::loader::LoadingError;
use vk;
use check_errors;

macro_rules! extensions {
    ($sname:ident, $($ext:ident => $s:expr,)*) => (
        /// List of extensions that are enabled or available.
        #[derive(Debug, Copy, Clone, PartialEq, Eq)]
        #[allow(missing_docs)]
        pub struct $sname {
            $(
                pub $ext: bool,
            )*

            /// This field ensures that an instance of this `Extensions` struct
            /// can only be created through Vulkano functions and the update
            /// syntax. This way, extensions can be added to Vulkano without
            /// breaking existing code.
            pub _unbuildable: Unbuildable,
        }

        impl $sname {
            /// Returns an `Extensions` object with all members set to `false`.
            #[inline]
            pub fn none() -> $sname {
                $sname {
                    $($ext: false,)*
					_unbuildable: Unbuildable(())
                }
            }

            /// Builds a Vec containing the list of extensions.
            pub fn build_extensions_list(&self) -> Vec<CString> {
                let mut data = Vec::new();
                $(if self.$ext { data.push(CString::new(&$s[..]).unwrap()); })*
                data
            }

            /// Returns the intersection of this list and another list.
            #[inline]
            pub fn intersection(&self, other: &$sname) -> $sname {
                $sname {
                    $(
                        $ext: self.$ext && other.$ext,
                    )*
					_unbuildable: Unbuildable(())
                }
            }
        }
    );
}

macro_rules! instance_extensions {
    ($sname:ident, $($ext:ident => $s:expr,)*) => (
        extensions! {
            $sname,
            $( $ext => $s,)*
        }

        impl $sname {
            /// See the docs of supported_by_core().
            pub fn supported_by_core_raw() -> Result<$sname, SupportedExtensionsError> {
                let entry_points = try!(loader::entry_points());

                let properties: Vec<vk::ExtensionProperties> = unsafe {
                    let mut num = 0;
                    try!(check_errors(entry_points.EnumerateInstanceExtensionProperties(
                        ptr::null(), &mut num, ptr::null_mut())));

                    let mut properties = Vec::with_capacity(num as usize);
                    try!(check_errors(entry_points.EnumerateInstanceExtensionProperties(
                        ptr::null(), &mut num, properties.as_mut_ptr())));
                    properties.set_len(num as usize);
                    properties
                };

                let mut extensions = $sname::none();
                for property in properties {
                    let name = property.extensionName;
                    $(
                        // TODO: this is VERY inefficient
                        // TODO: Check specVersion?
                        let same = {
                            let mut i = 0;
                            while name[i] != 0 && $s[i] != 0 && name[i] as u8 == $s[i] && i < $s.len() { i += 1; }
                            name[i] == 0 && (i >= $s.len() || name[i] as u8 == $s[i])
                        };
                        if same {
                            extensions.$ext = true;
                        }
                    )*
                }

                Ok(extensions)
            }

            /// Returns an `Extensions` object with extensions supported by the core driver.
            pub fn supported_by_core() -> Result<$sname, LoadingError> {
                match $sname::supported_by_core_raw() {
                    Ok(l) => Ok(l),
                    Err(SupportedExtensionsError::LoadingError(e)) => Err(e),
                    Err(SupportedExtensionsError::OomError(e)) => panic!("{:?}", e),
                }
            }
        }
    );
}

instance_extensions! {
    InstanceExtensions,
    khr_surface => b"VK_KHR_surface",
    khr_display => b"VK_KHR_display",
    khr_xlib_surface => b"VK_KHR_xlib_surface",
    khr_xcb_surface => b"VK_KHR_xcb_surface",
    khr_wayland_surface => b"VK_KHR_wayland_surface",
    khr_mir_surface => b"VK_KHR_mir_surface",
    khr_android_surface => b"VK_KHR_android_surface",
    khr_win32_surface => b"VK_KHR_win32_surface",
    ext_debug_report => b"VK_EXT_debug_report",
}

extensions! {
    DeviceExtensions,
    khr_swapchain => b"VK_KHR_swapchain",
    khr_display_swapchain => b"VK_KHR_display_swapchain",
}

/// Error that can happen when loading the list of layers.
#[derive(Clone, Debug)]
pub enum SupportedExtensionsError {
    /// Failed to load the Vulkan shared library.
    LoadingError(LoadingError),
    /// Not enough memory.
    OomError(OomError),
}

impl error::Error for SupportedExtensionsError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            SupportedExtensionsError::LoadingError(_) => "failed to load the Vulkan shared library",
            SupportedExtensionsError::OomError(_) => "not enough memory available",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            SupportedExtensionsError::LoadingError(ref err) => Some(err),
            SupportedExtensionsError::OomError(ref err) => Some(err),
        }
    }
}

impl fmt::Display for SupportedExtensionsError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<OomError> for SupportedExtensionsError {
    #[inline]
    fn from(err: OomError) -> SupportedExtensionsError {
        SupportedExtensionsError::OomError(err)
    }
}

impl From<LoadingError> for SupportedExtensionsError {
    #[inline]
    fn from(err: LoadingError) -> SupportedExtensionsError {
        SupportedExtensionsError::LoadingError(err)
    }
}

impl From<Error> for SupportedExtensionsError {
    #[inline]
    fn from(err: Error) -> SupportedExtensionsError {
        match err {
            err @ Error::OutOfHostMemory => {
                SupportedExtensionsError::OomError(OomError::from(err))
            },
            err @ Error::OutOfDeviceMemory => {
                SupportedExtensionsError::OomError(OomError::from(err))
            },
            _ => panic!("unexpected error: {:?}", err)
        }
    }
}

/// This helper type can only be instantiated inside this module.
/// See `*Extensions::_unbuildable`.
#[doc(hidden)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Unbuildable(());

#[cfg(test)]
mod tests {
    use instance::InstanceExtensions;
    use instance::DeviceExtensions;

    #[test]
    fn empty_extensions() {
        let i = InstanceExtensions::none().build_extensions_list();
        assert!(i.is_empty());

        let d = DeviceExtensions::none().build_extensions_list();
        assert!(d.is_empty());
    }
}
<|endoftext|>


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

use std::error;
use std::ffi::CString;
use std::fmt;
use std::ptr;
use std::str;

use Error;
use OomError;
use instance::loader;
use instance::loader::LoadingError;
use vk;
use check_errors;

macro_rules! extensions {
    ($sname:ident, $($ext:ident => $s:expr,)*) => (
        /// List of extensions that are enabled or available.
        #[derive(Copy, Clone, PartialEq, Eq)]
        #[allow(missing_docs)]
        pub struct $sname {
            $(
                pub $ext: bool,
            )*

            /// This field ensures that an instance of this `Extensions` struct
            /// can only be created through Vulkano functions and the update
            /// syntax. This way, extensions can be added to Vulkano without
            /// breaking existing code.
            pub _unbuildable: Unbuildable,
        }

        impl $sname {
            /// Returns an `Extensions` object with all members set to `false`.
            #[inline]
            pub fn none() -> $sname {
                $sname {
                    $($ext: false,)*
                    _unbuildable: Unbuildable(())
                }
            }

            /// Builds a Vec containing the list of extensions.
            pub fn build_extensions_list(&self) -> Vec<CString> {
                let mut data = Vec::new();
                $(if self.$ext { data.push(CString::new(&$s[..]).unwrap()); })*
                data
            }

            /// Returns the intersection of this list and another list.
            #[inline]
            pub fn intersection(&self, other: &$sname) -> $sname {
                $sname {
                    $(
                        $ext: self.$ext && other.$ext,
                    )*
                    _unbuildable: Unbuildable(())
                }
            }
        }

        impl fmt::Debug for $sname {
            #[allow(unused_assignments)]
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                try!(write!(f, "["));

                let mut first = true;

                $(
                    if self.$ext {
                        if !first { try!(write!(f, ", ")); }
                        else { first = false; }
                        try!(f.write_str(str::from_utf8($s).unwrap()));
                    }
                )*

                write!(f, "]")
            }
        }
    );
}

macro_rules! instance_extensions {
    ($sname:ident, $($ext:ident => $s:expr,)*) => (
        extensions! {
            $sname,
            $( $ext => $s,)*
        }

        impl $sname {
            /// See the docs of supported_by_core().
            pub fn supported_by_core_raw() -> Result<$sname, SupportedExtensionsError> {
                let entry_points = try!(loader::entry_points());

                let properties: Vec<vk::ExtensionProperties> = unsafe {
                    let mut num = 0;
                    try!(check_errors(entry_points.EnumerateInstanceExtensionProperties(
                        ptr::null(), &mut num, ptr::null_mut())));

                    let mut properties = Vec::with_capacity(num as usize);
                    try!(check_errors(entry_points.EnumerateInstanceExtensionProperties(
                        ptr::null(), &mut num, properties.as_mut_ptr())));
                    properties.set_len(num as usize);
                    properties
                };

                let mut extensions = $sname::none();
                for property in properties {
                    let name = property.extensionName;
                    $(
                        // TODO: this is VERY inefficient
                        // TODO: Check specVersion?
                        let same = {
                            let mut i = 0;
                            while name[i] != 0 && $s[i] != 0 && name[i] as u8 == $s[i] && i < $s.len() { i += 1; }
                            name[i] == 0 && (i >= $s.len() || name[i] as u8 == $s[i])
                        };
                        if same {
                            extensions.$ext = true;
                        }
                    )*
                }

                Ok(extensions)
            }

            /// Returns an `Extensions` object with extensions supported by the core driver.
            pub fn supported_by_core() -> Result<$sname, LoadingError> {
                match $sname::supported_by_core_raw() {
                    Ok(l) => Ok(l),
                    Err(SupportedExtensionsError::LoadingError(e)) => Err(e),
                    Err(SupportedExtensionsError::OomError(e)) => panic!("{:?}", e),
                }
            }
        }
    );
}

instance_extensions! {
    InstanceExtensions,
    khr_surface => b"VK_KHR_surface",
    khr_display => b"VK_KHR_display",
    khr_xlib_surface => b"VK_KHR_xlib_surface",
    khr_xcb_surface => b"VK_KHR_xcb_surface",
    khr_wayland_surface => b"VK_KHR_wayland_surface",
    khr_mir_surface => b"VK_KHR_mir_surface",
    khr_android_surface => b"VK_KHR_android_surface",
    khr_win32_surface => b"VK_KHR_win32_surface",
    ext_debug_report => b"VK_EXT_debug_report",
    nn_vi_surface => b"VK_NN_vi_surface",
    ext_swapchain_colorspace => b"VK_EXT_swapchain_colorspace",
    khr_get_physical_device_properties2 => b"VK_KHR_get_physical_device_properties2",
}

extensions! {
    DeviceExtensions,
    khr_swapchain => b"VK_KHR_swapchain",
    khr_display_swapchain => b"VK_KHR_display_swapchain",
    khr_maintenance1 => b"VK_KHR_maintenance1",
    khr_descriptor_update_template => b"VK_KHR_descriptor_update_template",
    khr_push_descriptor => b"VK_KHR_push_descriptor",
}

/// Error that can happen when loading the list of layers.
#[derive(Clone, Debug)]
pub enum SupportedExtensionsError {
    /// Failed to load the Vulkan shared library.
    LoadingError(LoadingError),
    /// Not enough memory.
    OomError(OomError),
}

impl error::Error for SupportedExtensionsError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            SupportedExtensionsError::LoadingError(_) => "failed to load the Vulkan shared library",
            SupportedExtensionsError::OomError(_) => "not enough memory available",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            SupportedExtensionsError::LoadingError(ref err) => Some(err),
            SupportedExtensionsError::OomError(ref err) => Some(err),
        }
    }
}

impl fmt::Display for SupportedExtensionsError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<OomError> for SupportedExtensionsError {
    #[inline]
    fn from(err: OomError) -> SupportedExtensionsError {
        SupportedExtensionsError::OomError(err)
    }
}

impl From<LoadingError> for SupportedExtensionsError {
    #[inline]
    fn from(err: LoadingError) -> SupportedExtensionsError {
        SupportedExtensionsError::LoadingError(err)
    }
}

impl From<Error> for SupportedExtensionsError {
    #[inline]
    fn from(err: Error) -> SupportedExtensionsError {
        match err {
            err @ Error::OutOfHostMemory => {
                SupportedExtensionsError::OomError(OomError::from(err))
            },
            err @ Error::OutOfDeviceMemory => {
                SupportedExtensionsError::OomError(OomError::from(err))
            },
            _ => panic!("unexpected error: {:?}", err)
        }
    }
}

/// This helper type can only be instantiated inside this module.
/// See `*Extensions::_unbuildable`.
#[doc(hidden)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Unbuildable(());

#[cfg(test)]
mod tests {
    use instance::InstanceExtensions;
    use instance::DeviceExtensions;

    #[test]
    fn empty_extensions() {
        let i = InstanceExtensions::none().build_extensions_list();
        assert!(i.is_empty());

        let d = DeviceExtensions::none().build_extensions_list();
        assert!(d.is_empty());
    }
}
<commit_msg>Add support for VK_KHR_sampler_mirror_clamp_to_edge<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error;
use std::ffi::CString;
use std::fmt;
use std::ptr;
use std::str;

use Error;
use OomError;
use instance::loader;
use instance::loader::LoadingError;
use vk;
use check_errors;

macro_rules! extensions {
    ($sname:ident, $($ext:ident => $s:expr,)*) => (
        /// List of extensions that are enabled or available.
        #[derive(Copy, Clone, PartialEq, Eq)]
        #[allow(missing_docs)]
        pub struct $sname {
            $(
                pub $ext: bool,
            )*

            /// This field ensures that an instance of this `Extensions` struct
            /// can only be created through Vulkano functions and the update
            /// syntax. This way, extensions can be added to Vulkano without
            /// breaking existing code.
            pub _unbuildable: Unbuildable,
        }

        impl $sname {
            /// Returns an `Extensions` object with all members set to `false`.
            #[inline]
            pub fn none() -> $sname {
                $sname {
                    $($ext: false,)*
                    _unbuildable: Unbuildable(())
                }
            }

            /// Builds a Vec containing the list of extensions.
            pub fn build_extensions_list(&self) -> Vec<CString> {
                let mut data = Vec::new();
                $(if self.$ext { data.push(CString::new(&$s[..]).unwrap()); })*
                data
            }

            /// Returns the intersection of this list and another list.
            #[inline]
            pub fn intersection(&self, other: &$sname) -> $sname {
                $sname {
                    $(
                        $ext: self.$ext && other.$ext,
                    )*
                    _unbuildable: Unbuildable(())
                }
            }
        }

        impl fmt::Debug for $sname {
            #[allow(unused_assignments)]
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                try!(write!(f, "["));

                let mut first = true;

                $(
                    if self.$ext {
                        if !first { try!(write!(f, ", ")); }
                        else { first = false; }
                        try!(f.write_str(str::from_utf8($s).unwrap()));
                    }
                )*

                write!(f, "]")
            }
        }
    );
}

macro_rules! instance_extensions {
    ($sname:ident, $($ext:ident => $s:expr,)*) => (
        extensions! {
            $sname,
            $( $ext => $s,)*
        }

        impl $sname {
            /// See the docs of supported_by_core().
            pub fn supported_by_core_raw() -> Result<$sname, SupportedExtensionsError> {
                let entry_points = try!(loader::entry_points());

                let properties: Vec<vk::ExtensionProperties> = unsafe {
                    let mut num = 0;
                    try!(check_errors(entry_points.EnumerateInstanceExtensionProperties(
                        ptr::null(), &mut num, ptr::null_mut())));

                    let mut properties = Vec::with_capacity(num as usize);
                    try!(check_errors(entry_points.EnumerateInstanceExtensionProperties(
                        ptr::null(), &mut num, properties.as_mut_ptr())));
                    properties.set_len(num as usize);
                    properties
                };

                let mut extensions = $sname::none();
                for property in properties {
                    let name = property.extensionName;
                    $(
                        // TODO: this is VERY inefficient
                        // TODO: Check specVersion?
                        let same = {
                            let mut i = 0;
                            while name[i] != 0 && $s[i] != 0 && name[i] as u8 == $s[i] && i < $s.len() { i += 1; }
                            name[i] == 0 && (i >= $s.len() || name[i] as u8 == $s[i])
                        };
                        if same {
                            extensions.$ext = true;
                        }
                    )*
                }

                Ok(extensions)
            }

            /// Returns an `Extensions` object with extensions supported by the core driver.
            pub fn supported_by_core() -> Result<$sname, LoadingError> {
                match $sname::supported_by_core_raw() {
                    Ok(l) => Ok(l),
                    Err(SupportedExtensionsError::LoadingError(e)) => Err(e),
                    Err(SupportedExtensionsError::OomError(e)) => panic!("{:?}", e),
                }
            }
        }
    );
}

instance_extensions! {
    InstanceExtensions,
    khr_surface => b"VK_KHR_surface",
    khr_display => b"VK_KHR_display",
    khr_xlib_surface => b"VK_KHR_xlib_surface",
    khr_xcb_surface => b"VK_KHR_xcb_surface",
    khr_wayland_surface => b"VK_KHR_wayland_surface",
    khr_mir_surface => b"VK_KHR_mir_surface",
    khr_android_surface => b"VK_KHR_android_surface",
    khr_win32_surface => b"VK_KHR_win32_surface",
    ext_debug_report => b"VK_EXT_debug_report",
    nn_vi_surface => b"VK_NN_vi_surface",
    ext_swapchain_colorspace => b"VK_EXT_swapchain_colorspace",
    khr_get_physical_device_properties2 => b"VK_KHR_get_physical_device_properties2",
}

extensions! {
    DeviceExtensions,
    khr_swapchain => b"VK_KHR_swapchain",
    khr_display_swapchain => b"VK_KHR_display_swapchain",
    khr_maintenance1 => b"VK_KHR_maintenance1",
    khr_descriptor_update_template => b"VK_KHR_descriptor_update_template",
    khr_push_descriptor => b"VK_KHR_push_descriptor",
    khr_sampler_mirror_clamp_to_edge => b"VK_KHR_sampler_mirror_clamp_to_edge",
}

/// Error that can happen when loading the list of layers.
#[derive(Clone, Debug)]
pub enum SupportedExtensionsError {
    /// Failed to load the Vulkan shared library.
    LoadingError(LoadingError),
    /// Not enough memory.
    OomError(OomError),
}

impl error::Error for SupportedExtensionsError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            SupportedExtensionsError::LoadingError(_) => "failed to load the Vulkan shared library",
            SupportedExtensionsError::OomError(_) => "not enough memory available",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            SupportedExtensionsError::LoadingError(ref err) => Some(err),
            SupportedExtensionsError::OomError(ref err) => Some(err),
        }
    }
}

impl fmt::Display for SupportedExtensionsError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<OomError> for SupportedExtensionsError {
    #[inline]
    fn from(err: OomError) -> SupportedExtensionsError {
        SupportedExtensionsError::OomError(err)
    }
}

impl From<LoadingError> for SupportedExtensionsError {
    #[inline]
    fn from(err: LoadingError) -> SupportedExtensionsError {
        SupportedExtensionsError::LoadingError(err)
    }
}

impl From<Error> for SupportedExtensionsError {
    #[inline]
    fn from(err: Error) -> SupportedExtensionsError {
        match err {
            err @ Error::OutOfHostMemory => {
                SupportedExtensionsError::OomError(OomError::from(err))
            },
            err @ Error::OutOfDeviceMemory => {
                SupportedExtensionsError::OomError(OomError::from(err))
            },
            _ => panic!("unexpected error: {:?}", err)
        }
    }
}

/// This helper type can only be instantiated inside this module.
/// See `*Extensions::_unbuildable`.
#[doc(hidden)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Unbuildable(());

#[cfg(test)]
mod tests {
    use instance::InstanceExtensions;
    use instance::DeviceExtensions;

    #[test]
    fn empty_extensions() {
        let i = InstanceExtensions::none().build_extensions_list();
        assert!(i.is_empty());

        let d = DeviceExtensions::none().build_extensions_list();
        assert!(d.is_empty());
    }
}
<|endoftext|>


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

use std::error;
use std::fmt;
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Duration;
use crossbeam::sync::MsQueue;

use device::Device;
use device::Queue;
use format::Format;
use format::FormatDesc;
use image::sys::Dimensions;
use image::sys::UnsafeImage;
use image::sys::Usage as ImageUsage;
use image::swapchain::SwapchainImage;
use swapchain::CompositeAlpha;
use swapchain::PresentMode;
use swapchain::Surface;
use swapchain::SurfaceTransform;
use sync::Semaphore;
use sync::SharingMode;

use check_errors;
use Error;
use OomError;
use Success;
use SynchronizedVulkanObject;
use VulkanObject;
use VulkanPointers;
use vk;

/// Contains the swapping system and the images that can be shown on a surface.
// TODO: #[derive(Debug)] (waiting on https://github.com/aturon/crossbeam/issues/62)
pub struct Swapchain {
    device: Arc<Device>,
    surface: Arc<Surface>,
    swapchain: vk::SwapchainKHR,

    /// Pool of semaphores from which a semaphore is retreived when acquiring an image.
    ///
    /// We need to use a queue so that we don't use the same semaphore twice in a row. The length
    /// of the queue is strictly superior to the number of images, in case the driver lets us
    /// acquire an image before it is presented.
    semaphores_pool: MsQueue<Arc<Semaphore>>,

    images_semaphores: Mutex<Vec<Option<Arc<Semaphore>>>>,
}

impl Swapchain {
    /// Builds a new swapchain. Allocates images who content can be made visible on a surface.
    ///
    /// See also the `Surface::get_capabilities` function which returns the values that are
    /// supported by the implementation. All the parameters that you pass to `Swapchain::new`
    /// must be supported.
    ///
    /// The `clipped` parameter indicates whether the implementation is allowed to discard
    /// rendering operations that affect regions of the surface which aren't visible. This is
    /// important to take into account if your fragment shader has side-effects or if you want to
    /// read back the content of the image afterwards.
    ///
    /// This function returns the swapchain plus a list of the images that belong to the
    /// swapchain. The order in which the images are returned is important for the
    /// `acquire_next_image` and `present` functions.
    ///
    /// # Panic
    ///
    /// - Panicks if the device and the surface don't belong to the same instance.
    /// - Panicks if `color_attachment` is false in `usage`.
    ///
    #[inline]
    pub fn new<F, S>(device: &Arc<Device>, surface: &Arc<Surface>, num_images: u32, format: F,
                     dimensions: [u32; 2], layers: u32, usage: &ImageUsage, sharing: S,
                     transform: SurfaceTransform, alpha: CompositeAlpha, mode: PresentMode,
                     clipped: bool) -> Result<(Arc<Swapchain>, Vec<Arc<SwapchainImage>>), OomError>
        where F: FormatDesc, S: Into<SharingMode>
    {
        Swapchain::new_inner(device, surface, num_images, format.format(), dimensions, layers,
                             usage, sharing.into(), transform, alpha, mode, clipped)
    }

    // TODO:
    //pub fn recreate() { ... }

    // TODO: images layouts should always be set to "PRESENT", since we have no way to switch the
    //       layout at present time
    fn new_inner(device: &Arc<Device>, surface: &Arc<Surface>, num_images: u32, format: Format,
                 dimensions: [u32; 2], layers: u32, usage: &ImageUsage, sharing: SharingMode,
                 transform: SurfaceTransform, alpha: CompositeAlpha, mode: PresentMode,
                 clipped: bool) -> Result<(Arc<Swapchain>, Vec<Arc<SwapchainImage>>), OomError>
    {
        // FIXME: check that the parameters are supported

        // FIXME: check that the device and the surface belong to the same instance
        let vk = device.pointers();
        assert!(device.loaded_extensions().khr_swapchain);     // TODO: return error instead

        assert!(usage.color_attachment);
        let usage = usage.to_usage_bits();

        let sharing = sharing.into();

        let swapchain = unsafe {
            let (sh_mode, sh_count, sh_indices) = match sharing {
                SharingMode::Exclusive(id) => (vk::SHARING_MODE_EXCLUSIVE, 0, ptr::null()),
                SharingMode::Concurrent(ref ids) => (vk::SHARING_MODE_CONCURRENT, ids.len() as u32,
                                                     ids.as_ptr()),
            };

            let infos = vk::SwapchainCreateInfoKHR {
                sType: vk::STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
                pNext: ptr::null(),
                flags: 0,   // reserved
                surface: surface.internal_object(),
                minImageCount: num_images,
                imageFormat: format as u32,
                imageColorSpace: vk::COLORSPACE_SRGB_NONLINEAR_KHR,     // only available value
                imageExtent: vk::Extent2D { width: dimensions[0], height: dimensions[1] },
                imageArrayLayers: layers,
                imageUsage: usage,
                imageSharingMode: sh_mode,
                queueFamilyIndexCount: sh_count,
                pQueueFamilyIndices: sh_indices,
                preTransform: transform as u32,
                compositeAlpha: alpha as u32,
                presentMode: mode as u32,
                clipped: if clipped { vk::TRUE } else { vk::FALSE },
                oldSwapchain: 0,      // TODO:
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateSwapchainKHR(device.internal_object(), &infos,
                                                    ptr::null(), &mut output)));
            output
        };

        let swapchain = Arc::new(Swapchain {
            device: device.clone(),
            surface: surface.clone(),
            swapchain: swapchain,
            semaphores_pool: MsQueue::new(),
            images_semaphores: Mutex::new(Vec::new()),
        });

        let images = unsafe {
            let mut num = 0;
            try!(check_errors(vk.GetSwapchainImagesKHR(device.internal_object(),
                                                       swapchain.swapchain, &mut num,
                                                       ptr::null_mut())));

            let mut images = Vec::with_capacity(num as usize);
            try!(check_errors(vk.GetSwapchainImagesKHR(device.internal_object(),
                                                       swapchain.swapchain, &mut num,
                                                       images.as_mut_ptr())));
            images.set_len(num as usize);
            images
        };

        let images = images.into_iter().enumerate().map(|(id, image)| unsafe {
            let unsafe_image = UnsafeImage::from_raw(device, image, usage, format,
                                                     Dimensions::Dim2d { width: dimensions[0], height: dimensions[1] }, 1, 1);
            SwapchainImage::from_raw(unsafe_image, format, &swapchain, id as u32).unwrap()     // TODO: propagate error
        }).collect::<Vec<_>>();

        {
            let mut semaphores = swapchain.images_semaphores.lock().unwrap();
            for _ in 0 .. images.len() {
                semaphores.push(None);
            }
        }

        for _ in 0 .. images.len() + 1 {
            swapchain.semaphores_pool.push(try!(Semaphore::new(device)));
        }

        Ok((swapchain, images))
    }

    /// Tries to take ownership of an image in order to draw on it.
    ///
    /// The function returns the index of the image in the array of images that was returned
    /// when creating the swapchain.
    ///
    /// If you try to draw on an image without acquiring it first, the execution will block. (TODO
    /// behavior may change).
    pub fn acquire_next_image(&self, timeout: Duration) -> Result<usize, AcquireError> {
        let vk = self.device.pointers();

        unsafe {
            let semaphore = self.semaphores_pool.try_pop().expect("Failed to obtain a semaphore \
                                                                   from the swapchain semaphores \
                                                                   pool");

            let timeout_ns = timeout.as_secs().saturating_mul(1_000_000_000)
                                              .saturating_add(timeout.subsec_nanos() as u64);

            let mut out = mem::uninitialized();
            let r = try!(check_errors(vk.AcquireNextImageKHR(self.device.internal_object(),
                                                             self.swapchain, timeout_ns,
                                                             semaphore.internal_object(), 0,     // TODO: timeout
                                                             &mut out)));

            let id = match r {
                Success::Success => out as usize,
                Success::Suboptimal => out as usize,        // TODO: give that info to the user
                Success::NotReady => return Err(AcquireError::Timeout),
                Success::Timeout => return Err(AcquireError::Timeout),
                s => panic!("unexpected success value: {:?}", s)
            };

            let mut images_semaphores = self.images_semaphores.lock().unwrap();
            images_semaphores[id] = Some(semaphore);

            Ok(id)
        }
    }

    /// Presents an image on the screen.
    ///
    /// The parameter is the same index as what `acquire_next_image` returned. The image must
    /// have been acquired first.
    ///
    /// The actual behavior depends on the present mode that you passed when creating the
    /// swapchain.
    pub fn present(&self, queue: &Arc<Queue>, index: usize) -> Result<(), OomError> {     // FIXME: wrong error
        let vk = self.device.pointers();

        let wait_semaphore = {
            let mut images_semaphores = self.images_semaphores.lock().unwrap();
            images_semaphores[index].take().expect("Trying to present an image that was \
                                                    not acquired")
        };

        // FIXME: the semaphore may be destroyed ; need to return it

        unsafe {
            let mut result = mem::uninitialized();

            let queue = queue.internal_object_guard();
            let index = index as u32;

            let infos = vk::PresentInfoKHR {
                sType: vk::STRUCTURE_TYPE_PRESENT_INFO_KHR,
                pNext: ptr::null(),
                waitSemaphoreCount: 1,
                pWaitSemaphores: &wait_semaphore.internal_object(),
                swapchainCount: 1,
                pSwapchains: &self.swapchain,
                pImageIndices: &index,
                pResults: &mut result,
            };

            try!(check_errors(vk.QueuePresentKHR(*queue, &infos)));
            //try!(check_errors(result));       // TODO: AMD driver doesn't seem to write the result
        }

        self.semaphores_pool.push(wait_semaphore);
        Ok(())
    }

    /*/// Returns the semaphore that is going to be signalled when the image is going to be ready
    /// to be drawn upon.
    ///
    /// Returns `None` if the image was not acquired first, or was already presented.
    // TODO: racy, as someone could present the image before using the semaphore
    #[inline]
    pub fn image_semaphore(&self, id: u32) -> Option<Arc<Semaphore>> {
        let semaphores = self.images_semaphores.lock().unwrap();
        semaphores[id as usize].as_ref().map(|s| s.clone())
    }*/
    // TODO: the design of this functions depends on https://github.com/KhronosGroup/Vulkan-Docs/issues/155
    #[inline]
    pub fn image_semaphore(&self, id: u32, semaphore: Arc<Semaphore>) -> Option<Arc<Semaphore>> {
        let mut semaphores = self.images_semaphores.lock().unwrap();
        mem::replace(&mut semaphores[id as usize], Some(semaphore))
    }
}

impl Drop for Swapchain {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroySwapchainKHR(self.device.internal_object(), self.swapchain, ptr::null());
        }
    }
}

/// Error that can happen when calling `acquire_next_image`.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum AcquireError {
    Timeout,
    SurfaceLost,
    OutOfDate,
}

impl error::Error for AcquireError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            AcquireError::Timeout => "no image is available for acquiring yet",
            AcquireError::SurfaceLost => "the surface of this swapchain is no longer valid",
            AcquireError::OutOfDate => "the swapchain needs to be recreated",
        }
    }
}

impl fmt::Display for AcquireError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<Error> for AcquireError {
    #[inline]
    fn from(err: Error) -> AcquireError {
        match err {
            Error::SurfaceLost => AcquireError::SurfaceLost,
            Error::OutOfDate => AcquireError::OutOfDate,
            _ => panic!("unexpected error: {:?}", err)
        }
    }
}
<commit_msg>Add missing variants to AcquireError<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error;
use std::fmt;
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Duration;
use crossbeam::sync::MsQueue;

use device::Device;
use device::Queue;
use format::Format;
use format::FormatDesc;
use image::sys::Dimensions;
use image::sys::UnsafeImage;
use image::sys::Usage as ImageUsage;
use image::swapchain::SwapchainImage;
use swapchain::CompositeAlpha;
use swapchain::PresentMode;
use swapchain::Surface;
use swapchain::SurfaceTransform;
use sync::Semaphore;
use sync::SharingMode;

use check_errors;
use Error;
use OomError;
use Success;
use SynchronizedVulkanObject;
use VulkanObject;
use VulkanPointers;
use vk;

/// Contains the swapping system and the images that can be shown on a surface.
// TODO: #[derive(Debug)] (waiting on https://github.com/aturon/crossbeam/issues/62)
pub struct Swapchain {
    device: Arc<Device>,
    surface: Arc<Surface>,
    swapchain: vk::SwapchainKHR,

    /// Pool of semaphores from which a semaphore is retreived when acquiring an image.
    ///
    /// We need to use a queue so that we don't use the same semaphore twice in a row. The length
    /// of the queue is strictly superior to the number of images, in case the driver lets us
    /// acquire an image before it is presented.
    semaphores_pool: MsQueue<Arc<Semaphore>>,

    images_semaphores: Mutex<Vec<Option<Arc<Semaphore>>>>,
}

impl Swapchain {
    /// Builds a new swapchain. Allocates images who content can be made visible on a surface.
    ///
    /// See also the `Surface::get_capabilities` function which returns the values that are
    /// supported by the implementation. All the parameters that you pass to `Swapchain::new`
    /// must be supported.
    ///
    /// The `clipped` parameter indicates whether the implementation is allowed to discard
    /// rendering operations that affect regions of the surface which aren't visible. This is
    /// important to take into account if your fragment shader has side-effects or if you want to
    /// read back the content of the image afterwards.
    ///
    /// This function returns the swapchain plus a list of the images that belong to the
    /// swapchain. The order in which the images are returned is important for the
    /// `acquire_next_image` and `present` functions.
    ///
    /// # Panic
    ///
    /// - Panicks if the device and the surface don't belong to the same instance.
    /// - Panicks if `color_attachment` is false in `usage`.
    ///
    #[inline]
    pub fn new<F, S>(device: &Arc<Device>, surface: &Arc<Surface>, num_images: u32, format: F,
                     dimensions: [u32; 2], layers: u32, usage: &ImageUsage, sharing: S,
                     transform: SurfaceTransform, alpha: CompositeAlpha, mode: PresentMode,
                     clipped: bool) -> Result<(Arc<Swapchain>, Vec<Arc<SwapchainImage>>), OomError>
        where F: FormatDesc, S: Into<SharingMode>
    {
        Swapchain::new_inner(device, surface, num_images, format.format(), dimensions, layers,
                             usage, sharing.into(), transform, alpha, mode, clipped)
    }

    // TODO:
    //pub fn recreate() { ... }

    // TODO: images layouts should always be set to "PRESENT", since we have no way to switch the
    //       layout at present time
    fn new_inner(device: &Arc<Device>, surface: &Arc<Surface>, num_images: u32, format: Format,
                 dimensions: [u32; 2], layers: u32, usage: &ImageUsage, sharing: SharingMode,
                 transform: SurfaceTransform, alpha: CompositeAlpha, mode: PresentMode,
                 clipped: bool) -> Result<(Arc<Swapchain>, Vec<Arc<SwapchainImage>>), OomError>
    {
        // FIXME: check that the parameters are supported

        // FIXME: check that the device and the surface belong to the same instance
        let vk = device.pointers();
        assert!(device.loaded_extensions().khr_swapchain);     // TODO: return error instead

        assert!(usage.color_attachment);
        let usage = usage.to_usage_bits();

        let sharing = sharing.into();

        let swapchain = unsafe {
            let (sh_mode, sh_count, sh_indices) = match sharing {
                SharingMode::Exclusive(id) => (vk::SHARING_MODE_EXCLUSIVE, 0, ptr::null()),
                SharingMode::Concurrent(ref ids) => (vk::SHARING_MODE_CONCURRENT, ids.len() as u32,
                                                     ids.as_ptr()),
            };

            let infos = vk::SwapchainCreateInfoKHR {
                sType: vk::STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
                pNext: ptr::null(),
                flags: 0,   // reserved
                surface: surface.internal_object(),
                minImageCount: num_images,
                imageFormat: format as u32,
                imageColorSpace: vk::COLORSPACE_SRGB_NONLINEAR_KHR,     // only available value
                imageExtent: vk::Extent2D { width: dimensions[0], height: dimensions[1] },
                imageArrayLayers: layers,
                imageUsage: usage,
                imageSharingMode: sh_mode,
                queueFamilyIndexCount: sh_count,
                pQueueFamilyIndices: sh_indices,
                preTransform: transform as u32,
                compositeAlpha: alpha as u32,
                presentMode: mode as u32,
                clipped: if clipped { vk::TRUE } else { vk::FALSE },
                oldSwapchain: 0,      // TODO:
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateSwapchainKHR(device.internal_object(), &infos,
                                                    ptr::null(), &mut output)));
            output
        };

        let swapchain = Arc::new(Swapchain {
            device: device.clone(),
            surface: surface.clone(),
            swapchain: swapchain,
            semaphores_pool: MsQueue::new(),
            images_semaphores: Mutex::new(Vec::new()),
        });

        let images = unsafe {
            let mut num = 0;
            try!(check_errors(vk.GetSwapchainImagesKHR(device.internal_object(),
                                                       swapchain.swapchain, &mut num,
                                                       ptr::null_mut())));

            let mut images = Vec::with_capacity(num as usize);
            try!(check_errors(vk.GetSwapchainImagesKHR(device.internal_object(),
                                                       swapchain.swapchain, &mut num,
                                                       images.as_mut_ptr())));
            images.set_len(num as usize);
            images
        };

        let images = images.into_iter().enumerate().map(|(id, image)| unsafe {
            let unsafe_image = UnsafeImage::from_raw(device, image, usage, format,
                                                     Dimensions::Dim2d { width: dimensions[0], height: dimensions[1] }, 1, 1);
            SwapchainImage::from_raw(unsafe_image, format, &swapchain, id as u32).unwrap()     // TODO: propagate error
        }).collect::<Vec<_>>();

        {
            let mut semaphores = swapchain.images_semaphores.lock().unwrap();
            for _ in 0 .. images.len() {
                semaphores.push(None);
            }
        }

        for _ in 0 .. images.len() + 1 {
            swapchain.semaphores_pool.push(try!(Semaphore::new(device)));
        }

        Ok((swapchain, images))
    }

    /// Tries to take ownership of an image in order to draw on it.
    ///
    /// The function returns the index of the image in the array of images that was returned
    /// when creating the swapchain.
    ///
    /// If you try to draw on an image without acquiring it first, the execution will block. (TODO
    /// behavior may change).
    pub fn acquire_next_image(&self, timeout: Duration) -> Result<usize, AcquireError> {
        let vk = self.device.pointers();

        unsafe {
            let semaphore = self.semaphores_pool.try_pop().expect("Failed to obtain a semaphore \
                                                                   from the swapchain semaphores \
                                                                   pool");

            let timeout_ns = timeout.as_secs().saturating_mul(1_000_000_000)
                                              .saturating_add(timeout.subsec_nanos() as u64);

            let mut out = mem::uninitialized();
            let r = try!(check_errors(vk.AcquireNextImageKHR(self.device.internal_object(),
                                                             self.swapchain, timeout_ns,
                                                             semaphore.internal_object(), 0,     // TODO: timeout
                                                             &mut out)));

            let id = match r {
                Success::Success => out as usize,
                Success::Suboptimal => out as usize,        // TODO: give that info to the user
                Success::NotReady => return Err(AcquireError::Timeout),
                Success::Timeout => return Err(AcquireError::Timeout),
                s => panic!("unexpected success value: {:?}", s)
            };

            let mut images_semaphores = self.images_semaphores.lock().unwrap();
            images_semaphores[id] = Some(semaphore);

            Ok(id)
        }
    }

    /// Presents an image on the screen.
    ///
    /// The parameter is the same index as what `acquire_next_image` returned. The image must
    /// have been acquired first.
    ///
    /// The actual behavior depends on the present mode that you passed when creating the
    /// swapchain.
    pub fn present(&self, queue: &Arc<Queue>, index: usize) -> Result<(), OomError> {     // FIXME: wrong error
        let vk = self.device.pointers();

        let wait_semaphore = {
            let mut images_semaphores = self.images_semaphores.lock().unwrap();
            images_semaphores[index].take().expect("Trying to present an image that was \
                                                    not acquired")
        };

        // FIXME: the semaphore may be destroyed ; need to return it

        unsafe {
            let mut result = mem::uninitialized();

            let queue = queue.internal_object_guard();
            let index = index as u32;

            let infos = vk::PresentInfoKHR {
                sType: vk::STRUCTURE_TYPE_PRESENT_INFO_KHR,
                pNext: ptr::null(),
                waitSemaphoreCount: 1,
                pWaitSemaphores: &wait_semaphore.internal_object(),
                swapchainCount: 1,
                pSwapchains: &self.swapchain,
                pImageIndices: &index,
                pResults: &mut result,
            };

            try!(check_errors(vk.QueuePresentKHR(*queue, &infos)));
            //try!(check_errors(result));       // TODO: AMD driver doesn't seem to write the result
        }

        self.semaphores_pool.push(wait_semaphore);
        Ok(())
    }

    /*/// Returns the semaphore that is going to be signalled when the image is going to be ready
    /// to be drawn upon.
    ///
    /// Returns `None` if the image was not acquired first, or was already presented.
    // TODO: racy, as someone could present the image before using the semaphore
    #[inline]
    pub fn image_semaphore(&self, id: u32) -> Option<Arc<Semaphore>> {
        let semaphores = self.images_semaphores.lock().unwrap();
        semaphores[id as usize].as_ref().map(|s| s.clone())
    }*/
    // TODO: the design of this functions depends on https://github.com/KhronosGroup/Vulkan-Docs/issues/155
    #[inline]
    pub fn image_semaphore(&self, id: u32, semaphore: Arc<Semaphore>) -> Option<Arc<Semaphore>> {
        let mut semaphores = self.images_semaphores.lock().unwrap();
        mem::replace(&mut semaphores[id as usize], Some(semaphore))
    }
}

impl Drop for Swapchain {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroySwapchainKHR(self.device.internal_object(), self.swapchain, ptr::null());
        }
    }
}

/// Error that can happen when calling `acquire_next_image`.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum AcquireError {
    /// Not enough memory.
    OomError(OomError),

    /// The connection to the device has been lost.
    DeviceLost,

    /// The timeout of the function has been reached before an image was available.
    Timeout,

    /// The surface is no longer accessible and must be recreated.
    SurfaceLost,

    /// The surface has changed in a way that makes the swapchain unusable. You must query the
    /// surface's new properties and recreate a new swapchain if you want to continue drawing.
    OutOfDate,
}

impl error::Error for AcquireError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            AcquireError::OomError(_) => "not enough memory",
            AcquireError::DeviceLost => "the connection to the device has been lost",
            AcquireError::Timeout => "no image is available for acquiring yet",
            AcquireError::SurfaceLost => "the surface of this swapchain is no longer valid",
            AcquireError::OutOfDate => "the swapchain needs to be recreated",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            AcquireError::OomError(ref err) => Some(err),
            _ => None
        }
    }
}

impl fmt::Display for AcquireError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

impl From<Error> for AcquireError {
    #[inline]
    fn from(err: Error) -> AcquireError {
        match err {
            err @ Error::OutOfHostMemory => AcquireError::OomError(OomError::from(err)),
            err @ Error::OutOfDeviceMemory => AcquireError::OomError(OomError::from(err)),
            Error::DeviceLost => AcquireError::DeviceLost,
            Error::SurfaceLost => AcquireError::SurfaceLost,
            Error::OutOfDate => AcquireError::OutOfDate,
            _ => panic!("unexpected error: {:?}", err)
        }
    }
}
<|endoftext|>