use std::cell::{Cell, RefCell, Ref};
use std::collections::{HashMap, BTreeSet};
use ocl::{Event, EventList};
pub struct RwCmdIdxs {
writers: Vec<usize>,
readers: Vec<usize>,
}
impl RwCmdIdxs {
fn new() -> RwCmdIdxs {
RwCmdIdxs { writers: Vec::new(), readers: Vec::new() }
}
}
#[allow(dead_code)]
pub struct KernelArgBuffer {
arg_idx: usize, buffer_id: usize,
}
impl KernelArgBuffer {
pub fn new(arg_idx: usize, buffer_id: usize) -> KernelArgBuffer {
KernelArgBuffer { arg_idx: arg_idx, buffer_id: buffer_id }
}
}
pub enum CommandDetails {
Fill { target: usize },
Write { target: usize },
Read { source: usize },
Copy { source: usize, target: usize },
Kernel { id: usize, sources: Vec<KernelArgBuffer>, targets: Vec<KernelArgBuffer> },
}
impl CommandDetails {
pub fn sources(&self) -> Vec<usize> {
match *self {
CommandDetails::Fill { .. } => vec![],
CommandDetails::Read { source } => vec![source],
CommandDetails::Write { .. } => vec![],
CommandDetails::Copy { source, .. } => vec![source],
CommandDetails::Kernel { ref sources, .. } => {
sources.iter().map(|arg| arg.buffer_id).collect()
},
}
}
pub fn targets(&self) -> Vec<usize> {
match *self {
CommandDetails::Fill { target } => vec![target],
CommandDetails::Read { .. } => vec![],
CommandDetails::Write { target } => vec![target],
CommandDetails::Copy { target, .. } => vec![target],
CommandDetails::Kernel { ref targets, .. } => {
targets.iter().map(|arg| arg.buffer_id).collect()
},
}
}
}
pub struct Command {
details: CommandDetails,
event: RefCell<Option<Event>>,
requisite_events: RefCell<EventList>,
}
impl Command {
pub fn new(details: CommandDetails) -> Command {
Command {
details: details,
event: RefCell::new(None),
requisite_events: RefCell::new(EventList::new()),
}
}
pub fn preceding_writers(&self, cmds: &HashMap<usize, RwCmdIdxs>) -> BTreeSet<usize> {
self.details.sources().iter().flat_map(|cmd_src_block|
cmds.get(cmd_src_block).unwrap().writers.iter().cloned()).collect()
}
pub fn following_readers(&self, cmds: &HashMap<usize, RwCmdIdxs>) -> BTreeSet<usize> {
self.details.targets().iter().flat_map(|cmd_tar_block|
cmds.get(cmd_tar_block).unwrap().readers.iter().cloned()).collect()
}
pub fn details(&self) -> &CommandDetails { &self.details }
}
pub struct CommandGraph {
commands: Vec<Command>,
command_requisites: Vec<Vec<usize>>,
ends: (Vec<usize>, Vec<usize>),
locked: bool,
next_cmd_idx: Cell<usize>,
}
impl CommandGraph {
pub fn new() -> CommandGraph {
CommandGraph {
commands: Vec::new(),
command_requisites: Vec::new(),
ends: (Vec::new(), Vec::new()),
locked: false,
next_cmd_idx: Cell::new(0),
}
}
pub fn add(&mut self, command: Command) -> Result<usize, ()> {
if self.locked { return Err(()); }
self.commands.push(command);
self.command_requisites.push(Vec::new());
Ok(self.commands.len() - 1)
}
fn readers_and_writers_by_buffer(&self) -> HashMap<usize, RwCmdIdxs> {
let mut cmds = HashMap::new();
for (cmd_idx, cmd) in self.commands.iter().enumerate() {
for cmd_src_block in cmd.details.sources().into_iter() {
let rw_cmd_idxs = cmds.entry(cmd_src_block.clone())
.or_insert(RwCmdIdxs::new());
rw_cmd_idxs.readers.push(cmd_idx);
}
for cmd_tar_block in cmd.details.targets().into_iter() {
let rw_cmd_idxs = cmds.entry(cmd_tar_block.clone())
.or_insert(RwCmdIdxs::new());
rw_cmd_idxs.writers.push(cmd_idx);
}
}
cmds
}
pub fn populate_requisites(&mut self) {
let cmds = self.readers_and_writers_by_buffer();
for (cmd_idx, cmd) in self.commands.iter_mut().enumerate() {
assert!(self.command_requisites[cmd_idx].is_empty());
let preceding_writers = cmd.preceding_writers(&cmds);
if preceding_writers.len() == 0 { self.ends.0.push(cmd_idx); }
for &req_cmd_idx in preceding_writers.iter() {
self.command_requisites[cmd_idx].push(req_cmd_idx);
}
let following_readers = cmd.following_readers(&cmds);
if following_readers.len() == 0 { self.ends.1.push(cmd_idx); }
for &req_cmd_idx in following_readers.iter() {
self.command_requisites[cmd_idx].push(req_cmd_idx);
}
self.command_requisites[cmd_idx].shrink_to_fit();
}
self.commands.shrink_to_fit();
self.command_requisites.shrink_to_fit();
self.locked = true;
}
pub fn get_req_events(&self, cmd_idx: usize) -> Result<Ref<EventList>, &'static str> {
if !self.locked { return Err("Call '::populate_requisites' first."); }
if self.next_cmd_idx.get() != cmd_idx { return Err("Command events requested out of order."); }
self.commands.get(cmd_idx).unwrap().requisite_events.borrow_mut().clear();
for &req_idx in self.command_requisites[cmd_idx].iter() {
let event_opt = self.commands[req_idx].event.borrow().clone();
if let Some(event) = event_opt {
self.commands[cmd_idx].requisite_events.borrow_mut().push(event);
}
}
Ok(self.commands[cmd_idx].requisite_events.borrow())
}
pub fn set_cmd_event(&self, cmd_idx: usize, event: Event) -> Result<(), &'static str> {
if !self.locked { return Err("Call '::populate_requisites' first."); }
*self.commands.get(cmd_idx).unwrap().event.borrow_mut() = Some(event);
if (self.next_cmd_idx.get() + 1) == self.commands.len() {
self.next_cmd_idx.set(0);
} else {
self.next_cmd_idx.set(self.next_cmd_idx.get() + 1);
}
Ok(())
}
pub fn commands<'a>(&'a self) -> &'a [Command] {
self.commands.as_slice()
}
pub fn get_finish_events (&self, event_list: &mut EventList) {
assert!(self.next_cmd_idx.get() == 0, "Finish events can only be determined \
for each cycle just after the graph has set its last cmd event.");
for &cmd_idx in self.ends.1.iter() {
let event_opt = self.commands[cmd_idx].event.borrow().clone();
if let Some(event) = event_opt {
event_list.push(event);
}
}
}
}