Bug 1417062 - Update webrender to commit d490a74c438d987122c600afca6bb2247ab38637. r=nical

MozReview-Commit-ID: 4i2RKAFTAMd

--HG--
extra : rebase_source : df9734e5475a2d0e133553dbb10b07f417d5b985
This commit is contained in:
Kartikaya Gupta 2017-11-17 08:42:23 -05:00
parent 7387e4c386
commit 6b90531555
29 changed files with 2401 additions and 2140 deletions

View File

@ -175,4 +175,4 @@ Troubleshooting tips:
-------------------------------------------------------------------------------
The version of WebRender currently in the tree is:
8a39cf24f493e894a66c2465dd310a2b2923e558
d490a74c438d987122c600afca6bb2247ab38637

View File

@ -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"

View File

@ -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);
}

View File

@ -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,
_,

View File

@ -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

View File

@ -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,
@ -51,8 +53,8 @@ impl FrameBuilder {
spread_amount,
);
let shadow_rect = prim_info.rect
.translate(box_offset)
.inflate(spread_amount, spread_amount);
.translate(box_offset)
.inflate(spread_amount, spread_amount);
if blur_radius == 0.0 {
let mut clips = Vec::new();
@ -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

View File

@ -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>;

View File

@ -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

View File

@ -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) => {

View File

@ -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();

View File

@ -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,

View File

@ -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,42 +914,45 @@ 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 renderbuffer_ids = self.gl.gen_renderbuffers(1);
let depth_rb = renderbuffer_ids[0];
texture.depth_rb = Some(RBOId(depth_rb));
depth_rb
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, true)
}
};
self.gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
self.gl.renderbuffer_storage(
gl::RENDERBUFFER,
gl::DEPTH_COMPONENT24,
texture.width as gl::GLsizei,
texture.height as gl::GLsizei,
);
if depth_alloc {
self.gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
self.gl.renderbuffer_storage(
gl::RENDERBUFFER,
gl::DEPTH_COMPONENT24,
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);

View File

@ -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))

File diff suppressed because it is too large Load Diff

View File

@ -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,
}
}
}

View File

@ -76,6 +76,7 @@ mod picture;
mod prim_store;
mod print_tree;
mod profiler;
mod query;
mod record;
mod render_backend;
mod render_task;

View File

@ -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>,
) {
// 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.
// 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;
let cache_height =
(self.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, .. } => {
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 =
(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."
@ -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,
}
}
}

View File

@ -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,18 +59,17 @@ 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 => {
font_face.get_recommended_rendering_mode_default_params(em_size, 1.0, measure_mode)
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)
}
}
};

View File

@ -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 device_rect = match metadata.screen_rect {
Some(device_rect) => device_rect,
None => {
if perform_culling {
return None
} else {
DeviceIntRect::zero()
}
}
};
let dependencies = match metadata.prim_kind {
PrimitiveKind::Picture =>
self.cpu_pictures[metadata.cpu_prim_index.0].prim_runs.clone(),
_ => Vec::new(),
};
(geometry, dependencies)
(local_rect, device_rect)
};
// 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,
);
}
}
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 i in 0 .. run.count {
let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
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 let Some(prim_geom) = self.prepare_prim_for_render(
prim_index,
prim_context,
resource_cache,
gpu_cache,
render_tasks,
clip_store,
) {
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;
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_local_rect) = self.prepare_prim_for_render(
prim_index,
&child_prim_context,
resource_cache,
gpu_cache,
render_tasks,
clip_store,
clip_scroll_tree,
pipelines,
perform_culling,
parent_tasks,
scene_properties,
profile_counters,
) {
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))
}

View File

@ -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,22 +879,24 @@ impl Profiler {
false,
);
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.
for sampler in gpu_samplers {
let value = sampler.count as f32 * screen_fraction;
match samplers.iter().position(|s| {
s.description as *const _ == sampler.tag.label as *const _
}) {
Some(pos) => samplers[pos].value += value,
None => samplers.push(FloatProfileCounter {
description: sampler.tag.label,
value,
}),
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.
for sampler in gpu_samplers {
let value = sampler.count as f32 * screen_fraction;
match samplers.iter().position(|s| {
s.description as *const _ == sampler.tag.label as *const _
}) {
Some(pos) => samplers[pos].value += value,
None => samplers.push(FloatProfileCounter {
description: sampler.tag.label,
value,
}),
}
}
self.draw_counters(&samplers, debug_renderer, false);
}
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 =

319
gfx/webrender/src/query.rs Normal file
View File

@ -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;

View File

@ -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 {

View File

@ -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))
}
}
}

View File

@ -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,30 +2199,26 @@ 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()
{
if self.max_recorded_profiles > 0 {
while self.gpu_profiles.len() >= self.max_recorded_profiles {
self.gpu_profiles.pop_front();
}
self.gpu_profiles
.push_back(GpuProfile::new(gpu_frame_id, &timers));
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();
}
profile_timers.gpu_samples = timers;
profile_samplers = samplers;
self.gpu_profiles
.push_back(GpuProfile::new(gpu_frame_id, &timers));
}
}
profile_timers.gpu_samples = timers;
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 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("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 = 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);

View File

@ -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
.get(&key.id)
.cloned()
.unwrap_or_else(|| {
warn!("Property binding {:?} has an invalid value.", key);
LayoutTransform::identity()
}),
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
.get(&key.id)
.cloned()
.unwrap_or_else(|| {
warn!("Property binding {:?} has an invalid value.", key);
default_value
}),
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

File diff suppressed because it is too large Load Diff

View File

@ -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,
}

View File

@ -471,7 +471,7 @@ pub enum FilterOp {
Grayscale(f32),
HueRotate(f32),
Invert(f32),
Opacity(PropertyBinding<f32>),
Opacity(PropertyBinding<f32>, f32),
Saturate(f32),
Sepia(f32),
}

View File

@ -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: