// Copyright 2025 Google // SPDX-License-Identifier: MIT use std::collections::VecDeque; use std::mem::size_of; use mesa3d_util::AsBorrowedDescriptor; use mesa3d_util::MesaError; use mesa3d_util::MesaHandle; use mesa3d_util::MesaResult; use mesa3d_util::OwnedDescriptor; use mesa3d_util::Reader; use mesa3d_util::Tube; use mesa3d_util::Writer; use mesa3d_util::MESA_HANDLE_TYPE_SIGNAL_EVENT_FD; use zerocopy::FromBytes; use zerocopy::Immutable; use zerocopy::IntoBytes; use crate::protocols::kumquat_gpu_protocol::*; const MAX_COMMAND_SIZE: usize = 4096; pub struct KumquatStream { stream: Tube, write_buffer: [u8; MAX_COMMAND_SIZE], read_buffer: [u8; MAX_COMMAND_SIZE], } impl KumquatStream { pub fn new(stream: Tube) -> KumquatStream { KumquatStream { stream, write_buffer: [0; MAX_COMMAND_SIZE], read_buffer: [0; MAX_COMMAND_SIZE], } } pub fn write( &mut self, encode: KumquatGpuProtocolWrite, ) -> MesaResult<()> { let mut writer = Writer::new(&mut self.write_buffer); let array: &[OwnedDescriptor] = match encode { KumquatGpuProtocolWrite::Cmd(cmd) => { writer.write_obj(cmd)?; &[] } KumquatGpuProtocolWrite::CmdWithHandle(cmd, handle) => { writer.write_obj(cmd)?; &[handle.os_handle] } KumquatGpuProtocolWrite::CmdWithData(cmd, data) => { writer.write_obj(cmd)?; writer.write_all(&data)?; &[] } }; let bytes_written = writer.bytes_written(); self.stream .send(&self.write_buffer[0..bytes_written], array)?; Ok(()) } pub fn read(&mut self) -> MesaResult> { let mut vec: Vec = Vec::new(); let (bytes_read, descriptor_vec) = self.stream.receive(&mut self.read_buffer)?; let mut descriptors: VecDeque = descriptor_vec.into(); if bytes_read == 0 { vec.push(KumquatGpuProtocol::OkNoData); return Ok(vec); } let mut reader = Reader::new(&self.read_buffer[0..bytes_read]); while reader.available_bytes() != 0 { let hdr = reader.peek_obj::()?; let protocol = match hdr.type_ { KUMQUAT_GPU_PROTOCOL_GET_NUM_CAPSETS => { reader.consume(size_of::()); KumquatGpuProtocol::GetNumCapsets } KUMQUAT_GPU_PROTOCOL_GET_CAPSET_INFO => { reader.consume(size_of::()); KumquatGpuProtocol::GetCapsetInfo(hdr.payload) } KUMQUAT_GPU_PROTOCOL_GET_CAPSET => { KumquatGpuProtocol::GetCapset(reader.read_obj()?) } KUMQUAT_GPU_PROTOCOL_CTX_CREATE => { KumquatGpuProtocol::CtxCreate(reader.read_obj()?) } KUMQUAT_GPU_PROTOCOL_CTX_DESTROY => { reader.consume(size_of::()); KumquatGpuProtocol::CtxDestroy(hdr.payload) } KUMQUAT_GPU_PROTOCOL_CTX_ATTACH_RESOURCE => { KumquatGpuProtocol::CtxAttachResource(reader.read_obj()?) } KUMQUAT_GPU_PROTOCOL_CTX_DETACH_RESOURCE => { KumquatGpuProtocol::CtxDetachResource(reader.read_obj()?) } KUMQUAT_GPU_PROTOCOL_RESOURCE_CREATE_3D => { KumquatGpuProtocol::ResourceCreate3d(reader.read_obj()?) } KUMQUAT_GPU_PROTOCOL_TRANSFER_TO_HOST_3D => { let os_handle = descriptors.pop_front().ok_or(MesaError::Unsupported)?; let resp: kumquat_gpu_protocol_transfer_host_3d = reader.read_obj()?; let handle = MesaHandle { os_handle, handle_type: MESA_HANDLE_TYPE_SIGNAL_EVENT_FD, }; KumquatGpuProtocol::TransferToHost3d(resp, handle) } KUMQUAT_GPU_PROTOCOL_TRANSFER_FROM_HOST_3D => { let os_handle = descriptors.pop_front().ok_or(MesaError::Unsupported)?; let resp: kumquat_gpu_protocol_transfer_host_3d = reader.read_obj()?; let handle = MesaHandle { os_handle, handle_type: MESA_HANDLE_TYPE_SIGNAL_EVENT_FD, }; KumquatGpuProtocol::TransferFromHost3d(resp, handle) } KUMQUAT_GPU_PROTOCOL_SUBMIT_3D => { let cmd: kumquat_gpu_protocol_cmd_submit = reader.read_obj()?; if reader.available_bytes() < cmd.size.try_into()? { // Large command buffers should handled via shared memory. return Err(MesaError::Unsupported); } else if reader.available_bytes() != 0 { let num_in_fences = cmd.num_in_fences as usize; let cmd_size = cmd.size as usize; let mut cmd_buf = vec![0; cmd_size]; let mut fence_ids: Vec = Vec::with_capacity(num_in_fences); for _ in 0..num_in_fences { match reader.read_obj::() { Ok(fence_id) => { fence_ids.push(fence_id); } Err(_) => return Err(MesaError::Unsupported), } } reader.read_exact(&mut cmd_buf[..])?; KumquatGpuProtocol::CmdSubmit3d(cmd, cmd_buf, fence_ids) } else { KumquatGpuProtocol::CmdSubmit3d(cmd, Vec::new(), Vec::new()) } } KUMQUAT_GPU_PROTOCOL_RESOURCE_CREATE_BLOB => { KumquatGpuProtocol::ResourceCreateBlob(reader.read_obj()?) } KUMQUAT_GPU_PROTOCOL_SNAPSHOT_SAVE => { reader.consume(size_of::()); KumquatGpuProtocol::SnapshotSave } KUMQUAT_GPU_PROTOCOL_SNAPSHOT_RESTORE => { reader.consume(size_of::()); KumquatGpuProtocol::SnapshotRestore } KUMQUAT_GPU_PROTOCOL_RESP_NUM_CAPSETS => { reader.consume(size_of::()); KumquatGpuProtocol::RespNumCapsets(hdr.payload) } KUMQUAT_GPU_PROTOCOL_RESP_CAPSET_INFO => { KumquatGpuProtocol::RespCapsetInfo(reader.read_obj()?) } KUMQUAT_GPU_PROTOCOL_RESP_CAPSET => { let len: usize = hdr.payload.try_into()?; reader.consume(size_of::()); let mut capset: Vec = vec![0; len]; reader.read_exact(&mut capset)?; KumquatGpuProtocol::RespCapset(capset) } KUMQUAT_GPU_PROTOCOL_RESP_CONTEXT_CREATE => { reader.consume(size_of::()); KumquatGpuProtocol::RespContextCreate(hdr.payload) } KUMQUAT_GPU_PROTOCOL_RESP_RESOURCE_CREATE => { let os_handle = descriptors.pop_front().ok_or(MesaError::Unsupported)?; let resp: kumquat_gpu_protocol_resp_resource_create = reader.read_obj()?; let handle = MesaHandle { os_handle, handle_type: resp.handle_type, }; KumquatGpuProtocol::RespResourceCreate(resp, handle) } KUMQUAT_GPU_PROTOCOL_RESP_CMD_SUBMIT_3D => { let os_handle = descriptors.pop_front().ok_or(MesaError::Unsupported)?; let resp: kumquat_gpu_protocol_resp_cmd_submit_3d = reader.read_obj()?; let handle = MesaHandle { os_handle, handle_type: resp.handle_type, }; KumquatGpuProtocol::RespCmdSubmit3d(resp.fence_id, handle) } KUMQUAT_GPU_PROTOCOL_RESP_OK_SNAPSHOT => { reader.consume(size_of::()); KumquatGpuProtocol::RespOkSnapshot } _ => { return Err(MesaError::Unsupported); } }; vec.push(protocol); } Ok(vec) } pub fn as_borrowed_descriptor(&self) -> &OwnedDescriptor { self.stream.as_borrowed_descriptor() } }