carnelian/render/generic/forma/
context.rs1use std::cell::RefCell;
6use std::collections::HashMap;
7use std::io::Read;
8use std::{mem, ptr, u32};
9
10use anyhow::Error;
11use display_utils::PixelFormat;
12use euclid::default::{Rect, Size2D};
13use fidl::endpoints::ClientEnd;
14use fidl_fuchsia_images2::{ColorSpace, PixelFormat as Images2PixelFormat, PixelFormatModifier};
15use fidl_fuchsia_math::SizeU;
16use fidl_fuchsia_sysmem2::{
17 AllocatorBindSharedCollectionRequest, AllocatorMarker, BufferCollectionConstraints,
18 BufferCollectionMarker, BufferCollectionSetConstraintsRequest,
19 BufferCollectionSynchronousProxy, BufferCollectionTokenMarker, BufferMemoryConstraints,
20 BufferUsage, CoherencyDomain, ImageFormatConstraints, CPU_USAGE_WRITE_OFTEN,
21};
22use fuchsia_component::client::connect_to_protocol;
23use fuchsia_framebuffer::sysmem::set_allocator_name;
24use fuchsia_trace::{duration_begin, duration_end};
25use zx::sys;
26
27use crate::drawing::DisplayRotation;
28use crate::render::generic::forma::image::VmoImage;
29use crate::render::generic::forma::{
30 Forma, FormaComposition, FormaImage, FormaPathBuilder, FormaRasterBuilder,
31};
32use crate::render::generic::{Context, CopyRegion, PostCopy, PreClear, PreCopy, RenderExt};
33use crate::ViewAssistantContext;
34
35fn buffer_collection_constraints(width: u32, height: u32) -> BufferCollectionConstraints {
36 let image_format_constraints = ImageFormatConstraints {
37 pixel_format: Some(Images2PixelFormat::B8G8R8A8),
38 pixel_format_modifier: Some(PixelFormatModifier::Linear),
39 color_spaces: Some(vec![ColorSpace::Srgb]),
40 min_size: Some(SizeU { width, height }),
41 min_bytes_per_row: Some(width * mem::size_of::<u32>() as u32),
42 ..Default::default()
43 };
44
45 BufferCollectionConstraints {
46 usage: Some(BufferUsage { cpu: Some(CPU_USAGE_WRITE_OFTEN), ..Default::default() }),
47 min_buffer_count: Some(1),
48 buffer_memory_constraints: Some(BufferMemoryConstraints {
49 min_size_bytes: Some(width as u64 * height as u64 * mem::size_of::<u32>() as u64),
50 ram_domain_supported: Some(true),
51 cpu_domain_supported: Some(true),
52 inaccessible_domain_supported: Some(false),
53 ..Default::default()
54 }),
55 image_format_constraints: Some(vec![image_format_constraints]),
56 ..Default::default()
57 }
58}
59
60fn copy_region_to_image(
61 src_ptr: *mut u8,
62 src_width: usize,
63 src_height: usize,
64 src_bytes_per_row: usize,
65 dst_ptr: *mut u8,
66 dst_len: usize,
67 dst_bytes_per_row: usize,
68 dst_coherency_domain: CoherencyDomain,
69 region: &CopyRegion,
70) {
71 let (mut y, dy) = if region.dst_offset.y < region.src_offset.y {
72 (0, 1)
74 } else {
75 (region.extent.height as i32 - 1, -1)
77 };
78
79 let mut extent = region.extent.height;
80 while extent > 0 {
81 let src_y = (region.src_offset.y + y as u32) % src_height as u32;
82 let dst_y = region.dst_offset.y + y as u32;
83
84 let mut src_x = region.src_offset.x as usize;
85 let mut dst_x = region.dst_offset.x as usize;
86 let mut width = region.extent.width as usize;
87
88 while width > 0 {
89 let columns = width.min(src_width - src_x);
90 let src_offset = src_y as usize * src_bytes_per_row + src_x * 4;
91 let dst_offset = dst_y as usize * dst_bytes_per_row + dst_x * 4;
92
93 assert!((dst_offset + (columns * 4)) <= dst_len);
94 let src = (src_ptr as usize).checked_add(src_offset).unwrap() as *mut u8;
95 let dst = (dst_ptr as usize).checked_add(dst_offset).unwrap() as *mut u8;
96 unsafe {
97 ptr::copy(src, dst, (columns * 4) as usize);
98 if dst_coherency_domain == CoherencyDomain::Ram {
99 sys::zx_cache_flush(dst, columns * 4, sys::ZX_CACHE_FLUSH_DATA);
100 }
101 }
102
103 width -= columns;
104 dst_x += columns;
105 src_x = 0;
106 }
107
108 y += dy;
109 extent -= 1;
110 }
111}
112
113#[derive(Debug)]
114pub struct FormaContext {
115 renderer: forma::CpuRenderer,
116 buffer_collection: Option<BufferCollectionSynchronousProxy>,
117 size: Size2D<u32>,
118 display_rotation: DisplayRotation,
119 images: Vec<RefCell<VmoImage>>,
120 index_map: HashMap<u32, usize>,
121 composition_id: usize,
122}
123
124impl FormaContext {
125 pub(crate) fn new(
126 token: ClientEnd<BufferCollectionTokenMarker>,
127 size: Size2D<u32>,
128 display_rotation: DisplayRotation,
129 ) -> Self {
130 let sysmem = connect_to_protocol::<AllocatorMarker>().expect("failed to connect to sysmem");
131 set_allocator_name(&sysmem).unwrap_or_else(|e| eprintln!("set_allocator_name: {:?}", e));
132 let (collection_client, collection_request) =
133 fidl::endpoints::create_endpoints::<BufferCollectionMarker>();
134 sysmem
135 .bind_shared_collection(AllocatorBindSharedCollectionRequest {
136 token: Some(token),
137 buffer_collection_request: Some(collection_request),
138 ..Default::default()
139 })
140 .expect("failed to bind shared collection");
141 let buffer_collection = collection_client.into_sync_proxy();
142 let constraints = buffer_collection_constraints(size.width, size.height);
143 buffer_collection
144 .set_constraints(BufferCollectionSetConstraintsRequest {
145 constraints: Some(constraints),
146 ..Default::default()
147 })
148 .expect("failed to set constraints on sysmem buffer");
149
150 Self {
151 renderer: forma::CpuRenderer::new(),
152 buffer_collection: Some(buffer_collection),
153 size,
154 display_rotation,
155 images: vec![],
156 index_map: HashMap::new(),
157 composition_id: 0,
158 }
159 }
160
161 pub(crate) fn without_token(size: Size2D<u32>, display_rotation: DisplayRotation) -> Self {
162 Self {
163 renderer: forma::CpuRenderer::new(),
164 buffer_collection: None,
165 size,
166 display_rotation,
167 images: vec![],
168 index_map: HashMap::new(),
169 composition_id: 0,
170 }
171 }
172}
173
174impl Context<Forma> for FormaContext {
175 fn pixel_format(&self) -> PixelFormat {
176 PixelFormat::R8G8B8A8
177 }
178
179 fn path_builder(&self) -> Option<FormaPathBuilder> {
180 Some(FormaPathBuilder::new())
181 }
182
183 fn raster_builder(&self) -> Option<FormaRasterBuilder> {
184 Some(FormaRasterBuilder::new())
185 }
186
187 fn new_image(&mut self, size: Size2D<u32>) -> FormaImage {
188 let image = FormaImage(self.images.len());
189 self.images.push(RefCell::new(VmoImage::new(size.width, size.height)));
190
191 image
192 }
193
194 fn new_image_from_png<R: Read>(
195 &mut self,
196 reader: &mut png::Reader<R>,
197 ) -> Result<FormaImage, Error> {
198 let image = FormaImage(self.images.len());
199 self.images.push(RefCell::new(VmoImage::from_png(reader)?));
200
201 Ok(image)
202 }
203
204 fn get_image(&mut self, image_index: u32) -> FormaImage {
205 let buffer_collection = self.buffer_collection.as_mut().expect("buffer_collection");
206 let images = &mut self.images;
207 let width = self.size.width;
208 let height = self.size.height;
209
210 let index = self.index_map.entry(image_index).or_insert_with(|| {
211 let index = images.len();
212 images.push(RefCell::new(VmoImage::from_buffer_collection(
213 buffer_collection,
214 width,
215 height,
216 image_index,
217 )));
218
219 index
220 });
221
222 FormaImage(*index)
223 }
224
225 fn get_current_image(&mut self, context: &ViewAssistantContext) -> FormaImage {
226 self.get_image(context.image_index)
227 }
228
229 fn render_with_clip(
230 &mut self,
231 composition: &mut FormaComposition,
232 clip: Rect<u32>,
233 image: FormaImage,
234 ext: &RenderExt<Forma>,
235 ) {
236 let image_id = image;
237 let mut image = self
238 .images
239 .get(image.0 as usize)
240 .unwrap_or_else(|| panic!("invalid image {:?}", image_id))
241 .borrow_mut();
242 let width = self.size.width as usize;
243 let height = self.size.height as usize;
244
245 if let Some(PreClear { color }) = ext.pre_clear {
246 image.clear([color.b, color.g, color.r, color.a]);
247 }
248
249 if let Some(PreCopy { image: src_image_id, copy_region }) = ext.pre_copy {
250 let dst_coherency_domain = image.coherency_domain();
251 let dst_slice = image.as_mut_slice();
252 let dst_ptr = dst_slice.as_mut_ptr();
253 let dst_len = dst_slice.len();
254 let dst_bytes_per_row = image.bytes_per_row();
255
256 let src_image = self
257 .images
258 .get(src_image_id.0 as usize)
259 .unwrap_or_else(|| panic!("invalid PreCopy image {:?}", src_image_id))
260 .try_borrow_mut();
261
262 let (src_ptr, src_bytes_per_row) = match src_image {
263 Ok(mut image) => (image.as_mut_slice().as_mut_ptr(), image.bytes_per_row()),
264 Err(_) => (image.as_mut_slice().as_mut_ptr(), image.bytes_per_row()),
265 };
266
267 copy_region_to_image(
268 src_ptr,
269 width,
270 height,
271 src_bytes_per_row,
272 dst_ptr,
273 dst_len,
274 dst_bytes_per_row,
275 dst_coherency_domain,
276 ©_region,
277 );
278 }
279
280 let transform = self.display_rotation.transform(&self.size.to_f32());
281 composition.set_cached_display_transform(transform);
282
283 if composition.id.is_none() {
284 let next_id = self.composition_id + 1;
285 composition.id = Some(mem::replace(&mut self.composition_id, next_id));
286 }
287
288 if image.buffer_layer_cache.as_ref().map(|&(id, _)| id) != composition.id {
289 image.buffer_layer_cache = self
290 .renderer
291 .create_buffer_layer_cache()
292 .map(|cache| (composition.id.unwrap(), cache));
293 }
294
295 duration_begin!(c"gfx", c"render::Context<Forma>::render_composition");
296 self.renderer.render(
297 &mut composition.composition,
298 &mut image.as_buffer(),
299 forma::BGRA,
300 forma::Color::from(&composition.background_color),
301 Some(forma::Rect::new(
302 clip.origin.x as usize..(clip.origin.x + clip.size.width) as usize,
303 clip.origin.y as usize..(clip.origin.y + clip.size.height) as usize,
304 )),
305 );
306 duration_end!(c"gfx", c"render::Context<Forma>::render_composition");
307
308 if let Some(PostCopy { image: dst_image_id, copy_region, .. }) = ext.post_copy {
310 let mut dst_image = self
311 .images
312 .get(dst_image_id.0 as usize)
313 .unwrap_or_else(|| panic!("invalid PostCopy image {:?}", dst_image_id))
314 .try_borrow_mut()
315 .unwrap_or_else(|e| {
316 panic!("image {:?} as already used for rendering: {:?}", dst_image_id, e)
317 });
318
319 let src_bytes_per_row = image.bytes_per_row();
320 let dst_bytes_per_row = dst_image.bytes_per_row();
321 let src_slice = image.as_mut_slice();
322 let dst_coherency_domain = dst_image.coherency_domain();
323 let dst_slice = dst_image.as_mut_slice();
324
325 copy_region_to_image(
326 src_slice.as_mut_ptr(),
327 width,
328 height,
329 src_bytes_per_row,
330 dst_slice.as_mut_ptr(),
331 dst_slice.len(),
332 dst_bytes_per_row,
333 dst_coherency_domain,
334 ©_region,
335 );
336 }
337 }
338}