use api::{ColorF, DebugCommand, DocumentId, ExternalImageData, ExternalImageId, PrimitiveFlags};
use api::{ImageFormat, ItemTag, NotificationRequest, Shadow, FilterOp};
use api::units::*;
use api;
use crate::composite::NativeSurfaceOperation;
use crate::device::TextureFilter;
use crate::renderer::PipelineInfo;
use crate::gpu_cache::GpuCacheUpdateList;
use crate::frame_builder::Frame;
use fxhash::FxHasher;
use plane_split::BspSplitter;
use crate::profiler::BackendProfileCounters;
use smallvec::SmallVec;
use std::{usize, i32};
use std::collections::{HashMap, HashSet};
use std::f32;
use std::hash::BuildHasherDefault;
use std::path::PathBuf;
use std::sync::Arc;
#[cfg(feature = "capture")]
use crate::capture::{CaptureConfig, ExternalCaptureImage};
#[cfg(feature = "replay")]
use crate::capture::PlainExternalImage;
pub type FastHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FxHasher>>;
pub type FastHashSet<K> = HashSet<K, BuildHasherDefault<FxHasher>>;
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct PlaneSplitAnchor {
pub cluster_index: usize,
pub instance_index: usize,
}
impl PlaneSplitAnchor {
pub fn new(cluster_index: usize, instance_index: usize) -> Self {
PlaneSplitAnchor {
cluster_index,
instance_index,
}
}
}
impl Default for PlaneSplitAnchor {
fn default() -> Self {
PlaneSplitAnchor {
cluster_index: 0,
instance_index: 0,
}
}
}
pub type PlaneSplitter = BspSplitter<f64, WorldPixel, PlaneSplitAnchor>;
const OPACITY_EPSILON: f32 = 0.001;
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum Filter {
Identity,
Blur(f32),
Brightness(f32),
Contrast(f32),
Grayscale(f32),
HueRotate(f32),
Invert(f32),
Opacity(api::PropertyBinding<f32>, f32),
Saturate(f32),
Sepia(f32),
DropShadows(SmallVec<[Shadow; 1]>),
ColorMatrix(Box<[f32; 20]>),
SrgbToLinear,
LinearToSrgb,
ComponentTransfer,
Flood(ColorF),
}
impl Filter {
pub fn is_visible(&self) -> bool {
match *self {
Filter::Identity |
Filter::Blur(..) |
Filter::Brightness(..) |
Filter::Contrast(..) |
Filter::Grayscale(..) |
Filter::HueRotate(..) |
Filter::Invert(..) |
Filter::Saturate(..) |
Filter::Sepia(..) |
Filter::DropShadows(..) |
Filter::ColorMatrix(..) |
Filter::SrgbToLinear |
Filter::LinearToSrgb |
Filter::ComponentTransfer => true,
Filter::Opacity(_, amount) => {
amount > OPACITY_EPSILON
},
Filter::Flood(color) => {
color.a > OPACITY_EPSILON
}
}
}
pub fn is_noop(&self) -> bool {
match *self {
Filter::Identity => false,
Filter::Blur(length) => length == 0.0,
Filter::Brightness(amount) => amount == 1.0,
Filter::Contrast(amount) => amount == 1.0,
Filter::Grayscale(amount) => amount == 0.0,
Filter::HueRotate(amount) => amount == 0.0,
Filter::Invert(amount) => amount == 0.0,
Filter::Opacity(_, amount) => amount >= 1.0,
Filter::Saturate(amount) => amount == 1.0,
Filter::Sepia(amount) => amount == 0.0,
Filter::DropShadows(ref shadows) => {
for shadow in shadows {
if shadow.offset.x != 0.0 || shadow.offset.y != 0.0 || shadow.blur_radius != 0.0 {
return false;
}
}
true
}
Filter::ColorMatrix(ref matrix) => {
**matrix == [
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
0.0, 0.0, 0.0, 0.0
]
}
Filter::SrgbToLinear |
Filter::LinearToSrgb |
Filter::ComponentTransfer |
Filter::Flood(..) => false,
}
}
pub fn as_int(&self) -> i32 {
match *self {
Filter::Identity => 0,
Filter::Contrast(..) => 0,
Filter::Grayscale(..) => 1,
Filter::HueRotate(..) => 2,
Filter::Invert(..) => 3,
Filter::Saturate(..) => 4,
Filter::Sepia(..) => 5,
Filter::Brightness(..) => 6,
Filter::ColorMatrix(..) => 7,
Filter::SrgbToLinear => 8,
Filter::LinearToSrgb => 9,
Filter::Flood(..) => 10,
Filter::ComponentTransfer => 11,
Filter::Blur(..) => 12,
Filter::DropShadows(..) => 13,
Filter::Opacity(..) => 14,
}
}
}
impl From<FilterOp> for Filter {
fn from(op: FilterOp) -> Self {
match op {
FilterOp::Identity => Filter::Identity,
FilterOp::Blur(r) => Filter::Blur(r),
FilterOp::Brightness(b) => Filter::Brightness(b),
FilterOp::Contrast(c) => Filter::Contrast(c),
FilterOp::Grayscale(g) => Filter::Grayscale(g),
FilterOp::HueRotate(h) => Filter::HueRotate(h),
FilterOp::Invert(i) => Filter::Invert(i),
FilterOp::Opacity(binding, opacity) => Filter::Opacity(binding, opacity),
FilterOp::Saturate(s) => Filter::Saturate(s),
FilterOp::Sepia(s) => Filter::Sepia(s),
FilterOp::ColorMatrix(mat) => Filter::ColorMatrix(Box::new(mat)),
FilterOp::SrgbToLinear => Filter::SrgbToLinear,
FilterOp::LinearToSrgb => Filter::LinearToSrgb,
FilterOp::ComponentTransfer => Filter::ComponentTransfer,
FilterOp::DropShadow(shadow) => Filter::DropShadows(smallvec![shadow]),
FilterOp::Flood(color) => Filter::Flood(color),
}
}
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
pub enum Swizzle {
Rgba,
Bgra,
}
impl Default for Swizzle {
fn default() -> Self {
Swizzle::Rgba
}
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
pub struct SwizzleSettings {
pub bgra8_sampling_swizzle: Swizzle,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct CacheTextureId(pub u64);
pub type LayerIndex = usize;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct SavedTargetIndex(pub usize);
impl SavedTargetIndex {
pub const PENDING: Self = SavedTargetIndex(!0);
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum TextureSource {
Invalid,
TextureCache(CacheTextureId, Swizzle),
External(ExternalImageData),
PrevPassAlpha,
PrevPassColor,
RenderTaskCache(SavedTargetIndex, Swizzle),
Dummy,
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct RenderTargetInfo {
pub has_depth: bool,
}
#[derive(Debug)]
pub enum TextureUpdateSource {
External {
id: ExternalImageId,
channel_index: u8,
},
Bytes { data: Arc<Vec<u8>> },
DebugClear,
}
#[derive(Debug)]
pub struct TextureCacheAllocation {
pub id: CacheTextureId,
pub kind: TextureCacheAllocationKind,
}
#[derive(Debug)]
pub struct TextureCacheAllocInfo {
pub width: i32,
pub height: i32,
pub layer_count: i32,
pub format: ImageFormat,
pub filter: TextureFilter,
pub is_shared_cache: bool,
pub has_depth: bool,
}
#[derive(Debug)]
pub enum TextureCacheAllocationKind {
Alloc(TextureCacheAllocInfo),
Realloc(TextureCacheAllocInfo),
Reset(TextureCacheAllocInfo),
Free,
}
#[derive(Debug)]
pub struct TextureCacheUpdate {
pub rect: DeviceIntRect,
pub stride: Option<i32>,
pub offset: i32,
pub layer_index: i32,
pub format_override: Option<ImageFormat>,
pub source: TextureUpdateSource,
}
#[derive(Default)]
pub struct TextureUpdateList {
pub clears_shared_cache: bool,
pub allocations: Vec<TextureCacheAllocation>,
pub updates: FastHashMap<CacheTextureId, Vec<TextureCacheUpdate>>,
}
impl TextureUpdateList {
pub fn new() -> Self {
TextureUpdateList {
clears_shared_cache: false,
allocations: Vec::new(),
updates: FastHashMap::default(),
}
}
pub fn is_nop(&self) -> bool {
self.allocations.is_empty() && self.updates.is_empty()
}
#[inline]
pub fn note_clear(&mut self) {
self.clears_shared_cache = true;
}
#[inline]
pub fn push_update(&mut self, id: CacheTextureId, update: TextureCacheUpdate) {
self.updates
.entry(id)
.or_default()
.push(update);
}
#[cold]
pub fn push_debug_clear(
&mut self,
id: CacheTextureId,
origin: DeviceIntPoint,
width: i32,
height: i32,
layer_index: usize
) {
let size = DeviceIntSize::new(width, height);
let rect = DeviceIntRect::new(origin, size);
self.push_update(id, TextureCacheUpdate {
rect,
stride: None,
offset: 0,
layer_index: layer_index as i32,
format_override: None,
source: TextureUpdateSource::DebugClear,
});
}
pub fn push_alloc(&mut self, id: CacheTextureId, info: TextureCacheAllocInfo) {
debug_assert!(!self.allocations.iter().any(|x| x.id == id));
self.allocations.push(TextureCacheAllocation {
id,
kind: TextureCacheAllocationKind::Alloc(info),
});
}
pub fn push_realloc(&mut self, id: CacheTextureId, info: TextureCacheAllocInfo) {
self.debug_assert_coalesced(id);
if let Some(cur) = self.allocations.iter_mut().find(|x| x.id == id) {
match cur.kind {
TextureCacheAllocationKind::Alloc(ref mut i) => *i = info,
TextureCacheAllocationKind::Realloc(ref mut i) => *i = info,
TextureCacheAllocationKind::Reset(ref mut i) => *i = info,
TextureCacheAllocationKind::Free => panic!("Reallocating freed texture"),
}
return
}
self.allocations.push(TextureCacheAllocation {
id,
kind: TextureCacheAllocationKind::Realloc(info),
});
}
pub fn push_reset(&mut self, id: CacheTextureId, info: TextureCacheAllocInfo) {
self.debug_assert_coalesced(id);
if let Some(cur) = self.allocations.iter_mut().find(|x| x.id == id) {
match cur.kind {
TextureCacheAllocationKind::Alloc(ref mut i) => *i = info,
TextureCacheAllocationKind::Reset(ref mut i) => *i = info,
TextureCacheAllocationKind::Free => panic!("Resetting freed texture"),
TextureCacheAllocationKind::Realloc(_) => {
cur.kind = TextureCacheAllocationKind::Reset(info);
}
}
return
}
self.allocations.push(TextureCacheAllocation {
id,
kind: TextureCacheAllocationKind::Reset(info),
});
}
pub fn push_free(&mut self, id: CacheTextureId) {
self.debug_assert_coalesced(id);
self.updates.remove(&id);
let idx = self.allocations.iter().position(|x| x.id == id);
let removed_kind = idx.map(|i| self.allocations.remove(i).kind);
match removed_kind {
Some(TextureCacheAllocationKind::Alloc(..)) => { },
Some(TextureCacheAllocationKind::Free) => panic!("Double free"),
Some(TextureCacheAllocationKind::Realloc(..)) |
Some(TextureCacheAllocationKind::Reset(..)) |
None => {
self.allocations.push(TextureCacheAllocation {
id,
kind: TextureCacheAllocationKind::Free,
});
}
};
}
fn debug_assert_coalesced(&self, id: CacheTextureId) {
debug_assert!(
self.allocations.iter().filter(|x| x.id == id).count() <= 1,
"Allocations should have been coalesced",
);
}
}
pub struct ResourceUpdateList {
pub native_surface_updates: Vec<NativeSurfaceOperation>,
pub texture_updates: TextureUpdateList,
}
impl ResourceUpdateList {
pub fn is_nop(&self) -> bool {
self.texture_updates.is_nop() && self.native_surface_updates.is_empty()
}
}
pub struct RenderedDocument {
pub frame: Frame,
pub is_new_scene: bool,
}
pub enum DebugOutput {
FetchDocuments(String),
FetchClipScrollTree(String),
#[cfg(feature = "capture")]
SaveCapture(CaptureConfig, Vec<ExternalCaptureImage>),
#[cfg(feature = "replay")]
LoadCapture(PathBuf, Vec<PlainExternalImage>),
}
#[allow(dead_code)]
pub enum ResultMsg {
DebugCommand(DebugCommand),
DebugOutput(DebugOutput),
RefreshShader(PathBuf),
UpdateGpuCache(GpuCacheUpdateList),
UpdateResources {
resource_updates: ResourceUpdateList,
memory_pressure: bool,
},
PublishPipelineInfo(PipelineInfo),
PublishDocument(
DocumentId,
RenderedDocument,
ResourceUpdateList,
BackendProfileCounters,
),
AppendNotificationRequests(Vec<NotificationRequest>),
ForceRedraw,
}
#[derive(Clone, Debug)]
pub struct ResourceCacheError {
description: String,
}
impl ResourceCacheError {
pub fn new(description: String) -> ResourceCacheError {
ResourceCacheError {
description,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct LayoutPrimitiveInfo {
pub rect: LayoutRect,
pub clip_rect: LayoutRect,
pub flags: PrimitiveFlags,
pub hit_info: Option<ItemTag>,
}
impl LayoutPrimitiveInfo {
pub fn with_clip_rect(rect: LayoutRect, clip_rect: LayoutRect) -> Self {
Self {
rect,
clip_rect,
flags: PrimitiveFlags::default(),
hit_info: None,
}
}
}