1use crate::rendering::composition::server::{ServerCompositor, ServerObjectId};
10use crate::rendering::composition::server::IServerObject;
11use crate::media::imaging::{BitmapBlendingMode, BitmapInterpolationMode};
12use crate::media::{
13 AlignmentX, AlignmentY, BaselinePixelAlignment, BoxShadow, Color, EdgeMode, GradientSpreadMethod, PenLineCap,
14 PenLineJoin, RenderOptions, Stretch, TextHintingMode, TextOptions, TextRenderingMode, TileMode,
15};
16use crate::numerics::{Matrix3x2, Matrix4x4, Quaternion, Vector2, Vector3, Vector4};
17use crate::rendering::composition::{
18 CompositionBlendMode, CompositionGradientExtendMode, CompositionStretch, CompositionTileMode,
19 CompositionTransparencyLevel,
20};
21use crate::rendering::{LayoutPassTiming, RendererDebugOverlays};
22use crate::{
23 CornerRadius, Matrix, Point, Rect, RelativePoint, RelativeRect, RelativeScalar, RelativeUnit, RoundedRect, Size,
24 Vector, Vector3D,
25};
26use std::any::Any;
27use std::collections::VecDeque;
28use std::rc::Rc;
29use std::time::Duration;
30
31pub type ServerJob = Box<dyn FnOnce(&ServerCompositor) + Send>;
33
34pub type ServerObjectJob = Box<dyn FnOnce(&ServerCompositor, Option<Rc<dyn IServerObject>>) + Send>;
39
40pub type ServerObjectFactory = Box<dyn FnOnce(&Rc<ServerCompositor>, ServerObjectId) -> Rc<dyn IServerObject> + Send>;
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46pub enum BatchMarker {
47 CreateStart,
50 RenderThreadDisposeStart,
54 RenderThreadJobsStart,
55 RenderThreadJobsEnd,
56 RenderThreadPostTargetJobsStart,
57 RenderThreadPostTargetJobsEnd,
58 ObjectEnd,
61}
62
63pub enum BatchObject {
72 Null,
73 ServerObject(ServerObjectId),
75 Marker(BatchMarker),
76 Job(ServerJob),
77 ObjectJob(ServerObjectJob),
79 Create(ServerObjectFactory),
80 Value(Box<dyn Any + Send>),
83}
84
85impl BatchObject {
86 pub fn value<T: Any + Send>(value: T) -> BatchObject {
88 BatchObject::Value(Box::new(value))
89 }
90}
91
92pub enum BatchResource<T: ?Sized> {
97 Value(Rc<T>),
98 Server(ServerObjectId),
99}
100
101impl<T: ?Sized> Clone for BatchResource<T> {
102 fn clone(&self) -> Self {
103 match self {
104 BatchResource::Value(value) => BatchResource::Value(value.clone()),
105 BatchResource::Server(id) => BatchResource::Server(*id),
106 }
107 }
108}
109
110pub trait BatchResourceValue: 'static {
116 type Wire: Any + Send;
118
119 fn to_wire(value: &Rc<Self>) -> Self::Wire;
121
122 fn from_wire(wire: Self::Wire) -> Rc<Self>;
124}
125
126impl BatchResourceValue for dyn crate::media::IBrush {
127 type Wire = crate::media::SharedBrush;
128
129 fn to_wire(value: &Rc<Self>) -> Self::Wire {
130 crate::media::shared_brush_of(&**value)
131 .unwrap_or_else(|| panic!("The brush is not compatible with composition"))
132 }
133
134 fn from_wire(wire: Self::Wire) -> Rc<Self> {
135 Rc::new(wire)
136 }
137}
138
139impl BatchResourceValue for dyn crate::media::ITransform {
140 type Wire = crate::Matrix;
141
142 fn to_wire(value: &Rc<Self>) -> Self::Wire {
143 value.value()
144 }
145
146 fn from_wire(wire: Self::Wire) -> Rc<Self> {
147 Rc::new(crate::media::immutable::ImmutableTransform::new(wire))
148 }
149}
150
151impl BatchResourceValue for dyn crate::media::IGradientStop {
152 type Wire = (f64, crate::media::Color);
153
154 fn to_wire(value: &Rc<Self>) -> Self::Wire {
155 (value.offset(), value.color())
156 }
157
158 fn from_wire((offset, color): Self::Wire) -> Rc<Self> {
159 Rc::new(crate::media::immutable::ImmutableGradientStop::new(offset, color))
160 }
161}
162
163#[derive(Default)]
165pub struct BatchStreamData {
166 pub(crate) objects: VecDeque<BatchObject>,
167 pub(crate) structs: Vec<u8>,
168 pub(crate) struct_read_offset: usize,
169}
170
171impl BatchStreamData {
172 pub fn new() -> Self {
173 Self::default()
174 }
175
176 pub(crate) fn reset(&mut self) {
178 self.objects.clear();
179 self.structs.clear();
180 self.struct_read_offset = 0;
181 }
182}
183
184pub trait BatchValue: Sized {
186 fn write_to(&self, out: &mut Vec<u8>);
187 fn read_from(input: &mut BatchValueReader<'_>) -> Self;
188}
189
190pub struct BatchValueReader<'a> {
192 data: &'a [u8],
193 offset: &'a mut usize,
194}
195
196impl<'a> BatchValueReader<'a> {
197 pub fn new(data: &'a [u8], offset: &'a mut usize) -> Self {
199 Self { data, offset }
200 }
201
202 pub fn take<const N: usize>(&mut self) -> [u8; N] {
204 let end = *self.offset + N;
205 if end > self.data.len() {
206 panic!("attempted to read past the end of the batch value stream");
207 }
208 let mut bytes = [0u8; N];
209 bytes.copy_from_slice(&self.data[*self.offset..end]);
210 *self.offset = end;
211 bytes
212 }
213}
214
215macro_rules! impl_batch_value_for_number {
216 ($($ty:ty),*) => {$(
217 impl BatchValue for $ty {
218 #[inline]
219 fn write_to(&self, out: &mut Vec<u8>) {
220 out.extend_from_slice(&self.to_le_bytes());
221 }
222 #[inline]
223 fn read_from(input: &mut BatchValueReader<'_>) -> Self {
224 <$ty>::from_le_bytes(input.take())
225 }
226 }
227 )*};
228}
229
230impl_batch_value_for_number!(u8, i8, u16, i16, u32, i32, u64, i64, u128, f32, f64);
231
232impl BatchValue for bool {
233 fn write_to(&self, out: &mut Vec<u8>) {
234 out.push(u8::from(*self));
235 }
236 fn read_from(input: &mut BatchValueReader<'_>) -> Self {
237 input.take::<1>()[0] != 0
238 }
239}
240
241impl BatchValue for Duration {
242 fn write_to(&self, out: &mut Vec<u8>) {
243 self.as_nanos().write_to(out);
244 }
245 fn read_from(input: &mut BatchValueReader<'_>) -> Self {
246 let nanos = u128::read_from(input);
247 Duration::new((nanos / 1_000_000_000) as u64, (nanos % 1_000_000_000) as u32)
248 }
249}
250
251impl<T: BatchValue> BatchValue for Option<T> {
252 fn write_to(&self, out: &mut Vec<u8>) {
253 match self {
254 Some(value) => {
255 true.write_to(out);
256 value.write_to(out);
257 }
258 None => false.write_to(out),
259 }
260 }
261 fn read_from(input: &mut BatchValueReader<'_>) -> Self {
262 if bool::read_from(input) {
263 Some(T::read_from(input))
264 } else {
265 None
266 }
267 }
268}
269
270macro_rules! impl_batch_value_for_fields {
271 ($ty:ty { $($field:ident : $fty:ty),* } => $ctor:expr) => {
272 impl BatchValue for $ty {
273 fn write_to(&self, out: &mut Vec<u8>) {
274 $(self.$field.write_to(out);)*
275 }
276 fn read_from(input: &mut BatchValueReader<'_>) -> Self {
277 $(let $field = <$fty>::read_from(input);)*
278 $ctor
279 }
280 }
281 };
282}
283
284impl_batch_value_for_fields!(Point { x: f64, y: f64 } => Point::new(x, y));
285impl_batch_value_for_fields!(Vector { x: f64, y: f64 } => Vector::new(x, y));
286impl_batch_value_for_fields!(Size { width: f64, height: f64 } => Size::new(width, height));
287impl_batch_value_for_fields!(Rect { x: f64, y: f64, width: f64, height: f64 } => Rect::new(x, y, width, height));
288impl_batch_value_for_fields!(Vector3D { x: f64, y: f64, z: f64 } => Vector3D::new(x, y, z));
289impl_batch_value_for_fields!(RoundedRect {
290 rect: Rect, radii_top_left: Vector, radii_top_right: Vector, radii_bottom_left: Vector, radii_bottom_right: Vector
291} => RoundedRect { rect, radii_top_left, radii_top_right, radii_bottom_left, radii_bottom_right });
292
293impl_batch_value_for_fields!(BoxShadow {
294 offset_x: f64, offset_y: f64, blur: f64, spread: f64, color: Color, is_inset: bool
295} => BoxShadow { offset_x, offset_y, blur, spread, color, is_inset });
296
297impl_batch_value_for_fields!(Vector2 { x: f32, y: f32 } => Vector2::new(x, y));
298impl_batch_value_for_fields!(Vector3 { x: f32, y: f32, z: f32 } => Vector3::new(x, y, z));
299impl_batch_value_for_fields!(Vector4 { x: f32, y: f32, z: f32, w: f32 } => Vector4::new(x, y, z, w));
300impl_batch_value_for_fields!(Quaternion { x: f32, y: f32, z: f32, w: f32 } => Quaternion::new(x, y, z, w));
301impl_batch_value_for_fields!(Matrix3x2 { m11: f32, m12: f32, m21: f32, m22: f32, m31: f32, m32: f32 }
302 => Matrix3x2::new(m11, m12, m21, m22, m31, m32));
303impl_batch_value_for_fields!(Matrix4x4 {
304 m11: f32, m12: f32, m13: f32, m14: f32, m21: f32, m22: f32, m23: f32, m24: f32,
305 m31: f32, m32: f32, m33: f32, m34: f32, m41: f32, m42: f32, m43: f32, m44: f32
306} => Matrix4x4 { m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44 });
307impl_batch_value_for_fields!(CornerRadius { top_left: f64, top_right: f64, bottom_right: f64, bottom_left: f64 }
308 => CornerRadius::new(top_left, top_right, bottom_right, bottom_left));
309impl_batch_value_for_fields!(RelativePoint { point: Point, unit: RelativeUnit }
310 => RelativePoint::from_point(point, unit));
311impl_batch_value_for_fields!(RelativeScalar { scalar: f64, unit: RelativeUnit } => RelativeScalar::new(scalar, unit));
312impl_batch_value_for_fields!(RelativeRect { rect: Rect, unit: RelativeUnit } => RelativeRect::from_rect(rect, unit));
313impl_batch_value_for_fields!(LayoutPassTiming { pass_counter: i32, elapsed: Duration }
314 => LayoutPassTiming::new(pass_counter, elapsed));
315impl_batch_value_for_fields!(RenderOptions {
316 text_rendering_mode: TextRenderingMode,
317 bitmap_interpolation_mode: BitmapInterpolationMode,
318 edge_mode: EdgeMode,
319 bitmap_blending_mode: BitmapBlendingMode,
320 requires_full_opacity_handling: Option<bool>
321} => RenderOptions {
322 text_rendering_mode,
323 bitmap_interpolation_mode,
324 edge_mode,
325 bitmap_blending_mode,
326 requires_full_opacity_handling,
327});
328impl_batch_value_for_fields!(TextOptions {
329 text_rendering_mode: TextRenderingMode,
330 text_hinting_mode: TextHintingMode,
331 baseline_pixel_alignment: BaselinePixelAlignment
332} => TextOptions { text_rendering_mode, text_hinting_mode, baseline_pixel_alignment });
333
334macro_rules! impl_batch_value_for_enum {
337 ($ty:ty as $repr:ty { $($variant:ident),* $(,)? }) => {
338 impl BatchValue for $ty {
339 #[inline]
340 fn write_to(&self, out: &mut Vec<u8>) {
341 (*self as $repr).write_to(out);
342 }
343 fn read_from(input: &mut BatchValueReader<'_>) -> Self {
344 let value = <$repr>::read_from(input);
345 $(if value == <$ty>::$variant as $repr {
346 return <$ty>::$variant;
347 })*
348 panic!("the batch value stream holds {value}, which is not a {}", stringify!($ty));
349 }
350 }
351 };
352}
353
354impl_batch_value_for_enum!(RelativeUnit as u8 { Relative, Absolute });
355impl_batch_value_for_enum!(PenLineCap as i32 { Flat, Round, Square });
356impl_batch_value_for_enum!(PenLineJoin as i32 { Bevel, Miter, Round });
357impl_batch_value_for_enum!(AlignmentX as i32 { Left, Center, Right });
358impl_batch_value_for_enum!(AlignmentY as i32 { Top, Center, Bottom });
359impl_batch_value_for_enum!(Stretch as i32 { None, Fill, Uniform, UniformToFill });
360impl_batch_value_for_enum!(TileMode as i32 { None, FlipX, FlipY, FlipXY, Tile });
361impl_batch_value_for_enum!(GradientSpreadMethod as i32 { Pad, Reflect, Repeat });
362impl_batch_value_for_enum!(TextRenderingMode as u8 { Unspecified, SubpixelAntialias, Antialias, Alias });
363impl_batch_value_for_enum!(TextHintingMode as u8 { Unspecified, None, Light, Strong });
364impl_batch_value_for_enum!(BaselinePixelAlignment as u8 { Unspecified, Aligned, Unaligned });
365impl_batch_value_for_enum!(EdgeMode as u8 { Unspecified, Antialias, Aliased });
366impl_batch_value_for_enum!(BitmapInterpolationMode as u8 {
367 Unspecified, None, LowQuality, MediumQuality, HighQuality
368});
369impl_batch_value_for_enum!(BitmapBlendingMode as u8 {
370 Unspecified, SourceOver, Source, Destination, DestinationOver, SourceIn, DestinationIn, SourceOut,
371 DestinationOut, SourceAtop, DestinationAtop, Xor, Plus, Screen, Overlay, Darken, Lighten, ColorDodge,
372 ColorBurn, HardLight, SoftLight, Difference, Exclusion, Multiply, Hue, Saturation, Color, Luminosity
373});
374impl_batch_value_for_enum!(CompositionTransparencyLevel as u8 { None, Transparent, Blur, AcrylicBlur, Mica });
375impl_batch_value_for_enum!(CompositionBlendMode as i32 {
376 Clear, Src, Dst, SrcOver, DstOver, SrcIn, DstIn, SrcOut, DstOut, SrcATop, DstATop, Xor, Plus, Modulate,
377 Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion,
378 Multiply, Hue, Saturation, Color, Luminosity
379});
380impl_batch_value_for_enum!(CompositionGradientExtendMode as i32 { Clamp, Wrap, Mirror });
381impl_batch_value_for_enum!(CompositionStretch as i32 { None, Fill });
382
383impl BatchValue for RendererDebugOverlays {
384 fn write_to(&self, out: &mut Vec<u8>) {
385 self.bits().write_to(out);
386 }
387 fn read_from(input: &mut BatchValueReader<'_>) -> Self {
388 RendererDebugOverlays::from_bits_retain(i32::read_from(input))
389 }
390}
391
392impl BatchValue for CompositionTileMode {
393 fn write_to(&self, out: &mut Vec<u8>) {
394 self.bits().write_to(out);
395 }
396 fn read_from(input: &mut BatchValueReader<'_>) -> Self {
397 CompositionTileMode::from_bits_retain(i32::read_from(input))
398 }
399}
400
401impl BatchValue for Color {
402 fn write_to(&self, out: &mut Vec<u8>) {
403 self.to_uint32().write_to(out);
404 }
405 fn read_from(input: &mut BatchValueReader<'_>) -> Self {
406 Color::from_uint32(u32::read_from(input))
407 }
408}
409
410impl BatchValue for Matrix {
411 fn write_to(&self, out: &mut Vec<u8>) {
412 for value in [self.m11, self.m12, self.m13, self.m21, self.m22, self.m23, self.m31, self.m32, self.m33] {
413 value.write_to(out);
414 }
415 }
416 fn read_from(input: &mut BatchValueReader<'_>) -> Self {
417 let mut v = [0.0f64; 9];
418 for value in &mut v {
419 *value = f64::read_from(input);
420 }
421 Matrix::new_3x3(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8])
422 }
423}
424
425pub struct BatchStreamWriter<'a> {
427 output: &'a mut BatchStreamData,
428}
429
430impl<'a> BatchStreamWriter<'a> {
431 pub fn new(output: &'a mut BatchStreamData) -> Self {
432 Self { output }
433 }
434
435 #[inline]
437 pub fn write<T: BatchValue>(&mut self, item: T) {
438 item.write_to(&mut self.output.structs);
439 }
440
441 #[inline]
443 pub fn write_bytes(&mut self, data: &[u8]) {
444 self.output.structs.extend_from_slice(data);
445 }
446
447 #[inline]
449 pub fn write_object(&mut self, item: BatchObject) {
450 self.output.objects.push_back(item);
451 }
452
453 #[inline]
455 pub fn write_server_object(&mut self, id: Option<ServerObjectId>) {
456 self.write_object(match id {
457 Some(id) => BatchObject::ServerObject(id),
458 None => BatchObject::Null,
459 });
460 }
461
462 #[inline]
464 pub fn write_value<T: Any + Send>(&mut self, item: Option<T>) {
465 self.write_object(match item {
466 Some(item) => BatchObject::value(item),
467 None => BatchObject::Null,
468 });
469 }
470
471 #[inline]
473 pub fn write_resource<T: ?Sized + BatchResourceValue>(&mut self, item: Option<BatchResource<T>>) {
474 self.write_object(match item {
475 Some(BatchResource::Value(value)) => BatchObject::value(T::to_wire(&value)),
476 Some(BatchResource::Server(id)) => BatchObject::ServerObject(id),
477 None => BatchObject::Null,
478 });
479 }
480
481 #[inline]
483 pub fn write_marker(&mut self, marker: BatchMarker) {
484 self.write_object(BatchObject::Marker(marker));
485 }
486}
487
488pub struct BatchStreamReader<'a> {
490 input: &'a mut BatchStreamData,
491}
492
493impl<'a> BatchStreamReader<'a> {
494 pub fn new(input: &'a mut BatchStreamData) -> Self {
495 Self { input }
496 }
497
498 #[inline]
501 pub fn read<T: BatchValue>(&mut self) -> T {
502 let mut reader =
503 BatchValueReader { data: &self.input.structs, offset: &mut self.input.struct_read_offset };
504 T::read_from(&mut reader)
505 }
506
507 pub fn read_bytes(&mut self, destination: &mut [u8]) {
510 let start = self.input.struct_read_offset;
511 let end = start + destination.len();
512 if end > self.input.structs.len() {
513 panic!("attempted to read past the end of the batch value stream");
514 }
515 destination.copy_from_slice(&self.input.structs[start..end]);
516 self.input.struct_read_offset = end;
517 }
518
519 #[inline]
521 pub fn read_object(&mut self) -> BatchObject {
522 match self.input.objects.pop_front() {
523 Some(object) => object,
524 None => panic!("attempted to read past the end of the batch object stream"),
525 }
526 }
527
528 pub fn read_server_object(&mut self) -> Option<ServerObjectId> {
530 match self.read_object() {
531 BatchObject::Null => None,
532 BatchObject::ServerObject(id) => Some(id),
533 _ => panic!("the batch object stream does not hold a server object reference here"),
534 }
535 }
536
537 pub fn read_value<T: Any>(&mut self) -> Option<T> {
539 match self.read_object() {
540 BatchObject::Null => None,
541 BatchObject::Value(value) => match value.downcast::<T>() {
542 Ok(value) => Some(*value),
543 Err(_) => panic!("the batch object stream holds a value of another type here"),
544 },
545 _ => panic!("the batch object stream does not hold a value here"),
546 }
547 }
548
549 pub fn read_resource<T: ?Sized + BatchResourceValue>(&mut self) -> Option<BatchResource<T>> {
552 match self.read_object() {
553 BatchObject::Null => None,
554 BatchObject::ServerObject(id) => Some(BatchResource::Server(id)),
555 BatchObject::Value(value) => match value.downcast::<T::Wire>() {
556 Ok(value) => Some(BatchResource::Value(T::from_wire(*value))),
557 Err(_) => panic!("the batch object stream holds a value of another type here"),
558 },
559 _ => panic!("the batch object stream does not hold a resource reference here"),
560 }
561 }
562
563 pub fn is_object_eof(&self) -> bool {
565 self.input.objects.is_empty()
566 }
567
568 pub fn is_struct_eof(&self) -> bool {
570 self.input.struct_read_offset >= self.input.structs.len()
571 }
572}
573
574#[cfg(test)]
575mod tests {
576 use super::*;
577
578 #[test]
579 fn batch_stream_correctly_writes_and_reads_data() {
580 let mut data = BatchStreamData::new();
581 let values: Vec<u128> = (0..453u128).map(|c| c.wrapping_mul(0x9E37_79B9_7F4A_7C15_F39C_C060_5CED_C835)).collect();
582 let objects: Vec<std::sync::Arc<()>> = (0..453).map(|_| std::sync::Arc::new(())).collect();
583
584 {
585 let mut writer = BatchStreamWriter::new(&mut data);
586 for value in &values {
587 writer.write(*value);
588 }
589 for object in &objects {
590 writer.write_object(BatchObject::value(object.clone()));
591 }
592 }
593
594 let mut reader = BatchStreamReader::new(&mut data);
595 for value in &values {
596 assert_eq!(*value, reader.read::<u128>());
597 }
598 for object in &objects {
599 assert!(std::sync::Arc::ptr_eq(object, &reader.read_value::<std::sync::Arc<()>>().unwrap()));
600 }
601 assert!(reader.is_object_eof());
602 assert!(reader.is_struct_eof());
603 }
604
605 #[test]
606 fn values_round_trip() {
607 let mut data = BatchStreamData::new();
608 {
609 let mut writer = BatchStreamWriter::new(&mut data);
610 writer.write(true);
611 writer.write(-7i32);
612 writer.write(1.5f32);
613 writer.write(Some(Rect::new(1.0, 2.0, 3.0, 4.0)));
614 writer.write(None::<f64>);
615 writer.write(Matrix::create_translation(3.0, 4.0));
616 writer.write(Duration::from_millis(1234));
617 writer.write(Color::from_uint32(0x80112233));
618 writer.write_server_object(None);
619 writer.write_marker(BatchMarker::RenderThreadJobsEnd);
620 }
621 let mut reader = BatchStreamReader::new(&mut data);
622 assert!(reader.read::<bool>());
623 assert_eq!(reader.read::<i32>(), -7);
624 assert_eq!(reader.read::<f32>(), 1.5);
625 assert_eq!(reader.read::<Option<Rect>>(), Some(Rect::new(1.0, 2.0, 3.0, 4.0)));
626 assert_eq!(reader.read::<Option<f64>>(), None);
627 assert_eq!(reader.read::<Matrix>(), Matrix::create_translation(3.0, 4.0));
628 assert_eq!(reader.read::<Duration>(), Duration::from_millis(1234));
629 assert_eq!(reader.read::<Color>(), Color::from_uint32(0x80112233));
630 assert_eq!(reader.read_server_object(), None);
631 assert!(matches!(reader.read_object(), BatchObject::Marker(BatchMarker::RenderThreadJobsEnd)));
632 }
633
634 #[test]
635 #[should_panic(expected = "past the end")]
636 fn reading_past_the_end_panics() {
637 let mut data = BatchStreamData::new();
638 let mut reader = BatchStreamReader::new(&mut data);
639 let _ = reader.read::<i32>();
640 }
641}