mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Define data types for ID-based resource handling (#4168)
* Add resource lib * rename ResourceSource to ResourceInput * Rename ResourceInput -> DataSource and improve API * Review --------- Co-authored-by: Dennis Kobert <dennis@kobert.dev>
This commit is contained in:
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[features]
|
||||
default = ["serde"]
|
||||
serde = ["dep:serde", "core-types/serde", "vector-types/serde", "text-nodes/serde"]
|
||||
serde = ["dep:serde", "core-types/serde", "vector-types/serde", "text-nodes/serde", "graphene-resource/serde"]
|
||||
wasm = ["dep:web-sys"]
|
||||
wgpu = ["dep:wgpu"]
|
||||
|
||||
@@ -18,6 +18,7 @@ dyn-any = { workspace = true }
|
||||
core-types = { workspace = true }
|
||||
vector-types = { workspace = true }
|
||||
text-nodes = { workspace = true }
|
||||
graphene-resource = { workspace = true }
|
||||
|
||||
# Workspace dependencies
|
||||
blake3 = { workspace = true }
|
||||
|
||||
@@ -9,9 +9,10 @@ use std::time::Duration;
|
||||
use text_nodes::FontCache;
|
||||
use vector_types::vector::style::RenderMode;
|
||||
|
||||
pub mod resource;
|
||||
pub use core_types::resource::Resource;
|
||||
pub use resource::{LoadResource, ResourceFuture, ResourceHash, ResourceStorage};
|
||||
pub mod resource {
|
||||
pub use core_types::resource::*;
|
||||
pub use graphene_resource::*;
|
||||
}
|
||||
|
||||
#[cfg(feature = "wgpu")]
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, DynAny)]
|
||||
@@ -49,7 +50,7 @@ pub trait ApplicationIo {
|
||||
fn gpu_executor(&self) -> Option<&Self::Executor> {
|
||||
None
|
||||
}
|
||||
fn load_resource(&self, hash: ResourceHash) -> resource::ResourceFuture;
|
||||
fn load_resource(&self, hash: resource::ResourceHash) -> resource::ResourceFuture;
|
||||
}
|
||||
|
||||
impl<T: ApplicationIo> ApplicationIo for &T {
|
||||
@@ -59,7 +60,7 @@ impl<T: ApplicationIo> ApplicationIo for &T {
|
||||
(**self).gpu_executor()
|
||||
}
|
||||
|
||||
fn load_resource(&self, hash: ResourceHash) -> resource::ResourceFuture {
|
||||
fn load_resource(&self, hash: resource::ResourceHash) -> resource::ResourceFuture {
|
||||
(**self).load_resource(hash)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ mod uuid_generation {
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify), tsify(large_number_types_as_bigints))]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, graphene_hash::CacheHash, PartialOrd, Ord, DynAny)]
|
||||
#[cfg_attr(feature = "wasm", derive(tsify::Tsify), tsify(large_number_types_as_bigints))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct NodeId(pub u64);
|
||||
|
||||
|
||||
23
node-graph/libraries/resources/Cargo.toml
Normal file
23
node-graph/libraries/resources/Cargo.toml
Normal file
@@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "graphene-resource"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
description = "graphene resource interface"
|
||||
authors = ["Graphite Authors <contact@graphite.art>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[features]
|
||||
default = ["serde"]
|
||||
serde = ["dep:serde", "url/serde"]
|
||||
|
||||
[dependencies]
|
||||
# Local dependencies
|
||||
dyn-any = { workspace = true }
|
||||
core-types = { workspace = true }
|
||||
|
||||
# Workspace dependencies
|
||||
blake3 = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
||||
# Optional workspace dependencies
|
||||
serde = { workspace = true, optional = true }
|
||||
@@ -1,6 +1,7 @@
|
||||
use core_types::CacheHash;
|
||||
use core_types::resource::Resource;
|
||||
use core_types::{CacheHash, graphene_hash};
|
||||
use dyn_any::DynAny;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::hash::Hash;
|
||||
@@ -18,6 +19,23 @@ pub trait ResourceStorage: LoadResource {
|
||||
fn garbage_collect(&mut self, used: &[ResourceHash]);
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, graphene_hash::CacheHash, PartialOrd, Ord, DynAny)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct ResourceId(pub u64);
|
||||
|
||||
impl ResourceId {
|
||||
pub fn new() -> Self {
|
||||
Self(core_types::uuid::generate_uuid())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ResourceId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Blake3 content hash of a resource, represented as 32 bytes
|
||||
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord, DynAny)]
|
||||
pub struct ResourceHash([u8; 32]);
|
||||
@@ -166,3 +184,86 @@ impl CacheHash for ResourceHash {
|
||||
core::hash::Hash::hash(self, state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ResourceInfo {
|
||||
pub id: ResourceId,
|
||||
pub hash: Option<ResourceHash>,
|
||||
pub inputs: DataSources,
|
||||
}
|
||||
|
||||
pub type DataSources = Box<[DataSource]>;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum DataSource {
|
||||
Embedded,
|
||||
Url(url::Url),
|
||||
Font { family: String, style: Option<String> },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct ResourceRegistry {
|
||||
hashes: HashMap<ResourceId, ResourceHash>,
|
||||
sources: HashMap<ResourceId, Vec<DataSource>>,
|
||||
}
|
||||
|
||||
impl ResourceRegistry {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.hashes.is_empty() && self.sources.is_empty()
|
||||
}
|
||||
|
||||
pub fn contains(&self, id: &ResourceId) -> bool {
|
||||
self.hashes.contains_key(id) || self.sources.contains_key(id)
|
||||
}
|
||||
|
||||
pub fn ids(&self) -> impl Iterator<Item = ResourceId> + '_ {
|
||||
self.hashes.keys().chain(self.sources.keys().filter(|id| !self.hashes.contains_key(id))).copied()
|
||||
}
|
||||
|
||||
pub fn info(&self, id: &ResourceId) -> Option<ResourceInfo> {
|
||||
self.contains(id).then(|| ResourceInfo {
|
||||
id: *id,
|
||||
hash: self.hashes.get(id).copied(),
|
||||
inputs: self.sources.get(id).cloned().unwrap_or_default().into_boxed_slice(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn push_source_back(&mut self, id: &ResourceId, source: DataSource) {
|
||||
self.sources.entry(*id).or_default().push(source);
|
||||
}
|
||||
|
||||
pub fn push_source_front(&mut self, id: &ResourceId, source: DataSource) {
|
||||
self.sources.entry(*id).or_default().insert(0, source);
|
||||
}
|
||||
|
||||
pub fn delete(&mut self, id: &ResourceId) -> Option<ResourceInfo> {
|
||||
let hash = self.hashes.remove(id);
|
||||
let sources = self.sources.remove(id);
|
||||
if hash.is_none() && sources.is_none() {
|
||||
return None;
|
||||
}
|
||||
Some(ResourceInfo {
|
||||
id: *id,
|
||||
hash,
|
||||
inputs: sources.unwrap_or_default().into_boxed_slice(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn resolve(&mut self, id: &ResourceId, hash: ResourceHash) -> Option<ResourceHash> {
|
||||
self.hashes.insert(*id, hash)
|
||||
}
|
||||
|
||||
pub fn hash(&self, id: &ResourceId) -> Option<ResourceHash> {
|
||||
self.hashes.get(id).copied()
|
||||
}
|
||||
|
||||
pub fn unresolved(&self) -> impl Iterator<Item = (ResourceId, &[DataSource])> + '_ {
|
||||
self.sources.iter().filter(|(id, _)| !self.hashes.contains_key(id)).map(|(id, sources)| (*id, sources.as_slice()))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user