1use api::{BorderStyle, NormalBorder, PremultipliedColorF, RasterSpace, Shadow};
6use api::units::*;
7use crate::border::{self, build_border_instances, get_max_scale_for_border};
8use crate::border::NormalBorderAu;
9use crate::gpu_types::ImageBrushPrimitiveData;
10use crate::render_backend::DataStores;
11use crate::render_task_cache::{RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskParent, to_cache_size};
12use crate::renderer::{GpuBufferAddress, GpuBufferWriterF};
13use crate::scene_building::{CreateShadow, IsVisible};
14use crate::frame_builder::{FrameBuildingContext, FrameBuildingState};
15use crate::intern;
16use crate::internal_types::{LayoutPrimitiveInfo, FrameId};
17use crate::prim_store::{
18 BorderSegmentInfo, BrushSegment, InternablePrimitive, NinePatchDescriptor, PrimKey, PrimTemplate, PrimTemplateCommonData, PrimitiveInstanceIndex, PrimitiveKind, PrimitiveOpacity, PrimitiveScratchBuffer, PrimitiveStore, VECS_PER_SEGMENT
19};
20use crate::resource_cache::ImageRequest;
21use crate::render_task::{RenderTask, RenderTaskKind};
22use crate::render_task_graph::RenderTaskId;
23use crate::spatial_tree::SpatialNodeIndex;
24use crate::util::clamp_to_scale_factor;
25use crate::visibility::KindScratchHandle;
26
27use crate::prim_store::storage;
28
29#[derive(Copy, Clone, Debug)]
31#[cfg_attr(feature = "capture", derive(Serialize))]
32pub struct NormalBorderScratch {
33 pub task_ids: storage::Range<RenderTaskId>,
36 pub brush_segments_range: storage::Range<BrushSegment>,
41 pub border_segments_range: storage::Range<BorderSegmentInfo>,
45 pub gpu_address: GpuBufferAddress,
50 pub may_need_repetition: bool,
54}
55
56impl NormalBorderScratch {
57 pub fn build_for_prim(
70 data_handle: NormalBorderDataHandle,
71 prim_instance_index: PrimitiveInstanceIndex,
72 prim_size: LayoutSize,
73 data_stores: &DataStores,
74 scratch: &mut PrimitiveScratchBuffer,
75 ) {
76 let prim_data = &data_stores.normal_border[data_handle];
77 let border = &prim_data.kind.border;
78 let widths = &prim_data.kind.widths;
79
80 let brush_open = scratch.frame.segments.open_range();
81 let border_open = scratch.frame.border_segments.open_range();
82 border::create_border_segments(
83 prim_size,
84 border,
85 widths,
86 scratch.frame.border_segments.data_mut(),
87 scratch.frame.segments.data_mut(),
88 );
89 let brush_segments_range = scratch.frame.segments.close_range(brush_open);
90 let border_segments_range = scratch.frame.border_segments.close_range(border_open);
91
92 let may_need_repetition =
93 matches!(border.top.style, BorderStyle::Dotted | BorderStyle::Dashed)
94 || matches!(border.right.style, BorderStyle::Dotted | BorderStyle::Dashed)
95 || matches!(border.bottom.style, BorderStyle::Dotted | BorderStyle::Dashed)
96 || matches!(border.left.style, BorderStyle::Dotted | BorderStyle::Dashed);
97
98 let segment_count = border_segments_range.end.0
99 .saturating_sub(border_segments_range.start.0) as usize;
100 let task_ids = scratch.frame.border_task_ids.extend(
101 std::iter::repeat(RenderTaskId::INVALID).take(segment_count),
102 );
103 let handle = scratch.frame.normal_border.push(NormalBorderScratch {
104 task_ids,
105 brush_segments_range,
106 border_segments_range,
107 gpu_address: GpuBufferAddress::INVALID,
108 may_need_repetition,
109 });
110 scratch.frame.draws[prim_instance_index.0 as usize].kind_scratch =
111 KindScratchHandle::NormalBorder(handle);
112 }
113}
114
115#[cfg_attr(feature = "capture", derive(Serialize))]
116#[cfg_attr(feature = "replay", derive(Deserialize))]
117#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
118pub struct NormalBorderPrim {
119 pub border: NormalBorderAu,
120 pub widths: LayoutSideOffsetsAu,
121}
122
123pub type NormalBorderKey = PrimKey<NormalBorderPrim>;
124
125impl NormalBorderKey {
126 pub fn new(
127 info: &LayoutPrimitiveInfo,
128 normal_border: NormalBorderPrim,
129 ) -> Self {
130 NormalBorderKey {
131 common: info.into(),
132 kind: normal_border,
133 }
134 }
135}
136
137impl intern::InternDebug for NormalBorderKey {}
138
139#[cfg_attr(feature = "capture", derive(Serialize))]
140#[cfg_attr(feature = "replay", derive(Deserialize))]
141#[derive(MallocSizeOf)]
142pub struct NormalBorderData {
143 pub border: NormalBorder,
144 pub widths: LayoutSideOffsets,
145}
146
147impl NormalBorderData {
148 pub fn write_brush_gpu_blocks(
153 &mut self,
154 common: &mut PrimTemplateCommonData,
155 prim_size: LayoutSize,
156 brush_segments: &[BrushSegment],
157 frame_state: &mut FrameBuildingState,
158 ) -> GpuBufferAddress {
159 let mut writer = frame_state.frame_gpu_data.f32.write_blocks(3 + brush_segments.len() * VECS_PER_SEGMENT);
160
161 writer.push(&ImageBrushPrimitiveData {
165 color: PremultipliedColorF::WHITE,
166 background_color: PremultipliedColorF::WHITE,
167 stretch_size: prim_size,
168 });
169
170 for segment in brush_segments {
171 segment.write_gpu_blocks(&mut writer);
172 }
173
174 let gpu_address = writer.finish();
175 common.opacity = PrimitiveOpacity::translucent();
176 gpu_address
177 }
178
179 pub fn update(
180 &mut self,
181 border_segments: &[BorderSegmentInfo],
182 prim_spatial_node_index: SpatialNodeIndex,
183 device_pixel_scale: DevicePixelScale,
184 frame_context: &FrameBuildingContext,
185 frame_state: &mut FrameBuildingState,
186 task_ids: &mut [RenderTaskId],
187 ) {
188 let scale = frame_context
193 .spatial_tree
194 .get_world_transform(prim_spatial_node_index)
195 .scale_factors();
196
197 let scale_width = clamp_to_scale_factor(scale.0, false);
206 let scale_height = clamp_to_scale_factor(scale.1, false);
207 let world_scale = LayoutToWorldScale::new(scale_width.max(scale_height));
209 let mut scale = world_scale * device_pixel_scale;
210 let max_scale = get_max_scale_for_border(border_segments);
211 scale.0 = scale.0.min(max_scale.0);
212
213 for (i, segment) in border_segments.iter().enumerate() {
220 let cache_size = to_cache_size(segment.local_task_size, &mut scale);
222 let cache_key = RenderTaskCacheKey {
223 kind: RenderTaskCacheKeyKind::BorderSegment(segment.cache_key.clone()),
224 origin: DeviceIntPoint::zero(),
225 size: cache_size,
226 };
227
228 let task_id = frame_state.resource_cache.request_render_task(
229 Some(cache_key),
230 false, RenderTaskParent::Surface,
232 &mut frame_state.frame_gpu_data.f32,
233 frame_state.rg_builder,
234 &mut frame_state.surface_builder,
235 &mut |rg_builder, _| {
236 rg_builder.add().init(RenderTask::new_dynamic(
237 cache_size,
238 RenderTaskKind::new_border_segment(
239 build_border_instances(
240 &segment.cache_key,
241 cache_size,
242 &self.border,
243 scale,
244 )
245 ),
246 ))
247 }
248 );
249
250 task_ids[i] = task_id;
251 }
252 }
253}
254
255pub type NormalBorderTemplate = PrimTemplate<NormalBorderData>;
256
257impl From<NormalBorderKey> for NormalBorderTemplate {
258 fn from(key: NormalBorderKey) -> Self {
259 let common = PrimTemplateCommonData::with_key_common(key.common);
260
261 let mut border: NormalBorder = key.kind.border.into();
262 let widths = LayoutSideOffsets::from_au(key.kind.widths);
263
264 border.normalize(&widths);
266
267 NormalBorderTemplate {
268 common,
269 kind: NormalBorderData {
270 border,
271 widths,
272 }
273 }
274 }
275}
276
277pub type NormalBorderDataHandle = intern::Handle<NormalBorderPrim>;
278
279impl intern::Internable for NormalBorderPrim {
280 type Key = NormalBorderKey;
281 type StoreData = NormalBorderTemplate;
282 type InternData = ();
283 const PROFILE_COUNTER: usize = crate::profiler::INTERNED_NORMAL_BORDERS;
284}
285
286impl InternablePrimitive for NormalBorderPrim {
287 fn into_key(
288 self,
289 info: &LayoutPrimitiveInfo,
290 ) -> NormalBorderKey {
291 NormalBorderKey::new(
292 info,
293 self,
294 )
295 }
296
297 fn make_instance_kind(
298 _key: NormalBorderKey,
299 data_handle: NormalBorderDataHandle,
300 _: &mut PrimitiveStore,
301 ) -> PrimitiveKind {
302 PrimitiveKind::NormalBorder {
303 data_handle,
304 }
305 }
306}
307
308impl CreateShadow for NormalBorderPrim {
309 fn create_shadow(
310 &self,
311 shadow: &Shadow,
312 _: bool,
313 _: RasterSpace,
314 ) -> Self {
315 let border = self.border.with_color(shadow.color.into());
316 NormalBorderPrim {
317 border,
318 widths: self.widths,
319 }
320 }
321}
322
323impl IsVisible for NormalBorderPrim {
324 fn is_visible(&self) -> bool {
325 true
326 }
327}
328
329#[cfg_attr(feature = "capture", derive(Serialize))]
332#[cfg_attr(feature = "replay", derive(Deserialize))]
333#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
334pub struct ImageBorder {
335 #[ignore_malloc_size_of = "Arc"]
336 pub request: ImageRequest,
337 pub nine_patch: NinePatchDescriptor,
338}
339
340pub type ImageBorderKey = PrimKey<ImageBorder>;
341
342impl ImageBorderKey {
343 pub fn new(
344 info: &LayoutPrimitiveInfo,
345 image_border: ImageBorder,
346 ) -> Self {
347 ImageBorderKey {
348 common: info.into(),
349 kind: image_border,
350 }
351 }
352}
353
354impl intern::InternDebug for ImageBorderKey {}
355
356
357#[derive(Copy, Clone, Debug)]
359#[cfg_attr(feature = "capture", derive(Serialize))]
360pub struct ImageBorderScratch {
361 pub brush_segments_range: storage::Range<BrushSegment>,
366 pub gpu_address: GpuBufferAddress,
371}
372
373impl ImageBorderScratch {
374 pub fn build_for_prim(
382 data_handle: ImageBorderDataHandle,
383 prim_instance_index: PrimitiveInstanceIndex,
384 prim_size: LayoutSize,
385 data_stores: &DataStores,
386 scratch: &mut PrimitiveScratchBuffer,
387 ) {
388 let prim_data = &data_stores.image_border[data_handle];
389 let nine_patch = &prim_data.kind.nine_patch;
390
391 let brush_open = scratch.frame.segments.open_range();
392 scratch.frame.segments.data_mut().extend(
393 nine_patch.create_brush_segments(prim_size),
394 );
395 let brush_segments_range = scratch.frame.segments.close_range(brush_open);
396
397 let handle = scratch.frame.image_border.push(ImageBorderScratch {
398 brush_segments_range,
399 gpu_address: GpuBufferAddress::INVALID,
400 });
401 scratch.frame.draws[prim_instance_index.0 as usize].kind_scratch =
402 KindScratchHandle::ImageBorder(handle);
403 }
404}
405
406#[cfg_attr(feature = "capture", derive(Serialize))]
407#[cfg_attr(feature = "replay", derive(Deserialize))]
408#[derive(MallocSizeOf)]
409pub struct ImageBorderData {
410 #[ignore_malloc_size_of = "Arc"]
411 pub request: ImageRequest,
412 pub nine_patch: NinePatchDescriptor,
413 pub src_color: Option<RenderTaskId>,
414 pub frame_id: FrameId,
415 pub is_opaque: bool,
416}
417
418impl ImageBorderData {
419 pub fn update(
424 &mut self,
425 common: &mut PrimTemplateCommonData,
426 prim_size: LayoutSize,
427 brush_segments: &[BrushSegment],
428 frame_state: &mut FrameBuildingState,
429 ) -> GpuBufferAddress {
430 let mut writer = frame_state.frame_gpu_data.f32.write_blocks(3 + brush_segments.len() * VECS_PER_SEGMENT);
431 self.write_prim_gpu_blocks(&mut writer, &prim_size);
432 Self::write_segment_gpu_blocks(&mut writer, brush_segments);
433 let gpu_address = writer.finish();
434
435 let frame_id = frame_state.rg_builder.frame_id();
436 if self.frame_id != frame_id {
437 self.frame_id = frame_id;
438
439 let size = frame_state.resource_cache.request_image(
440 self.request,
441 &mut frame_state.frame_gpu_data.f32,
442 );
443
444 let task_id = frame_state.rg_builder.add().init(
445 RenderTask::new_image(size, self.request, false)
446 );
447
448 self.src_color = Some(task_id);
449
450 let image_properties = frame_state
451 .resource_cache
452 .get_image_properties(self.request.key);
453
454 self.is_opaque = image_properties
455 .map(|properties| properties.descriptor.is_opaque())
456 .unwrap_or(true);
457 }
458
459 common.opacity = PrimitiveOpacity { is_opaque: self.is_opaque };
460 gpu_address
461 }
462
463 fn write_prim_gpu_blocks(
464 &self,
465 writer: &mut GpuBufferWriterF,
466 prim_size: &LayoutSize,
467 ) {
468 writer.push(&ImageBrushPrimitiveData {
472 color: PremultipliedColorF::WHITE,
473 background_color: PremultipliedColorF::WHITE,
474 stretch_size: *prim_size,
475 });
476 }
477
478 fn write_segment_gpu_blocks(
479 writer: &mut GpuBufferWriterF,
480 brush_segments: &[BrushSegment],
481 ) {
482 for segment in brush_segments {
483 segment.write_gpu_blocks(writer);
484 }
485 }
486}
487
488pub type ImageBorderTemplate = PrimTemplate<ImageBorderData>;
489
490impl From<ImageBorderKey> for ImageBorderTemplate {
491 fn from(key: ImageBorderKey) -> Self {
492 let common = PrimTemplateCommonData::with_key_common(key.common);
493
494 ImageBorderTemplate {
495 common,
496 kind: ImageBorderData {
497 request: key.kind.request,
498 nine_patch: key.kind.nine_patch,
499 src_color: None,
500 frame_id: FrameId::INVALID,
501 is_opaque: false,
502 }
503 }
504 }
505}
506
507pub type ImageBorderDataHandle = intern::Handle<ImageBorder>;
508
509impl intern::Internable for ImageBorder {
510 type Key = ImageBorderKey;
511 type StoreData = ImageBorderTemplate;
512 type InternData = ();
513 const PROFILE_COUNTER: usize = crate::profiler::INTERNED_IMAGE_BORDERS;
514}
515
516impl InternablePrimitive for ImageBorder {
517 fn into_key(
518 self,
519 info: &LayoutPrimitiveInfo,
520 ) -> ImageBorderKey {
521 ImageBorderKey::new(
522 info,
523 self,
524 )
525 }
526
527 fn make_instance_kind(
528 _key: ImageBorderKey,
529 data_handle: ImageBorderDataHandle,
530 _: &mut PrimitiveStore,
531 ) -> PrimitiveKind {
532 PrimitiveKind::ImageBorder {
533 data_handle
534 }
535 }
536}
537
538impl IsVisible for ImageBorder {
539 fn is_visible(&self) -> bool {
540 true
541 }
542}
543
544#[test]
545#[cfg(target_pointer_width = "64")]
546fn test_struct_sizes() {
547 use std::mem;
548 assert_eq!(mem::size_of::<NormalBorderPrim>(), 84, "NormalBorderPrim size changed");
555 assert_eq!(mem::size_of::<NormalBorderTemplate>(), 140, "NormalBorderTemplate size changed");
556 assert_eq!(mem::size_of::<NormalBorderKey>(), 88, "NormalBorderKey size changed");
557 assert_eq!(mem::size_of::<ImageBorder>(), 68, "ImageBorder size changed");
558 assert_eq!(mem::size_of::<ImageBorderTemplate>(), 104, "ImageBorderTemplate size changed");
559 assert_eq!(mem::size_of::<ImageBorderKey>(), 72, "ImageBorderKey size changed");
560}