use api::{BorderStyle, NormalBorder, PremultipliedColorF, RasterSpace, Shadow};
use api::units::*;
use crate::border::{self, build_border_instances, get_max_scale_for_border};
use crate::border::NormalBorderAu;
use crate::gpu_types::ImageBrushPrimitiveData;
use crate::render_backend::DataStores;
use crate::render_task_cache::{RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskParent, to_cache_size};
use crate::renderer::{GpuBufferAddress, GpuBufferWriterF};
use crate::scene_building::{CreateShadow, IsVisible};
use crate::frame_builder::{FrameBuildingContext, FrameBuildingState};
use crate::intern;
use crate::internal_types::{LayoutPrimitiveInfo, FrameId};
use crate::prim_store::{
BorderSegmentInfo, BrushSegment, InternablePrimitive, NinePatchDescriptor, PrimKey, PrimTemplate, PrimTemplateCommonData, PrimitiveInstanceIndex, PrimitiveKind, PrimitiveOpacity, PrimitiveScratchBuffer, PrimitiveStore, VECS_PER_SEGMENT
};
use crate::resource_cache::ImageRequest;
use crate::render_task::{RenderTask, RenderTaskKind};
use crate::render_task_graph::RenderTaskId;
use crate::spatial_tree::SpatialNodeIndex;
use crate::util::clamp_to_scale_factor;
use crate::visibility::KindScratchHandle;
use crate::prim_store::storage;
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct NormalBorderScratch {
pub task_ids: storage::Range<RenderTaskId>,
pub brush_segments_range: storage::Range<BrushSegment>,
pub border_segments_range: storage::Range<BorderSegmentInfo>,
pub gpu_address: GpuBufferAddress,
pub may_need_repetition: bool,
}
impl NormalBorderScratch {
pub fn build_for_prim(
data_handle: NormalBorderDataHandle,
prim_instance_index: PrimitiveInstanceIndex,
prim_size: LayoutSize,
data_stores: &DataStores,
scratch: &mut PrimitiveScratchBuffer,
) {
let prim_data = &data_stores.normal_border[data_handle];
let border = &prim_data.kind.border;
let widths = &prim_data.kind.widths;
let brush_open = scratch.frame.segments.open_range();
let border_open = scratch.frame.border_segments.open_range();
border::create_border_segments(
prim_size,
border,
widths,
scratch.frame.border_segments.data_mut(),
scratch.frame.segments.data_mut(),
);
let brush_segments_range = scratch.frame.segments.close_range(brush_open);
let border_segments_range = scratch.frame.border_segments.close_range(border_open);
let may_need_repetition =
matches!(border.top.style, BorderStyle::Dotted | BorderStyle::Dashed)
|| matches!(border.right.style, BorderStyle::Dotted | BorderStyle::Dashed)
|| matches!(border.bottom.style, BorderStyle::Dotted | BorderStyle::Dashed)
|| matches!(border.left.style, BorderStyle::Dotted | BorderStyle::Dashed);
let segment_count = border_segments_range.end.0
.saturating_sub(border_segments_range.start.0) as usize;
let task_ids = scratch.frame.border_task_ids.extend(
std::iter::repeat(RenderTaskId::INVALID).take(segment_count),
);
let handle = scratch.frame.normal_border.push(NormalBorderScratch {
task_ids,
brush_segments_range,
border_segments_range,
gpu_address: GpuBufferAddress::INVALID,
may_need_repetition,
});
scratch.frame.draws[prim_instance_index.0 as usize].kind_scratch =
KindScratchHandle::NormalBorder(handle);
}
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
pub struct NormalBorderPrim {
pub border: NormalBorderAu,
pub widths: LayoutSideOffsetsAu,
}
pub type NormalBorderKey = PrimKey<NormalBorderPrim>;
impl NormalBorderKey {
pub fn new(
info: &LayoutPrimitiveInfo,
normal_border: NormalBorderPrim,
) -> Self {
NormalBorderKey {
common: info.into(),
kind: normal_border,
}
}
}
impl intern::InternDebug for NormalBorderKey {}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(MallocSizeOf)]
pub struct NormalBorderData {
pub border: NormalBorder,
pub widths: LayoutSideOffsets,
}
impl NormalBorderData {
pub fn write_brush_gpu_blocks(
&mut self,
common: &mut PrimTemplateCommonData,
prim_size: LayoutSize,
brush_segments: &[BrushSegment],
frame_state: &mut FrameBuildingState,
) -> GpuBufferAddress {
let mut writer = frame_state.frame_gpu_data.f32.write_blocks(3 + brush_segments.len() * VECS_PER_SEGMENT);
writer.push(&ImageBrushPrimitiveData {
color: PremultipliedColorF::WHITE,
background_color: PremultipliedColorF::WHITE,
stretch_size: prim_size,
});
for segment in brush_segments {
segment.write_gpu_blocks(&mut writer);
}
let gpu_address = writer.finish();
common.opacity = PrimitiveOpacity::translucent();
gpu_address
}
pub fn update(
&mut self,
border_segments: &[BorderSegmentInfo],
prim_spatial_node_index: SpatialNodeIndex,
device_pixel_scale: DevicePixelScale,
frame_context: &FrameBuildingContext,
frame_state: &mut FrameBuildingState,
task_ids: &mut [RenderTaskId],
) {
let scale = frame_context
.spatial_tree
.get_world_transform(prim_spatial_node_index)
.scale_factors();
let scale_width = clamp_to_scale_factor(scale.0, false);
let scale_height = clamp_to_scale_factor(scale.1, false);
let world_scale = LayoutToWorldScale::new(scale_width.max(scale_height));
let mut scale = world_scale * device_pixel_scale;
let max_scale = get_max_scale_for_border(border_segments);
scale.0 = scale.0.min(max_scale.0);
for (i, segment) in border_segments.iter().enumerate() {
let cache_size = to_cache_size(segment.local_task_size, &mut scale);
let cache_key = RenderTaskCacheKey {
kind: RenderTaskCacheKeyKind::BorderSegment(segment.cache_key.clone()),
origin: DeviceIntPoint::zero(),
size: cache_size,
};
let task_id = frame_state.resource_cache.request_render_task(
Some(cache_key),
false, RenderTaskParent::Surface,
&mut frame_state.frame_gpu_data.f32,
frame_state.rg_builder,
&mut frame_state.surface_builder,
&mut |rg_builder, _| {
rg_builder.add().init(RenderTask::new_dynamic(
cache_size,
RenderTaskKind::new_border_segment(
build_border_instances(
&segment.cache_key,
cache_size,
&self.border,
scale,
)
),
))
}
);
task_ids[i] = task_id;
}
}
}
pub type NormalBorderTemplate = PrimTemplate<NormalBorderData>;
impl From<NormalBorderKey> for NormalBorderTemplate {
fn from(key: NormalBorderKey) -> Self {
let common = PrimTemplateCommonData::with_key_common(key.common);
let mut border: NormalBorder = key.kind.border.into();
let widths = LayoutSideOffsets::from_au(key.kind.widths);
border.normalize(&widths);
NormalBorderTemplate {
common,
kind: NormalBorderData {
border,
widths,
}
}
}
}
pub type NormalBorderDataHandle = intern::Handle<NormalBorderPrim>;
impl intern::Internable for NormalBorderPrim {
type Key = NormalBorderKey;
type StoreData = NormalBorderTemplate;
type InternData = ();
const PROFILE_COUNTER: usize = crate::profiler::INTERNED_NORMAL_BORDERS;
}
impl InternablePrimitive for NormalBorderPrim {
fn into_key(
self,
info: &LayoutPrimitiveInfo,
) -> NormalBorderKey {
NormalBorderKey::new(
info,
self,
)
}
fn make_instance_kind(
_key: NormalBorderKey,
data_handle: NormalBorderDataHandle,
_: &mut PrimitiveStore,
) -> PrimitiveKind {
PrimitiveKind::NormalBorder {
data_handle,
}
}
}
impl CreateShadow for NormalBorderPrim {
fn create_shadow(
&self,
shadow: &Shadow,
_: bool,
_: RasterSpace,
) -> Self {
let border = self.border.with_color(shadow.color.into());
NormalBorderPrim {
border,
widths: self.widths,
}
}
}
impl IsVisible for NormalBorderPrim {
fn is_visible(&self) -> bool {
true
}
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
pub struct ImageBorder {
#[ignore_malloc_size_of = "Arc"]
pub request: ImageRequest,
pub nine_patch: NinePatchDescriptor,
}
pub type ImageBorderKey = PrimKey<ImageBorder>;
impl ImageBorderKey {
pub fn new(
info: &LayoutPrimitiveInfo,
image_border: ImageBorder,
) -> Self {
ImageBorderKey {
common: info.into(),
kind: image_border,
}
}
}
impl intern::InternDebug for ImageBorderKey {}
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct ImageBorderScratch {
pub brush_segments_range: storage::Range<BrushSegment>,
pub gpu_address: GpuBufferAddress,
}
impl ImageBorderScratch {
pub fn build_for_prim(
data_handle: ImageBorderDataHandle,
prim_instance_index: PrimitiveInstanceIndex,
prim_size: LayoutSize,
data_stores: &DataStores,
scratch: &mut PrimitiveScratchBuffer,
) {
let prim_data = &data_stores.image_border[data_handle];
let nine_patch = &prim_data.kind.nine_patch;
let brush_open = scratch.frame.segments.open_range();
scratch.frame.segments.data_mut().extend(
nine_patch.create_brush_segments(prim_size),
);
let brush_segments_range = scratch.frame.segments.close_range(brush_open);
let handle = scratch.frame.image_border.push(ImageBorderScratch {
brush_segments_range,
gpu_address: GpuBufferAddress::INVALID,
});
scratch.frame.draws[prim_instance_index.0 as usize].kind_scratch =
KindScratchHandle::ImageBorder(handle);
}
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(MallocSizeOf)]
pub struct ImageBorderData {
#[ignore_malloc_size_of = "Arc"]
pub request: ImageRequest,
pub nine_patch: NinePatchDescriptor,
pub src_color: Option<RenderTaskId>,
pub frame_id: FrameId,
pub is_opaque: bool,
}
impl ImageBorderData {
pub fn update(
&mut self,
common: &mut PrimTemplateCommonData,
prim_size: LayoutSize,
brush_segments: &[BrushSegment],
frame_state: &mut FrameBuildingState,
) -> GpuBufferAddress {
let mut writer = frame_state.frame_gpu_data.f32.write_blocks(3 + brush_segments.len() * VECS_PER_SEGMENT);
self.write_prim_gpu_blocks(&mut writer, &prim_size);
Self::write_segment_gpu_blocks(&mut writer, brush_segments);
let gpu_address = writer.finish();
let frame_id = frame_state.rg_builder.frame_id();
if self.frame_id != frame_id {
self.frame_id = frame_id;
let size = frame_state.resource_cache.request_image(
self.request,
&mut frame_state.frame_gpu_data.f32,
);
let task_id = frame_state.rg_builder.add().init(
RenderTask::new_image(size, self.request, false)
);
self.src_color = Some(task_id);
let image_properties = frame_state
.resource_cache
.get_image_properties(self.request.key);
self.is_opaque = image_properties
.map(|properties| properties.descriptor.is_opaque())
.unwrap_or(true);
}
common.opacity = PrimitiveOpacity { is_opaque: self.is_opaque };
gpu_address
}
fn write_prim_gpu_blocks(
&self,
writer: &mut GpuBufferWriterF,
prim_size: &LayoutSize,
) {
writer.push(&ImageBrushPrimitiveData {
color: PremultipliedColorF::WHITE,
background_color: PremultipliedColorF::WHITE,
stretch_size: *prim_size,
});
}
fn write_segment_gpu_blocks(
writer: &mut GpuBufferWriterF,
brush_segments: &[BrushSegment],
) {
for segment in brush_segments {
segment.write_gpu_blocks(writer);
}
}
}
pub type ImageBorderTemplate = PrimTemplate<ImageBorderData>;
impl From<ImageBorderKey> for ImageBorderTemplate {
fn from(key: ImageBorderKey) -> Self {
let common = PrimTemplateCommonData::with_key_common(key.common);
ImageBorderTemplate {
common,
kind: ImageBorderData {
request: key.kind.request,
nine_patch: key.kind.nine_patch,
src_color: None,
frame_id: FrameId::INVALID,
is_opaque: false,
}
}
}
}
pub type ImageBorderDataHandle = intern::Handle<ImageBorder>;
impl intern::Internable for ImageBorder {
type Key = ImageBorderKey;
type StoreData = ImageBorderTemplate;
type InternData = ();
const PROFILE_COUNTER: usize = crate::profiler::INTERNED_IMAGE_BORDERS;
}
impl InternablePrimitive for ImageBorder {
fn into_key(
self,
info: &LayoutPrimitiveInfo,
) -> ImageBorderKey {
ImageBorderKey::new(
info,
self,
)
}
fn make_instance_kind(
_key: ImageBorderKey,
data_handle: ImageBorderDataHandle,
_: &mut PrimitiveStore,
) -> PrimitiveKind {
PrimitiveKind::ImageBorder {
data_handle
}
}
}
impl IsVisible for ImageBorder {
fn is_visible(&self) -> bool {
true
}
}
#[test]
#[cfg(target_pointer_width = "64")]
fn test_struct_sizes() {
use std::mem;
assert_eq!(mem::size_of::<NormalBorderPrim>(), 84, "NormalBorderPrim size changed");
assert_eq!(mem::size_of::<NormalBorderTemplate>(), 140, "NormalBorderTemplate size changed");
assert_eq!(mem::size_of::<NormalBorderKey>(), 88, "NormalBorderKey size changed");
assert_eq!(mem::size_of::<ImageBorder>(), 68, "ImageBorder size changed");
assert_eq!(mem::size_of::<ImageBorderTemplate>(), 104, "ImageBorderTemplate size changed");
assert_eq!(mem::size_of::<ImageBorderKey>(), 72, "ImageBorderKey size changed");
}