use drmkit_planes::LayerId;
use crate::release::{Acquisition, ReleaseQueue, ReleaseReason, Released, ScenePendingFlip};
use crate::report::CommitReport;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum KernelResult {
Ok,
NotMaster,
Rejected,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum CommitKind {
Real {
arms_flip: bool,
},
Test,
}
#[derive(Debug)]
#[non_exhaustive]
pub struct FrameOutcome {
pub released: Released,
pub report: CommitReport,
pub suspended: bool,
pub invalidate_allocation: bool,
}
#[derive(Debug, Default)]
pub struct FrameLifecycle {
releases: ReleaseQueue,
pending_flip: ScenePendingFlip,
suspended: bool,
warned_fast_path_reject: bool,
last_commit_seq: u64,
}
impl FrameLifecycle {
#[must_use]
pub const fn new() -> Self {
Self {
releases: ReleaseQueue::new(),
pending_flip: ScenePendingFlip::new(),
suspended: false,
warned_fast_path_reject: false,
last_commit_seq: 0,
}
}
#[must_use]
pub const fn is_suspended(&self) -> bool {
self.suspended
}
#[must_use]
pub const fn has_pending_flip(&self) -> bool {
self.pending_flip.is_armed()
}
#[must_use]
pub fn buffers_in_flight(&self) -> usize {
self.releases.in_flight()
}
pub const fn flip_landed(&mut self) {
self.pending_flip.landed();
}
pub const fn resume(&mut self) {
self.suspended = false;
}
pub fn finalize(
&mut self,
kind: CommitKind,
result: KernelResult,
acquisitions: Vec<Acquisition>,
mut report: CommitReport,
release_fence: Option<&drmkit_sync::SyncFence>,
wants_fence: impl FnMut(LayerId) -> bool,
) -> FrameOutcome {
if let CommitKind::Test = kind {
return FrameOutcome {
released: self.releases.test_committed(acquisitions),
report,
suspended: self.suspended,
invalidate_allocation: false,
};
}
match result {
KernelResult::Ok => {
if matches!(kind, CommitKind::Real { arms_flip: true }) {
self.pending_flip.arm();
}
self.last_commit_seq += 1;
report.commit_seq = self.last_commit_seq;
let released =
self.releases
.commit_succeeded(acquisitions, release_fence, wants_fence);
FrameOutcome {
released,
report,
suspended: self.suspended,
invalidate_allocation: false,
}
}
KernelResult::NotMaster => {
self.suspended = true;
FrameOutcome {
released: self.releases.commit_failed(acquisitions),
report,
suspended: true,
invalidate_allocation: false,
}
}
KernelResult::Rejected => {
let invalidate = report.fb_delta_fast_path;
if invalidate {
if !self.warned_fast_path_reject {
self.warned_fast_path_reject = true;
drmkit_log::log_warn!(
"scene: FB-only fast-path commit rejected by kernel; \
invalidating cached allocation and re-testing next frame"
);
}
report.fb_delta_fast_path = false;
}
FrameOutcome {
released: self.releases.commit_failed(acquisitions),
report,
suspended: self.suspended,
invalidate_allocation: invalidate,
}
}
}
}
#[must_use]
pub const fn has_warned_fast_path_reject(&self) -> bool {
self.warned_fast_path_reject
}
pub fn release_displaced(&mut self, commit_seq: u64) -> Released {
if commit_seq == 0 || commit_seq != self.last_commit_seq {
return Released {
acquisitions: Vec::new(),
reason: ReleaseReason::Retired,
};
}
self.releases.release_displaced()
}
pub fn drain(&mut self) -> Released {
self.releases.drain()
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct AcquireTally {
pub acquired: usize,
pub skipped_no_frame: usize,
}
impl AcquireTally {
pub const fn record(&mut self, acquired: bool) -> bool {
if acquired {
self.acquired += 1;
} else {
self.skipped_no_frame += 1;
}
acquired
}
#[must_use]
pub const fn considered(&self) -> usize {
self.acquired + self.skipped_no_frame
}
}