Skip to main content

Agent

Trait Agent 

Source
pub trait Agent: Send + Sync {
    // Required methods
    fn id(&self) -> AgentId;
    fn negotiate(&mut self, request: NegotiationRequest) -> NegotiationResponse;
    fn apply_budget(&mut self, budget: ResourceBudget);
    fn report_status(&self) -> AgentStatus;
    fn execute(&mut self, context: &mut EngineContext<'_>);
    fn as_any(&self) -> &dyn Any;
    fn as_any_mut(&mut self) -> &mut dyn Any;

    // Provided methods
    fn on_initialize(&mut self, _context: &mut EngineContext<'_>) { ... }
    fn execution_timing(&self) -> ExecutionTiming { ... }
    fn access(&self) -> AgentAccess { ... }
    fn deck_writes(&self) -> Vec<TypeId> { ... }
}
Expand description

The foundational interface for an Intelligent Subsystem Agent (ISA).

Each major subsystem (Rendering, Physics, etc.) implements this trait to participate in the engine’s dynamic resource negotiation (GORNA).

§Lifecycle

  1. on_initialize(ctx) — called once after registration. The agent caches required services and initializes its lanes.
  2. execute(ctx) — called every frame. The agent selects the appropriate lanes based on the current GORNA budget and dispatches their Lane::execute().
  3. negotiate(request) / apply_budget(budget) — called by the GORNA arbitrator on the DCC background thread when the system re-evaluates strategy.
  4. report_status() — polled by GORNA for health monitoring.

An agent must contain no business logic beyond lane selection, budget negotiation, and lane dispatch. All real work belongs in [Lane] implementations.

§Examples

A skeleton agent. Real agents cache their lanes in on_initialize and select one per frame in execute based on the budget applied via apply_budget. (Marked ignore because a compiling impl needs the GORNA request/response types and an EngineContext from the running engine.)

use khora_core::agent::{Agent, ExecutionTiming};
use khora_core::control::gorna::{
    AgentId, AgentStatus, NegotiationRequest, NegotiationResponse, ResourceBudget,
};
use khora_core::EngineContext;
use std::any::Any;

#[derive(Default)]
struct MyAgent;

impl Agent for MyAgent {
    fn id(&self) -> AgentId { AgentId::Renderer }

    fn negotiate(&mut self, request: NegotiationRequest) -> NegotiationResponse {
        // Propose a strategy that fits `request`'s constraints.
        NegotiationResponse::default()
    }

    fn apply_budget(&mut self, _budget: ResourceBudget) {
        // Adjust quality / LOD to stay within the allocated budget.
    }

    fn report_status(&self) -> AgentStatus { AgentStatus::default() }

    fn execute(&mut self, _ctx: &mut EngineContext<'_>) {
        // Pick a lane for this frame and dispatch `Lane::execute`.
    }

    fn as_any(&self) -> &dyn Any { self }
    fn as_any_mut(&mut self) -> &mut dyn Any { self }
}

Required Methods§

Source

fn id(&self) -> AgentId

Returns the unique identifier for this agent.

Source

fn negotiate(&mut self, request: NegotiationRequest) -> NegotiationResponse

Negotiates with the DCC to determine the best execution strategy given the current global resource constraints and priorities.

Source

fn apply_budget(&mut self, budget: ResourceBudget)

Applies a resource budget issued by the DCC. The agent must adjust its internal logic (e.g., LOD, quality settings) to stay within the allocated limits.

Source

fn report_status(&self) -> AgentStatus

Reports the current status and health of the agent.

Source

fn execute(&mut self, context: &mut EngineContext<'_>)

Called every frame by the engine loop.

The agent selects the appropriate lanes based on the current GORNA strategy, builds a LaneContext, and dispatches Lane::execute() for each lane that should run.

Source

fn as_any(&self) -> &dyn Any

Allows downcasting to concrete agent types.

Source

fn as_any_mut(&mut self) -> &mut dyn Any

Allows mutable downcasting to concrete agent types.

Provided Methods§

Source

fn on_initialize(&mut self, _context: &mut EngineContext<'_>)

Called once after the agent is registered with the DCC.

The agent should cache services from context.services, initialize its lane registry, and prepare any persistent state. Default implementation is a no-op.

Source

fn execution_timing(&self) -> ExecutionTiming

Declares WHEN and HOW this agent should execute within the frame pipeline.

The Scheduler uses this information to filter agents by phase, order them by priority, and skip optional agents under budget pressure.

Default implementation returns a timing that allows execution in all phases with Important priority.

Source

fn access(&self) -> AgentAccess

Declares this agent’s data-access footprint during execute, so the scheduler can decide whether it is safe to run concurrently with other agents in the same phase.

Defaults to AgentAccess::Exclusive — the safe fallback: the agent is assumed to need exclusive &mut World, so it runs serially. An agent overrides this to AgentAccess::Isolated only if execute provably touches no World and no shared mutable engine resource (it reads the LaneBus and writes solely its own OutputDeck).

Source

fn deck_writes(&self) -> Vec<TypeId>

Declares the OutputDeck slot types this agent writes during execute, by TypeId.

The scheduler uses this at wave-formation time: two concurrency-eligible agents grouped into the same wave write into private deck shards that are folded back together afterwards, so they must write disjoint slot types. Declaring the written slots lets the scheduler catch a collision when the wave is built — naming the offending agents — instead of only discovering it defensively during the shard merge.

Defaults to empty: an agent that writes no deck slot (or only runs Exclusive, i.e. never in a concurrent wave) need not override it.

Implementors§