use crate::core::{font, SpriteData};
use crate::prelude::*;
use crate::backends::backend;
pub const NUM_BUCKETS: usize = 6;
pub const INITIAL_CAPACITY: usize = 512;
static GENERATION: AtomicUsize = AtomicUsize::new(0);
#[derive(Clone)]
pub struct Context (Arc<Mutex<ContextData>>);
unsafe impl Send for Context { }
unsafe impl Sync for Context { }
impl Debug for Context {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Context")
}
}
impl Context {
pub fn new() -> Context {
let context_data = ContextData::new();
Context(Arc::new(Mutex::new(context_data)))
}
pub fn prune(self: &Self) {
self.lock().prune();
}
pub(crate) fn lock<'a>(self: &'a Self) -> MutexGuard<'a, ContextData> {
self.0.lock().unwrap()
}
}
#[derive(Clone)]
pub struct RawFrame {
pub data : Vec<u8>,
pub width : u32,
pub height : u32,
pub channels: u8,
}
struct SpriteBackRef (Weak<SpriteData>);
impl SpriteBackRef {
fn new(data: Weak<SpriteData>) -> Self {
SpriteBackRef(data)
}
fn upgrade(self: &Self) -> Option<Arc<SpriteData>> {
self.0.upgrade()
}
fn range(self: &Self) -> Option<(usize, usize)> {
if let Some(data) = self.upgrade() {
Some((data.texture_id.load(Ordering::Relaxed), data.num_frames as usize * data.components as usize))
} else {
None
}
}
}
pub struct RawFrameArray {
pub dirty : bool,
pub data : backend::Texture2dArray,
pub raw : Vec<RawFrame>,
sprites : Vec<SpriteBackRef>,
}
impl RawFrameArray {
fn new(context: &backend::Context) -> Self {
RawFrameArray {
dirty : false,
data : backend::Texture2dArray::new(context, &Vec::new()),
raw : Vec::new(),
sprites : Vec::new(),
}
}
pub fn store_frames<'a>(self: &mut Self, raw_frames: Vec<RawFrame>) -> u32 {
let texture_id = self.raw.len() as u32;
for frame in raw_frames {
self.raw.push(frame);
}
self.dirty = true;
texture_id
}
pub fn store_sprite(self: &mut Self, sprite_data: Weak<SpriteData>) {
self.sprites.push(SpriteBackRef::new(sprite_data));
}
fn update(self: &mut Self, context: &backend::Context) {
if self.dirty {
self.dirty = false;
self.data = backend::Texture2dArray::new(context, &self.raw);
}
}
fn create_prune_map(self: &Self) -> Option<Vec<(usize, usize)>> {
let mut mapping = self.sprites.iter().filter_map(|sprite| sprite.range()).collect::<Vec<(usize, usize)>>();
mapping.sort_by_key(|a| a.0);
let mut num_items = 0;
for i in 0..mapping.len() {
let items = mapping[i].1;
mapping[i].1 = mapping[i].0 - num_items;
num_items += items;
}
if mapping.len() > 0 { Some(mapping) } else { None }
}
fn prune_raw_textures(self: &mut Self, mapping: &Vec<(usize, usize)>) -> HashMap<usize, usize> {
let new_size = self.raw.len() - mapping.last().unwrap().1;
let mut destination_map = HashMap::new();
for m in 0..mapping.len() {
destination_map.insert(mapping[m].0, mapping[m].0 - mapping[m].1);
let end = if m + 1 < mapping.len() { mapping[m+1].0 } else { new_size -1 };
for i in (mapping[m].0)..end {
let destination_index = i - mapping[m].1;
self.raw.swap(i, destination_index);
}
}
self.raw.truncate(new_size);
destination_map
}
fn prune_sprites<T>(self: &mut Self, mut func: T) where T: FnMut(&Arc<SpriteData>) {
let mut removed = Vec::new();
for (i, sprite) in self.sprites.iter().enumerate() {
if let Some(sprite) = sprite.upgrade() {
func(&sprite);
} else {
removed.push(i);
}
}
for index in removed.iter().rev() {
self.sprites.swap_remove(*index);
}
}
fn prune(self: &mut Self, context: &backend::Context, generation: usize) {
if let Some(mapping) = self.create_prune_map() {
let destination_map = self.prune_raw_textures(&mapping);
self.dirty = true;
self.update(context);
self.prune_sprites(|sprite| {
let texture_id = sprite.texture_id.load(Ordering::Relaxed);
if let Some(new_texture_id) = destination_map.get(&texture_id) {
sprite.texture_id.store(*new_texture_id, Ordering::Relaxed);
}
sprite.generation.store(generation, Ordering::Relaxed);
});
} else {
self.prune_sprites(|sprite| {
sprite.generation.store(generation, Ordering::Relaxed);
})
}
}
}
pub struct ContextData {
pub backend_context : Option<backend::Context>,
pub tex_arrays : Vec<RawFrameArray>,
pub font_cache_dimensions: u32,
pub font_cache : font::FontCache,
pub font_texture : Option<backend::Texture2d>,
generation : usize,
}
impl ContextData {
fn init_backend(self: &mut Self, display: &backend::Display) {
let backend_context = backend::Context::new(display, INITIAL_CAPACITY);
for _ in 0..NUM_BUCKETS {
self.tex_arrays.push(RawFrameArray::new(&backend_context));
}
let data = crate::core::RawFrame {
width : self.font_cache_dimensions,
height : self.font_cache_dimensions,
data : vec![0u8; self.font_cache_dimensions as usize * self.font_cache_dimensions as usize],
channels: 1,
};
let texture = backend::Texture2d::new(&backend_context, self.font_cache_dimensions, self.font_cache_dimensions, crate::core::TextureFormat::U8, Some(data));
self.font_texture = Some(texture);
self.backend_context = Some(backend_context);
}
fn new() -> Self {
let font_cache_dimensions = 512;
ContextData {
backend_context : None,
tex_arrays : Vec::new(),
font_cache : font::FontCache::new(font_cache_dimensions, font_cache_dimensions, 0.01, 0.01),
font_texture : None,
font_cache_dimensions,
generation : Self::create_generation(),
}
}
pub fn has_primary_display(self: &Self) -> bool {
self.backend_context.is_some()
}
pub fn set_primary_display(self: &mut Self, display: &backend::Display) {
self.init_backend(&display);
}
pub fn generation(self: &Self) -> usize {
self.generation
}
pub fn update_font_cache(self: &Self) {
self.font_cache.update(self.font_texture.as_ref().unwrap());
}
pub fn update_tex_array(self: &mut Self) {
for ref mut array in self.tex_arrays.iter_mut() {
array.update(self.backend_context.as_ref().unwrap());
}
}
pub fn store_frames(self: &mut Self, bucket_id: u32, raw_frames: Vec<RawFrame>) -> u32 {
self.tex_arrays[bucket_id as usize].store_frames(raw_frames)
}
pub fn store_sprite(self: &mut Self, bucket_id: u32, sprite_data: Weak<SpriteData>) {
self.tex_arrays[bucket_id as usize].store_sprite(sprite_data);
}
fn prune(self: &mut Self) {
self.generation = Self::create_generation();
for array in self.tex_arrays.iter_mut() {
array.prune(self.backend_context.as_ref().unwrap(), self.generation);
}
}
fn create_generation() -> usize {
GENERATION.fetch_add(1, Ordering::Relaxed) + 1
}
}