lowenware / dotrix

A 3D engine with ECS and Vulkan renderer for Rust developers
https://dotrix.rs
MIT License
290 stars 11 forks source link

Feat/arbitrary assets #168

Closed QuantumEntangledAndy closed 2 years ago

QuantumEntangledAndy commented 2 years ago

This should allow for arbitrary assets to be stored and accessed in the asset manager.

p.s. Not happy with the solution for deleted assets, have some ideas for tackling them differently (by storing and polling a kind of are you still alive struct). But will tackle that in a different PR

Lowentwickler commented 2 years ago

Also I am thinking about UUIDs for Id object... Maybe later. Probably should take into account all cons and pros. Otherwise, the implementations looks clear and straightforward.

Lowentwickler commented 2 years ago

Also I am thinking about UUIDs for Id object... Maybe later. Probably should take into account all cons and pros. Otherwise, the implementations looks clear and straightforward.

Lowentwickler commented 2 years ago

I've fixed the CI, if you rebase, it should pass.

QuantumEntangledAndy commented 2 years ago

Build seems to be passing now.

I have another PR that separates shader and pipelines so we don't need to keep track of cleaning them up. We might be able to remove the deleted assets code entirely.


Also I considered something like this to track specific assets as deleted: By adding something like this to an Assets struct and handling it all at the Asset level and not the Assets level.

struct Deletable {
  deleted: Arc<AtomicBool>,
}
impl Deletable {
  pub fn new() -> Self {
    Self {
        deleted: Arc::new(AtomicBool::new(false))
    }
  }
  pub fn get_tracker(&self) -> DeleteTracker {
    DeleteTracker::new(self)
  }
}

struct DeleteTracker {
    tracker: Weak<AtomicBool>>,
}

impl DeleteTracker {
    pub fn new(deletable: &Deletable) -> Self {
        Self {
           tracker: Arc::downgrade(deletable.deleted),
        }
     }
    pub fn is_deleteted(&self) -> bool {
        match self.tracker.upgrade() {
            Some(atomic_bool) => atomic_bool.load(Ordering::Relaxed),
            None => false,
        }
    }
}

// Using like this
struct SomeAsset {
    other_things: Things
    deletable: Deletable,
}

impl SomeAsset {
    pub fn delete_tracker(&self) -> DeleteTracker {
       self.deletable.get_tracker()
    }
}

fn create_asset(assets: &mut Asset) {
    let delete_tracked_asset = SomeAsset::new();
    let delete_tracker = delete_tracked_asset.delete_tracker();
    // Store asset away
    let asset_id = assets.store(delete_tracked_asset);
    // Reports as alive
    asset_eq!(delete_tracker.is_deleteted(), false);
    // Remove asset
    assets.remove(asset_id);
    // Reports as dead
    asset_eq!(delete_tracker.is_deleteted(), true);
}

The advantage of doing it this way is that it can be attached only to assets that need them. It also means it can be mostly user land managed. The disadvantage is perhaps the Arc, Weak cost. As I am not sure about their performance, however, since it is only on needed assets and likely rarely called it might not be significant.

Lowentwickler commented 2 years ago

hm, nice... could be... I would only adjust names of wrappers and traits. :)