memkit-co

Crates.iomemkit-co
lib.rsmemkit-co
version0.1.0-alpha.1
created_at2025-12-27 00:39:58.746014+00
updated_at2025-12-27 00:39:58.746014+00
descriptionCPU-GPU memory coordination for the memkit ecosystem
homepage
repositoryhttps://github.com/YelenaTor/memkit
max_upload_size
id2006495
size68,577
YoruXIII (YelenaTor)

documentation

https://docs.rs/memkit-co

README

memkit-co

CPU-GPU memory coordination for the memkit ecosystem.

Version: 0.1.0-alpha.1

Crates.io Documentation License: MPL-2.0

Overview

memkit-co is the coordination layer that bridges CPU memory management (memkit) with GPU memory management (memkit-gpu). It provides unified APIs for:

  • Frame-based coordination — Allocate on CPU arena, auto-upload to GPU
  • Staging pool management — Efficient staging buffer reuse
  • Transfer scheduling — Batch and optimize CPU→GPU transfers
  • Fence tracking — Track GPU work completion
  • Pipeline synchronization — Double/triple buffering patterns

When to Use memkit-co

Use Case Crate
CPU-only allocation memkit
GPU-only memory memkit-gpu
CPU ↔ GPU coordination memkit-co

Quick Start

use memkit_co::{MkCoordinator, CoordinatorConfig};
use memkit_gpu::DummyBackend;

// Create coordinator with default config
let config = CoordinatorConfig::default();
let mut coordinator = MkCoordinator::new(DummyBackend::new(), config);

// Allocate data on CPU, upload to GPU in one call
let vertices: &[f32] = &[0.0, 0.5, 1.0, -0.5, -0.5, 1.0, 0.5, -0.5, 1.0];
let gpu_buffer = coordinator.upload_slice(vertices)?;

// Frame-based workflow
coordinator.begin_frame();
{
    // All allocations this frame go to the frame arena
    let frame_data = coordinator.frame_alloc::<[f32; 4]>();
    *frame_data = [1.0, 0.0, 0.0, 1.0];
    
    // Stage for upload (batched at end of frame)
    coordinator.stage_for_upload(frame_data);
}
coordinator.end_frame()?; // Commits all staged transfers

Architecture

┌─────────────────────────────────────────────────────────────┐
│                      memkit-co                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
│  │   Frame     │  │  Staging    │  │    Transfer         │  │
│  │ Coordinator │  │   Pool      │  │    Scheduler        │  │
│  └──────┬──────┘  └──────┬──────┘  └──────────┬──────────┘  │
└─────────┼────────────────┼─────────────────────┼────────────┘
          │                │                     │
          ▼                ▼                     ▼
┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
│     memkit      │  │   memkit-gpu    │  │   memkit-gpu    │
│  (CPU arenas)   │  │ (staging bufs)  │  │ (device bufs)   │
└─────────────────┘  └─────────────────┘  └─────────────────┘

Features

Frame Coordinator

Manages per-frame CPU allocations that automatically sync to GPU:

// Frame-scoped allocation with automatic GPU upload
coordinator.begin_frame();

// Allocate transform matrices for this frame
let transforms = coordinator.frame_alloc_slice::<Mat4>(entity_count);
for (i, transform) in transforms.iter_mut().enumerate() {
    *transform = compute_transform(i);
}

// Mark for GPU upload
let gpu_transforms = coordinator.stage_transforms(transforms)?;

// End frame commits all pending transfers
coordinator.end_frame()?;

Staging Pool

Reuses staging buffers to minimize allocation overhead:

// Acquire staging buffer from pool
let staging = coordinator.acquire_staging(size)?;

// Write data
staging.write_data(&my_data);

// Transfer and release back to pool
coordinator.transfer_and_release(staging, &device_buffer)?;

Transfer Scheduler

Batches transfers for optimal GPU utilization:

// Queue multiple transfers
coordinator.queue_transfer(src1, dst1);
coordinator.queue_transfer(src2, dst2);
coordinator.queue_transfer(src3, dst3);

// Submit all as single batch
coordinator.flush_transfers()?;

Double/Triple Buffering

Built-in support for frame-pipelined uploads:

let coordinator = MkCoordinator::with_buffering(
    backend,
    BufferingMode::Triple, // Double or Triple
);

// Coordinator automatically rotates buffers each frame
for frame in 0..1000 {
    coordinator.begin_frame();
    // ... frame work ...
    coordinator.end_frame()?;
}

Integration with Other Crates

Standalone memkit-gpu

If you're using memkit-gpu without coordination needs:

use memkit_gpu::{MkGpu, DummyBackend};

let gpu = MkGpu::new(DummyBackend::new());
// Direct GPU operations, no coordinator needed

Standalone memkit

If you're using memkit without GPU:

use memkit::MkFastArena;

let arena = MkFastArena::new(1024 * 1024);
// CPU-only allocations

Full Stack with Coordinator

When you need coordinated CPU-GPU memory:

use memkit_co::MkCoordinator;

let coordinator = MkCoordinator::new(backend, config);
// Coordinator internally uses both memkit and memkit-gpu

Configuration

use memkit_co::{CoordinatorConfig, BufferingMode};

let config = CoordinatorConfig {
    // Frame arena size for per-frame CPU allocations
    frame_arena_size: 16 * 1024 * 1024, // 16 MB
    
    // Staging buffer pool settings
    staging_pool_size: 32,
    staging_buffer_size: 64 * 1024, // 64 KB each
    
    // Buffering mode
    buffering: BufferingMode::Double,
    
    // Max pending transfers before auto-flush
    max_pending_transfers: 64,
};

let coordinator = MkCoordinator::new(backend, config);

Performance Tips

  1. Batch transfers — Queue multiple transfers, flush once per frame
  2. Reuse staging buffers — Use the staging pool instead of creating new buffers
  3. Frame arenas — Use frame allocations for temporary per-frame data
  4. Double buffering — Allows GPU to work on previous frame while CPU prepares next

License

Licensed under the Mozilla Public License 2.0.

Commit count: 0

cargo fmt