engine/resources/
file_reader.rs1use platform::{FileHandle, FileReadTask, Platform};
6
7use crate::{
8 allocators::LinearAllocator,
9 collections::{Queue, RingAllocationMetadata, RingBuffer, RingSlice},
10};
11
12#[derive(Debug)]
14pub enum FileReadError {
15 NoReadsQueued,
17 WouldBlock,
19 Platform,
21}
22
23struct LoadRequest {
24 first_byte: u64,
25 size: usize,
26}
27
28struct LoadTask {
29 file_read_task: FileReadTask,
30 read_buffer_metadata: RingAllocationMetadata,
31}
32
33pub struct FileReader {
38 staging_buffer: RingBuffer<'static, u8>,
39 to_load_queue: Queue<'static, LoadRequest>,
40 in_flight_queue: Queue<'static, LoadTask>,
41 file: FileHandle,
42}
43
44impl FileReader {
45 pub fn new(
48 arena: &'static LinearAllocator,
49 file: FileHandle,
50 staging_buffer_size: usize,
51 queue_capacity: usize,
52 ) -> Option<FileReader> {
53 Some(FileReader {
54 staging_buffer: RingBuffer::new(arena, staging_buffer_size)?,
55 to_load_queue: Queue::new(arena, queue_capacity)?,
56 in_flight_queue: Queue::new(arena, queue_capacity)?,
57 file,
58 })
59 }
60
61 pub fn staging_buffer_size(&self) -> usize {
64 self.staging_buffer.capacity()
65 }
66
67 #[must_use]
73 pub fn push_read(&mut self, first_byte: u64, size: usize) -> bool {
74 if size > self.staging_buffer_size() {
75 return false;
76 }
77
78 self.to_load_queue
79 .push_back(LoadRequest { first_byte, size })
80 .is_ok()
81 }
82
83 pub fn dispatch_reads(&mut self, platform: &dyn Platform) {
85 profiling::function_scope!();
86 while let Some(LoadRequest { size, .. }) = self.to_load_queue.peek_front() {
87 profiling::scope!("dispatch");
88 let Some(staging_slice) = self.staging_buffer.allocate(*size) else {
89 break;
90 };
91 let (buffer, read_buffer_metadata) = staging_slice.into_parts();
92
93 let LoadRequest {
94 first_byte,
95 size: _,
96 } = self.to_load_queue.pop_front().unwrap();
97
98 let file_read_task = platform.begin_file_read(self.file, first_byte, buffer);
99
100 self.in_flight_queue
101 .push_back(LoadTask {
102 file_read_task,
103 read_buffer_metadata,
104 })
105 .ok()
106 .unwrap();
107 }
108 }
109
110 pub fn pop_read<T, F>(
116 &mut self,
117 platform: &dyn Platform,
118 blocking: bool,
119 use_result: F,
120 ) -> Result<T, FileReadError>
121 where
122 F: FnOnce(&mut [u8]) -> T,
123 {
124 profiling::function_scope!();
125 if blocking && self.in_flight_queue.is_empty() {
126 self.dispatch_reads(platform);
127 }
128
129 let Some(LoadTask { file_read_task, .. }) = self.in_flight_queue.peek_front() else {
130 return Err(FileReadError::NoReadsQueued);
131 };
132
133 if !blocking && !platform.is_file_read_finished(file_read_task) {
134 return Err(FileReadError::WouldBlock);
135 }
136
137 let LoadTask {
138 file_read_task,
139 read_buffer_metadata,
140 } = self.in_flight_queue.pop_front().unwrap();
141
142 let (mut buffer, read_success) = match platform.finish_file_read(file_read_task) {
143 Ok(buffer) => (buffer, true),
144 Err(buffer) => (buffer, false),
145 };
146
147 let result = if read_success {
148 Ok(use_result(&mut buffer))
149 } else {
150 Err(FileReadError::Platform)
151 };
152
153 let slice = unsafe { RingSlice::from_parts(buffer, read_buffer_metadata) };
156 self.staging_buffer.free(slice).unwrap();
157
158 result
159 }
160}