mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 15:28:04 +08:00
* Extract CEF rendered UI into a separate process and crate * Review * Review * Review * Review * Remove necessary workarounds * Block on frame copy ack * Crop and resample frames correctly * Skip blank frames * Fix deps * Fix fmt * Fix clippy warning * Review * Fix todo
156 lines
4.5 KiB
Rust
156 lines
4.5 KiB
Rust
use ipc_channel::ipc::{IpcSender, IpcSharedMemory};
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
#[cfg(feature = "accelerated_paint")]
|
|
use super::plane;
|
|
use super::sequence::{FrameSequenceClaim, SequenceState};
|
|
use crate::consts::{FRAME_SEGMENT_GRANULARITY, FRAME_SEGMENT_POOL_SIZE};
|
|
use crate::remote::messages::EventMessage;
|
|
|
|
#[derive(Clone)]
|
|
pub(crate) struct FrameStreamer(Arc<StreamerInner>);
|
|
|
|
struct StreamerInner {
|
|
events: Arc<Mutex<IpcSender<EventMessage>>>,
|
|
sequence: Arc<SequenceState>,
|
|
#[cfg(feature = "accelerated_paint")]
|
|
plane: Option<plane::PlaneSender>,
|
|
staged: Mutex<Staged>,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct Staged {
|
|
segments: Vec<IpcSharedMemory>,
|
|
pending_adverts: Vec<(u32, IpcSharedMemory)>,
|
|
buffer: Option<StagedBuffer>,
|
|
#[cfg(feature = "accelerated_paint")]
|
|
accelerated: Option<(FrameSequenceClaim, plane::StagedFrame)>,
|
|
}
|
|
|
|
struct StagedBuffer {
|
|
claim: FrameSequenceClaim,
|
|
segment: u32,
|
|
width: u32,
|
|
height: u32,
|
|
}
|
|
|
|
impl FrameStreamer {
|
|
pub(crate) fn new(events: Arc<Mutex<IpcSender<EventMessage>>>, sequence: Arc<SequenceState>, #[cfg(feature = "accelerated_paint")] plane: Option<plane::PlaneSender>) -> Self {
|
|
Self(Arc::new(StreamerInner {
|
|
events,
|
|
sequence,
|
|
#[cfg(feature = "accelerated_paint")]
|
|
plane,
|
|
staged: Mutex::new(Staged::default()),
|
|
}))
|
|
}
|
|
|
|
pub(crate) fn stage_buffer(&self, buffer: &[u8], width: u32, height: u32) {
|
|
debug_assert_eq!(buffer.len(), width as usize * height as usize * 4);
|
|
if buffer.is_empty() {
|
|
return;
|
|
}
|
|
|
|
let Some(claim) = self.0.sequence.claim() else {
|
|
return;
|
|
};
|
|
let segment = (claim.seq() % FRAME_SEGMENT_POOL_SIZE) as u32;
|
|
|
|
let Ok(mut staged) = self.0.staged.lock() else {
|
|
tracing::error!("Failed to lock the frame staging state");
|
|
return;
|
|
};
|
|
let staged = &mut *staged;
|
|
if staged.segments.len() < FRAME_SEGMENT_POOL_SIZE as usize {
|
|
staged.segments.resize_with(FRAME_SEGMENT_POOL_SIZE as usize, || IpcSharedMemory::from_bytes(&[]));
|
|
}
|
|
|
|
let backing = &mut staged.segments[segment as usize];
|
|
if backing.len() < buffer.len() {
|
|
let capacity = buffer.len().next_multiple_of(FRAME_SEGMENT_GRANULARITY);
|
|
*backing = IpcSharedMemory::from_byte(0, capacity);
|
|
staged.pending_adverts.push((segment, backing.clone()));
|
|
}
|
|
|
|
unsafe { backing.deref_mut()[..buffer.len()].copy_from_slice(buffer) };
|
|
|
|
#[cfg(target_os = "macos")]
|
|
if !staged.pending_adverts.iter().any(|(index, _)| *index == segment) {
|
|
staged.pending_adverts.push((segment, backing.clone()));
|
|
}
|
|
|
|
staged.buffer = Some(StagedBuffer { claim, segment, width, height });
|
|
}
|
|
|
|
#[cfg(feature = "accelerated_paint")]
|
|
pub(crate) fn stage_texture(&self, info: &cef::AcceleratedPaintInfo) {
|
|
let Some(plane) = &self.0.plane else {
|
|
tracing::error!("Accelerated paint delivered without a frame plane");
|
|
return;
|
|
};
|
|
|
|
let Some(claim) = self.0.sequence.claim() else {
|
|
return;
|
|
};
|
|
let Some(frame) = plane.stage(info) else {
|
|
return;
|
|
};
|
|
let Ok(mut staged) = self.0.staged.lock() else {
|
|
tracing::error!("Failed to lock the frame staging state");
|
|
return;
|
|
};
|
|
staged.accelerated = Some((claim, frame));
|
|
}
|
|
|
|
pub(crate) fn publish(&self) {
|
|
let Ok(mut staged) = self.0.staged.lock() else {
|
|
tracing::error!("Failed to lock the frame staging state");
|
|
return;
|
|
};
|
|
let adverts = std::mem::take(&mut staged.pending_adverts);
|
|
let software = staged.buffer.take();
|
|
#[cfg(feature = "accelerated_paint")]
|
|
let accelerated = staged.accelerated.take();
|
|
drop(staged);
|
|
|
|
#[cfg(feature = "accelerated_paint")]
|
|
if let Some((claim, frame)) = accelerated
|
|
&& let Some(plane) = &self.0.plane
|
|
{
|
|
match plane.send(claim.seq(), frame) {
|
|
Ok(()) => {
|
|
if !claim.wait_for_ack() {
|
|
tracing::warn!("Accelerated frame {} was not acked", claim.seq());
|
|
}
|
|
claim.commit();
|
|
}
|
|
Err(e) => tracing::debug!("Failed to send accelerated frame to main process: {e}"),
|
|
}
|
|
}
|
|
|
|
if adverts.is_empty() && software.is_none() {
|
|
return;
|
|
}
|
|
let Ok(sender) = self.0.events.lock() else {
|
|
tracing::error!("Failed to lock host message sender");
|
|
return;
|
|
};
|
|
for (index, shm) in adverts {
|
|
if let Err(e) = sender.send(EventMessage::AdvertiseFrameSegment { index, shm }) {
|
|
tracing::debug!("Failed to send frame segment to main process: {e}");
|
|
}
|
|
}
|
|
if let Some(StagedBuffer { claim, segment, width, height }) = software {
|
|
match sender.send(EventMessage::SoftwareFrame {
|
|
seq: claim.seq(),
|
|
segment,
|
|
width,
|
|
height,
|
|
}) {
|
|
Ok(()) => claim.commit(),
|
|
Err(e) => tracing::debug!("Failed to send frame to main process: {e}"),
|
|
}
|
|
}
|
|
}
|
|
}
|