зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1417062
- Update webrender to commit d490a74c438d987122c600afca6bb2247ab38637. r=nical
MozReview-Commit-ID: 4i2RKAFTAMd --HG-- extra : rebase_source : df9734e5475a2d0e133553dbb10b07f417d5b985
This commit is contained in:
Родитель
7387e4c386
Коммит
6b90531555
|
@ -175,4 +175,4 @@ Troubleshooting tips:
|
|||
-------------------------------------------------------------------------------
|
||||
|
||||
The version of WebRender currently in the tree is:
|
||||
8a39cf24f493e894a66c2465dd310a2b2923e558
|
||||
d490a74c438d987122c600afca6bb2247ab38637
|
||||
|
|
|
@ -11,7 +11,6 @@ default = ["freetype-lib"]
|
|||
freetype-lib = ["freetype/servo-freetype-sys"]
|
||||
profiler = ["thread_profiler/thread_profiler"]
|
||||
debugger = ["ws", "serde_json", "serde", "serde_derive"]
|
||||
query = []
|
||||
|
||||
[dependencies]
|
||||
app_units = "0.5.6"
|
||||
|
|
|
@ -24,7 +24,9 @@ use webrender::api::*;
|
|||
|
||||
struct App {
|
||||
property_key: PropertyBindingKey<LayoutTransform>,
|
||||
opacity_key: PropertyBindingKey<f32>,
|
||||
transform: LayoutTransform,
|
||||
opacity: f32,
|
||||
}
|
||||
|
||||
impl Example for App {
|
||||
|
@ -49,6 +51,10 @@ impl Example for App {
|
|||
.. LayoutPrimitiveInfo::new(bounds)
|
||||
};
|
||||
|
||||
let filters = vec![
|
||||
FilterOp::Opacity(PropertyBinding::Binding(self.opacity_key), self.opacity),
|
||||
];
|
||||
|
||||
builder.push_stacking_context(
|
||||
&info,
|
||||
ScrollPolicy::Scrollable,
|
||||
|
@ -56,7 +62,7 @@ impl Example for App {
|
|||
TransformStyle::Flat,
|
||||
None,
|
||||
MixBlendMode::Normal,
|
||||
Vec::new(),
|
||||
filters,
|
||||
);
|
||||
|
||||
// Fill it with a white rect
|
||||
|
@ -68,18 +74,21 @@ impl Example for App {
|
|||
fn on_event(&mut self, event: glutin::Event, api: &RenderApi, document_id: DocumentId) -> bool {
|
||||
match event {
|
||||
glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
|
||||
let (offset_x, offset_y, angle) = match key {
|
||||
glutin::VirtualKeyCode::Down => (0.0, 10.0, 0.0),
|
||||
glutin::VirtualKeyCode::Up => (0.0, -10.0, 0.0),
|
||||
glutin::VirtualKeyCode::Right => (10.0, 0.0, 0.0),
|
||||
glutin::VirtualKeyCode::Left => (-10.0, 0.0, 0.0),
|
||||
glutin::VirtualKeyCode::Comma => (0.0, 0.0, 0.1),
|
||||
glutin::VirtualKeyCode::Period => (0.0, 0.0, -0.1),
|
||||
let (offset_x, offset_y, angle, delta_opacity) = match key {
|
||||
glutin::VirtualKeyCode::Down => (0.0, 10.0, 0.0, 0.0),
|
||||
glutin::VirtualKeyCode::Up => (0.0, -10.0, 0.0, 0.0),
|
||||
glutin::VirtualKeyCode::Right => (10.0, 0.0, 0.0, 0.0),
|
||||
glutin::VirtualKeyCode::Left => (-10.0, 0.0, 0.0, 0.0),
|
||||
glutin::VirtualKeyCode::Comma => (0.0, 0.0, 0.1, 0.0),
|
||||
glutin::VirtualKeyCode::Period => (0.0, 0.0, -0.1, 0.0),
|
||||
glutin::VirtualKeyCode::Z => (0.0, 0.0, 0.0, -0.1),
|
||||
glutin::VirtualKeyCode::X => (0.0, 0.0, 0.0, 0.1),
|
||||
_ => return false,
|
||||
};
|
||||
// Update the transform based on the keyboard input and push it to
|
||||
// webrender using the generate_frame API. This will recomposite with
|
||||
// the updated transform.
|
||||
self.opacity += delta_opacity;
|
||||
let new_transform = self.transform
|
||||
.pre_rotate(0.0, 0.0, 1.0, Radians::new(angle))
|
||||
.post_translate(LayoutVector3D::new(offset_x, offset_y, 0.0));
|
||||
|
@ -92,7 +101,12 @@ impl Example for App {
|
|||
value: new_transform,
|
||||
},
|
||||
],
|
||||
floats: vec![],
|
||||
floats: vec![
|
||||
PropertyValue {
|
||||
key: self.opacity_key,
|
||||
value: self.opacity,
|
||||
}
|
||||
],
|
||||
}),
|
||||
);
|
||||
self.transform = new_transform;
|
||||
|
@ -107,7 +121,9 @@ impl Example for App {
|
|||
fn main() {
|
||||
let mut app = App {
|
||||
property_key: PropertyBindingKey::new(42), // arbitrary magic number
|
||||
opacity_key: PropertyBindingKey::new(43),
|
||||
transform: LayoutTransform::create_translation(0.0, 0.0, 0.0),
|
||||
opacity: 0.5,
|
||||
};
|
||||
boilerplate::main_wrapper(&mut app, None);
|
||||
}
|
||||
|
|
|
@ -182,8 +182,7 @@ pub fn main_wrapper(example: &mut Example, options: Option<webrender::RendererOp
|
|||
for event in events {
|
||||
match event {
|
||||
glutin::Event::Closed |
|
||||
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
|
||||
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
|
||||
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) => break 'outer,
|
||||
|
||||
glutin::Event::KeyboardInput(
|
||||
glutin::ElementState::Pressed,
|
||||
|
@ -221,6 +220,13 @@ pub fn main_wrapper(example: &mut Example, options: Option<webrender::RendererOp
|
|||
flags.toggle(webrender::DebugFlags::ALPHA_PRIM_DBG);
|
||||
renderer.set_debug_flags(flags);
|
||||
}
|
||||
glutin::Event::KeyboardInput(
|
||||
glutin::ElementState::Pressed,
|
||||
_,
|
||||
Some(glutin::VirtualKeyCode::Q),
|
||||
) => {
|
||||
renderer.toggle_queries_enabled();
|
||||
}
|
||||
glutin::Event::KeyboardInput(
|
||||
glutin::ElementState::Pressed,
|
||||
_,
|
||||
|
|
|
@ -90,39 +90,23 @@ void main(void) {
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
vec4 Blur(float radius, vec2 direction) {
|
||||
// TODO(gw): Support blur in WR2!
|
||||
return vec4(1.0);
|
||||
}
|
||||
|
||||
vec4 Contrast(vec4 Cs, float amount) {
|
||||
return vec4(Cs.rgb * amount - 0.5 * amount + 0.5, 1.0);
|
||||
return vec4(Cs.rgb * amount - 0.5 * amount + 0.5, Cs.a);
|
||||
}
|
||||
|
||||
vec4 Invert(vec4 Cs, float amount) {
|
||||
Cs.rgb /= Cs.a;
|
||||
|
||||
vec3 color = mix(Cs.rgb, vec3(1.0) - Cs.rgb, amount);
|
||||
|
||||
// Pre-multiply the alpha into the output value.
|
||||
return vec4(color.rgb * Cs.a, Cs.a);
|
||||
return vec4(mix(Cs.rgb, vec3(1.0) - Cs.rgb, amount), Cs.a);
|
||||
}
|
||||
|
||||
vec4 Brightness(vec4 Cs, float amount) {
|
||||
// Un-premultiply the input.
|
||||
Cs.rgb /= Cs.a;
|
||||
|
||||
// Apply the brightness factor.
|
||||
// Resulting color needs to be clamped to output range
|
||||
// since we are pre-multiplying alpha in the shader.
|
||||
vec3 color = clamp(Cs.rgb * amount, vec3(0.0), vec3(1.0));
|
||||
|
||||
// Pre-multiply the alpha into the output value.
|
||||
return vec4(color.rgb * Cs.a, Cs.a);
|
||||
return vec4(clamp(Cs.rgb * amount, vec3(0.0), vec3(1.0)), Cs.a);
|
||||
}
|
||||
|
||||
vec4 Opacity(vec4 Cs, float amount) {
|
||||
return Cs * amount;
|
||||
return vec4(Cs.rgb, Cs.a * amount);
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
|
@ -133,10 +117,12 @@ void main(void) {
|
|||
discard;
|
||||
}
|
||||
|
||||
// Un-premultiply the input.
|
||||
Cs.rgb /= Cs.a;
|
||||
|
||||
switch (vOp) {
|
||||
case 0:
|
||||
// Gaussian blur is specially handled:
|
||||
oFragColor = Cs;// Blur(vAmount, vec2(0,0));
|
||||
oFragColor = Cs;
|
||||
break;
|
||||
case 1:
|
||||
oFragColor = Contrast(Cs, vAmount);
|
||||
|
@ -153,5 +139,8 @@ void main(void) {
|
|||
default:
|
||||
oFragColor = vColorMat * Cs;
|
||||
}
|
||||
|
||||
// Pre-multiply the alpha into the output value.
|
||||
oFragColor.rgb *= oFragColor.a;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
use api::{BorderRadiusKind, ColorF, LayerPoint, LayerRect, LayerSize, LayerVector2D};
|
||||
use api::{BorderRadius, BoxShadowClipMode, LayoutSize, LayerPrimitiveInfo};
|
||||
use api::{ClipMode, ComplexClipRegion, EdgeAaSegmentMask, LocalClip, ClipAndScrollInfo};
|
||||
use api::{ClipMode, ClipAndScrollInfo, ComplexClipRegion, EdgeAaSegmentMask, LocalClip};
|
||||
use api::{PipelineId};
|
||||
use clip::ClipSource;
|
||||
use frame_builder::FrameBuilder;
|
||||
use prim_store::{PrimitiveContainer, RectangleContent, RectanglePrimitive};
|
||||
|
@ -24,6 +25,7 @@ pub const MASK_CORNER_PADDING: f32 = 4.0;
|
|||
impl FrameBuilder {
|
||||
pub fn add_box_shadow(
|
||||
&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
clip_and_scroll: ClipAndScrollInfo,
|
||||
prim_info: &LayerPrimitiveInfo,
|
||||
box_offset: &LayerVector2D,
|
||||
|
@ -185,13 +187,12 @@ impl FrameBuilder {
|
|||
Vec::new(),
|
||||
clip_mode,
|
||||
radii_kind,
|
||||
pipeline_id,
|
||||
);
|
||||
pic_prim.add_primitive(
|
||||
brush_prim_index,
|
||||
&brush_rect,
|
||||
clip_and_scroll
|
||||
);
|
||||
pic_prim.build();
|
||||
|
||||
// TODO(gw): Right now, we always use a clip out
|
||||
// mask for outset shadows. We can make this
|
||||
|
@ -264,13 +265,12 @@ impl FrameBuilder {
|
|||
BoxShadowClipMode::Inset,
|
||||
// TODO(gw): Make use of optimization for inset.
|
||||
BorderRadiusKind::NonUniform,
|
||||
pipeline_id,
|
||||
);
|
||||
pic_prim.add_primitive(
|
||||
brush_prim_index,
|
||||
&brush_rect,
|
||||
clip_and_scroll
|
||||
);
|
||||
pic_prim.build();
|
||||
|
||||
// Draw the picture one pixel outside the original
|
||||
// rect to account for the inflate above. This
|
||||
|
|
|
@ -13,7 +13,7 @@ use prim_store::{ClipData, ImageMaskData};
|
|||
use resource_cache::ResourceCache;
|
||||
use util::{extract_inner_rect_safe, TransformedRect};
|
||||
|
||||
const MAX_CLIP: f32 = 1000000.0;
|
||||
pub const MAX_CLIP: f32 = 1000000.0;
|
||||
|
||||
pub type ClipStore = FreeList<ClipSources>;
|
||||
pub type ClipSourcesHandle = FreeListHandle<ClipSources>;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
use api::{ClipId, DeviceIntRect, LayerPixel, LayerPoint, LayerRect, LayerSize};
|
||||
use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, LayoutVector2D, PipelineId};
|
||||
use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity};
|
||||
use api::{StickyOffsetBounds, WorldPoint};
|
||||
use api::{LayoutTransform, PropertyBinding, StickyOffsetBounds, WorldPoint};
|
||||
use clip::{ClipSourcesHandle, ClipStore};
|
||||
use clip_scroll_tree::{CoordinateSystemId, TransformUpdateState};
|
||||
use euclid::SideOffsets2D;
|
||||
|
@ -14,6 +14,7 @@ use gpu_cache::GpuCache;
|
|||
use gpu_types::{ClipScrollNodeIndex, ClipScrollNodeData};
|
||||
use render_task::{ClipChain, ClipChainNode, ClipWorkItem};
|
||||
use resource_cache::ResourceCache;
|
||||
use scene::SceneProperties;
|
||||
use spring::{DAMPING, STIFFNESS, Spring};
|
||||
use std::rc::Rc;
|
||||
use util::{MatrixHelpers, MaxRect};
|
||||
|
@ -185,12 +186,16 @@ impl ClipScrollNode {
|
|||
pub fn new_reference_frame(
|
||||
parent_id: Option<ClipId>,
|
||||
frame_rect: &LayerRect,
|
||||
transform: &LayerToScrollTransform,
|
||||
source_transform: Option<PropertyBinding<LayoutTransform>>,
|
||||
source_perspective: Option<LayoutTransform>,
|
||||
origin_in_parent_reference_frame: LayerVector2D,
|
||||
pipeline_id: PipelineId,
|
||||
) -> Self {
|
||||
let identity = LayoutTransform::identity();
|
||||
let info = ReferenceFrameInfo {
|
||||
transform: *transform,
|
||||
resolved_transform: LayerToScrollTransform::identity(),
|
||||
source_transform: source_transform.unwrap_or(PropertyBinding::Value(identity)),
|
||||
source_perspective: source_perspective.unwrap_or(identity),
|
||||
origin_in_parent_reference_frame,
|
||||
};
|
||||
Self::new(pipeline_id, parent_id, frame_rect, NodeType::ReferenceFrame(info))
|
||||
|
@ -273,12 +278,13 @@ impl ClipScrollNode {
|
|||
clip_store: &mut ClipStore,
|
||||
resource_cache: &mut ResourceCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
scene_properties: &SceneProperties,
|
||||
) {
|
||||
// We set this earlier so that we can use it before we have all the data necessary
|
||||
// to populate the ClipScrollNodeData.
|
||||
self.node_data_index = ClipScrollNodeIndex(node_data.len() as u32);
|
||||
|
||||
self.update_transform(state);
|
||||
self.update_transform(state, scene_properties);
|
||||
self.update_clip_work_item(
|
||||
state,
|
||||
device_pixel_ratio,
|
||||
|
@ -309,6 +315,7 @@ impl ClipScrollNode {
|
|||
}
|
||||
None => {
|
||||
state.combined_outer_clip_bounds = DeviceIntRect::zero();
|
||||
self.combined_clip_outer_bounds = DeviceIntRect::zero();
|
||||
ClipScrollNodeData::invalid()
|
||||
}
|
||||
};
|
||||
|
@ -365,7 +372,11 @@ impl ClipScrollNode {
|
|||
state.parent_clip_chain = self.clip_chain_node.clone();
|
||||
}
|
||||
|
||||
pub fn update_transform(&mut self, state: &mut TransformUpdateState) {
|
||||
pub fn update_transform(
|
||||
&mut self,
|
||||
state: &mut TransformUpdateState,
|
||||
scene_properties: &SceneProperties,
|
||||
) {
|
||||
// We calculate this here to avoid a double-borrow later.
|
||||
let sticky_offset = self.calculate_sticky_offset(
|
||||
&state.nearest_scrolling_ancestor_offset,
|
||||
|
@ -373,12 +384,21 @@ impl ClipScrollNode {
|
|||
);
|
||||
|
||||
let (local_transform, accumulated_scroll_offset) = match self.node_type {
|
||||
NodeType::ReferenceFrame(ref info) => {
|
||||
self.combined_local_viewport_rect = info.transform
|
||||
NodeType::ReferenceFrame(ref mut info) => {
|
||||
// Resolve the transform against any property bindings.
|
||||
let source_transform = scene_properties.resolve_layout_transform(&info.source_transform);
|
||||
info.resolved_transform = LayerToScrollTransform::create_translation(
|
||||
info.origin_in_parent_reference_frame.x,
|
||||
info.origin_in_parent_reference_frame.y,
|
||||
0.0
|
||||
).pre_mul(&source_transform)
|
||||
.pre_mul(&info.source_perspective);
|
||||
|
||||
self.combined_local_viewport_rect = info.resolved_transform
|
||||
.with_destination::<LayerPixel>()
|
||||
.inverse_rect_footprint(&state.parent_combined_viewport_rect);
|
||||
self.reference_frame_relative_scroll_offset = LayerVector2D::zero();
|
||||
(info.transform, state.parent_accumulated_scroll_offset)
|
||||
(info.resolved_transform, state.parent_accumulated_scroll_offset)
|
||||
}
|
||||
NodeType::Clip(_) | NodeType::ScrollFrame(_) => {
|
||||
// Move the parent's viewport into the local space (of the node origin)
|
||||
|
@ -436,7 +456,7 @@ impl ClipScrollNode {
|
|||
state.nearest_scrolling_ancestor_viewport
|
||||
.translate(&info.origin_in_parent_reference_frame);
|
||||
|
||||
if !info.transform.preserves_2d_axis_alignment() {
|
||||
if !info.resolved_transform.preserves_2d_axis_alignment() {
|
||||
state.current_coordinate_system_id = state.next_coordinate_system_id;
|
||||
state.next_coordinate_system_id = state.next_coordinate_system_id.next();
|
||||
}
|
||||
|
@ -787,7 +807,14 @@ impl ScrollingState {
|
|||
pub struct ReferenceFrameInfo {
|
||||
/// The transformation that establishes this reference frame, relative to the parent
|
||||
/// reference frame. The origin of the reference frame is included in the transformation.
|
||||
pub transform: LayerToScrollTransform,
|
||||
pub resolved_transform: LayerToScrollTransform,
|
||||
|
||||
/// The source transform and perspective matrices provided by the stacking context
|
||||
/// that forms this reference frame. We maintain the property binding information
|
||||
/// here so that we can resolve the animated transform and update the tree each
|
||||
/// frame.
|
||||
pub source_transform: PropertyBinding<LayoutTransform>,
|
||||
pub source_perspective: LayoutTransform,
|
||||
|
||||
/// The original, not including the transform and relative to the parent reference frame,
|
||||
/// origin of this reference frame. This is already rolled into the `transform' property, but
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use api::{ClipId, DeviceIntRect, LayerPoint, LayerRect, LayerToScrollTransform};
|
||||
use api::{ClipId, DeviceIntRect, LayerPoint, LayerRect};
|
||||
use api::{LayerToWorldTransform, LayerVector2D, PipelineId, ScrollClamping, ScrollEventPhase};
|
||||
use api::{ScrollLayerState, ScrollLocation, WorldPoint};
|
||||
use api::{PropertyBinding, LayoutTransform, ScrollLayerState, ScrollLocation, WorldPoint};
|
||||
use clip::ClipStore;
|
||||
use clip_scroll_node::{ClipScrollNode, NodeType, ScrollingState, StickyFrameInfo};
|
||||
use gpu_cache::GpuCache;
|
||||
|
@ -13,6 +13,7 @@ use internal_types::{FastHashMap, FastHashSet};
|
|||
use print_tree::{PrintTree, PrintTreePrinter};
|
||||
use render_task::ClipChain;
|
||||
use resource_cache::ResourceCache;
|
||||
use scene::SceneProperties;
|
||||
|
||||
pub type ScrollStates = FastHashMap<ClipId, ScrollingState>;
|
||||
|
||||
|
@ -193,9 +194,8 @@ impl ClipScrollTree {
|
|||
return false;
|
||||
}
|
||||
|
||||
let point_in_clips = transformed_point - node.local_clip_rect.origin.to_vector();
|
||||
for &(ref clip, _) in clip_store.get(&clip_sources_handle).clips() {
|
||||
if !clip.contains(&point_in_clips) {
|
||||
if !clip.contains(&transformed_point) {
|
||||
cache.insert(*node_id, None);
|
||||
return false;
|
||||
}
|
||||
|
@ -334,6 +334,7 @@ impl ClipScrollTree {
|
|||
gpu_cache: &mut GpuCache,
|
||||
pan: LayerPoint,
|
||||
node_data: &mut Vec<ClipScrollNodeData>,
|
||||
scene_properties: &SceneProperties,
|
||||
) {
|
||||
if self.nodes.is_empty() {
|
||||
return;
|
||||
|
@ -365,6 +366,7 @@ impl ClipScrollTree {
|
|||
resource_cache,
|
||||
gpu_cache,
|
||||
node_data,
|
||||
scene_properties,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -377,6 +379,7 @@ impl ClipScrollTree {
|
|||
resource_cache: &mut ResourceCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
node_data: &mut Vec<ClipScrollNodeData>,
|
||||
scene_properties: &SceneProperties,
|
||||
) {
|
||||
// TODO(gw): This is an ugly borrow check workaround to clone these.
|
||||
// Restructure this to avoid the clones!
|
||||
|
@ -394,6 +397,7 @@ impl ClipScrollTree {
|
|||
clip_store,
|
||||
resource_cache,
|
||||
gpu_cache,
|
||||
scene_properties,
|
||||
);
|
||||
|
||||
node.children.clone()
|
||||
|
@ -408,6 +412,7 @@ impl ClipScrollTree {
|
|||
resource_cache,
|
||||
gpu_cache,
|
||||
node_data,
|
||||
scene_properties,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -441,7 +446,8 @@ impl ClipScrollTree {
|
|||
pub fn add_reference_frame(
|
||||
&mut self,
|
||||
rect: &LayerRect,
|
||||
transform: &LayerToScrollTransform,
|
||||
source_transform: Option<PropertyBinding<LayoutTransform>>,
|
||||
source_perspective: Option<LayoutTransform>,
|
||||
origin_in_parent_reference_frame: LayerVector2D,
|
||||
pipeline_id: PipelineId,
|
||||
parent_id: Option<ClipId>,
|
||||
|
@ -456,7 +462,8 @@ impl ClipScrollTree {
|
|||
let node = ClipScrollNode::new_reference_frame(
|
||||
parent_id,
|
||||
rect,
|
||||
transform,
|
||||
source_transform,
|
||||
source_perspective,
|
||||
origin_in_parent_reference_frame,
|
||||
pipeline_id,
|
||||
);
|
||||
|
@ -516,7 +523,7 @@ impl ClipScrollTree {
|
|||
pt.end_level();
|
||||
}
|
||||
NodeType::ReferenceFrame(ref info) => {
|
||||
pt.new_level(format!("ReferenceFrame {:?}", info.transform));
|
||||
pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform));
|
||||
pt.add_item(format!("id: {:?}", id));
|
||||
}
|
||||
NodeType::ScrollFrame(scrolling_info) => {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use api::{ColorU, DeviceIntRect, DeviceUintSize, ImageFormat};
|
||||
use debug_font_data;
|
||||
use device::{Device, GpuMarker, Program, Texture, TextureSlot, VertexDescriptor, VAO};
|
||||
use device::{Device, Program, Texture, TextureSlot, VertexDescriptor, VAO};
|
||||
use device::{TextureFilter, TextureTarget, VertexAttribute, VertexAttributeKind, VertexUsageHint};
|
||||
use euclid::{Point2D, Rect, Size2D, Transform3D};
|
||||
use internal_types::{ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE};
|
||||
|
@ -105,7 +105,7 @@ pub struct DebugRenderer {
|
|||
}
|
||||
|
||||
impl DebugRenderer {
|
||||
pub fn new(device: &mut Device) -> DebugRenderer {
|
||||
pub fn new(device: &mut Device) -> Self {
|
||||
let font_program = device.create_program("debug_font", "", &DESC_FONT).unwrap();
|
||||
device.bind_shader_samplers(&font_program, &[("sColor0", DebugSampler::Font)]);
|
||||
|
||||
|
@ -263,7 +263,6 @@ impl DebugRenderer {
|
|||
}
|
||||
|
||||
pub fn render(&mut self, device: &mut Device, viewport_size: &DeviceUintSize) {
|
||||
let _gm = GpuMarker::new(device.rc_gl(), "debug");
|
||||
device.disable_depth();
|
||||
device.set_blend(true);
|
||||
device.set_blend_mode_premultiplied_alpha();
|
||||
|
|
|
@ -53,6 +53,10 @@ impl ws::Handler for Server {
|
|||
"disable_render_target_debug" => DebugCommand::EnableRenderTargetDebug(false),
|
||||
"enable_alpha_rects_debug" => DebugCommand::EnableAlphaRectsDebug(true),
|
||||
"disable_alpha_rects_debug" => DebugCommand::EnableAlphaRectsDebug(false),
|
||||
"enable_gpu_time_queries" => DebugCommand::EnableGpuTimeQueries(true),
|
||||
"disable_gpu_time_queries" => DebugCommand::EnableGpuTimeQueries(false),
|
||||
"enable_gpu_sample_queries" => DebugCommand::EnableGpuSampleQueries(true),
|
||||
"disable_gpu_sample_queries" => DebugCommand::EnableGpuSampleQueries(false),
|
||||
"fetch_passes" => DebugCommand::FetchPasses,
|
||||
"fetch_documents" => DebugCommand::FetchDocuments,
|
||||
"fetch_clipscrolltree" => DebugCommand::FetchClipScrollTree,
|
||||
|
|
|
@ -485,308 +485,6 @@ pub struct VBOId(gl::GLuint);
|
|||
#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
|
||||
struct IBOId(gl::GLuint);
|
||||
|
||||
#[cfg(feature = "query")]
|
||||
const MAX_PROFILE_FRAMES: usize = 4;
|
||||
|
||||
pub trait NamedTag {
|
||||
fn get_label(&self) -> &str;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GpuTimer<T> {
|
||||
pub tag: T,
|
||||
pub time_ns: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GpuSampler<T> {
|
||||
pub tag: T,
|
||||
pub count: u64,
|
||||
}
|
||||
|
||||
#[cfg(feature = "query")]
|
||||
pub struct QuerySet<T> {
|
||||
set: Vec<gl::GLuint>,
|
||||
data: Vec<T>,
|
||||
pending: gl::GLuint,
|
||||
}
|
||||
|
||||
#[cfg(feature = "query")]
|
||||
impl<T> QuerySet<T> {
|
||||
fn new(set: Vec<gl::GLuint>) -> Self {
|
||||
QuerySet {
|
||||
set,
|
||||
data: Vec::new(),
|
||||
pending: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.data.clear();
|
||||
self.pending = 0;
|
||||
}
|
||||
|
||||
fn add(&mut self, value: T) -> Option<gl::GLuint> {
|
||||
assert_eq!(self.pending, 0);
|
||||
self.set.get(self.data.len()).cloned().map(|query_id| {
|
||||
self.data.push(value);
|
||||
self.pending = query_id;
|
||||
query_id
|
||||
})
|
||||
}
|
||||
|
||||
fn take<F: Fn(&mut T, gl::GLuint)>(&mut self, fun: F) -> Vec<T> {
|
||||
let mut data = mem::replace(&mut self.data, Vec::new());
|
||||
for (value, &query) in data.iter_mut().zip(self.set.iter()) {
|
||||
fun(value, query)
|
||||
}
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "query")]
|
||||
pub struct GpuFrameProfile<T> {
|
||||
gl: Rc<gl::Gl>,
|
||||
timers: QuerySet<GpuTimer<T>>,
|
||||
samplers: QuerySet<GpuSampler<T>>,
|
||||
frame_id: FrameId,
|
||||
inside_frame: bool,
|
||||
}
|
||||
|
||||
#[cfg(feature = "query")]
|
||||
impl<T> GpuFrameProfile<T> {
|
||||
const MAX_TIMERS_PER_FRAME: usize = 256;
|
||||
// disable samplers on OSX due to driver bugs
|
||||
#[cfg(target_os = "macos")]
|
||||
const MAX_SAMPLERS_PER_FRAME: usize = 0;
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
const MAX_SAMPLERS_PER_FRAME: usize = 16;
|
||||
|
||||
fn new(gl: Rc<gl::Gl>) -> Self {
|
||||
assert_eq!(gl.get_type(), gl::GlType::Gl);
|
||||
let time_queries = gl.gen_queries(Self::MAX_TIMERS_PER_FRAME as _);
|
||||
let sample_queries = gl.gen_queries(Self::MAX_SAMPLERS_PER_FRAME as _);
|
||||
|
||||
GpuFrameProfile {
|
||||
gl,
|
||||
timers: QuerySet::new(time_queries),
|
||||
samplers: QuerySet::new(sample_queries),
|
||||
frame_id: FrameId(0),
|
||||
inside_frame: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn begin_frame(&mut self, frame_id: FrameId) {
|
||||
self.frame_id = frame_id;
|
||||
self.timers.reset();
|
||||
self.samplers.reset();
|
||||
self.inside_frame = true;
|
||||
}
|
||||
|
||||
fn end_frame(&mut self) {
|
||||
self.done_marker();
|
||||
self.done_sampler();
|
||||
self.inside_frame = false;
|
||||
}
|
||||
|
||||
fn done_marker(&mut self) {
|
||||
debug_assert!(self.inside_frame);
|
||||
if self.timers.pending != 0 {
|
||||
self.gl.end_query(gl::TIME_ELAPSED);
|
||||
self.timers.pending = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn add_marker(&mut self, tag: T) -> GpuMarker
|
||||
where
|
||||
T: NamedTag,
|
||||
{
|
||||
self.done_marker();
|
||||
|
||||
let marker = GpuMarker::new(&self.gl, tag.get_label());
|
||||
|
||||
if let Some(query) = self.timers.add(GpuTimer { tag, time_ns: 0 }) {
|
||||
self.gl.begin_query(gl::TIME_ELAPSED, query);
|
||||
}
|
||||
|
||||
marker
|
||||
}
|
||||
|
||||
fn done_sampler(&mut self) {
|
||||
debug_assert!(self.inside_frame);
|
||||
if self.samplers.pending != 0 {
|
||||
self.gl.end_query(gl::SAMPLES_PASSED);
|
||||
self.samplers.pending = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn add_sampler(&mut self, tag: T)
|
||||
where
|
||||
T: NamedTag,
|
||||
{
|
||||
self.done_sampler();
|
||||
|
||||
if let Some(query) = self.samplers.add(GpuSampler { tag, count: 0 }) {
|
||||
self.gl.begin_query(gl::SAMPLES_PASSED, query);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> bool {
|
||||
!self.timers.set.is_empty() || !self.samplers.set.is_empty()
|
||||
}
|
||||
|
||||
fn build_samples(&mut self) -> (Vec<GpuTimer<T>>, Vec<GpuSampler<T>>) {
|
||||
debug_assert!(!self.inside_frame);
|
||||
let gl = &self.gl;
|
||||
|
||||
(
|
||||
self.timers.take(|timer, query| {
|
||||
timer.time_ns = gl.get_query_object_ui64v(query, gl::QUERY_RESULT)
|
||||
}),
|
||||
self.samplers.take(|sampler, query| {
|
||||
sampler.count = gl.get_query_object_ui64v(query, gl::QUERY_RESULT)
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "query")]
|
||||
impl<T> Drop for GpuFrameProfile<T> {
|
||||
fn drop(&mut self) {
|
||||
if !self.timers.set.is_empty() {
|
||||
self.gl.delete_queries(&self.timers.set);
|
||||
}
|
||||
if !self.samplers.set.is_empty() {
|
||||
self.gl.delete_queries(&self.samplers.set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "query")]
|
||||
pub struct GpuProfiler<T> {
|
||||
frames: [GpuFrameProfile<T>; MAX_PROFILE_FRAMES],
|
||||
next_frame: usize,
|
||||
}
|
||||
|
||||
#[cfg(feature = "query")]
|
||||
impl<T> GpuProfiler<T> {
|
||||
pub fn new(gl: &Rc<gl::Gl>) -> Self {
|
||||
GpuProfiler {
|
||||
next_frame: 0,
|
||||
frames: [
|
||||
GpuFrameProfile::new(Rc::clone(gl)),
|
||||
GpuFrameProfile::new(Rc::clone(gl)),
|
||||
GpuFrameProfile::new(Rc::clone(gl)),
|
||||
GpuFrameProfile::new(Rc::clone(gl)),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_samples(&mut self) -> Option<(FrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>)> {
|
||||
let frame = &mut self.frames[self.next_frame];
|
||||
if frame.is_valid() {
|
||||
let (timers, samplers) = frame.build_samples();
|
||||
Some((frame.frame_id, timers, samplers))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn begin_frame(&mut self, frame_id: FrameId) {
|
||||
let frame = &mut self.frames[self.next_frame];
|
||||
frame.begin_frame(frame_id);
|
||||
}
|
||||
|
||||
pub fn end_frame(&mut self) {
|
||||
let frame = &mut self.frames[self.next_frame];
|
||||
frame.end_frame();
|
||||
self.next_frame = (self.next_frame + 1) % MAX_PROFILE_FRAMES;
|
||||
}
|
||||
|
||||
pub fn add_marker(&mut self, tag: T) -> GpuMarker
|
||||
where
|
||||
T: NamedTag,
|
||||
{
|
||||
self.frames[self.next_frame].add_marker(tag)
|
||||
}
|
||||
|
||||
pub fn add_sampler(&mut self, tag: T)
|
||||
where
|
||||
T: NamedTag,
|
||||
{
|
||||
self.frames[self.next_frame].add_sampler(tag)
|
||||
}
|
||||
|
||||
pub fn done_sampler(&mut self) {
|
||||
self.frames[self.next_frame].done_sampler()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "query"))]
|
||||
pub struct GpuProfiler<T>(Option<T>);
|
||||
|
||||
#[cfg(not(feature = "query"))]
|
||||
impl<T> GpuProfiler<T> {
|
||||
pub fn new(_: &Rc<gl::Gl>) -> Self {
|
||||
GpuProfiler(None)
|
||||
}
|
||||
|
||||
pub fn build_samples(&mut self) -> Option<(FrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>)> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn begin_frame(&mut self, _: FrameId) {}
|
||||
|
||||
pub fn end_frame(&mut self) {}
|
||||
|
||||
pub fn add_marker(&mut self, _: T) -> GpuMarker {
|
||||
GpuMarker {}
|
||||
}
|
||||
|
||||
pub fn add_sampler(&mut self, _: T) {}
|
||||
|
||||
pub fn done_sampler(&mut self) {}
|
||||
}
|
||||
|
||||
|
||||
#[must_use]
|
||||
pub struct GpuMarker {
|
||||
#[cfg(feature = "query")]
|
||||
gl: Rc<gl::Gl>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "query")]
|
||||
impl GpuMarker {
|
||||
pub fn new(gl: &Rc<gl::Gl>, message: &str) -> Self {
|
||||
debug_assert_eq!(gl.get_type(), gl::GlType::Gl);
|
||||
gl.push_group_marker_ext(message);
|
||||
GpuMarker { gl: Rc::clone(gl) }
|
||||
}
|
||||
|
||||
pub fn fire(gl: &gl::Gl, message: &str) {
|
||||
debug_assert_eq!(gl.get_type(), gl::GlType::Gl);
|
||||
gl.insert_event_marker_ext(message);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "query")]
|
||||
impl Drop for GpuMarker {
|
||||
fn drop(&mut self) {
|
||||
self.gl.pop_group_marker_ext();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "query"))]
|
||||
impl GpuMarker {
|
||||
#[inline]
|
||||
pub fn new(_: &Rc<gl::Gl>, _: &str) -> Self {
|
||||
GpuMarker{}
|
||||
}
|
||||
#[inline]
|
||||
pub fn fire(_: &gl::Gl, _: &str) {}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum VertexUsageHint {
|
||||
Static,
|
||||
|
@ -1138,15 +836,14 @@ impl Device {
|
|||
let (internal_format, gl_format) = gl_texture_formats_for_image_format(self.gl(), format);
|
||||
let type_ = gl_type_for_texture_format(format);
|
||||
|
||||
self.bind_texture(DEFAULT_TEXTURE, texture);
|
||||
self.set_texture_parameters(texture.target, filter);
|
||||
|
||||
match mode {
|
||||
RenderTargetMode::RenderTarget => {
|
||||
self.bind_texture(DEFAULT_TEXTURE, texture);
|
||||
self.set_texture_parameters(texture.target, filter);
|
||||
self.update_texture_storage(texture, layer_count, resized);
|
||||
self.update_texture_storage(texture, resized);
|
||||
}
|
||||
RenderTargetMode::None => {
|
||||
self.bind_texture(DEFAULT_TEXTURE, texture);
|
||||
self.set_texture_parameters(texture.target, filter);
|
||||
let expanded_data: Vec<u8>;
|
||||
let actual_pixels = if pixels.is_some() && format == ImageFormat::A8 &&
|
||||
cfg!(any(target_arch = "arm", target_arch = "aarch64"))
|
||||
|
@ -1197,13 +894,13 @@ impl Device {
|
|||
|
||||
/// Updates the texture storage for the texture, creating
|
||||
/// FBOs as required.
|
||||
fn update_texture_storage(&mut self, texture: &mut Texture, layer_count: i32, resized: bool) {
|
||||
assert!(layer_count > 0);
|
||||
fn update_texture_storage(&mut self, texture: &mut Texture, resized: bool) {
|
||||
assert!(texture.layer_count > 0);
|
||||
assert_eq!(texture.target, gl::TEXTURE_2D_ARRAY);
|
||||
|
||||
let current_layer_count = texture.fbo_ids.len() as i32;
|
||||
let needed_layer_count = texture.layer_count - texture.fbo_ids.len() as i32;
|
||||
// If the texture is already the required size skip.
|
||||
if current_layer_count == layer_count && !resized {
|
||||
if needed_layer_count == 0 && !resized {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1217,35 +914,37 @@ impl Device {
|
|||
internal_format as gl::GLint,
|
||||
texture.width as gl::GLint,
|
||||
texture.height as gl::GLint,
|
||||
layer_count,
|
||||
texture.layer_count,
|
||||
0,
|
||||
gl_format,
|
||||
type_,
|
||||
None,
|
||||
);
|
||||
|
||||
let needed_layer_count = layer_count - current_layer_count;
|
||||
if needed_layer_count > 0 {
|
||||
// Create more framebuffers to fill the gap
|
||||
let new_fbos = self.gl.gen_framebuffers(needed_layer_count);
|
||||
texture
|
||||
.fbo_ids
|
||||
.extend(new_fbos.into_iter().map(|id| FBOId(id)));
|
||||
.extend(new_fbos.into_iter().map(FBOId));
|
||||
} else if needed_layer_count < 0 {
|
||||
// Remove extra framebuffers
|
||||
for old in texture.fbo_ids.drain(layer_count as usize ..) {
|
||||
for old in texture.fbo_ids.drain(texture.layer_count as usize ..) {
|
||||
self.gl.delete_framebuffers(&[old.0]);
|
||||
}
|
||||
}
|
||||
|
||||
let depth_rb = if let Some(rbo) = texture.depth_rb {
|
||||
rbo.0
|
||||
} else {
|
||||
let (depth_rb, depth_alloc) = match texture.depth_rb {
|
||||
Some(rbo) => (rbo.0, resized),
|
||||
None => {
|
||||
let renderbuffer_ids = self.gl.gen_renderbuffers(1);
|
||||
let depth_rb = renderbuffer_ids[0];
|
||||
texture.depth_rb = Some(RBOId(depth_rb));
|
||||
depth_rb
|
||||
(depth_rb, true)
|
||||
}
|
||||
};
|
||||
|
||||
if depth_alloc {
|
||||
self.gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
|
||||
self.gl.renderbuffer_storage(
|
||||
gl::RENDERBUFFER,
|
||||
|
@ -1253,6 +952,7 @@ impl Device {
|
|||
texture.width as gl::GLsizei,
|
||||
texture.height as gl::GLsizei,
|
||||
);
|
||||
}
|
||||
|
||||
for (fbo_index, fbo_id) in texture.fbo_ids.iter().enumerate() {
|
||||
self.gl.bind_framebuffer(gl::FRAMEBUFFER, fbo_id.0);
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
use api::{BuiltDisplayListIter, ClipAndScrollInfo, ClipId, ColorF, ComplexClipRegion};
|
||||
use api::{DeviceUintRect, DeviceUintSize, DisplayItemRef, Epoch, FilterOp};
|
||||
use api::{ImageDisplayItem, ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect};
|
||||
use api::{LayerSize, LayerToScrollTransform, LayerVector2D};
|
||||
use api::{LayoutRect, LayoutSize, LayoutTransform};
|
||||
use api::{LayerSize, LayerVector2D};
|
||||
use api::{LayoutRect, LayoutSize};
|
||||
use api::{LocalClip, PipelineId, ScrollClamping, ScrollEventPhase, ScrollLayerState};
|
||||
use api::{ScrollLocation, ScrollPolicy, ScrollSensitivity, SpecificDisplayItem, StackingContext};
|
||||
use api::{ClipMode, TileOffset, TransformStyle, WorldPoint};
|
||||
|
@ -21,7 +21,7 @@ use internal_types::{FastHashMap, FastHashSet, RendererFrame};
|
|||
use prim_store::RectangleContent;
|
||||
use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
|
||||
use resource_cache::{FontInstanceMap,ResourceCache, TiledImageMap};
|
||||
use scene::{Scene, StackingContextHelpers, ScenePipeline};
|
||||
use scene::{Scene, StackingContextHelpers, ScenePipeline, SceneProperties};
|
||||
use tiling::{CompositeOps, Frame};
|
||||
use util::ComplexClipRegionHelpers;
|
||||
|
||||
|
@ -48,6 +48,7 @@ struct FlattenContext<'a> {
|
|||
opaque_parts: Vec<LayoutRect>,
|
||||
/// Same for the transparent rectangles.
|
||||
transparent_parts: Vec<LayoutRect>,
|
||||
output_pipelines: &'a FastHashSet<PipelineId>,
|
||||
}
|
||||
|
||||
impl<'a> FlattenContext<'a> {
|
||||
|
@ -88,21 +89,18 @@ impl<'a> FlattenContext<'a> {
|
|||
root_reference_frame_id: ClipId,
|
||||
root_scroll_frame_id: ClipId,
|
||||
) {
|
||||
let clip_id = ClipId::root_scroll_node(pipeline_id);
|
||||
|
||||
self.builder.push_stacking_context(
|
||||
&LayerVector2D::zero(),
|
||||
pipeline_id,
|
||||
CompositeOps::default(),
|
||||
TransformStyle::Flat,
|
||||
true,
|
||||
true,
|
||||
ClipAndScrollInfo::simple(clip_id),
|
||||
self.output_pipelines,
|
||||
);
|
||||
|
||||
// We do this here, rather than above because we want any of the top-level
|
||||
// stacking contexts in the display list to be treated like root stacking contexts.
|
||||
// FIXME(mrobinson): Currently only the first one will, which for the moment is
|
||||
// sufficient for all our use cases.
|
||||
self.builder.notify_waiting_for_root_stacking_context();
|
||||
|
||||
// For the root pipeline, there's no need to add a full screen rectangle
|
||||
// here, as it's handled by the framebuffer clear.
|
||||
if self.scene.root_pipeline_id != Some(pipeline_id) {
|
||||
|
@ -121,7 +119,11 @@ impl<'a> FlattenContext<'a> {
|
|||
}
|
||||
|
||||
|
||||
self.flatten_items(traversal, pipeline_id, LayerVector2D::zero());
|
||||
self.flatten_items(
|
||||
traversal,
|
||||
pipeline_id,
|
||||
LayerVector2D::zero(),
|
||||
);
|
||||
|
||||
if self.builder.config.enable_scrollbars {
|
||||
let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0));
|
||||
|
@ -154,7 +156,11 @@ impl<'a> FlattenContext<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
self.flatten_item(item, pipeline_id, reference_frame_relative_offset)
|
||||
self.flatten_item(
|
||||
item,
|
||||
pipeline_id,
|
||||
reference_frame_relative_offset,
|
||||
)
|
||||
};
|
||||
|
||||
// If flatten_item created a sub-traversal, we need `traversal` to have the
|
||||
|
@ -240,7 +246,6 @@ impl<'a> FlattenContext<'a> {
|
|||
stacking_context.filter_ops_for_compositing(
|
||||
display_list,
|
||||
filters,
|
||||
&self.scene.properties,
|
||||
),
|
||||
stacking_context.mix_blend_mode_for_compositing(),
|
||||
)
|
||||
|
@ -258,23 +263,15 @@ impl<'a> FlattenContext<'a> {
|
|||
let is_reference_frame =
|
||||
stacking_context.transform.is_some() || stacking_context.perspective.is_some();
|
||||
if is_reference_frame {
|
||||
let transform = stacking_context.transform.as_ref();
|
||||
let transform = self.scene.properties.resolve_layout_transform(transform);
|
||||
let perspective = stacking_context
|
||||
.perspective
|
||||
.unwrap_or_else(LayoutTransform::identity);
|
||||
let origin = reference_frame_relative_offset + bounds.origin.to_vector();
|
||||
let transform = LayerToScrollTransform::create_translation(origin.x, origin.y, 0.0)
|
||||
.pre_mul(&transform)
|
||||
.pre_mul(&perspective);
|
||||
|
||||
let reference_frame_bounds = LayerRect::new(LayerPoint::zero(), bounds.size);
|
||||
let mut clip_id = self.apply_scroll_frame_id_replacement(context_scroll_node_id);
|
||||
clip_id = self.builder.push_reference_frame(
|
||||
Some(clip_id),
|
||||
pipeline_id,
|
||||
&reference_frame_bounds,
|
||||
&transform,
|
||||
stacking_context.transform,
|
||||
stacking_context.perspective,
|
||||
origin,
|
||||
false,
|
||||
self.clip_scroll_tree,
|
||||
|
@ -286,15 +283,18 @@ impl<'a> FlattenContext<'a> {
|
|||
reference_frame_relative_offset.x + bounds.origin.x,
|
||||
reference_frame_relative_offset.y + bounds.origin.y,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let sc_scroll_node_id = self.apply_scroll_frame_id_replacement(context_scroll_node_id);
|
||||
|
||||
self.builder.push_stacking_context(
|
||||
&reference_frame_relative_offset,
|
||||
pipeline_id,
|
||||
composition_operations,
|
||||
stacking_context.transform_style,
|
||||
is_backface_visible,
|
||||
false,
|
||||
ClipAndScrollInfo::simple(sc_scroll_node_id),
|
||||
self.output_pipelines,
|
||||
);
|
||||
|
||||
self.flatten_items(
|
||||
|
@ -347,12 +347,12 @@ impl<'a> FlattenContext<'a> {
|
|||
|
||||
let iframe_rect = LayerRect::new(LayerPoint::zero(), bounds.size);
|
||||
let origin = reference_frame_relative_offset + bounds.origin.to_vector();
|
||||
let transform = LayerToScrollTransform::create_translation(origin.x, origin.y, 0.0);
|
||||
let iframe_reference_frame_id = self.builder.push_reference_frame(
|
||||
Some(clip_id),
|
||||
pipeline_id,
|
||||
&iframe_rect,
|
||||
&transform,
|
||||
None,
|
||||
None,
|
||||
origin,
|
||||
true,
|
||||
self.clip_scroll_tree,
|
||||
|
@ -518,6 +518,7 @@ impl<'a> FlattenContext<'a> {
|
|||
let mut prim_info = prim_info.clone();
|
||||
prim_info.rect = bounds;
|
||||
self.builder.add_box_shadow(
|
||||
pipeline_id,
|
||||
clip_and_scroll,
|
||||
&prim_info,
|
||||
&box_shadow_info.offset,
|
||||
|
@ -1088,6 +1089,7 @@ impl FrameContext {
|
|||
window_size: DeviceUintSize,
|
||||
inner_rect: DeviceUintRect,
|
||||
device_pixel_ratio: f32,
|
||||
output_pipelines: &FastHashSet<PipelineId>,
|
||||
) -> Option<FrameBuilder> {
|
||||
let root_pipeline_id = match scene.root_pipeline_id {
|
||||
Some(root_pipeline_id) => root_pipeline_id,
|
||||
|
@ -1128,6 +1130,7 @@ impl FrameContext {
|
|||
replacements: Vec::new(),
|
||||
opaque_parts: Vec::new(),
|
||||
transparent_parts: Vec::new(),
|
||||
output_pipelines,
|
||||
};
|
||||
|
||||
roller.builder.push_root(
|
||||
|
@ -1154,6 +1157,8 @@ impl FrameContext {
|
|||
scroll_frame_id,
|
||||
);
|
||||
|
||||
debug_assert!(roller.builder.picture_stack.is_empty());
|
||||
|
||||
self.pipeline_epoch_map.extend(roller.pipeline_epochs.drain(..));
|
||||
roller.builder
|
||||
};
|
||||
|
@ -1180,9 +1185,9 @@ impl FrameContext {
|
|||
pipelines: &FastHashMap<PipelineId, ScenePipeline>,
|
||||
device_pixel_ratio: f32,
|
||||
pan: LayerPoint,
|
||||
output_pipelines: &FastHashSet<PipelineId>,
|
||||
texture_cache_profile: &mut TextureCacheProfileCounters,
|
||||
gpu_cache_profile: &mut GpuCacheProfileCounters,
|
||||
scene_properties: &SceneProperties,
|
||||
) -> RendererFrame {
|
||||
let frame = frame_builder.build(
|
||||
resource_cache,
|
||||
|
@ -1192,9 +1197,9 @@ impl FrameContext {
|
|||
pipelines,
|
||||
device_pixel_ratio,
|
||||
pan,
|
||||
output_pipelines,
|
||||
texture_cache_profile,
|
||||
gpu_cache_profile,
|
||||
scene_properties,
|
||||
);
|
||||
|
||||
self.get_renderer_frame_impl(Some(frame))
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -9,7 +9,6 @@ use api::DebugCommand;
|
|||
use device::TextureFilter;
|
||||
use fxhash::FxHasher;
|
||||
use profiler::BackendProfileCounters;
|
||||
use renderer::BlendMode;
|
||||
use std::{usize, i32};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::f32;
|
||||
|
@ -187,24 +186,8 @@ pub enum ResultMsg {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
|
||||
pub struct StackingContextIndex(pub usize);
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct UvRect {
|
||||
pub uv0: DevicePoint,
|
||||
pub uv1: DevicePoint,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum HardwareCompositeOp {
|
||||
PremultipliedAlpha,
|
||||
}
|
||||
|
||||
impl HardwareCompositeOp {
|
||||
pub fn to_blend_mode(&self) -> BlendMode {
|
||||
match *self {
|
||||
HardwareCompositeOp::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ mod picture;
|
|||
mod prim_store;
|
||||
mod print_tree;
|
||||
mod profiler;
|
||||
mod query;
|
||||
mod record;
|
||||
mod render_backend;
|
||||
mod render_task;
|
||||
|
|
|
@ -2,14 +2,16 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use api::{BorderRadiusKind, ColorF, ClipAndScrollInfo};
|
||||
use api::{device_length, DeviceIntSize};
|
||||
use api::{BorderRadiusKind, ColorF, ClipAndScrollInfo, FilterOp, MixBlendMode};
|
||||
use api::{device_length, DeviceIntRect, DeviceIntSize, PipelineId};
|
||||
use api::{BoxShadowClipMode, LayerPoint, LayerRect, LayerSize, LayerVector2D, Shadow};
|
||||
use api::{ClipId, PremultipliedColorF};
|
||||
use box_shadow::BLUR_SAMPLE_SCALE;
|
||||
use frame_builder::PrimitiveContext;
|
||||
use gpu_cache::GpuDataRequest;
|
||||
use prim_store::{PrimitiveIndex, PrimitiveRun};
|
||||
use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect};
|
||||
use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskTree};
|
||||
use scene::{FilterOpHelpers, SceneProperties};
|
||||
use tiling::RenderTargetKind;
|
||||
|
||||
/*
|
||||
|
@ -22,12 +24,26 @@ use tiling::RenderTargetKind;
|
|||
this picture (e.g. in screen space or local space).
|
||||
*/
|
||||
|
||||
/// Specifies how this Picture should be composited
|
||||
/// onto the target it belongs to.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum PictureCompositeMode {
|
||||
/// Apply CSS mix-blend-mode effect.
|
||||
MixBlend(MixBlendMode),
|
||||
/// Apply a CSS filter.
|
||||
Filter(FilterOp),
|
||||
/// Draw to intermediate surface, copy straight across. This
|
||||
/// is used for CSS isolation, and plane splitting.
|
||||
Blit,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PictureKind {
|
||||
TextShadow {
|
||||
offset: LayerVector2D,
|
||||
color: ColorF,
|
||||
blur_radius: f32,
|
||||
content_rect: LayerRect,
|
||||
},
|
||||
BoxShadow {
|
||||
blur_radius: f32,
|
||||
|
@ -35,15 +51,49 @@ pub enum PictureKind {
|
|||
blur_regions: Vec<LayerRect>,
|
||||
clip_mode: BoxShadowClipMode,
|
||||
radii_kind: BorderRadiusKind,
|
||||
content_rect: LayerRect,
|
||||
},
|
||||
Image {
|
||||
// If a mix-blend-mode, contains the render task for
|
||||
// the readback of the framebuffer that we use to sample
|
||||
// from in the mix-blend-mode shader.
|
||||
readback_render_task_id: Option<RenderTaskId>,
|
||||
/// How this picture should be composited.
|
||||
/// If None, don't composite - just draw directly on parent surface.
|
||||
composite_mode: Option<PictureCompositeMode>,
|
||||
// If true, this picture is part of a 3D context.
|
||||
is_in_3d_context: bool,
|
||||
// If requested as a frame output (for rendering
|
||||
// pages to a texture), this is the pipeline this
|
||||
// picture is the root of.
|
||||
frame_output_pipeline_id: Option<PipelineId>,
|
||||
// The original reference frame ID for this picture.
|
||||
// It is only different if this is part of a 3D
|
||||
// rendering context.
|
||||
reference_frame_id: ClipId,
|
||||
real_local_rect: LayerRect,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PicturePrimitive {
|
||||
pub prim_runs: Vec<PrimitiveRun>,
|
||||
// If this picture is drawn to an intermediate surface,
|
||||
// the associated render task.
|
||||
pub render_task_id: Option<RenderTaskId>,
|
||||
|
||||
// Details specific to this type of picture.
|
||||
pub kind: PictureKind,
|
||||
pub content_rect: LayerRect,
|
||||
|
||||
// List of primitive runs that make up this picture.
|
||||
pub runs: Vec<PrimitiveRun>,
|
||||
|
||||
// The pipeline that the primitives on this picture belong to.
|
||||
pub pipeline_id: PipelineId,
|
||||
|
||||
// If true, apply visibility culling to primitives on this
|
||||
// picture. For text shadows and box shadows, we want to
|
||||
// unconditionally draw them.
|
||||
pub cull_children: bool,
|
||||
|
||||
// TODO(gw): Add a mode that specifies if this
|
||||
// picture should be rasterized in
|
||||
|
@ -51,16 +101,39 @@ pub struct PicturePrimitive {
|
|||
}
|
||||
|
||||
impl PicturePrimitive {
|
||||
pub fn new_text_shadow(shadow: Shadow) -> Self {
|
||||
pub fn new_text_shadow(shadow: Shadow, pipeline_id: PipelineId) -> Self {
|
||||
PicturePrimitive {
|
||||
prim_runs: Vec::new(),
|
||||
runs: Vec::new(),
|
||||
render_task_id: None,
|
||||
content_rect: LayerRect::zero(),
|
||||
kind: PictureKind::TextShadow {
|
||||
offset: shadow.offset,
|
||||
color: shadow.color,
|
||||
blur_radius: shadow.blur_radius,
|
||||
content_rect: LayerRect::zero(),
|
||||
},
|
||||
pipeline_id,
|
||||
cull_children: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_scene_properties(&mut self, properties: &SceneProperties) -> bool {
|
||||
match self.kind {
|
||||
PictureKind::Image { ref mut composite_mode, .. } => {
|
||||
match composite_mode {
|
||||
&mut Some(PictureCompositeMode::Filter(ref mut filter)) => {
|
||||
match filter {
|
||||
&mut FilterOp::Opacity(ref binding, ref mut value) => {
|
||||
*value = properties.resolve_float(binding, *value);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
filter.is_visible()
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,38 +143,53 @@ impl PicturePrimitive {
|
|||
blur_regions: Vec<LayerRect>,
|
||||
clip_mode: BoxShadowClipMode,
|
||||
radii_kind: BorderRadiusKind,
|
||||
pipeline_id: PipelineId,
|
||||
) -> Self {
|
||||
PicturePrimitive {
|
||||
prim_runs: Vec::new(),
|
||||
runs: Vec::new(),
|
||||
render_task_id: None,
|
||||
content_rect: LayerRect::zero(),
|
||||
kind: PictureKind::BoxShadow {
|
||||
blur_radius,
|
||||
color,
|
||||
blur_regions,
|
||||
clip_mode,
|
||||
radii_kind,
|
||||
content_rect: LayerRect::zero(),
|
||||
},
|
||||
pipeline_id,
|
||||
cull_children: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_image(
|
||||
composite_mode: Option<PictureCompositeMode>,
|
||||
is_in_3d_context: bool,
|
||||
pipeline_id: PipelineId,
|
||||
reference_frame_id: ClipId,
|
||||
frame_output_pipeline_id: Option<PipelineId>,
|
||||
) -> PicturePrimitive {
|
||||
PicturePrimitive {
|
||||
runs: Vec::new(),
|
||||
render_task_id: None,
|
||||
kind: PictureKind::Image {
|
||||
readback_render_task_id: None,
|
||||
composite_mode,
|
||||
is_in_3d_context,
|
||||
frame_output_pipeline_id,
|
||||
reference_frame_id,
|
||||
real_local_rect: LayerRect::zero(),
|
||||
},
|
||||
pipeline_id,
|
||||
cull_children: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_primitive(
|
||||
&mut self,
|
||||
prim_index: PrimitiveIndex,
|
||||
local_rect: &LayerRect,
|
||||
clip_and_scroll: ClipAndScrollInfo
|
||||
) {
|
||||
// TODO(gw): Accumulating the primitive local rect
|
||||
// into the content rect here is fine, for now.
|
||||
// The only way pictures are currently used,
|
||||
// all the items added to a picture are known
|
||||
// to be in the same local space. Once we start
|
||||
// using pictures for other uses, we will need
|
||||
// to consider the space of a primitive in order
|
||||
// to build a correct contect rect!
|
||||
self.content_rect = self.content_rect.union(local_rect);
|
||||
|
||||
if let Some(ref mut run) = self.prim_runs.last_mut() {
|
||||
if let Some(ref mut run) = self.runs.last_mut() {
|
||||
if run.clip_and_scroll == clip_and_scroll &&
|
||||
run.base_prim_index.0 + run.count == prim_index.0 {
|
||||
run.count += 1;
|
||||
|
@ -109,26 +197,44 @@ impl PicturePrimitive {
|
|||
}
|
||||
}
|
||||
|
||||
self.prim_runs.push(PrimitiveRun {
|
||||
self.runs.push(PrimitiveRun {
|
||||
base_prim_index: prim_index,
|
||||
count: 1,
|
||||
clip_and_scroll,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn build(&mut self) -> LayerRect {
|
||||
pub fn update_local_rect(&mut self,
|
||||
prim_local_rect: LayerRect,
|
||||
prim_run_rect: PrimitiveRunLocalRect,
|
||||
) -> LayerRect {
|
||||
let local_content_rect = prim_run_rect.local_rect_in_actual_parent_space;
|
||||
|
||||
match self.kind {
|
||||
PictureKind::TextShadow { offset, blur_radius, .. } => {
|
||||
PictureKind::Image { composite_mode, ref mut real_local_rect, .. } => {
|
||||
*real_local_rect = prim_run_rect.local_rect_in_original_parent_space;
|
||||
|
||||
match composite_mode {
|
||||
Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
|
||||
let inflate_size = blur_radius * BLUR_SAMPLE_SCALE;
|
||||
local_content_rect.inflate(inflate_size, inflate_size)
|
||||
}
|
||||
_ => {
|
||||
local_content_rect
|
||||
}
|
||||
}
|
||||
}
|
||||
PictureKind::TextShadow { offset, blur_radius, ref mut content_rect, .. } => {
|
||||
let blur_offset = blur_radius * BLUR_SAMPLE_SCALE;
|
||||
|
||||
self.content_rect = self.content_rect.inflate(
|
||||
*content_rect = local_content_rect.inflate(
|
||||
blur_offset,
|
||||
blur_offset,
|
||||
);
|
||||
|
||||
self.content_rect.translate(&offset)
|
||||
content_rect.translate(&offset)
|
||||
}
|
||||
PictureKind::BoxShadow { blur_radius, clip_mode, radii_kind, .. } => {
|
||||
PictureKind::BoxShadow { blur_radius, clip_mode, radii_kind, ref mut content_rect, .. } => {
|
||||
// We need to inflate the content rect if outset.
|
||||
match clip_mode {
|
||||
BoxShadowClipMode::Outset => {
|
||||
|
@ -141,29 +247,31 @@ impl PicturePrimitive {
|
|||
match radii_kind {
|
||||
BorderRadiusKind::Uniform => {
|
||||
let origin = LayerPoint::new(
|
||||
self.content_rect.origin.x - blur_offset,
|
||||
self.content_rect.origin.y - blur_offset,
|
||||
local_content_rect.origin.x - blur_offset,
|
||||
local_content_rect.origin.y - blur_offset,
|
||||
);
|
||||
let size = LayerSize::new(
|
||||
self.content_rect.size.width + blur_offset,
|
||||
self.content_rect.size.height + blur_offset,
|
||||
local_content_rect.size.width + blur_offset,
|
||||
local_content_rect.size.height + blur_offset,
|
||||
);
|
||||
self.content_rect = LayerRect::new(origin, size);
|
||||
*content_rect = LayerRect::new(origin, size);
|
||||
}
|
||||
BorderRadiusKind::NonUniform => {
|
||||
// For a non-uniform radii, we need to expand
|
||||
// the content rect on all sides for the blur.
|
||||
self.content_rect = self.content_rect.inflate(
|
||||
*content_rect = local_content_rect.inflate(
|
||||
blur_offset,
|
||||
blur_offset,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
BoxShadowClipMode::Inset => {}
|
||||
BoxShadowClipMode::Inset => {
|
||||
*content_rect = local_content_rect;
|
||||
}
|
||||
}
|
||||
|
||||
self.content_rect
|
||||
prim_local_rect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,27 +281,93 @@ impl PicturePrimitive {
|
|||
prim_index: PrimitiveIndex,
|
||||
prim_context: &PrimitiveContext,
|
||||
render_tasks: &mut RenderTaskTree,
|
||||
screen_rect: &DeviceIntRect,
|
||||
child_tasks: Vec<RenderTaskId>,
|
||||
parent_tasks: &mut Vec<RenderTaskId>,
|
||||
) {
|
||||
match self.kind {
|
||||
PictureKind::Image {
|
||||
ref mut readback_render_task_id,
|
||||
composite_mode,
|
||||
frame_output_pipeline_id,
|
||||
..
|
||||
} => {
|
||||
match composite_mode {
|
||||
Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
|
||||
let picture_task = RenderTask::new_dynamic_alpha_batch(
|
||||
screen_rect,
|
||||
prim_index,
|
||||
None,
|
||||
child_tasks,
|
||||
);
|
||||
|
||||
let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
|
||||
let blur_std_deviation = blur_radius.0 as f32;
|
||||
let picture_task_id = render_tasks.add(picture_task);
|
||||
|
||||
let blur_render_task = RenderTask::new_blur(
|
||||
blur_std_deviation,
|
||||
picture_task_id,
|
||||
render_tasks,
|
||||
RenderTargetKind::Color,
|
||||
&[],
|
||||
ClearMode::Transparent,
|
||||
PremultipliedColorF::TRANSPARENT,
|
||||
);
|
||||
|
||||
let blur_render_task_id = render_tasks.add(blur_render_task);
|
||||
self.render_task_id = Some(blur_render_task_id);
|
||||
}
|
||||
Some(PictureCompositeMode::MixBlend(..)) => {
|
||||
let picture_task = RenderTask::new_dynamic_alpha_batch(
|
||||
screen_rect,
|
||||
prim_index,
|
||||
None,
|
||||
child_tasks,
|
||||
);
|
||||
|
||||
let readback_task_id = render_tasks.add(RenderTask::new_readback(*screen_rect));
|
||||
|
||||
*readback_render_task_id = Some(readback_task_id);
|
||||
parent_tasks.push(readback_task_id);
|
||||
|
||||
self.render_task_id = Some(render_tasks.add(picture_task));
|
||||
}
|
||||
Some(PictureCompositeMode::Filter(..)) | Some(PictureCompositeMode::Blit) => {
|
||||
let picture_task = RenderTask::new_dynamic_alpha_batch(
|
||||
screen_rect,
|
||||
prim_index,
|
||||
frame_output_pipeline_id,
|
||||
child_tasks,
|
||||
);
|
||||
|
||||
self.render_task_id = Some(render_tasks.add(picture_task));
|
||||
}
|
||||
None => {
|
||||
parent_tasks.extend(child_tasks);
|
||||
self.render_task_id = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
PictureKind::TextShadow { blur_radius, color, content_rect, .. } => {
|
||||
// This is a shadow element. Create a render task that will
|
||||
// render the text run to a target, and then apply a gaussian
|
||||
// blur to that text run in order to build the actual primitive
|
||||
// which will be blitted to the framebuffer.
|
||||
|
||||
let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
|
||||
|
||||
// TODO(gw): Rounding the content rect here to device pixels is not
|
||||
// technically correct. Ideally we should ceil() here, and ensure that
|
||||
// the extra part pixel in the case of fractional sizes is correctly
|
||||
// handled. For now, just use rounding which passes the existing
|
||||
// Gecko tests.
|
||||
let cache_width =
|
||||
(self.content_rect.size.width * prim_context.device_pixel_ratio).round() as i32;
|
||||
(content_rect.size.width * prim_context.device_pixel_ratio).round() as i32;
|
||||
let cache_height =
|
||||
(self.content_rect.size.height * prim_context.device_pixel_ratio).round() as i32;
|
||||
(content_rect.size.height * prim_context.device_pixel_ratio).round() as i32;
|
||||
let cache_size = DeviceIntSize::new(cache_width, cache_height);
|
||||
|
||||
match self.kind {
|
||||
PictureKind::TextShadow { blur_radius, color, .. } => {
|
||||
let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
|
||||
|
||||
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
|
||||
// "the image that would be generated by applying to the shadow a
|
||||
// Gaussian blur with a standard deviation equal to half the blur radius."
|
||||
|
@ -203,7 +377,7 @@ impl PicturePrimitive {
|
|||
cache_size,
|
||||
prim_index,
|
||||
RenderTargetKind::Color,
|
||||
self.content_rect.origin,
|
||||
content_rect.origin,
|
||||
color.premultiplied(),
|
||||
ClearMode::Transparent,
|
||||
);
|
||||
|
@ -222,9 +396,20 @@ impl PicturePrimitive {
|
|||
|
||||
self.render_task_id = Some(render_tasks.add(render_task));
|
||||
}
|
||||
PictureKind::BoxShadow { blur_radius, clip_mode, ref blur_regions, color, .. } => {
|
||||
PictureKind::BoxShadow { blur_radius, clip_mode, ref blur_regions, color, content_rect, .. } => {
|
||||
let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
|
||||
|
||||
// TODO(gw): Rounding the content rect here to device pixels is not
|
||||
// technically correct. Ideally we should ceil() here, and ensure that
|
||||
// the extra part pixel in the case of fractional sizes is correctly
|
||||
// handled. For now, just use rounding which passes the existing
|
||||
// Gecko tests.
|
||||
let cache_width =
|
||||
(content_rect.size.width * prim_context.device_pixel_ratio).round() as i32;
|
||||
let cache_height =
|
||||
(content_rect.size.height * prim_context.device_pixel_ratio).round() as i32;
|
||||
let cache_size = DeviceIntSize::new(cache_width, cache_height);
|
||||
|
||||
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
|
||||
// "the image that would be generated by applying to the shadow a
|
||||
// Gaussian blur with a standard deviation equal to half the blur radius."
|
||||
|
@ -243,7 +428,7 @@ impl PicturePrimitive {
|
|||
cache_size,
|
||||
prim_index,
|
||||
RenderTargetKind::Alpha,
|
||||
self.content_rect.origin,
|
||||
content_rect.origin,
|
||||
color.premultiplied(),
|
||||
ClearMode::Zero,
|
||||
);
|
||||
|
@ -263,6 +448,10 @@ impl PicturePrimitive {
|
|||
self.render_task_id = Some(render_tasks.add(render_task));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(render_task_id) = self.render_task_id {
|
||||
parent_tasks.push(render_task_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_gpu_blocks(&self, mut _request: GpuDataRequest) {
|
||||
|
@ -275,6 +464,7 @@ impl PicturePrimitive {
|
|||
match self.kind {
|
||||
PictureKind::TextShadow { .. } => RenderTargetKind::Color,
|
||||
PictureKind::BoxShadow { .. } => RenderTargetKind::Alpha,
|
||||
PictureKind::Image { .. } => RenderTargetKind::Color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,11 +40,9 @@ fn dwrite_measure_mode(
|
|||
render_mode: FontRenderMode,
|
||||
options: Option<FontInstancePlatformOptions>,
|
||||
) -> dwrote::DWRITE_MEASURING_MODE {
|
||||
if let Some(FontInstancePlatformOptions {
|
||||
force_gdi_rendering: true,
|
||||
..
|
||||
}) = options
|
||||
{
|
||||
let FontInstancePlatformOptions { force_gdi_rendering, use_embedded_bitmap, .. } =
|
||||
options.unwrap_or_default();
|
||||
if force_gdi_rendering || use_embedded_bitmap {
|
||||
return dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC;
|
||||
}
|
||||
|
||||
|
@ -61,19 +59,18 @@ fn dwrite_render_mode(
|
|||
measure_mode: dwrote::DWRITE_MEASURING_MODE,
|
||||
options: Option<FontInstancePlatformOptions>,
|
||||
) -> dwrote::DWRITE_RENDERING_MODE {
|
||||
if let Some(FontInstancePlatformOptions {
|
||||
force_gdi_rendering: true,
|
||||
..
|
||||
}) = options
|
||||
{
|
||||
return dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC;
|
||||
}
|
||||
let FontInstancePlatformOptions { force_gdi_rendering, use_embedded_bitmap, .. } =
|
||||
options.unwrap_or_default();
|
||||
|
||||
let dwrite_render_mode = match render_mode {
|
||||
FontRenderMode::Mono | FontRenderMode::Bitmap => dwrote::DWRITE_RENDERING_MODE_ALIASED,
|
||||
FontRenderMode::Alpha | FontRenderMode::Subpixel => {
|
||||
if force_gdi_rendering || use_embedded_bitmap {
|
||||
dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC
|
||||
} else {
|
||||
font_face.get_recommended_rendering_mode_default_params(em_size, 1.0, measure_mode)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if dwrite_render_mode == dwrote::DWRITE_RENDERING_MODE_OUTLINE {
|
||||
|
|
|
@ -7,35 +7,33 @@ use api::{DevicePoint, ExtendMode, GlyphInstance, GlyphKey};
|
|||
use api::{GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerRect};
|
||||
use api::{ClipMode, LayerSize, LayerVector2D, LineOrientation, LineStyle};
|
||||
use api::{ClipAndScrollInfo, EdgeAaSegmentMask, PremultipliedColorF, TileOffset};
|
||||
use api::{YuvColorSpace, YuvFormat};
|
||||
use api::{ClipId, LayerTransform, PipelineId, YuvColorSpace, YuvFormat};
|
||||
use border::BorderCornerInstance;
|
||||
use clip::{ClipSourcesHandle, ClipStore, Geometry};
|
||||
use clip_scroll_tree::ClipScrollTree;
|
||||
use clip::{ClipSourcesHandle, ClipStore};
|
||||
use frame_builder::PrimitiveContext;
|
||||
use glyph_rasterizer::FontInstance;
|
||||
use internal_types::FastHashMap;
|
||||
use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
|
||||
ToGpuBlocks};
|
||||
use picture::PicturePrimitive;
|
||||
use render_task::{ClipWorkItem, ClipChainNode, RenderTask, RenderTaskId, RenderTaskTree};
|
||||
use picture::{PictureKind, PicturePrimitive};
|
||||
use profiler::FrameProfileCounters;
|
||||
use render_task::{ClipWorkItem, ClipChainNode};
|
||||
use render_task::{RenderTask, RenderTaskId, RenderTaskTree};
|
||||
use renderer::MAX_VERTEX_TEXTURE_WIDTH;
|
||||
use resource_cache::{ImageProperties, ResourceCache};
|
||||
use scene::{ScenePipeline, SceneProperties};
|
||||
use std::{mem, usize};
|
||||
use std::rc::Rc;
|
||||
use util::{pack_as_float, recycle_vec, MatrixHelpers, TransformedRect, TransformedRectKind};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct PrimitiveRun {
|
||||
pub base_prim_index: PrimitiveIndex,
|
||||
pub count: usize,
|
||||
pub clip_and_scroll: ClipAndScrollInfo,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PrimitiveRunResult {
|
||||
pub local_rect: LayerRect,
|
||||
pub device_rect: DeviceIntRect,
|
||||
pub visible_primitives: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct PrimitiveOpacity {
|
||||
pub is_opaque: bool,
|
||||
|
@ -61,6 +59,27 @@ impl PrimitiveOpacity {
|
|||
}
|
||||
}
|
||||
|
||||
// Represents the local space rect of a list of
|
||||
// primitive runs. For most primitive runs, the
|
||||
// primitive runs are attached to the parent they
|
||||
// are declared in. However, when a primitive run
|
||||
// is part of a 3d rendering context, it may get
|
||||
// hoisted to a higher level in the picture tree.
|
||||
// When this happens, we need to also calculate the
|
||||
// local space rects in the original space. This
|
||||
// allows constructing the true world space polygons
|
||||
// for the primitive, to enable the plane splitting
|
||||
// logic to work correctly.
|
||||
// TODO(gw) In the future, we can probably simplify
|
||||
// this - perhaps calculate the world space
|
||||
// polygons directly and store internally
|
||||
// in the picture structure.
|
||||
#[derive(Debug)]
|
||||
pub struct PrimitiveRunLocalRect {
|
||||
pub local_rect_in_actual_parent_space: LayerRect,
|
||||
pub local_rect_in_original_parent_space: LayerRect,
|
||||
}
|
||||
|
||||
/// Stores two coordinates in texel space. The coordinates
|
||||
/// are stored in texel coordinates because the texture atlas
|
||||
/// may grow. Storing them as texel coords and normalizing
|
||||
|
@ -1062,37 +1081,6 @@ impl PrimitiveStore {
|
|||
self.cpu_metadata.len()
|
||||
}
|
||||
|
||||
/// Add any task dependencies for this primitive to the provided task.
|
||||
pub fn add_render_tasks_for_prim(&self, prim_index: PrimitiveIndex, task: &mut RenderTask) {
|
||||
// Add any dynamic render tasks needed to render this primitive
|
||||
let metadata = &self.cpu_metadata[prim_index.0];
|
||||
|
||||
let render_task_id = match metadata.prim_kind {
|
||||
PrimitiveKind::Picture => {
|
||||
let picture = &self.cpu_pictures[metadata.cpu_prim_index.0];
|
||||
picture.render_task_id
|
||||
}
|
||||
PrimitiveKind::Rectangle |
|
||||
PrimitiveKind::TextRun |
|
||||
PrimitiveKind::Image |
|
||||
PrimitiveKind::AlignedGradient |
|
||||
PrimitiveKind::YuvImage |
|
||||
PrimitiveKind::Border |
|
||||
PrimitiveKind::AngleGradient |
|
||||
PrimitiveKind::RadialGradient |
|
||||
PrimitiveKind::Line |
|
||||
PrimitiveKind::Brush => None,
|
||||
};
|
||||
|
||||
if let Some(render_task_id) = render_task_id {
|
||||
task.children.push(render_task_id);
|
||||
}
|
||||
|
||||
if let Some(clip_task_id) = metadata.clip_task_id {
|
||||
task.children.push(clip_task_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_prim_for_render_inner(
|
||||
&mut self,
|
||||
prim_index: PrimitiveIndex,
|
||||
|
@ -1100,6 +1088,8 @@ impl PrimitiveStore {
|
|||
resource_cache: &mut ResourceCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
render_tasks: &mut RenderTaskTree,
|
||||
child_tasks: Vec<RenderTaskId>,
|
||||
parent_tasks: &mut Vec<RenderTaskId>,
|
||||
) {
|
||||
let metadata = &mut self.cpu_metadata[prim_index.0];
|
||||
match metadata.prim_kind {
|
||||
|
@ -1109,7 +1099,10 @@ impl PrimitiveStore {
|
|||
.prepare_for_render(
|
||||
prim_index,
|
||||
prim_context,
|
||||
render_tasks
|
||||
render_tasks,
|
||||
metadata.screen_rect.as_ref().expect("bug: trying to draw an off-screen picture!?"),
|
||||
child_tasks,
|
||||
parent_tasks,
|
||||
);
|
||||
}
|
||||
PrimitiveKind::TextRun => {
|
||||
|
@ -1226,8 +1219,10 @@ impl PrimitiveStore {
|
|||
gpu_cache: &mut GpuCache,
|
||||
render_tasks: &mut RenderTaskTree,
|
||||
clip_store: &mut ClipStore,
|
||||
tasks: &mut Vec<RenderTaskId>,
|
||||
) -> bool {
|
||||
let metadata = &mut self.cpu_metadata[prim_index.0];
|
||||
metadata.clip_task_id = None;
|
||||
let transform = &prim_context.scroll_node.world_content_transform;
|
||||
|
||||
clip_store.get_mut(&metadata.clip_sources).update(
|
||||
|
@ -1283,7 +1278,13 @@ impl PrimitiveStore {
|
|||
None
|
||||
};
|
||||
|
||||
metadata.clip_task_id = clip_task.map(|clip_task| render_tasks.add(clip_task));
|
||||
if let Some(clip_task) = clip_task {
|
||||
let clip_task_id = render_tasks.add(clip_task);
|
||||
|
||||
metadata.clip_task_id = Some(clip_task_id);
|
||||
tasks.push(clip_task_id);
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -1295,19 +1296,89 @@ impl PrimitiveStore {
|
|||
gpu_cache: &mut GpuCache,
|
||||
render_tasks: &mut RenderTaskTree,
|
||||
clip_store: &mut ClipStore,
|
||||
) -> Option<Geometry> {
|
||||
let (geometry, dependent_primitives) = {
|
||||
clip_scroll_tree: &ClipScrollTree,
|
||||
pipelines: &FastHashMap<PipelineId, ScenePipeline>,
|
||||
perform_culling: bool,
|
||||
parent_tasks: &mut Vec<RenderTaskId>,
|
||||
scene_properties: &SceneProperties,
|
||||
profile_counters: &mut FrameProfileCounters,
|
||||
) -> Option<LayerRect> {
|
||||
// Reset the visibility of this primitive.
|
||||
// Do some basic checks first, that can early out
|
||||
// without even knowing the local rect.
|
||||
let (cpu_prim_index, dependencies, cull_children) = {
|
||||
let metadata = &mut self.cpu_metadata[prim_index.0];
|
||||
metadata.screen_rect = None;
|
||||
|
||||
if metadata.local_rect.size.width <= 0.0 ||
|
||||
metadata.local_rect.size.height <= 0.0 {
|
||||
warn!("invalid primitive rect {:?}", metadata.local_rect);
|
||||
if perform_culling &&
|
||||
!metadata.is_backface_visible &&
|
||||
prim_context.scroll_node.world_content_transform.is_backface_visible() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if !metadata.is_backface_visible &&
|
||||
prim_context.scroll_node.world_content_transform.is_backface_visible() {
|
||||
let (dependencies, cull_children) = match metadata.prim_kind {
|
||||
PrimitiveKind::Picture => {
|
||||
let pic = &mut self.cpu_pictures[metadata.cpu_prim_index.0];
|
||||
|
||||
if !pic.resolve_scene_properties(scene_properties) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let rfid = match pic.kind {
|
||||
PictureKind::Image { reference_frame_id, .. } => Some(reference_frame_id),
|
||||
_ => None,
|
||||
};
|
||||
(Some((pic.pipeline_id, mem::replace(&mut pic.runs, Vec::new()), rfid)), pic.cull_children)
|
||||
}
|
||||
_ => {
|
||||
(None, true)
|
||||
}
|
||||
};
|
||||
|
||||
(metadata.cpu_prim_index, dependencies, cull_children)
|
||||
};
|
||||
|
||||
// If we have dependencies, we need to prepare them first, in order
|
||||
// to know the actual rect of this primitive.
|
||||
// For example, scrolling may affect the location of an item in
|
||||
// local space, which may force us to render this item on a larger
|
||||
// picture target, if being composited.
|
||||
let mut child_tasks = Vec::new();
|
||||
if let Some((pipeline_id, dependencies, rfid)) = dependencies {
|
||||
let result = self.prepare_prim_runs(
|
||||
&dependencies,
|
||||
pipeline_id,
|
||||
gpu_cache,
|
||||
resource_cache,
|
||||
render_tasks,
|
||||
clip_store,
|
||||
clip_scroll_tree,
|
||||
pipelines,
|
||||
prim_context,
|
||||
cull_children,
|
||||
&mut child_tasks,
|
||||
profile_counters,
|
||||
rfid,
|
||||
scene_properties,
|
||||
);
|
||||
|
||||
let metadata = &mut self.cpu_metadata[prim_index.0];
|
||||
|
||||
// Restore the dependencies (borrow check dance)
|
||||
let pic = &mut self.cpu_pictures[cpu_prim_index.0];
|
||||
pic.runs = dependencies;
|
||||
|
||||
metadata.local_rect = pic.update_local_rect(
|
||||
metadata.local_rect,
|
||||
result,
|
||||
);
|
||||
}
|
||||
|
||||
let (local_rect, device_rect) = {
|
||||
let metadata = &mut self.cpu_metadata[prim_index.0];
|
||||
if metadata.local_rect.size.width <= 0.0 ||
|
||||
metadata.local_rect.size.height <= 0.0 {
|
||||
warn!("invalid primitive rect {:?}", metadata.local_rect);
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -1317,7 +1388,8 @@ impl PrimitiveStore {
|
|||
|
||||
let local_rect = match local_rect {
|
||||
Some(local_rect) => local_rect,
|
||||
None => return None,
|
||||
None if perform_culling => return None,
|
||||
None => LayerRect::zero(),
|
||||
};
|
||||
|
||||
let xf_rect = TransformedRect::new(
|
||||
|
@ -1330,50 +1402,30 @@ impl PrimitiveStore {
|
|||
metadata.screen_rect = xf_rect.bounding_rect
|
||||
.intersection(clip_bounds);
|
||||
|
||||
let geometry = match metadata.screen_rect {
|
||||
Some(device_rect) => Geometry {
|
||||
local_rect,
|
||||
device_rect,
|
||||
},
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let dependencies = match metadata.prim_kind {
|
||||
PrimitiveKind::Picture =>
|
||||
self.cpu_pictures[metadata.cpu_prim_index.0].prim_runs.clone(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
(geometry, dependencies)
|
||||
};
|
||||
|
||||
// Recurse into any sub primitives and prepare them for rendering first.
|
||||
// TODO(gw): This code is a bit hacky to work around the borrow checker.
|
||||
// Specifically, the clone() below on the primitive list for
|
||||
// text shadow primitives. Consider restructuring this code to
|
||||
// avoid borrow checker issues.
|
||||
for run in dependent_primitives {
|
||||
for i in 0 .. run.count {
|
||||
let sub_prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
|
||||
|
||||
self.prepare_prim_for_render_inner(
|
||||
sub_prim_index,
|
||||
prim_context,
|
||||
resource_cache,
|
||||
gpu_cache,
|
||||
render_tasks,
|
||||
);
|
||||
let device_rect = match metadata.screen_rect {
|
||||
Some(device_rect) => device_rect,
|
||||
None => {
|
||||
if perform_culling {
|
||||
return None
|
||||
} else {
|
||||
DeviceIntRect::zero()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(local_rect, device_rect)
|
||||
};
|
||||
|
||||
if !self.update_clip_task(
|
||||
prim_index,
|
||||
prim_context,
|
||||
geometry.device_rect,
|
||||
device_rect,
|
||||
resource_cache,
|
||||
gpu_cache,
|
||||
render_tasks,
|
||||
clip_store,
|
||||
) {
|
||||
parent_tasks,
|
||||
) && perform_culling {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -1383,40 +1435,117 @@ impl PrimitiveStore {
|
|||
resource_cache,
|
||||
gpu_cache,
|
||||
render_tasks,
|
||||
child_tasks,
|
||||
parent_tasks,
|
||||
);
|
||||
|
||||
Some(geometry)
|
||||
Some(local_rect)
|
||||
}
|
||||
|
||||
pub fn prepare_prim_run(
|
||||
// TODO(gw): Make this simpler / more efficient by tidying
|
||||
// up the logic that early outs from prepare_prim_for_render.
|
||||
pub fn reset_prim_visibility(&mut self) {
|
||||
for md in &mut self.cpu_metadata {
|
||||
md.screen_rect = None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare_prim_runs(
|
||||
&mut self,
|
||||
run: &PrimitiveRun,
|
||||
prim_context: &PrimitiveContext,
|
||||
runs: &[PrimitiveRun],
|
||||
pipeline_id: PipelineId,
|
||||
gpu_cache: &mut GpuCache,
|
||||
resource_cache: &mut ResourceCache,
|
||||
render_tasks: &mut RenderTaskTree,
|
||||
clip_store: &mut ClipStore,
|
||||
) -> PrimitiveRunResult {
|
||||
let mut result = PrimitiveRunResult {
|
||||
local_rect: LayerRect::zero(),
|
||||
device_rect: DeviceIntRect::zero(),
|
||||
visible_primitives: 0,
|
||||
clip_scroll_tree: &ClipScrollTree,
|
||||
pipelines: &FastHashMap<PipelineId, ScenePipeline>,
|
||||
parent_prim_context: &PrimitiveContext,
|
||||
perform_culling: bool,
|
||||
parent_tasks: &mut Vec<RenderTaskId>,
|
||||
profile_counters: &mut FrameProfileCounters,
|
||||
original_reference_frame_id: Option<ClipId>,
|
||||
scene_properties: &SceneProperties,
|
||||
) -> PrimitiveRunLocalRect {
|
||||
let mut result = PrimitiveRunLocalRect {
|
||||
local_rect_in_actual_parent_space: LayerRect::zero(),
|
||||
local_rect_in_original_parent_space: LayerRect::zero(),
|
||||
};
|
||||
|
||||
for run in runs {
|
||||
// TODO(gw): Perhaps we can restructure this to not need to create
|
||||
// a new primitive context for every run (if the hash
|
||||
// lookups ever show up in a profile).
|
||||
let scroll_node = &clip_scroll_tree.nodes[&run.clip_and_scroll.scroll_node_id];
|
||||
let clip_node = &clip_scroll_tree.nodes[&run.clip_and_scroll.clip_node_id()];
|
||||
|
||||
if perform_culling && !clip_node.is_visible() {
|
||||
debug!("{:?} of clipped out {:?}", run.base_prim_index, pipeline_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
let parent_relative_transform = parent_prim_context
|
||||
.scroll_node
|
||||
.world_content_transform
|
||||
.inverse()
|
||||
.map(|inv_parent| {
|
||||
inv_parent.pre_mul(&scroll_node.world_content_transform)
|
||||
});
|
||||
|
||||
let original_relative_transform = original_reference_frame_id
|
||||
.and_then(|original_reference_frame_id| {
|
||||
let parent = clip_scroll_tree
|
||||
.nodes[&original_reference_frame_id]
|
||||
.world_content_transform;
|
||||
parent.inverse()
|
||||
.map(|inv_parent| {
|
||||
inv_parent.pre_mul(&scroll_node.world_content_transform)
|
||||
})
|
||||
});
|
||||
|
||||
let display_list = &pipelines
|
||||
.get(&pipeline_id)
|
||||
.expect("No display list?")
|
||||
.display_list;
|
||||
|
||||
let child_prim_context = PrimitiveContext::new(
|
||||
parent_prim_context.device_pixel_ratio,
|
||||
display_list,
|
||||
clip_node,
|
||||
scroll_node,
|
||||
);
|
||||
|
||||
for i in 0 .. run.count {
|
||||
let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
|
||||
|
||||
if let Some(prim_geom) = self.prepare_prim_for_render(
|
||||
if let Some(prim_local_rect) = self.prepare_prim_for_render(
|
||||
prim_index,
|
||||
prim_context,
|
||||
&child_prim_context,
|
||||
resource_cache,
|
||||
gpu_cache,
|
||||
render_tasks,
|
||||
clip_store,
|
||||
clip_scroll_tree,
|
||||
pipelines,
|
||||
perform_culling,
|
||||
parent_tasks,
|
||||
scene_properties,
|
||||
profile_counters,
|
||||
) {
|
||||
result.local_rect = result.local_rect.union(&prim_geom.local_rect);
|
||||
result.device_rect = result.device_rect.union(&prim_geom.device_rect);
|
||||
result.visible_primitives += 1;
|
||||
profile_counters.visible_primitives.inc();
|
||||
|
||||
if let Some(ref matrix) = original_relative_transform {
|
||||
let bounds = get_local_bounding_rect(&prim_local_rect, matrix);
|
||||
result.local_rect_in_original_parent_space =
|
||||
result.local_rect_in_original_parent_space.union(&bounds);
|
||||
}
|
||||
|
||||
if let Some(ref matrix) = parent_relative_transform {
|
||||
let bounds = get_local_bounding_rect(&prim_local_rect, matrix);
|
||||
result.local_rect_in_actual_parent_space =
|
||||
result.local_rect_in_actual_parent_space.union(&bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1448,3 +1577,29 @@ impl InsideTest<ComplexClipRegion> for ComplexClipRegion {
|
|||
clip.radii.bottom_right.height >= self.radii.bottom_right.height - delta_bottom
|
||||
}
|
||||
}
|
||||
|
||||
fn get_local_bounding_rect(
|
||||
local_rect: &LayerRect,
|
||||
matrix: &LayerTransform
|
||||
) -> LayerRect {
|
||||
let vertices = [
|
||||
matrix.transform_point3d(&local_rect.origin.to_3d()),
|
||||
matrix.transform_point3d(&local_rect.bottom_left().to_3d()),
|
||||
matrix.transform_point3d(&local_rect.bottom_right().to_3d()),
|
||||
matrix.transform_point3d(&local_rect.top_right().to_3d()),
|
||||
];
|
||||
|
||||
let mut x0 = vertices[0].x;
|
||||
let mut y0 = vertices[0].y;
|
||||
let mut x1 = vertices[0].x;
|
||||
let mut y1 = vertices[0].y;
|
||||
|
||||
for v in &vertices[1..] {
|
||||
x0 = x0.min(v.x);
|
||||
y0 = y0.min(v.y);
|
||||
x1 = x1.max(v.x);
|
||||
y1 = y1.max(v.y);
|
||||
}
|
||||
|
||||
LayerRect::new(LayerPoint::new(x0, y0), LayerSize::new(x1 - x0, y1 - y0))
|
||||
}
|
||||
|
|
|
@ -4,11 +4,10 @@
|
|||
|
||||
use api::{ColorF, ColorU};
|
||||
use debug_render::DebugRenderer;
|
||||
use device::{Device, GpuMarker, GpuSampler, GpuTimer, NamedTag};
|
||||
use euclid::{Point2D, Rect, Size2D, vec2};
|
||||
use query::{GpuSampler, GpuTimer, NamedTag};
|
||||
use std::collections::vec_deque::VecDeque;
|
||||
use std::f32;
|
||||
use std::mem;
|
||||
use std::{f32, mem};
|
||||
use time::precise_time_ns;
|
||||
|
||||
const GRAPH_WIDTH: f32 = 1024.0;
|
||||
|
@ -794,7 +793,6 @@ impl Profiler {
|
|||
|
||||
pub fn draw_profile(
|
||||
&mut self,
|
||||
device: &mut Device,
|
||||
frame_profile: &FrameProfileCounters,
|
||||
backend_profile: &BackendProfileCounters,
|
||||
renderer_profile: &RendererProfileCounters,
|
||||
|
@ -803,15 +801,14 @@ impl Profiler {
|
|||
screen_fraction: f32,
|
||||
debug_renderer: &mut DebugRenderer,
|
||||
) {
|
||||
let _gm = GpuMarker::new(device.rc_gl(), "profile");
|
||||
self.x_left = 20.0;
|
||||
self.y_left = 40.0;
|
||||
self.x_right = 400.0;
|
||||
self.y_right = 40.0;
|
||||
|
||||
let mut gpu_time = 0;
|
||||
let gpu_samples = mem::replace(&mut renderer_timers.gpu_samples, Vec::new());
|
||||
for sample in &gpu_samples {
|
||||
let gpu_timers = mem::replace(&mut renderer_timers.gpu_samples, Vec::new());
|
||||
for sample in &gpu_timers {
|
||||
gpu_time += sample.time_ns;
|
||||
}
|
||||
renderer_timers.gpu_time.set(gpu_time);
|
||||
|
@ -882,6 +879,7 @@ impl Profiler {
|
|||
false,
|
||||
);
|
||||
|
||||
if !gpu_samplers.is_empty() {
|
||||
let mut samplers = Vec::<FloatProfileCounter>::new();
|
||||
// Gathering unique GPU samplers. This has O(N^2) complexity,
|
||||
// but we only have a few samplers per target.
|
||||
|
@ -898,6 +896,7 @@ impl Profiler {
|
|||
}
|
||||
}
|
||||
self.draw_counters(&samplers, debug_renderer, false);
|
||||
}
|
||||
|
||||
self.backend_time
|
||||
.push(backend_profile.total_time.nanoseconds);
|
||||
|
@ -906,7 +905,7 @@ impl Profiler {
|
|||
self.ipc_time
|
||||
.push(backend_profile.ipc.total_time.nanoseconds);
|
||||
self.gpu_time.push(gpu_time);
|
||||
self.gpu_frames.push(gpu_time, gpu_samples);
|
||||
self.gpu_frames.push(gpu_time, gpu_timers);
|
||||
|
||||
|
||||
let rect =
|
||||
|
|
|
@ -0,0 +1,319 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use gleam::gl;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
|
||||
use device::FrameId;
|
||||
|
||||
|
||||
pub trait NamedTag {
|
||||
fn get_label(&self) -> &str;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GpuTimer<T> {
|
||||
pub tag: T,
|
||||
pub time_ns: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GpuSampler<T> {
|
||||
pub tag: T,
|
||||
pub count: u64,
|
||||
}
|
||||
|
||||
pub struct QuerySet<T> {
|
||||
set: Vec<gl::GLuint>,
|
||||
data: Vec<T>,
|
||||
pending: gl::GLuint,
|
||||
}
|
||||
|
||||
impl<T> QuerySet<T> {
|
||||
fn new() -> Self {
|
||||
QuerySet {
|
||||
set: Vec::new(),
|
||||
data: Vec::new(),
|
||||
pending: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.data.clear();
|
||||
self.pending = 0;
|
||||
}
|
||||
|
||||
fn add(&mut self, value: T) -> Option<gl::GLuint> {
|
||||
assert_eq!(self.pending, 0);
|
||||
self.set.get(self.data.len()).cloned().map(|query_id| {
|
||||
self.data.push(value);
|
||||
self.pending = query_id;
|
||||
query_id
|
||||
})
|
||||
}
|
||||
|
||||
fn take<F: Fn(&mut T, gl::GLuint)>(&mut self, fun: F) -> Vec<T> {
|
||||
let mut data = mem::replace(&mut self.data, Vec::new());
|
||||
for (value, &query) in data.iter_mut().zip(self.set.iter()) {
|
||||
fun(value, query)
|
||||
}
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GpuFrameProfile<T> {
|
||||
gl: Rc<gl::Gl>,
|
||||
timers: QuerySet<GpuTimer<T>>,
|
||||
samplers: QuerySet<GpuSampler<T>>,
|
||||
frame_id: FrameId,
|
||||
inside_frame: bool,
|
||||
}
|
||||
|
||||
impl<T> GpuFrameProfile<T> {
|
||||
fn new(gl: Rc<gl::Gl>) -> Self {
|
||||
GpuFrameProfile {
|
||||
gl,
|
||||
timers: QuerySet::new(),
|
||||
samplers: QuerySet::new(),
|
||||
frame_id: FrameId::new(0),
|
||||
inside_frame: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_timers(&mut self, count: i32) {
|
||||
self.timers.set = self.gl.gen_queries(count);
|
||||
}
|
||||
|
||||
fn disable_timers(&mut self) {
|
||||
if !self.timers.set.is_empty() {
|
||||
self.gl.delete_queries(&self.timers.set);
|
||||
}
|
||||
self.timers.set = Vec::new();
|
||||
}
|
||||
|
||||
fn enable_samplers(&mut self, count: i32) {
|
||||
self.samplers.set = self.gl.gen_queries(count);
|
||||
}
|
||||
|
||||
fn disable_samplers(&mut self) {
|
||||
if !self.samplers.set.is_empty() {
|
||||
self.gl.delete_queries(&self.samplers.set);
|
||||
}
|
||||
self.samplers.set = Vec::new();
|
||||
}
|
||||
|
||||
fn begin_frame(&mut self, frame_id: FrameId) {
|
||||
self.frame_id = frame_id;
|
||||
self.timers.reset();
|
||||
self.samplers.reset();
|
||||
self.inside_frame = true;
|
||||
}
|
||||
|
||||
fn end_frame(&mut self) {
|
||||
self.finish_timer();
|
||||
self.finish_sampler();
|
||||
self.inside_frame = false;
|
||||
}
|
||||
|
||||
fn finish_timer(&mut self) {
|
||||
debug_assert!(self.inside_frame);
|
||||
if self.timers.pending != 0 {
|
||||
self.gl.end_query(gl::TIME_ELAPSED);
|
||||
self.timers.pending = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn finish_sampler(&mut self) {
|
||||
debug_assert!(self.inside_frame);
|
||||
if self.samplers.pending != 0 {
|
||||
self.gl.end_query(gl::SAMPLES_PASSED);
|
||||
self.samplers.pending = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NamedTag> GpuFrameProfile<T> {
|
||||
fn start_timer(&mut self, tag: T) -> GpuTimeQuery {
|
||||
self.finish_timer();
|
||||
|
||||
let marker = GpuMarker::new(&self.gl, tag.get_label());
|
||||
|
||||
if let Some(query) = self.timers.add(GpuTimer { tag, time_ns: 0 }) {
|
||||
self.gl.begin_query(gl::TIME_ELAPSED, query);
|
||||
}
|
||||
|
||||
GpuTimeQuery(marker)
|
||||
}
|
||||
|
||||
fn start_sampler(&mut self, tag: T) -> GpuSampleQuery {
|
||||
self.finish_sampler();
|
||||
|
||||
if let Some(query) = self.samplers.add(GpuSampler { tag, count: 0 }) {
|
||||
self.gl.begin_query(gl::SAMPLES_PASSED, query);
|
||||
}
|
||||
|
||||
GpuSampleQuery
|
||||
}
|
||||
|
||||
fn build_samples(&mut self) -> (FrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>) {
|
||||
debug_assert!(!self.inside_frame);
|
||||
let gl = &self.gl;
|
||||
|
||||
(
|
||||
self.frame_id,
|
||||
self.timers.take(|timer, query| {
|
||||
timer.time_ns = gl.get_query_object_ui64v(query, gl::QUERY_RESULT)
|
||||
}),
|
||||
self.samplers.take(|sampler, query| {
|
||||
sampler.count = gl.get_query_object_ui64v(query, gl::QUERY_RESULT)
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for GpuFrameProfile<T> {
|
||||
fn drop(&mut self) {
|
||||
self.disable_timers();
|
||||
self.disable_samplers();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GpuProfiler<T> {
|
||||
gl: Rc<gl::Gl>,
|
||||
frames: Vec<GpuFrameProfile<T>>,
|
||||
next_frame: usize,
|
||||
}
|
||||
|
||||
impl<T> GpuProfiler<T> {
|
||||
pub fn new(gl: Rc<gl::Gl>) -> Self {
|
||||
const MAX_PROFILE_FRAMES: usize = 4;
|
||||
let frames = (0 .. MAX_PROFILE_FRAMES)
|
||||
.map(|_| GpuFrameProfile::new(Rc::clone(&gl)))
|
||||
.collect();
|
||||
|
||||
GpuProfiler {
|
||||
gl,
|
||||
next_frame: 0,
|
||||
frames,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_timers(&mut self) {
|
||||
const MAX_TIMERS_PER_FRAME: i32 = 256;
|
||||
|
||||
for frame in &mut self.frames {
|
||||
frame.enable_timers(MAX_TIMERS_PER_FRAME);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disable_timers(&mut self) {
|
||||
for frame in &mut self.frames {
|
||||
frame.disable_timers();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_timers_enabled(&mut self) {
|
||||
if self.frames[0].timers.set.is_empty() {
|
||||
self.enable_timers();
|
||||
} else {
|
||||
self.disable_timers();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_samplers(&mut self) {
|
||||
const MAX_SAMPLERS_PER_FRAME: i32 = 16;
|
||||
if cfg!(target_os = "macos") {
|
||||
warn!("Expect OSX driver bugs related to sample queries")
|
||||
}
|
||||
|
||||
for frame in &mut self.frames {
|
||||
frame.enable_samplers(MAX_SAMPLERS_PER_FRAME);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disable_samplers(&mut self) {
|
||||
for frame in &mut self.frames {
|
||||
frame.disable_samplers();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_samplers_enabled(&mut self) {
|
||||
if self.frames[0].samplers.set.is_empty() {
|
||||
self.enable_samplers();
|
||||
} else {
|
||||
self.disable_samplers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NamedTag> GpuProfiler<T> {
|
||||
pub fn build_samples(&mut self) -> (FrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>) {
|
||||
self.frames[self.next_frame].build_samples()
|
||||
}
|
||||
|
||||
pub fn begin_frame(&mut self, frame_id: FrameId) {
|
||||
self.frames[self.next_frame].begin_frame(frame_id);
|
||||
}
|
||||
|
||||
pub fn end_frame(&mut self) {
|
||||
self.frames[self.next_frame].end_frame();
|
||||
self.next_frame = (self.next_frame + 1) % self.frames.len();
|
||||
}
|
||||
|
||||
pub fn start_timer(&mut self, tag: T) -> GpuTimeQuery {
|
||||
self.frames[self.next_frame].start_timer(tag)
|
||||
}
|
||||
|
||||
pub fn start_sampler(&mut self, tag: T) -> GpuSampleQuery {
|
||||
self.frames[self.next_frame].start_sampler(tag)
|
||||
}
|
||||
|
||||
pub fn finish_sampler(&mut self, _sampler: GpuSampleQuery) {
|
||||
self.frames[self.next_frame].finish_sampler()
|
||||
}
|
||||
|
||||
pub fn start_marker(&mut self, label: &str) -> GpuMarker {
|
||||
GpuMarker::new(&self.gl, label)
|
||||
}
|
||||
|
||||
pub fn place_marker(&mut self, label: &str) {
|
||||
GpuMarker::fire(&self.gl, label)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct GpuMarker {
|
||||
gl: Option<Rc<gl::Gl>>,
|
||||
}
|
||||
|
||||
impl GpuMarker {
|
||||
fn new(gl: &Rc<gl::Gl>, message: &str) -> Self {
|
||||
if gl.get_type() == gl::GlType::Gl {
|
||||
gl.push_group_marker_ext(message);
|
||||
GpuMarker { gl: Some(Rc::clone(gl)) }
|
||||
} else {
|
||||
GpuMarker { gl: None }
|
||||
}
|
||||
}
|
||||
|
||||
fn fire(gl: &Rc<gl::Gl>, message: &str) {
|
||||
if gl.get_type() == gl::GlType::Gl {
|
||||
gl.insert_event_marker_ext(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GpuMarker {
|
||||
fn drop(&mut self) {
|
||||
if let Some(ref gl) = self.gl {
|
||||
gl.pop_group_marker_ext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct GpuTimeQuery(GpuMarker);
|
||||
#[must_use]
|
||||
pub struct GpuSampleQuery;
|
|
@ -93,6 +93,7 @@ impl Document {
|
|||
self.window_size,
|
||||
self.inner_rect,
|
||||
accumulated_scale_factor,
|
||||
&self.output_pipelines,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -116,9 +117,9 @@ impl Document {
|
|||
&self.scene.pipelines,
|
||||
accumulated_scale_factor,
|
||||
pan,
|
||||
&self.output_pipelines,
|
||||
&mut resource_profile.texture_cache,
|
||||
&mut resource_profile.gpu_cache,
|
||||
&self.scene.properties,
|
||||
)
|
||||
}
|
||||
None => {
|
||||
|
@ -410,19 +411,8 @@ impl RenderBackend {
|
|||
profile_scope!("GenerateFrame");
|
||||
let _timer = profile_counters.total_time.timer();
|
||||
|
||||
// Ideally, when there are property bindings present,
|
||||
// we won't need to rebuild the entire frame here.
|
||||
// However, to avoid conflicts with the ongoing work to
|
||||
// refactor how scroll roots + transforms work, this
|
||||
// just rebuilds the frame if there are animated property
|
||||
// bindings present for now.
|
||||
// TODO(gw): Once the scrolling / reference frame changes
|
||||
// are completed, optimize the internals of
|
||||
// animated properties to not require a full
|
||||
// rebuild of the frame!
|
||||
if let Some(property_bindings) = property_bindings {
|
||||
doc.scene.properties.set_properties(property_bindings);
|
||||
doc.build_scene(&mut self.resource_cache);
|
||||
}
|
||||
|
||||
if let Some(ref mut ros) = doc.render_on_scroll {
|
||||
|
|
|
@ -3,18 +3,16 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use api::{ClipId, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
|
||||
use api::{FilterOp, LayerPoint, LayerRect, MixBlendMode};
|
||||
use api::{LayerPoint, LayerRect};
|
||||
use api::{PipelineId, PremultipliedColorF};
|
||||
use clip::{ClipSource, ClipSourcesWeakHandle, ClipStore};
|
||||
use clip_scroll_tree::CoordinateSystemId;
|
||||
use gpu_cache::GpuCacheHandle;
|
||||
use gpu_types::{ClipScrollNodeIndex};
|
||||
use internal_types::HardwareCompositeOp;
|
||||
use prim_store::PrimitiveIndex;
|
||||
use prim_store::{PrimitiveIndex};
|
||||
use std::{cmp, usize, f32, i32};
|
||||
use std::rc::Rc;
|
||||
use tiling::{RenderPass, RenderTargetIndex};
|
||||
use tiling::{RenderTargetKind, StackingContextIndex};
|
||||
use tiling::{RenderTargetKind};
|
||||
|
||||
const FLOATS_PER_RENDER_TASK_INFO: usize = 12;
|
||||
pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0;
|
||||
|
@ -152,32 +150,10 @@ pub enum RenderTaskLocation {
|
|||
Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AlphaRenderItem {
|
||||
Primitive(ClipScrollNodeIndex, ClipScrollNodeIndex, PrimitiveIndex, i32),
|
||||
Blend(StackingContextIndex, RenderTaskId, FilterOp, i32),
|
||||
Composite(
|
||||
StackingContextIndex,
|
||||
RenderTaskId,
|
||||
RenderTaskId,
|
||||
MixBlendMode,
|
||||
i32,
|
||||
),
|
||||
SplitComposite(StackingContextIndex, RenderTaskId, GpuCacheHandle, i32),
|
||||
HardwareComposite(
|
||||
StackingContextIndex,
|
||||
RenderTaskId,
|
||||
HardwareCompositeOp,
|
||||
DeviceIntPoint,
|
||||
i32,
|
||||
DeviceIntSize,
|
||||
),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AlphaRenderTask {
|
||||
pub screen_origin: DeviceIntPoint,
|
||||
pub items: Vec<AlphaRenderItem>,
|
||||
pub prim_index: PrimitiveIndex,
|
||||
// If this render task is a registered frame output, this
|
||||
// contains the pipeline ID it maps to.
|
||||
pub frame_output_pipeline_id: Option<PipelineId>,
|
||||
|
@ -312,18 +288,23 @@ pub struct RenderTask {
|
|||
}
|
||||
|
||||
impl RenderTask {
|
||||
// TODO(gw): In the future we'll remove this
|
||||
// completely and convert everything
|
||||
// that is an alpha task to a Picture.
|
||||
pub fn new_alpha_batch(
|
||||
screen_origin: DeviceIntPoint,
|
||||
location: RenderTaskLocation,
|
||||
prim_index: PrimitiveIndex,
|
||||
frame_output_pipeline_id: Option<PipelineId>,
|
||||
children: Vec<RenderTaskId>,
|
||||
) -> Self {
|
||||
RenderTask {
|
||||
cache_key: None,
|
||||
children: Vec::new(),
|
||||
children,
|
||||
location,
|
||||
kind: RenderTaskKind::Alpha(AlphaRenderTask {
|
||||
screen_origin,
|
||||
items: Vec::new(),
|
||||
prim_index,
|
||||
frame_output_pipeline_id,
|
||||
}),
|
||||
clear_mode: ClearMode::Transparent,
|
||||
|
@ -332,10 +313,18 @@ impl RenderTask {
|
|||
|
||||
pub fn new_dynamic_alpha_batch(
|
||||
rect: &DeviceIntRect,
|
||||
prim_index: PrimitiveIndex,
|
||||
frame_output_pipeline_id: Option<PipelineId>,
|
||||
children: Vec<RenderTaskId>,
|
||||
) -> Self {
|
||||
let location = RenderTaskLocation::Dynamic(None, rect.size);
|
||||
Self::new_alpha_batch(rect.origin, location, frame_output_pipeline_id)
|
||||
Self::new_alpha_batch(
|
||||
rect.origin,
|
||||
location,
|
||||
prim_index,
|
||||
frame_output_pipeline_id,
|
||||
children,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_picture(
|
||||
|
@ -553,19 +542,6 @@ impl RenderTask {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn as_alpha_batch_mut<'a>(&'a mut self) -> &'a mut AlphaRenderTask {
|
||||
match self.kind {
|
||||
RenderTaskKind::Alpha(ref mut task) => task,
|
||||
RenderTaskKind::Picture(..) |
|
||||
RenderTaskKind::CacheMask(..) |
|
||||
RenderTaskKind::VerticalBlur(..) |
|
||||
RenderTaskKind::Readback(..) |
|
||||
RenderTaskKind::HorizontalBlur(..) |
|
||||
RenderTaskKind::Alias(..) |
|
||||
RenderTaskKind::Scaling(..) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_alpha_batch<'a>(&'a self) -> &'a AlphaRenderTask {
|
||||
match self.kind {
|
||||
RenderTaskKind::Alpha(ref task) => task,
|
||||
|
@ -693,34 +669,6 @@ impl RenderTask {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn inflate(&mut self, device_radius: i32) {
|
||||
match self.kind {
|
||||
RenderTaskKind::Alpha(ref mut info) => {
|
||||
match self.location {
|
||||
RenderTaskLocation::Fixed => {
|
||||
panic!("bug: inflate only supported for dynamic tasks");
|
||||
}
|
||||
RenderTaskLocation::Dynamic(_, ref mut size) => {
|
||||
size.width += device_radius * 2;
|
||||
size.height += device_radius * 2;
|
||||
info.screen_origin.x -= device_radius;
|
||||
info.screen_origin.y -= device_radius;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RenderTaskKind::Readback(..) |
|
||||
RenderTaskKind::CacheMask(..) |
|
||||
RenderTaskKind::VerticalBlur(..) |
|
||||
RenderTaskKind::HorizontalBlur(..) |
|
||||
RenderTaskKind::Picture(..) |
|
||||
RenderTaskKind::Alias(..) |
|
||||
RenderTaskKind::Scaling(..) => {
|
||||
panic!("bug: inflate only supported for alpha tasks");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_dynamic_size(&self) -> DeviceIntSize {
|
||||
match self.location {
|
||||
RenderTaskLocation::Fixed => DeviceIntSize::zero(),
|
||||
|
@ -730,12 +678,29 @@ impl RenderTask {
|
|||
|
||||
pub fn get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex) {
|
||||
match self.location {
|
||||
RenderTaskLocation::Fixed => (DeviceIntRect::zero(), RenderTargetIndex(0)),
|
||||
RenderTaskLocation::Dynamic(origin_and_target_index, size) => {
|
||||
let (origin, target_index) =
|
||||
origin_and_target_index.expect("Should have been allocated by now!");
|
||||
RenderTaskLocation::Fixed => {
|
||||
(DeviceIntRect::zero(), RenderTargetIndex(0))
|
||||
}
|
||||
// Previously, we only added render tasks after the entire
|
||||
// primitive chain was determined visible. This meant that
|
||||
// we could assert any render task in the list was also
|
||||
// allocated (assigned to passes). Now, we add render
|
||||
// tasks earlier, and the picture they belong to may be
|
||||
// culled out later, so we can't assert that the task
|
||||
// has been allocated.
|
||||
// Render tasks that are created but not assigned to
|
||||
// passes consume a row in the render task texture, but
|
||||
// don't allocate any space in render targets nor
|
||||
// draw any pixels.
|
||||
// TODO(gw): Consider some kind of tag or other method
|
||||
// to mark a task as unused explicitly. This
|
||||
// would allow us to restore this debug check.
|
||||
RenderTaskLocation::Dynamic(Some((origin, target_index)), size) => {
|
||||
(DeviceIntRect::new(origin, size), target_index)
|
||||
}
|
||||
RenderTaskLocation::Dynamic(None, _) => {
|
||||
(DeviceIntRect::zero(), RenderTargetIndex(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,11 +24,11 @@ use debug_colors;
|
|||
use debug_render::DebugRenderer;
|
||||
#[cfg(feature = "debugger")]
|
||||
use debug_server::{self, DebugServer};
|
||||
use device::{DepthFunction, Device, FrameId, GpuMarker, GpuProfiler, Program, Texture,
|
||||
use device::{DepthFunction, Device, FrameId, Program, Texture,
|
||||
VertexDescriptor, PBO};
|
||||
use device::{get_gl_format_bgra, ExternalTexture, FBOId, TextureSlot, VertexAttribute,
|
||||
VertexAttributeKind};
|
||||
use device::{FileWatcherHandler, GpuTimer, ShaderError, TextureFilter, TextureTarget,
|
||||
use device::{FileWatcherHandler, ShaderError, TextureFilter, TextureTarget,
|
||||
VertexUsageHint, VAO};
|
||||
use euclid::{rect, Transform3D};
|
||||
use frame_builder::FrameBuilderConfig;
|
||||
|
@ -41,6 +41,7 @@ use internal_types::{CacheTextureId, FastHashMap, RendererFrame, ResultMsg, Text
|
|||
use internal_types::{DebugOutput, RenderTargetMode, TextureUpdateList, TextureUpdateSource};
|
||||
use profiler::{BackendProfileCounters, Profiler};
|
||||
use profiler::{GpuProfileTag, RendererProfileCounters, RendererProfileTimers};
|
||||
use query::{GpuProfiler, GpuTimer};
|
||||
use rayon::Configuration as ThreadPoolConfig;
|
||||
use rayon::ThreadPool;
|
||||
use record::ApiRecordingReceiver;
|
||||
|
@ -555,10 +556,9 @@ impl SourceTextureResolver {
|
|||
|
||||
fn end_pass(
|
||||
&mut self,
|
||||
pass_index: usize,
|
||||
pass_count: usize,
|
||||
mut a8_texture: Option<Texture>,
|
||||
mut rgba8_texture: Option<Texture>,
|
||||
is_last: bool,
|
||||
a8_texture: Option<Texture>,
|
||||
rgba8_texture: Option<Texture>,
|
||||
a8_pool: &mut Vec<Texture>,
|
||||
rgba8_pool: &mut Vec<Texture>,
|
||||
) {
|
||||
|
@ -566,19 +566,15 @@ impl SourceTextureResolver {
|
|||
rgba8_pool.extend(self.cache_rgba8_texture.take());
|
||||
a8_pool.extend(self.cache_a8_texture.take());
|
||||
|
||||
if pass_index == pass_count - 1 {
|
||||
if is_last {
|
||||
// On the last pass, return the textures from this pass to the pool.
|
||||
if let Some(texture) = rgba8_texture.take() {
|
||||
rgba8_pool.push(texture);
|
||||
}
|
||||
if let Some(texture) = a8_texture.take() {
|
||||
a8_pool.push(texture);
|
||||
}
|
||||
rgba8_pool.extend(rgba8_texture);
|
||||
a8_pool.extend(a8_texture);
|
||||
} else {
|
||||
// We have another pass to process, make these textures available
|
||||
// as inputs to the next pass.
|
||||
self.cache_rgba8_texture = rgba8_texture.take();
|
||||
self.cache_a8_texture = a8_texture.take();
|
||||
self.cache_rgba8_texture = rgba8_texture;
|
||||
self.cache_a8_texture = a8_texture;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1838,8 +1834,7 @@ impl Renderer {
|
|||
};
|
||||
|
||||
let gpu_cache_texture = CacheTexture::new(&mut device);
|
||||
|
||||
let gpu_profile = GpuProfiler::new(device.rc_gl());
|
||||
let gpu_profile = GpuProfiler::new(Rc::clone(device.rc_gl()));
|
||||
|
||||
let renderer = Renderer {
|
||||
result_rx,
|
||||
|
@ -2153,6 +2148,16 @@ impl Renderer {
|
|||
} else {
|
||||
self.debug_flags.remove(DebugFlags::ALPHA_PRIM_DBG);
|
||||
},
|
||||
DebugCommand::EnableGpuTimeQueries(enable) => if enable {
|
||||
self.gpu_profile.enable_timers();
|
||||
} else {
|
||||
self.gpu_profile.disable_timers();
|
||||
},
|
||||
DebugCommand::EnableGpuSampleQueries(enable) => if enable {
|
||||
self.gpu_profile.enable_samplers();
|
||||
} else {
|
||||
self.gpu_profile.disable_samplers();
|
||||
},
|
||||
DebugCommand::FetchDocuments => {}
|
||||
DebugCommand::FetchClipScrollTree => {}
|
||||
DebugCommand::FetchPasses => {
|
||||
|
@ -2162,6 +2167,11 @@ impl Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn toggle_queries_enabled(&mut self) {
|
||||
self.gpu_profile.toggle_timers_enabled();
|
||||
self.gpu_profile.toggle_samplers_enabled();
|
||||
}
|
||||
|
||||
/// Set a callback for handling external images.
|
||||
pub fn set_external_image_handler(&mut self, handler: Box<ExternalImageHandler>) {
|
||||
self.external_image_handler = Some(handler);
|
||||
|
@ -2189,15 +2199,12 @@ impl Renderer {
|
|||
if let Some(mut frame) = self.current_frame.take() {
|
||||
if let Some(ref mut frame) = frame.frame {
|
||||
let mut profile_timers = RendererProfileTimers::new();
|
||||
let mut profile_samplers = Vec::new();
|
||||
|
||||
{
|
||||
//Note: avoiding `self.gpu_profile.add_marker` - it would block here
|
||||
let _gm = GpuMarker::new(self.device.rc_gl(), "build samples");
|
||||
let profile_samplers = {
|
||||
let _gm = self.gpu_profile.start_marker("build samples");
|
||||
// Block CPU waiting for last frame's GPU profiles to arrive.
|
||||
// In general this shouldn't block unless heavily GPU limited.
|
||||
if let Some((gpu_frame_id, timers, samplers)) = self.gpu_profile.build_samples()
|
||||
{
|
||||
let (gpu_frame_id, timers, samplers) = self.gpu_profile.build_samples();
|
||||
|
||||
if self.max_recorded_profiles > 0 {
|
||||
while self.gpu_profiles.len() >= self.max_recorded_profiles {
|
||||
self.gpu_profiles.pop_front();
|
||||
|
@ -2206,13 +2213,12 @@ impl Renderer {
|
|||
.push_back(GpuProfile::new(gpu_frame_id, &timers));
|
||||
}
|
||||
profile_timers.gpu_samples = timers;
|
||||
profile_samplers = samplers;
|
||||
}
|
||||
}
|
||||
samplers
|
||||
};
|
||||
|
||||
let cpu_frame_id = profile_timers.cpu_time.profile(|| {
|
||||
let cpu_frame_id = {
|
||||
let _gm = GpuMarker::new(self.device.rc_gl(), "begin frame");
|
||||
let _gm = self.gpu_profile.start_marker("begin frame");
|
||||
let frame_id = self.device.begin_frame(frame.device_pixel_ratio);
|
||||
self.gpu_profile.begin_frame(frame_id);
|
||||
|
||||
|
@ -2257,10 +2263,10 @@ impl Renderer {
|
|||
}
|
||||
|
||||
if self.debug_flags.contains(DebugFlags::PROFILER_DBG) {
|
||||
let _gm = self.gpu_profile.start_marker("profile");
|
||||
let screen_fraction = 1.0 / //TODO: take device/pixel ratio into equation?
|
||||
(framebuffer_size.width as f32 * framebuffer_size.height as f32);
|
||||
self.profiler.draw_profile(
|
||||
&mut self.device,
|
||||
&frame.profile_counters,
|
||||
&self.backend_profile_counters,
|
||||
&self.profile_counters,
|
||||
|
@ -2274,13 +2280,16 @@ impl Renderer {
|
|||
self.profile_counters.reset();
|
||||
self.profile_counters.frame_counter.inc();
|
||||
|
||||
{
|
||||
let _gm = self.gpu_profile.start_marker("debug");
|
||||
let debug_size = DeviceUintSize::new(
|
||||
framebuffer_size.width as u32,
|
||||
framebuffer_size.height as u32,
|
||||
);
|
||||
self.debug.render(&mut self.device, &debug_size);
|
||||
}
|
||||
{
|
||||
let _gm = GpuMarker::new(self.device.rc_gl(), "end frame");
|
||||
let _gm = self.gpu_profile.start_marker("end frame");
|
||||
self.device.end_frame();
|
||||
}
|
||||
self.last_time = current_time;
|
||||
|
@ -2304,7 +2313,7 @@ impl Renderer {
|
|||
}
|
||||
|
||||
fn update_gpu_cache(&mut self, frame: &mut Frame) {
|
||||
let _gm = GpuMarker::new(self.device.rc_gl(), "gpu cache update");
|
||||
let _gm = self.gpu_profile.start_marker("gpu cache update");
|
||||
for update_list in self.pending_gpu_cache_updates.drain(..) {
|
||||
self.gpu_cache_texture
|
||||
.update(&mut self.device, &update_list);
|
||||
|
@ -2314,7 +2323,7 @@ impl Renderer {
|
|||
}
|
||||
|
||||
fn update_texture_cache(&mut self) {
|
||||
let _gm = GpuMarker::new(self.device.rc_gl(), "texture cache update");
|
||||
let _gm = self.gpu_profile.start_marker("texture cache update");
|
||||
let mut pending_texture_updates = mem::replace(&mut self.pending_texture_updates, vec![]);
|
||||
|
||||
for update_list in pending_texture_updates.drain(..) {
|
||||
|
@ -2711,7 +2720,7 @@ impl Renderer {
|
|||
_ => {}
|
||||
}
|
||||
|
||||
let _gm = self.gpu_profile.add_marker(marker);
|
||||
let _timer = self.gpu_profile.start_timer(marker);
|
||||
self.draw_instanced_batch(instances, VertexArrayKind::Primitive, &key.textures);
|
||||
}
|
||||
|
||||
|
@ -2750,7 +2759,7 @@ impl Renderer {
|
|||
frame_id: FrameId,
|
||||
) {
|
||||
{
|
||||
let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET);
|
||||
let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_TARGET);
|
||||
self.device
|
||||
.bind_draw_target(render_target, Some(target_size));
|
||||
self.device.disable_depth();
|
||||
|
@ -2781,7 +2790,7 @@ impl Renderer {
|
|||
// fast path blur shaders for common
|
||||
// blur radii with fixed weights.
|
||||
if !target.vertical_blurs.is_empty() || !target.horizontal_blurs.is_empty() {
|
||||
let _gm = self.gpu_profile.add_marker(GPU_TAG_BLUR);
|
||||
let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR);
|
||||
|
||||
self.device.set_blend(false);
|
||||
self.cs_blur_rgba8
|
||||
|
@ -2816,7 +2825,7 @@ impl Renderer {
|
|||
self.device.set_blend(true);
|
||||
self.device.set_blend_mode_premultiplied_alpha();
|
||||
|
||||
let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_TEXT_RUN);
|
||||
let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_TEXT_RUN);
|
||||
self.cs_text_run
|
||||
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
|
||||
for (texture_id, instances) in &target.text_run_cache_prims {
|
||||
|
@ -2833,7 +2842,7 @@ impl Renderer {
|
|||
self.device.set_blend(true);
|
||||
self.device.set_blend_mode_premultiplied_alpha();
|
||||
|
||||
let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_LINE);
|
||||
let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_LINE);
|
||||
self.cs_line
|
||||
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
|
||||
self.draw_instanced_batch(
|
||||
|
@ -2846,11 +2855,11 @@ impl Renderer {
|
|||
//TODO: record the pixel count for cached primitives
|
||||
|
||||
if !target.alpha_batcher.is_empty() {
|
||||
let _gm2 = GpuMarker::new(self.device.rc_gl(), "alpha batches");
|
||||
let _gl = self.gpu_profile.start_marker("alpha batches");
|
||||
self.device.set_blend(false);
|
||||
let mut prev_blend_mode = BlendMode::None;
|
||||
|
||||
self.gpu_profile.add_sampler(GPU_SAMPLER_TAG_OPAQUE);
|
||||
let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE);
|
||||
|
||||
//Note: depth equality is needed for split planes
|
||||
self.device.set_depth_func(DepthFunction::LessEqual);
|
||||
|
@ -2878,7 +2887,8 @@ impl Renderer {
|
|||
}
|
||||
|
||||
self.device.disable_depth_write();
|
||||
self.gpu_profile.add_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
|
||||
self.gpu_profile.finish_sampler(opaque_sampler);
|
||||
let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
|
||||
|
||||
for batch in &target.alpha_batcher.batch_list.alpha_batch_list.batches {
|
||||
if self.debug_flags.contains(DebugFlags::ALPHA_PRIM_DBG) {
|
||||
|
@ -2904,7 +2914,7 @@ impl Renderer {
|
|||
// 1) Use dual source blending where available (almost all recent hardware).
|
||||
// 2) Use frame buffer fetch where available (most modern hardware).
|
||||
// 3) Consider the old constant color blend method where no clip is applied.
|
||||
let _gm = self.gpu_profile.add_marker(GPU_TAG_PRIM_TEXT_RUN);
|
||||
let _timer = self.gpu_profile.start_timer(GPU_TAG_PRIM_TEXT_RUN);
|
||||
|
||||
self.device.set_blend(true);
|
||||
|
||||
|
@ -3078,7 +3088,7 @@ impl Renderer {
|
|||
|
||||
self.device.disable_depth();
|
||||
self.device.set_blend(false);
|
||||
self.gpu_profile.done_sampler();
|
||||
self.gpu_profile.finish_sampler(transparent_sampler);
|
||||
}
|
||||
|
||||
// For any registered image outputs on this render target,
|
||||
|
@ -3123,10 +3133,10 @@ impl Renderer {
|
|||
projection: &Transform3D<f32>,
|
||||
render_tasks: &RenderTaskTree,
|
||||
) {
|
||||
self.gpu_profile.add_sampler(GPU_SAMPLER_TAG_ALPHA);
|
||||
let alpha_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_ALPHA);
|
||||
|
||||
{
|
||||
let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET);
|
||||
let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_TARGET);
|
||||
self.device
|
||||
.bind_draw_target(Some(render_target), Some(target_size));
|
||||
self.device.disable_depth();
|
||||
|
@ -3157,7 +3167,7 @@ impl Renderer {
|
|||
// fast path blur shaders for common
|
||||
// blur radii with fixed weights.
|
||||
if !target.vertical_blurs.is_empty() || !target.horizontal_blurs.is_empty() {
|
||||
let _gm = self.gpu_profile.add_marker(GPU_TAG_BLUR);
|
||||
let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR);
|
||||
|
||||
self.device.set_blend(false);
|
||||
self.cs_blur_a8
|
||||
|
@ -3185,7 +3195,7 @@ impl Renderer {
|
|||
if !target.brush_mask_corners.is_empty() {
|
||||
self.device.set_blend(false);
|
||||
|
||||
let _gm = self.gpu_profile.add_marker(GPU_TAG_BRUSH_MASK);
|
||||
let _timer = self.gpu_profile.start_timer(GPU_TAG_BRUSH_MASK);
|
||||
self.brush_mask_corner
|
||||
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
|
||||
self.draw_instanced_batch(
|
||||
|
@ -3198,7 +3208,7 @@ impl Renderer {
|
|||
if !target.brush_mask_rounded_rects.is_empty() {
|
||||
self.device.set_blend(false);
|
||||
|
||||
let _gm = self.gpu_profile.add_marker(GPU_TAG_BRUSH_MASK);
|
||||
let _timer = self.gpu_profile.start_timer(GPU_TAG_BRUSH_MASK);
|
||||
self.brush_mask_rounded_rect
|
||||
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
|
||||
self.draw_instanced_batch(
|
||||
|
@ -3210,13 +3220,13 @@ impl Renderer {
|
|||
|
||||
// Draw the clip items into the tiled alpha mask.
|
||||
{
|
||||
let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_CLIP);
|
||||
let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_CLIP);
|
||||
|
||||
// If we have border corner clips, the first step is to clear out the
|
||||
// area in the clip mask. This allows drawing multiple invididual clip
|
||||
// in regions below.
|
||||
if !target.clip_batcher.border_clears.is_empty() {
|
||||
let _gm2 = GpuMarker::new(self.device.rc_gl(), "clip borders [clear]");
|
||||
let _gm = self.gpu_profile.start_marker("clip borders [clear]");
|
||||
self.device.set_blend(false);
|
||||
self.cs_clip_border
|
||||
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
|
||||
|
@ -3229,7 +3239,7 @@ impl Renderer {
|
|||
|
||||
// Draw any dots or dashes for border corners.
|
||||
if !target.clip_batcher.borders.is_empty() {
|
||||
let _gm2 = GpuMarker::new(self.device.rc_gl(), "clip borders");
|
||||
let _gm = self.gpu_profile.start_marker("clip borders");
|
||||
// We are masking in parts of the corner (dots or dashes) here.
|
||||
// Blend mode is set to max to allow drawing multiple dots.
|
||||
// The individual dots and dashes in a border never overlap, so using
|
||||
|
@ -3251,7 +3261,7 @@ impl Renderer {
|
|||
|
||||
// draw rounded cornered rectangles
|
||||
if !target.clip_batcher.rectangles.is_empty() {
|
||||
let _gm2 = GpuMarker::new(self.device.rc_gl(), "clip rectangles");
|
||||
let _gm = self.gpu_profile.start_marker("clip rectangles");
|
||||
self.cs_clip_rectangle.bind(
|
||||
&mut self.device,
|
||||
projection,
|
||||
|
@ -3266,7 +3276,7 @@ impl Renderer {
|
|||
}
|
||||
// draw image masks
|
||||
for (mask_texture_id, items) in target.clip_batcher.images.iter() {
|
||||
let _gm2 = GpuMarker::new(self.device.rc_gl(), "clip images");
|
||||
let _gm = self.gpu_profile.start_marker("clip images");
|
||||
let textures = BatchTextures {
|
||||
colors: [
|
||||
mask_texture_id.clone(),
|
||||
|
@ -3280,7 +3290,7 @@ impl Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
self.gpu_profile.done_sampler();
|
||||
self.gpu_profile.finish_sampler(alpha_sampler);
|
||||
}
|
||||
|
||||
fn update_deferred_resolves(&mut self, frame: &mut Frame) {
|
||||
|
@ -3294,7 +3304,7 @@ impl Renderer {
|
|||
.expect("Found external image, but no handler set!");
|
||||
|
||||
for deferred_resolve in &frame.deferred_resolves {
|
||||
GpuMarker::fire(self.device.gl(), "deferred resolve");
|
||||
self.gpu_profile.place_marker("deferred resolve");
|
||||
let props = &deferred_resolve.image_properties;
|
||||
let ext_image = props
|
||||
.external_image
|
||||
|
@ -3365,7 +3375,7 @@ impl Renderer {
|
|||
}
|
||||
|
||||
fn start_frame(&mut self, frame: &mut Frame) {
|
||||
let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_DATA);
|
||||
let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_DATA);
|
||||
|
||||
// Assign render targets to the passes.
|
||||
for pass in &mut frame.passes {
|
||||
|
@ -3447,7 +3457,7 @@ impl Renderer {
|
|||
framebuffer_size: DeviceUintSize,
|
||||
frame_id: FrameId,
|
||||
) {
|
||||
let _gm = GpuMarker::new(self.device.rc_gl(), "tile frame draw");
|
||||
let _gm = self.gpu_profile.start_marker("tile frame draw");
|
||||
|
||||
// Some tests use a restricted viewport smaller than the main screen size.
|
||||
// Ensure we clear the framebuffer in these tests.
|
||||
|
@ -3464,7 +3474,10 @@ impl Renderer {
|
|||
.clear_target(Some(self.clear_color.to_array()), Some(1.0));
|
||||
} else {
|
||||
self.start_frame(frame);
|
||||
|
||||
let pass_count = frame.passes.len();
|
||||
let base_color_target_count = self.color_render_targets.len();
|
||||
let base_alpha_target_count = self.alpha_render_targets.len();
|
||||
|
||||
for (pass_index, pass) in frame.passes.iter_mut().enumerate() {
|
||||
self.texture_resolver.bind(
|
||||
|
@ -3549,8 +3562,7 @@ impl Renderer {
|
|||
}
|
||||
|
||||
self.texture_resolver.end_pass(
|
||||
pass_index,
|
||||
pass_count,
|
||||
pass_index == pass_count - 1,
|
||||
pass.alpha_texture.take(),
|
||||
pass.color_texture.take(),
|
||||
&mut self.alpha_render_targets,
|
||||
|
@ -3569,8 +3581,8 @@ impl Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
self.color_render_targets.reverse();
|
||||
self.alpha_render_targets.reverse();
|
||||
self.color_render_targets[base_color_target_count..].reverse();
|
||||
self.alpha_render_targets[base_alpha_target_count..].reverse();
|
||||
self.draw_render_target_debug(framebuffer_size);
|
||||
self.draw_texture_cache_debug(framebuffer_size);
|
||||
|
||||
|
|
|
@ -42,36 +42,39 @@ impl SceneProperties {
|
|||
/// Get the current value for a transform property.
|
||||
pub fn resolve_layout_transform(
|
||||
&self,
|
||||
property: Option<&PropertyBinding<LayoutTransform>>,
|
||||
property: &PropertyBinding<LayoutTransform>,
|
||||
) -> LayoutTransform {
|
||||
let property = match property {
|
||||
Some(property) => property,
|
||||
None => return LayoutTransform::identity(),
|
||||
};
|
||||
|
||||
match *property {
|
||||
PropertyBinding::Value(matrix) => matrix,
|
||||
PropertyBinding::Binding(ref key) => self.transform_properties
|
||||
PropertyBinding::Value(value) => value,
|
||||
PropertyBinding::Binding(ref key) => {
|
||||
self.transform_properties
|
||||
.get(&key.id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| {
|
||||
warn!("Property binding {:?} has an invalid value.", key);
|
||||
LayoutTransform::identity()
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current value for a float property.
|
||||
pub fn resolve_float(&self, property: &PropertyBinding<f32>, default_value: f32) -> f32 {
|
||||
pub fn resolve_float(
|
||||
&self,
|
||||
property: &PropertyBinding<f32>,
|
||||
default_value: f32
|
||||
) -> f32 {
|
||||
match *property {
|
||||
PropertyBinding::Value(value) => value,
|
||||
PropertyBinding::Binding(ref key) => self.float_properties
|
||||
PropertyBinding::Binding(ref key) => {
|
||||
self.float_properties
|
||||
.get(&key.id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| {
|
||||
warn!("Property binding {:?} has an invalid value.", key);
|
||||
default_value
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -141,19 +144,28 @@ impl Scene {
|
|||
}
|
||||
}
|
||||
|
||||
/// An arbitrary number which we assume opacity is invisible below.
|
||||
pub const OPACITY_EPSILON: f32 = 0.001;
|
||||
|
||||
pub trait FilterOpHelpers {
|
||||
fn resolve(self, properties: &SceneProperties) -> FilterOp;
|
||||
fn is_visible(&self) -> bool;
|
||||
fn is_noop(&self) -> bool;
|
||||
}
|
||||
|
||||
impl FilterOpHelpers for FilterOp {
|
||||
fn resolve(self, properties: &SceneProperties) -> FilterOp {
|
||||
match self {
|
||||
FilterOp::Opacity(ref value) => {
|
||||
let amount = properties.resolve_float(value, 1.0);
|
||||
FilterOp::Opacity(PropertyBinding::Value(amount))
|
||||
fn is_visible(&self) -> bool {
|
||||
match *self {
|
||||
FilterOp::Blur(..) |
|
||||
FilterOp::Brightness(..) |
|
||||
FilterOp::Contrast(..) |
|
||||
FilterOp::Grayscale(..) |
|
||||
FilterOp::HueRotate(..) |
|
||||
FilterOp::Invert(..) |
|
||||
FilterOp::Saturate(..) |
|
||||
FilterOp::Sepia(..) => true,
|
||||
FilterOp::Opacity(_, amount) => {
|
||||
amount > OPACITY_EPSILON
|
||||
}
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,12 +177,7 @@ impl FilterOpHelpers for FilterOp {
|
|||
FilterOp::Grayscale(amount) => amount == 0.0,
|
||||
FilterOp::HueRotate(amount) => amount == 0.0,
|
||||
FilterOp::Invert(amount) => amount == 0.0,
|
||||
FilterOp::Opacity(value) => match value {
|
||||
PropertyBinding::Value(amount) => amount == 1.0,
|
||||
PropertyBinding::Binding(..) => {
|
||||
panic!("bug: binding value should be resolved");
|
||||
}
|
||||
},
|
||||
FilterOp::Opacity(_, amount) => amount >= 1.0,
|
||||
FilterOp::Saturate(amount) => amount == 1.0,
|
||||
FilterOp::Sepia(amount) => amount == 0.0,
|
||||
}
|
||||
|
@ -183,7 +190,6 @@ pub trait StackingContextHelpers {
|
|||
&self,
|
||||
display_list: &BuiltDisplayList,
|
||||
input_filters: ItemRange<FilterOp>,
|
||||
properties: &SceneProperties,
|
||||
) -> Vec<FilterOp>;
|
||||
}
|
||||
|
||||
|
@ -199,14 +205,12 @@ impl StackingContextHelpers for StackingContext {
|
|||
&self,
|
||||
display_list: &BuiltDisplayList,
|
||||
input_filters: ItemRange<FilterOp>,
|
||||
properties: &SceneProperties,
|
||||
) -> Vec<FilterOp> {
|
||||
// TODO(gw): Now that we resolve these later on,
|
||||
// we could probably make it a bit
|
||||
// more efficient than cloning these here.
|
||||
let mut filters = vec![];
|
||||
for filter in display_list.get(input_filters) {
|
||||
let filter = filter.resolve(properties);
|
||||
if filter.is_noop() {
|
||||
continue;
|
||||
}
|
||||
filters.push(filter);
|
||||
}
|
||||
filters
|
||||
|
|
|
@ -5,12 +5,14 @@
|
|||
use api::{BorderRadiusKind, ClipId, ColorF, DeviceIntPoint, ImageKey};
|
||||
use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize};
|
||||
use api::{ExternalImageType, FilterOp, FontRenderMode, ImageRendering, LayerRect};
|
||||
use api::{MixBlendMode, PipelineId, PropertyBinding, TransformStyle};
|
||||
use api::{LayerVector2D, TileOffset, YuvColorSpace, YuvFormat};
|
||||
use api::{MixBlendMode, PipelineId};
|
||||
use api::{TileOffset, YuvColorSpace, YuvFormat};
|
||||
use api::{LayerToWorldTransform, WorldPixel};
|
||||
use border::{BorderCornerInstance, BorderCornerSide};
|
||||
use clip::{ClipSource, ClipStore};
|
||||
use clip_scroll_tree::CoordinateSystemId;
|
||||
use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId};
|
||||
use device::Texture;
|
||||
use euclid::{TypedTransform3D, vec3};
|
||||
use glyph_rasterizer::GlyphFormat;
|
||||
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuCacheUpdateList};
|
||||
use gpu_types::{BlurDirection, BlurInstance, BrushInstance, BrushImageKind, ClipMaskInstance};
|
||||
|
@ -18,11 +20,12 @@ use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveIn
|
|||
use gpu_types::{BRUSH_FLAG_USES_PICTURE, ClipScrollNodeIndex, ClipScrollNodeData};
|
||||
use internal_types::{FastHashMap, SourceTexture};
|
||||
use internal_types::BatchTextures;
|
||||
use picture::PictureKind;
|
||||
use picture::{PictureCompositeMode, PictureKind, PicturePrimitive};
|
||||
use plane_split::{BspSplitter, Polygon, Splitter};
|
||||
use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
|
||||
use prim_store::{BrushMaskKind, BrushKind, DeferredResolve, PrimitiveRun, RectangleContent};
|
||||
use profiler::FrameProfileCounters;
|
||||
use render_task::{AlphaRenderItem, ClipWorkItem, MaskGeometryKind, MaskSegment};
|
||||
use render_task::{ClipWorkItem, MaskGeometryKind, MaskSegment};
|
||||
use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKey, RenderTaskKind};
|
||||
use render_task::{BlurTask, ClearMode, RenderTaskLocation, RenderTaskTree};
|
||||
use renderer::BlendMode;
|
||||
|
@ -37,6 +40,52 @@ use util::{MatrixHelpers, TransformedRectKind};
|
|||
const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(i32::MAX as u32);
|
||||
const MIN_TARGET_SIZE: u32 = 2048;
|
||||
|
||||
// Helper to add an entire primitive run to a batch list.
|
||||
// TODO(gw): Restructure this so the param list isn't quite
|
||||
// so daunting!
|
||||
impl PrimitiveRun {
|
||||
fn add_to_batch(
|
||||
&self,
|
||||
clip_id: ClipScrollNodeIndex,
|
||||
scroll_id: ClipScrollNodeIndex,
|
||||
batch_list: &mut BatchList,
|
||||
ctx: &RenderTargetContext,
|
||||
gpu_cache: &mut GpuCache,
|
||||
render_tasks: &RenderTaskTree,
|
||||
task_id: RenderTaskId,
|
||||
task_address: RenderTaskAddress,
|
||||
deferred_resolves: &mut Vec<DeferredResolve>,
|
||||
glyph_fetch_buffer: &mut Vec<GlyphFetchResult>,
|
||||
splitter: &mut BspSplitter<f64, WorldPixel>,
|
||||
) {
|
||||
for i in 0 .. self.count {
|
||||
let prim_index = PrimitiveIndex(self.base_prim_index.0 + i);
|
||||
|
||||
let md = &ctx.prim_store.cpu_metadata[prim_index.0];
|
||||
|
||||
// Now that we walk the primitive runs in order to add
|
||||
// items to batches, we need to check if they are
|
||||
// visible here.
|
||||
if md.screen_rect.is_some() {
|
||||
add_to_batch(
|
||||
clip_id,
|
||||
scroll_id,
|
||||
prim_index,
|
||||
batch_list,
|
||||
ctx,
|
||||
gpu_cache,
|
||||
render_tasks,
|
||||
task_id,
|
||||
task_address,
|
||||
deferred_resolves,
|
||||
glyph_fetch_buffer,
|
||||
splitter,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait AlphaBatchHelpers {
|
||||
fn get_blend_mode(
|
||||
&self,
|
||||
|
@ -111,13 +160,6 @@ pub struct ScrollbarPrimitive {
|
|||
pub frame_rect: LayerRect,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PrimitiveRunCmd {
|
||||
PushStackingContext(StackingContextIndex),
|
||||
PopStackingContext,
|
||||
PrimitiveRun(PrimitiveRun),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct RenderTargetIndex(pub usize);
|
||||
|
||||
|
@ -208,25 +250,45 @@ impl AlphaBatchList {
|
|||
}
|
||||
|
||||
pub struct OpaqueBatchList {
|
||||
pub pixel_area_threshold_for_new_batch: i32,
|
||||
pub batches: Vec<OpaquePrimitiveBatch>,
|
||||
}
|
||||
|
||||
impl OpaqueBatchList {
|
||||
fn new() -> OpaqueBatchList {
|
||||
fn new(pixel_area_threshold_for_new_batch: i32) -> OpaqueBatchList {
|
||||
OpaqueBatchList {
|
||||
batches: Vec::new(),
|
||||
pixel_area_threshold_for_new_batch,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_suitable_batch(&mut self, key: BatchKey) -> &mut Vec<PrimitiveInstance> {
|
||||
fn get_suitable_batch(
|
||||
&mut self,
|
||||
key: BatchKey,
|
||||
item_bounding_rect: &DeviceIntRect
|
||||
) -> &mut Vec<PrimitiveInstance> {
|
||||
let mut selected_batch_index = None;
|
||||
let item_area = item_bounding_rect.size.area();
|
||||
|
||||
// If the area of this primitive is larger than the given threshold,
|
||||
// then it is large enough to warrant breaking a batch for. In this
|
||||
// case we just see if it can be added to the existing batch or
|
||||
// create a new one.
|
||||
if item_area > self.pixel_area_threshold_for_new_batch {
|
||||
if let Some(ref batch) = self.batches.last() {
|
||||
if batch.key.is_compatible_with(&key) {
|
||||
selected_batch_index = Some(self.batches.len() - 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Otherwise, look back through a reasonable number of batches.
|
||||
for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
|
||||
if batch.key.is_compatible_with(&key) {
|
||||
selected_batch_index = Some(batch_index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if selected_batch_index.is_none() {
|
||||
let new_batch = OpaquePrimitiveBatch::new(key);
|
||||
|
@ -258,10 +320,14 @@ pub struct BatchList {
|
|||
}
|
||||
|
||||
impl BatchList {
|
||||
fn new() -> BatchList {
|
||||
fn new(screen_size: DeviceIntSize) -> BatchList {
|
||||
// The threshold for creating a new batch is
|
||||
// one quarter the screen size.
|
||||
let batch_area_threshold = screen_size.width * screen_size.height / 4;
|
||||
|
||||
BatchList {
|
||||
alpha_batch_list: AlphaBatchList::new(),
|
||||
opaque_batch_list: OpaqueBatchList::new(),
|
||||
opaque_batch_list: OpaqueBatchList::new(batch_area_threshold),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -271,7 +337,10 @@ impl BatchList {
|
|||
item_bounding_rect: &DeviceIntRect,
|
||||
) -> &mut Vec<PrimitiveInstance> {
|
||||
match key.blend_mode {
|
||||
BlendMode::None => self.opaque_batch_list.get_suitable_batch(key),
|
||||
BlendMode::None => {
|
||||
self.opaque_batch_list
|
||||
.get_suitable_batch(key, item_bounding_rect)
|
||||
}
|
||||
BlendMode::PremultipliedAlpha |
|
||||
BlendMode::PremultipliedDestOut |
|
||||
BlendMode::SubpixelConstantTextColor(..) |
|
||||
|
@ -295,9 +364,14 @@ pub struct AlphaBatcher {
|
|||
glyph_fetch_buffer: Vec<GlyphFetchResult>,
|
||||
}
|
||||
|
||||
impl AlphaRenderItem {
|
||||
fn add_to_batch(
|
||||
&self,
|
||||
// A free function that adds a primitive to a batch.
|
||||
// It can recursively call itself in some situations, for
|
||||
// example if it encounters a picture where the items
|
||||
// in that picture are being drawn into the same target.
|
||||
fn add_to_batch(
|
||||
clip_id: ClipScrollNodeIndex,
|
||||
scroll_id: ClipScrollNodeIndex,
|
||||
prim_index: PrimitiveIndex,
|
||||
batch_list: &mut BatchList,
|
||||
ctx: &RenderTargetContext,
|
||||
gpu_cache: &mut GpuCache,
|
||||
|
@ -306,110 +380,9 @@ impl AlphaRenderItem {
|
|||
task_address: RenderTaskAddress,
|
||||
deferred_resolves: &mut Vec<DeferredResolve>,
|
||||
glyph_fetch_buffer: &mut Vec<GlyphFetchResult>,
|
||||
) {
|
||||
match *self {
|
||||
AlphaRenderItem::Blend(stacking_context_index, src_id, filter, z) => {
|
||||
let stacking_context = &ctx.stacking_context_store[stacking_context_index.0];
|
||||
let key = BatchKey::new(
|
||||
BatchKind::Blend,
|
||||
BlendMode::PremultipliedAlpha,
|
||||
BatchTextures::no_texture(),
|
||||
);
|
||||
let src_task_address = render_tasks.get_task_address(src_id);
|
||||
|
||||
let (filter_mode, amount) = match filter {
|
||||
FilterOp::Blur(..) => (0, 0.0),
|
||||
FilterOp::Contrast(amount) => (1, amount),
|
||||
FilterOp::Grayscale(amount) => (2, amount),
|
||||
FilterOp::HueRotate(angle) => (3, angle),
|
||||
FilterOp::Invert(amount) => (4, amount),
|
||||
FilterOp::Saturate(amount) => (5, amount),
|
||||
FilterOp::Sepia(amount) => (6, amount),
|
||||
FilterOp::Brightness(amount) => (7, amount),
|
||||
FilterOp::Opacity(PropertyBinding::Value(amount)) => (8, amount),
|
||||
FilterOp::Opacity(_) => unreachable!(),
|
||||
};
|
||||
|
||||
let amount = (amount * 65535.0).round() as i32;
|
||||
let batch = batch_list.get_suitable_batch(key, &stacking_context.screen_bounds);
|
||||
|
||||
let instance = CompositePrimitiveInstance::new(
|
||||
task_address,
|
||||
src_task_address,
|
||||
RenderTaskAddress(0),
|
||||
filter_mode,
|
||||
amount,
|
||||
z,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
batch.push(PrimitiveInstance::from(instance));
|
||||
}
|
||||
AlphaRenderItem::HardwareComposite(
|
||||
stacking_context_index,
|
||||
src_id,
|
||||
composite_op,
|
||||
screen_origin,
|
||||
z,
|
||||
dest_rect,
|
||||
) => {
|
||||
let stacking_context = &ctx.stacking_context_store[stacking_context_index.0];
|
||||
let src_task_address = render_tasks.get_task_address(src_id);
|
||||
let key = BatchKey::new(
|
||||
BatchKind::HardwareComposite,
|
||||
composite_op.to_blend_mode(),
|
||||
BatchTextures::no_texture(),
|
||||
);
|
||||
let batch = batch_list.get_suitable_batch(key, &stacking_context.screen_bounds);
|
||||
let dest_rect = if dest_rect.width > 0 && dest_rect.height > 0 {
|
||||
dest_rect
|
||||
} else {
|
||||
render_tasks.get(src_id).get_dynamic_size()
|
||||
};
|
||||
|
||||
let instance = CompositePrimitiveInstance::new(
|
||||
task_address,
|
||||
src_task_address,
|
||||
RenderTaskAddress(0),
|
||||
screen_origin.x,
|
||||
screen_origin.y,
|
||||
z,
|
||||
dest_rect.width,
|
||||
dest_rect.height,
|
||||
);
|
||||
|
||||
batch.push(PrimitiveInstance::from(instance));
|
||||
}
|
||||
AlphaRenderItem::Composite(stacking_context_index, source_id, backdrop_id, mode, z) => {
|
||||
let stacking_context = &ctx.stacking_context_store[stacking_context_index.0];
|
||||
let key = BatchKey::new(
|
||||
BatchKind::Composite {
|
||||
task_id,
|
||||
source_id,
|
||||
backdrop_id,
|
||||
},
|
||||
BlendMode::PremultipliedAlpha,
|
||||
BatchTextures::no_texture(),
|
||||
);
|
||||
let batch = batch_list.get_suitable_batch(key, &stacking_context.screen_bounds);
|
||||
let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
|
||||
let source_task_address = render_tasks.get_task_address(source_id);
|
||||
|
||||
let instance = CompositePrimitiveInstance::new(
|
||||
task_address,
|
||||
source_task_address,
|
||||
backdrop_task_address,
|
||||
mode as u32 as i32,
|
||||
0,
|
||||
z,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
batch.push(PrimitiveInstance::from(instance));
|
||||
}
|
||||
AlphaRenderItem::Primitive(clip_id, scroll_id, prim_index, z) => {
|
||||
splitter: &mut BspSplitter<f64, WorldPixel>,
|
||||
) {
|
||||
let z = prim_index.0 as i32;
|
||||
let prim_metadata = ctx.prim_store.get_metadata(prim_index);
|
||||
let scroll_node = &ctx.node_data[scroll_id.0 as usize];
|
||||
// TODO(gw): Calculating this for every primitive is a bit
|
||||
|
@ -609,29 +582,49 @@ impl AlphaRenderItem {
|
|||
PrimitiveKind::Picture => {
|
||||
let picture =
|
||||
&ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0];
|
||||
let cache_task_id = picture.render_task_id.expect("no render task!");
|
||||
|
||||
match picture.render_task_id {
|
||||
Some(cache_task_id) => {
|
||||
let cache_task_address = render_tasks.get_task_address(cache_task_id);
|
||||
let textures = BatchTextures::render_target_cache();
|
||||
|
||||
match picture.kind {
|
||||
PictureKind::TextShadow { .. } => {
|
||||
let kind = BatchKind::Brush(
|
||||
BrushBatchKind::Image(picture.target_kind()),
|
||||
);
|
||||
let key = BatchKey::new(kind, blend_mode, textures);
|
||||
let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
|
||||
let image_kind = match picture.kind {
|
||||
PictureKind::TextShadow { .. } => {
|
||||
BrushImageKind::Simple
|
||||
|
||||
let instance = BrushInstance {
|
||||
picture_address: task_address,
|
||||
prim_address: prim_cache_address,
|
||||
clip_id,
|
||||
scroll_id,
|
||||
clip_task_address,
|
||||
z,
|
||||
flags: 0,
|
||||
user_data0: cache_task_address.0 as i32,
|
||||
user_data1: BrushImageKind::Simple as i32,
|
||||
};
|
||||
batch.push(PrimitiveInstance::from(instance));
|
||||
}
|
||||
PictureKind::BoxShadow { radii_kind, .. } => {
|
||||
match radii_kind {
|
||||
let kind = BatchKind::Brush(
|
||||
BrushBatchKind::Image(picture.target_kind()),
|
||||
);
|
||||
let key = BatchKey::new(kind, blend_mode, textures);
|
||||
let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
|
||||
|
||||
let image_kind = match radii_kind {
|
||||
BorderRadiusKind::Uniform => {
|
||||
BrushImageKind::Mirror
|
||||
}
|
||||
BorderRadiusKind::NonUniform => {
|
||||
BrushImageKind::NinePatch
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let instance = BrushInstance {
|
||||
picture_address: task_address,
|
||||
prim_address: prim_cache_address,
|
||||
|
@ -645,6 +638,172 @@ impl AlphaRenderItem {
|
|||
};
|
||||
batch.push(PrimitiveInstance::from(instance));
|
||||
}
|
||||
PictureKind::Image {
|
||||
composite_mode,
|
||||
readback_render_task_id,
|
||||
is_in_3d_context,
|
||||
reference_frame_id,
|
||||
real_local_rect,
|
||||
..
|
||||
} => {
|
||||
// If this picture is participating in a 3D rendering context,
|
||||
// then don't add it to any batches here. Instead, create a polygon
|
||||
// for it and add it to the current plane splitter.
|
||||
if is_in_3d_context {
|
||||
// Push into parent plane splitter.
|
||||
|
||||
let real_xf = &ctx.clip_scroll_tree.nodes[&reference_frame_id].world_content_transform;
|
||||
|
||||
let polygon = make_polygon(
|
||||
real_local_rect,
|
||||
&real_xf,
|
||||
prim_index.0,
|
||||
);
|
||||
|
||||
splitter.add(polygon);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Depending on the composite mode of the picture, we generate the
|
||||
// old style Composite primitive instances. In the future, we'll
|
||||
// remove these and pass them through the brush batching pipeline.
|
||||
// This will allow us to unify some of the shaders, apply clip masks
|
||||
// when compositing pictures, and also correctly apply pixel snapping
|
||||
// to picture compositing operations.
|
||||
let source_id = picture.render_task_id.expect("no source!?");
|
||||
|
||||
match composite_mode.expect("bug: only composites here") {
|
||||
PictureCompositeMode::Filter(filter) => {
|
||||
match filter {
|
||||
FilterOp::Blur(..) => {
|
||||
let src_task_address = render_tasks.get_task_address(source_id);
|
||||
let key = BatchKey::new(
|
||||
BatchKind::HardwareComposite,
|
||||
BlendMode::PremultipliedAlpha,
|
||||
BatchTextures::no_texture(),
|
||||
);
|
||||
let batch = batch_list.get_suitable_batch(key, &item_bounding_rect);
|
||||
let instance = CompositePrimitiveInstance::new(
|
||||
task_address,
|
||||
src_task_address,
|
||||
RenderTaskAddress(0),
|
||||
item_bounding_rect.origin.x,
|
||||
item_bounding_rect.origin.y,
|
||||
z,
|
||||
item_bounding_rect.size.width,
|
||||
item_bounding_rect.size.height,
|
||||
);
|
||||
|
||||
batch.push(PrimitiveInstance::from(instance));
|
||||
}
|
||||
_ => {
|
||||
let key = BatchKey::new(
|
||||
BatchKind::Blend,
|
||||
BlendMode::PremultipliedAlpha,
|
||||
BatchTextures::no_texture(),
|
||||
);
|
||||
let src_task_address = render_tasks.get_task_address(source_id);
|
||||
|
||||
let (filter_mode, amount) = match filter {
|
||||
FilterOp::Blur(..) => (0, 0.0),
|
||||
FilterOp::Contrast(amount) => (1, amount),
|
||||
FilterOp::Grayscale(amount) => (2, amount),
|
||||
FilterOp::HueRotate(angle) => (3, angle),
|
||||
FilterOp::Invert(amount) => (4, amount),
|
||||
FilterOp::Saturate(amount) => (5, amount),
|
||||
FilterOp::Sepia(amount) => (6, amount),
|
||||
FilterOp::Brightness(amount) => (7, amount),
|
||||
FilterOp::Opacity(_, amount) => (8, amount),
|
||||
};
|
||||
|
||||
let amount = (amount * 65535.0).round() as i32;
|
||||
let batch = batch_list.get_suitable_batch(key, &item_bounding_rect);
|
||||
|
||||
let instance = CompositePrimitiveInstance::new(
|
||||
task_address,
|
||||
src_task_address,
|
||||
RenderTaskAddress(0),
|
||||
filter_mode,
|
||||
amount,
|
||||
z,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
batch.push(PrimitiveInstance::from(instance));
|
||||
}
|
||||
}
|
||||
}
|
||||
PictureCompositeMode::MixBlend(mode) => {
|
||||
let backdrop_id = readback_render_task_id.expect("no backdrop!?");
|
||||
|
||||
let key = BatchKey::new(
|
||||
BatchKind::Composite {
|
||||
task_id,
|
||||
source_id,
|
||||
backdrop_id,
|
||||
},
|
||||
BlendMode::PremultipliedAlpha,
|
||||
BatchTextures::no_texture(),
|
||||
);
|
||||
let batch = batch_list.get_suitable_batch(key, &item_bounding_rect);
|
||||
let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
|
||||
let source_task_address = render_tasks.get_task_address(source_id);
|
||||
|
||||
let instance = CompositePrimitiveInstance::new(
|
||||
task_address,
|
||||
source_task_address,
|
||||
backdrop_task_address,
|
||||
mode as u32 as i32,
|
||||
0,
|
||||
z,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
batch.push(PrimitiveInstance::from(instance));
|
||||
}
|
||||
PictureCompositeMode::Blit => {
|
||||
let src_task_address = render_tasks.get_task_address(source_id);
|
||||
let key = BatchKey::new(
|
||||
BatchKind::HardwareComposite,
|
||||
BlendMode::PremultipliedAlpha,
|
||||
BatchTextures::no_texture(),
|
||||
);
|
||||
let batch = batch_list.get_suitable_batch(key, &item_bounding_rect);
|
||||
let instance = CompositePrimitiveInstance::new(
|
||||
task_address,
|
||||
src_task_address,
|
||||
RenderTaskAddress(0),
|
||||
item_bounding_rect.origin.x,
|
||||
item_bounding_rect.origin.y,
|
||||
z,
|
||||
item_bounding_rect.size.width,
|
||||
item_bounding_rect.size.height,
|
||||
);
|
||||
|
||||
batch.push(PrimitiveInstance::from(instance));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// If this picture is being drawn into an existing target (i.e. with
|
||||
// no composition operation), recurse and add to the current batch list.
|
||||
picture.add_to_batch(
|
||||
task_id,
|
||||
ctx,
|
||||
gpu_cache,
|
||||
render_tasks,
|
||||
deferred_resolves,
|
||||
batch_list,
|
||||
glyph_fetch_buffer,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
PrimitiveKind::AlignedGradient => {
|
||||
let gradient_cpu =
|
||||
&ctx.prim_store.cpu_gradients[prim_metadata.cpu_prim_index.0];
|
||||
|
@ -759,16 +918,69 @@ impl AlphaRenderItem {
|
|||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PicturePrimitive {
|
||||
fn add_to_batch(
|
||||
&self,
|
||||
task_id: RenderTaskId,
|
||||
ctx: &RenderTargetContext,
|
||||
gpu_cache: &mut GpuCache,
|
||||
render_tasks: &RenderTaskTree,
|
||||
deferred_resolves: &mut Vec<DeferredResolve>,
|
||||
batch_list: &mut BatchList,
|
||||
glyph_fetch_buffer: &mut Vec<GlyphFetchResult>,
|
||||
) {
|
||||
let task_address = render_tasks.get_task_address(task_id);
|
||||
|
||||
// Even though most of the time a splitter isn't used or needed,
|
||||
// they are cheap to construct so we will always pass one down.
|
||||
let mut splitter = BspSplitter::new();
|
||||
|
||||
// Add each run in this picture to the batch.
|
||||
for run in &self.runs {
|
||||
let clip_node = &ctx.clip_scroll_tree.nodes[&run.clip_and_scroll.clip_node_id()];
|
||||
let clip_id = clip_node.node_data_index;
|
||||
|
||||
let scroll_node = &ctx.clip_scroll_tree.nodes[&run.clip_and_scroll.scroll_node_id];
|
||||
let scroll_id = scroll_node.node_data_index;
|
||||
|
||||
run.add_to_batch(
|
||||
clip_id,
|
||||
scroll_id,
|
||||
batch_list,
|
||||
ctx,
|
||||
gpu_cache,
|
||||
render_tasks,
|
||||
task_id,
|
||||
task_address,
|
||||
deferred_resolves,
|
||||
glyph_fetch_buffer,
|
||||
&mut splitter,
|
||||
);
|
||||
}
|
||||
AlphaRenderItem::SplitComposite(sc_index, task_id, gpu_handle, z) => {
|
||||
|
||||
// Flush the accumulated plane splits onto the task tree.
|
||||
// Z axis is directed at the screen, `sort` is ascending, and we need back-to-front order.
|
||||
for poly in splitter.sort(vec3(0.0, 0.0, 1.0)) {
|
||||
let prim_index = PrimitiveIndex(poly.anchor);
|
||||
debug!("process sorted poly {:?} {:?}", prim_index, poly.points);
|
||||
let pp = &poly.points;
|
||||
let gpu_blocks = [
|
||||
[pp[0].x as f32, pp[0].y as f32, pp[0].z as f32, pp[1].x as f32].into(),
|
||||
[pp[1].y as f32, pp[1].z as f32, pp[2].x as f32, pp[2].y as f32].into(),
|
||||
[pp[2].z as f32, pp[3].x as f32, pp[3].y as f32, pp[3].z as f32].into(),
|
||||
];
|
||||
let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
|
||||
let key = BatchKey::new(
|
||||
BatchKind::SplitComposite,
|
||||
BlendMode::PremultipliedAlpha,
|
||||
BatchTextures::no_texture(),
|
||||
);
|
||||
let stacking_context = &ctx.stacking_context_store[sc_index.0];
|
||||
let batch = batch_list.get_suitable_batch(key, &stacking_context.screen_bounds);
|
||||
let source_task_address = render_tasks.get_task_address(task_id);
|
||||
let pic_metadata = &ctx.prim_store.cpu_metadata[prim_index.0];
|
||||
let pic = &ctx.prim_store.cpu_pictures[pic_metadata.cpu_prim_index.0];
|
||||
let batch = batch_list.get_suitable_batch(key, pic_metadata.screen_rect.as_ref().expect("bug"));
|
||||
let source_task_address = render_tasks.get_task_address(pic.render_task_id.expect("bug"));
|
||||
let gpu_address = gpu_handle.as_int(gpu_cache);
|
||||
|
||||
let instance = CompositePrimitiveInstance::new(
|
||||
|
@ -777,7 +989,7 @@ impl AlphaRenderItem {
|
|||
RenderTaskAddress(0),
|
||||
gpu_address,
|
||||
0,
|
||||
z,
|
||||
prim_index.0 as i32,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
@ -785,14 +997,13 @@ impl AlphaRenderItem {
|
|||
batch.push(PrimitiveInstance::from(instance));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AlphaBatcher {
|
||||
fn new() -> AlphaBatcher {
|
||||
fn new(screen_size: DeviceIntSize) -> AlphaBatcher {
|
||||
AlphaBatcher {
|
||||
tasks: Vec::new(),
|
||||
batch_list: BatchList::new(),
|
||||
batch_list: BatchList::new(screen_size),
|
||||
glyph_fetch_buffer: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -811,21 +1022,17 @@ impl AlphaBatcher {
|
|||
for task_id in &self.tasks {
|
||||
let task_id = *task_id;
|
||||
let task = render_tasks.get(task_id).as_alpha_batch();
|
||||
let task_address = render_tasks.get_task_address(task_id);
|
||||
|
||||
for item in &task.items {
|
||||
item.add_to_batch(
|
||||
&mut self.batch_list,
|
||||
let pic = &ctx.prim_store.cpu_pictures[ctx.prim_store.cpu_metadata[task.prim_index.0].cpu_prim_index.0];
|
||||
pic.add_to_batch(
|
||||
task_id,
|
||||
ctx,
|
||||
gpu_cache,
|
||||
render_tasks,
|
||||
task_id,
|
||||
task_address,
|
||||
deferred_resolves,
|
||||
&mut self.glyph_fetch_buffer,
|
||||
&mut self.batch_list,
|
||||
&mut self.glyph_fetch_buffer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.batch_list.finalize();
|
||||
}
|
||||
|
@ -962,10 +1169,10 @@ impl ClipBatcher {
|
|||
|
||||
pub struct RenderTargetContext<'a> {
|
||||
pub device_pixel_ratio: f32,
|
||||
pub stacking_context_store: &'a [StackingContext],
|
||||
pub prim_store: &'a PrimitiveStore,
|
||||
pub resource_cache: &'a ResourceCache,
|
||||
pub node_data: &'a [ClipScrollNodeData],
|
||||
pub clip_scroll_tree: &'a ClipScrollTree,
|
||||
}
|
||||
|
||||
struct TextureAllocator {
|
||||
|
@ -1007,7 +1214,10 @@ impl TextureAllocator {
|
|||
}
|
||||
|
||||
pub trait RenderTarget {
|
||||
fn new(size: Option<DeviceUintSize>) -> Self;
|
||||
fn new(
|
||||
size: Option<DeviceUintSize>,
|
||||
screen_size: DeviceIntSize,
|
||||
) -> Self;
|
||||
fn allocate(&mut self, size: DeviceUintSize) -> Option<DeviceUintPoint>;
|
||||
fn build(
|
||||
&mut self,
|
||||
|
@ -1035,17 +1245,24 @@ pub enum RenderTargetKind {
|
|||
}
|
||||
|
||||
pub struct RenderTargetList<T> {
|
||||
screen_size: DeviceIntSize,
|
||||
pub targets: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T: RenderTarget> RenderTargetList<T> {
|
||||
fn new(create_initial_target: bool) -> RenderTargetList<T> {
|
||||
fn new(
|
||||
create_initial_target: bool,
|
||||
screen_size: DeviceIntSize
|
||||
) -> RenderTargetList<T> {
|
||||
let mut targets = Vec::new();
|
||||
if create_initial_target {
|
||||
targets.push(T::new(None));
|
||||
targets.push(T::new(None, screen_size));
|
||||
}
|
||||
|
||||
RenderTargetList { targets }
|
||||
RenderTargetList {
|
||||
targets,
|
||||
screen_size,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn target_count(&self) -> usize {
|
||||
|
@ -1093,7 +1310,7 @@ impl<T: RenderTarget> RenderTargetList<T> {
|
|||
let origin = match existing_origin {
|
||||
Some(origin) => origin,
|
||||
None => {
|
||||
let mut new_target = T::new(Some(target_size));
|
||||
let mut new_target = T::new(Some(target_size), self.screen_size);
|
||||
let origin = new_target.allocate(alloc_size).expect(&format!(
|
||||
"Each render task must allocate <= size of one target! ({:?})",
|
||||
alloc_size
|
||||
|
@ -1146,9 +1363,12 @@ impl RenderTarget for ColorRenderTarget {
|
|||
.allocate(&size)
|
||||
}
|
||||
|
||||
fn new(size: Option<DeviceUintSize>) -> ColorRenderTarget {
|
||||
fn new(
|
||||
size: Option<DeviceUintSize>,
|
||||
screen_size: DeviceIntSize,
|
||||
) -> Self {
|
||||
ColorRenderTarget {
|
||||
alpha_batcher: AlphaBatcher::new(),
|
||||
alpha_batcher: AlphaBatcher::new(screen_size),
|
||||
text_run_cache_prims: FastHashMap::default(),
|
||||
line_cache_prims: Vec::new(),
|
||||
vertical_blurs: Vec::new(),
|
||||
|
@ -1231,7 +1451,7 @@ impl RenderTarget for ColorRenderTarget {
|
|||
|
||||
let task_index = render_tasks.get_task_address(task_id);
|
||||
|
||||
for run in &prim.prim_runs {
|
||||
for run in &prim.runs {
|
||||
for i in 0 .. run.count {
|
||||
let sub_prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
|
||||
|
||||
|
@ -1328,7 +1548,10 @@ impl RenderTarget for AlphaRenderTarget {
|
|||
self.allocator.allocate(&size)
|
||||
}
|
||||
|
||||
fn new(size: Option<DeviceUintSize>) -> AlphaRenderTarget {
|
||||
fn new(
|
||||
size: Option<DeviceUintSize>,
|
||||
_: DeviceIntSize,
|
||||
) -> Self {
|
||||
AlphaRenderTarget {
|
||||
clip_batcher: ClipBatcher::new(),
|
||||
brush_mask_corners: Vec::new(),
|
||||
|
@ -1400,7 +1623,7 @@ impl RenderTarget for AlphaRenderTarget {
|
|||
|
||||
let task_index = render_tasks.get_task_address(task_id);
|
||||
|
||||
for run in &prim.prim_runs {
|
||||
for run in &prim.runs {
|
||||
for i in 0 .. run.count {
|
||||
let sub_prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
|
||||
|
||||
|
@ -1490,11 +1713,14 @@ pub struct RenderPass {
|
|||
}
|
||||
|
||||
impl RenderPass {
|
||||
pub fn new(is_framebuffer: bool) -> RenderPass {
|
||||
pub fn new(
|
||||
is_framebuffer: bool,
|
||||
screen_size: DeviceIntSize
|
||||
) -> RenderPass {
|
||||
RenderPass {
|
||||
is_framebuffer,
|
||||
color_targets: RenderTargetList::new(is_framebuffer),
|
||||
alpha_targets: RenderTargetList::new(false),
|
||||
color_targets: RenderTargetList::new(is_framebuffer, screen_size),
|
||||
alpha_targets: RenderTargetList::new(false, screen_size),
|
||||
tasks: vec![],
|
||||
color_texture: None,
|
||||
alpha_texture: None,
|
||||
|
@ -1730,111 +1956,6 @@ impl OpaquePrimitiveBatch {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
pub struct StackingContextIndex(pub usize);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
pub enum ContextIsolation {
|
||||
/// No isolation - the content is mixed up with everything else.
|
||||
None,
|
||||
/// Items are isolated and drawn into a separate render target.
|
||||
/// Child contexts are exposed.
|
||||
Items,
|
||||
/// All the content inside is isolated and drawn into a separate target.
|
||||
Full,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StackingContext {
|
||||
pub pipeline_id: PipelineId,
|
||||
|
||||
/// Offset in the parent reference frame to the origin of this stacking
|
||||
/// context's coordinate system.
|
||||
pub reference_frame_offset: LayerVector2D,
|
||||
|
||||
/// The `ClipId` of the owning reference frame.
|
||||
pub reference_frame_id: ClipId,
|
||||
|
||||
/// Screen space bounding rectangle for this stacking context,
|
||||
/// calculated based on the size and position of all its children.
|
||||
pub screen_bounds: DeviceIntRect,
|
||||
|
||||
/// Local bounding rectangle of this stacking context,
|
||||
/// computed as the union of all contained items that are not
|
||||
/// `ContextIsolation::Items` on their own
|
||||
pub isolated_items_bounds: LayerRect,
|
||||
|
||||
pub composite_ops: CompositeOps,
|
||||
|
||||
/// Type of the isolation of the content.
|
||||
pub isolation: ContextIsolation,
|
||||
|
||||
/// Set for the root stacking context of a display list or an iframe. Used for determining
|
||||
/// when to isolate a mix-blend-mode composite.
|
||||
pub is_page_root: bool,
|
||||
|
||||
/// Set to true if this is the root stacking context for a pipeline.
|
||||
pub is_pipeline_root: bool,
|
||||
|
||||
/// Whether or not this stacking context has any visible components, calculated
|
||||
/// based on the size and position of all children and how they are clipped.
|
||||
pub is_visible: bool,
|
||||
|
||||
/// Current stacking context visibility of backface.
|
||||
pub is_backface_visible: bool,
|
||||
|
||||
/// Allow subpixel AA for text runs on this stacking context.
|
||||
/// This is a temporary hack while we don't support subpixel AA
|
||||
/// on transparent stacking contexts.
|
||||
pub allow_subpixel_aa: bool,
|
||||
|
||||
/// Indicate that if any pritimive contained in this stacking context.
|
||||
pub has_any_primitive: bool,
|
||||
|
||||
/// Union of all stacking context bounds of all children.
|
||||
pub children_sc_bounds: LayerRect,
|
||||
}
|
||||
|
||||
impl StackingContext {
|
||||
pub fn new(
|
||||
pipeline_id: PipelineId,
|
||||
reference_frame_offset: LayerVector2D,
|
||||
is_page_root: bool,
|
||||
is_pipeline_root: bool,
|
||||
reference_frame_id: ClipId,
|
||||
transform_style: TransformStyle,
|
||||
composite_ops: CompositeOps,
|
||||
is_backface_visible: bool,
|
||||
) -> StackingContext {
|
||||
let isolation = match transform_style {
|
||||
TransformStyle::Flat => ContextIsolation::None,
|
||||
TransformStyle::Preserve3D => ContextIsolation::Items,
|
||||
};
|
||||
let allow_subpixel_aa = composite_ops.count() == 0 &&
|
||||
isolation == ContextIsolation::None;
|
||||
StackingContext {
|
||||
pipeline_id,
|
||||
reference_frame_offset,
|
||||
reference_frame_id,
|
||||
screen_bounds: DeviceIntRect::zero(),
|
||||
isolated_items_bounds: LayerRect::zero(),
|
||||
composite_ops,
|
||||
isolation,
|
||||
is_page_root,
|
||||
is_pipeline_root,
|
||||
is_visible: false,
|
||||
is_backface_visible,
|
||||
allow_subpixel_aa,
|
||||
has_any_primitive: false,
|
||||
children_sc_bounds: LayerRect::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_contribute_to_scene(&self) -> bool {
|
||||
!self.composite_ops.will_make_invisible()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct CompositeOps {
|
||||
// Requires only a single texture as input (e.g. most filters)
|
||||
|
@ -1855,15 +1976,6 @@ impl CompositeOps {
|
|||
pub fn count(&self) -> usize {
|
||||
self.filters.len() + if self.mix_blend_mode.is_some() { 1 } else { 0 }
|
||||
}
|
||||
|
||||
pub fn will_make_invisible(&self) -> bool {
|
||||
for op in &self.filters {
|
||||
if op == &FilterOp::Opacity(PropertyBinding::Value(0.0)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// A rendering-oriented representation of frame::Frame built by the render backend
|
||||
|
@ -1956,3 +2068,31 @@ impl BlurTask {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a polygon from stacking context boundaries.
|
||||
/// `anchor` here is an index that's going to be preserved in all the
|
||||
/// splits of the polygon.
|
||||
fn make_polygon(
|
||||
rect: LayerRect,
|
||||
transform: &LayerToWorldTransform,
|
||||
anchor: usize,
|
||||
) -> Polygon<f64, WorldPixel> {
|
||||
let mat = TypedTransform3D::row_major(
|
||||
transform.m11 as f64,
|
||||
transform.m12 as f64,
|
||||
transform.m13 as f64,
|
||||
transform.m14 as f64,
|
||||
transform.m21 as f64,
|
||||
transform.m22 as f64,
|
||||
transform.m23 as f64,
|
||||
transform.m24 as f64,
|
||||
transform.m31 as f64,
|
||||
transform.m32 as f64,
|
||||
transform.m33 as f64,
|
||||
transform.m34 as f64,
|
||||
transform.m41 as f64,
|
||||
transform.m42 as f64,
|
||||
transform.m43 as f64,
|
||||
transform.m44 as f64);
|
||||
Polygon::from_transformed_rect(rect.cast().unwrap(), mat, anchor)
|
||||
}
|
||||
|
|
|
@ -243,19 +243,23 @@ impl fmt::Debug for DocumentMsg {
|
|||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub enum DebugCommand {
|
||||
// Display the frame profiler on screen.
|
||||
/// Display the frame profiler on screen.
|
||||
EnableProfiler(bool),
|
||||
// Display all texture cache pages on screen.
|
||||
/// Display all texture cache pages on screen.
|
||||
EnableTextureCacheDebug(bool),
|
||||
// Display intermediate render targets on screen.
|
||||
/// Display intermediate render targets on screen.
|
||||
EnableRenderTargetDebug(bool),
|
||||
// Display alpha primitive rects.
|
||||
/// Display alpha primitive rects.
|
||||
EnableAlphaRectsDebug(bool),
|
||||
// Fetch current documents and display lists.
|
||||
/// Display GPU timing results.
|
||||
EnableGpuTimeQueries(bool),
|
||||
/// Display GPU overdraw results
|
||||
EnableGpuSampleQueries(bool),
|
||||
/// Fetch current documents and display lists.
|
||||
FetchDocuments,
|
||||
// Fetch current passes and batches.
|
||||
/// Fetch current passes and batches.
|
||||
FetchPasses,
|
||||
// Fetch clip-scroll tree.
|
||||
/// Fetch clip-scroll tree.
|
||||
FetchClipScrollTree,
|
||||
}
|
||||
|
||||
|
|
|
@ -471,7 +471,7 @@ pub enum FilterOp {
|
|||
Grayscale(f32),
|
||||
HueRotate(f32),
|
||||
Invert(f32),
|
||||
Opacity(PropertyBinding<f32>),
|
||||
Opacity(PropertyBinding<f32>, f32),
|
||||
Saturate(f32),
|
||||
Sepia(f32),
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* Generated with cbindgen:0.2.0 */
|
||||
/* Generated with cbindgen:0.2.2 */
|
||||
|
||||
/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
|
||||
* To generate this file:
|
||||
|
|
Загрузка…
Ссылка в новой задаче