diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/gem_init.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/gem_init.rs new file mode 100644 index 0000000000..e58aff2deb --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/gem_init.rs @@ -0,0 +1,263 @@ +use std::collections::{BTreeMap, VecDeque}; +use std::sync::Arc; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::time::Instant; + +use crate::driver::{DriverError, Result}; +use super::gem_object::GemObjectManager; +use super::gem_region::MemoryRegion; +use super::gem_vma::VmaManager; + +pub struct GemInitManager { + pub object_manager: Arc, + pub vma_manager: Arc, + pub system_region: MemoryRegion, + pub lmem_region: Option, + pub stolen_region: Option, + pub initialized: bool, +} + +impl GemInitManager { + pub fn new(max_system_mb: u64, _max_vram_mb: u64) -> Self { + let system_bytes = max_system_mb * 1024 * 1024; + let system_bytes_cap = system_bytes.min(256 * 1024 * 1024); + + let om = GemObjectManager::new(system_bytes_cap, 0); + + Self { + object_manager: Arc::new(om), + vma_manager: Arc::new(VmaManager::new()), + system_region: MemoryRegion::new_system(system_bytes_cap), + lmem_region: None, + stolen_region: None, + initialized: false, + } + } + + pub fn init(&mut self) -> Result<()> { + self.initialized = true; + Ok(()) + } + + pub fn set_stolen_region(&mut self, base: u64, size: u64) { + if size > 0 { + self.stolen_region = Some(MemoryRegion::new_stolen(size, base)); + } + } +} + +pub struct BusyTracker { + active: BTreeMap, + history: VecDeque, + max_history: usize, +} + +#[derive(Clone, Copy, Debug)] +pub struct EngineBusy { + pub engine_mask: u32, + pub last_submit: Option, + pub submit_count: u64, +} + +#[derive(Clone, Debug)] +pub struct BusyEvent { + pub gem_handle: u32, + pub timestamp: Instant, + pub engine: u32, +} + +impl BusyTracker { + pub fn new() -> Self { + Self { active: BTreeMap::new(), history: VecDeque::new(), max_history: 256 } + } + + pub fn mark_busy(&mut self, handle: u32, engine: u32) { + let entry = self.active.entry(handle).or_insert(EngineBusy { + engine_mask: 0, last_submit: None, submit_count: 0, + }); + entry.engine_mask |= 1 << engine; + entry.last_submit = Some(Instant::now()); + entry.submit_count += 1; + + self.history.push_back(BusyEvent { gem_handle: handle, timestamp: Instant::now(), engine }); + if self.history.len() > self.max_history { self.history.pop_front(); } + } + + pub fn mark_idle(&mut self, handle: u32) { + self.active.remove(&handle); + } + + pub fn is_busy(&self, handle: u32) -> bool { + self.active.contains_key(&handle) + } + + pub fn busy_engine_mask(&self, handle: u32) -> u32 { + self.active.get(&handle).map_or(0, |e| e.engine_mask) + } + + pub fn all_busy(&self) -> Vec { + self.active.keys().copied().collect() + } + + pub fn active_count(&self) -> usize { self.active.len() } +} + +pub struct ShrinkerWithEviction { + max_bytes: u64, + current_bytes: u64, + eviction_candidates: BTreeMap, + evict_count: u32, + shrink_count: u32, +} + +#[derive(Clone, Debug)] +pub struct EvictionEntry { + pub gem_handle: u32, + pub size: u64, + pub last_used: Option, + pub pinned: bool, + pub priority: u32, +} + +impl ShrinkerWithEviction { + pub fn new(max_bytes: u64) -> Self { + Self { + max_bytes, current_bytes: 0, + eviction_candidates: BTreeMap::new(), + evict_count: 0, shrink_count: 0, + } + } + + pub fn register(&mut self, handle: u32, size: u64, pinned: bool) { + self.eviction_candidates.insert(handle, EvictionEntry { + gem_handle: handle, size, last_used: Some(Instant::now()), + pinned, priority: 0, + }); + self.current_bytes += size; + } + + pub fn unregister(&mut self, handle: u32) { + if let Some(entry) = self.eviction_candidates.remove(&handle) { + self.current_bytes = self.current_bytes.saturating_sub(entry.size); + } + } + + pub fn try_shrink(&mut self, required: u64) -> Result { + if self.current_bytes + required <= self.max_bytes { return Ok(0); } + let target = self.current_bytes + required - self.max_bytes; + let mut freed: u64 = 0; + + let mut handles: Vec<(u32, Option)> = self.eviction_candidates.iter() + .filter(|(_, e)| !e.pinned) + .map(|(h, e)| (*h, e.last_used)) + .collect(); + handles.sort_by_key(|(_, last_used)| *last_used); + + for (handle, _) in &handles { + if freed >= target { break; } + if let Some(entry) = self.eviction_candidates.remove(handle) { + freed += entry.size; + self.current_bytes -= entry.size; + self.evict_count += 1; + } + } + + self.shrink_count += 1; + Ok(freed) + } + + pub fn eviction_count(&self) -> u32 { self.evict_count } + pub fn shrink_count(&self) -> u32 { self.shrink_count } + pub fn current_bytes(&self) -> u64 { self.current_bytes } +} + +pub struct VmaResourceManager { + resources: BTreeMap, + next_id: AtomicU64, +} + +#[derive(Clone, Debug)] +pub struct VmaResource { + pub id: u64, + pub vma_id: u64, + pub gem_handle: u32, + pub gtt_offset: u64, + pub size: u64, + pub released: bool, +} + +impl VmaResourceManager { + pub fn new() -> Self { + Self { resources: BTreeMap::new(), next_id: AtomicU64::new(1) } + } + + pub fn track(&mut self, vma_id: u64, gem_handle: u32, gtt_offset: u64, size: u64) -> u64 { + let id = self.next_id.fetch_add(1, Ordering::SeqCst); + self.resources.insert(id, VmaResource { + id, vma_id, gem_handle, gtt_offset, size, released: false, + }); + id + } + + pub fn release(&mut self, id: u64) { + if let Some(r) = self.resources.get_mut(&id) { r.released = true; } + self.resources.remove(&id); + } + + pub fn pending_releases(&self) -> usize { + self.resources.values().filter(|r| !r.released).count() + } +} + +pub struct RingThrottle { + ring_submissions: BTreeMap>, + ring_limits: BTreeMap, + global_limit: u32, +} + +#[derive(Clone, Debug)] +pub struct Submission { + pub gem_handle: u32, + pub fence: u64, + pub timestamp: Instant, +} + +impl RingThrottle { + pub fn new(global_limit: u32) -> Self { + Self { + ring_submissions: BTreeMap::new(), + ring_limits: BTreeMap::new(), + global_limit, + } + } + + pub fn set_ring_limit(&mut self, ring: u32, limit: u32) { + self.ring_limits.insert(ring, limit); + } + + pub fn submit(&mut self, ring: u32, handle: u32, fence: u64) -> Result<()> { + let total_pending: usize = self.ring_submissions.values().map(|v| v.len()).sum(); + if total_pending >= self.global_limit as usize { + return Err(DriverError::Buffer("global submission limit reached".into())); + } + + let ring_limit = self.ring_limits.get(&ring).copied().unwrap_or(256); + let ring_pending = self.ring_submissions.entry(ring).or_default(); + if ring_pending.len() >= ring_limit as usize { + return Err(DriverError::Buffer(format!("ring {} submission limit", ring))); + } + + ring_pending.push_back(Submission { gem_handle: handle, fence, timestamp: Instant::now() }); + Ok(()) + } + + pub fn retire(&mut self, ring: u32, fence: u64) { + if let Some(pending) = self.ring_submissions.get_mut(&ring) { + pending.retain(|s| s.fence > fence); + } + } + + pub fn pending_count(&self) -> usize { + self.ring_submissions.values().map(|v| v.len()).sum() + } +} diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/mod.rs index d8cbc5d854..1cbf408fc4 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/mod.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/mod.rs @@ -4,6 +4,7 @@ pub mod gem_create; pub mod gem_dmabuf; pub mod gem_domain; pub mod gem_execbuffer; +pub mod gem_init; pub mod gem_ioctl; pub mod gem_lmem; pub mod gem_mmap; @@ -21,7 +22,8 @@ pub use gem_create::{CreateManager, CreateParams}; pub use gem_dmabuf::{DmaBufExport, DmaBufImport, DmaBufManager}; pub use gem_domain::{BusyManager, DomainManager, DomainState, GpuDomain, ThrottleManager}; pub use gem_execbuffer::{ExecObject, ExecbufferManager, ExecbufferSubmission, RelocationEntry}; -pub use gem_ioctl::{FrontbufferState, FrontbufferTracker, UserptrEntry, UserptrManager, WaitManager}; +pub use gem_init::{BusyTracker, GemInitManager, RingThrottle, ShrinkerWithEviction, VmaResourceManager}; +pub use gem_ioctl::{FrontbufferState, FrontbufferTracker, UserptrManager, WaitManager}; pub use gem_lmem::LmemAllocator; pub use gem_mmap::{MmapEntry, MmapManager, MmapType}; pub use gem_object::{CacheLevel, GemHandle, GemObject, GemObjectManager, MemoryRegionType};