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
Родитель 7387e4c386
Коммит 6b90531555
29 изменённых файлов: 2401 добавлений и 2140 удалений

Просмотреть файл

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

Просмотреть файл

@ -11,7 +11,6 @@ default = ["freetype-lib"]
freetype-lib = ["freetype/servo-freetype-sys"]
profiler = ["thread_profiler/thread_profiler"]
debugger = ["ws", "serde_json", "serde", "serde_derive"]
query = []
[dependencies]
app_units = "0.5.6"

Просмотреть файл

@ -24,7 +24,9 @@ use webrender::api::*;
struct App {
property_key: PropertyBindingKey<LayoutTransform>,
opacity_key: PropertyBindingKey<f32>,
transform: LayoutTransform,
opacity: f32,
}
impl Example for App {
@ -49,6 +51,10 @@ impl Example for App {
.. LayoutPrimitiveInfo::new(bounds)
};
let filters = vec![
FilterOp::Opacity(PropertyBinding::Binding(self.opacity_key), self.opacity),
];
builder.push_stacking_context(
&info,
ScrollPolicy::Scrollable,
@ -56,7 +62,7 @@ impl Example for App {
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
filters,
);
// Fill it with a white rect
@ -68,18 +74,21 @@ impl Example for App {
fn on_event(&mut self, event: glutin::Event, api: &RenderApi, document_id: DocumentId) -> bool {
match event {
glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
let (offset_x, offset_y, angle) = match key {
glutin::VirtualKeyCode::Down => (0.0, 10.0, 0.0),
glutin::VirtualKeyCode::Up => (0.0, -10.0, 0.0),
glutin::VirtualKeyCode::Right => (10.0, 0.0, 0.0),
glutin::VirtualKeyCode::Left => (-10.0, 0.0, 0.0),
glutin::VirtualKeyCode::Comma => (0.0, 0.0, 0.1),
glutin::VirtualKeyCode::Period => (0.0, 0.0, -0.1),
let (offset_x, offset_y, angle, delta_opacity) = match key {
glutin::VirtualKeyCode::Down => (0.0, 10.0, 0.0, 0.0),
glutin::VirtualKeyCode::Up => (0.0, -10.0, 0.0, 0.0),
glutin::VirtualKeyCode::Right => (10.0, 0.0, 0.0, 0.0),
glutin::VirtualKeyCode::Left => (-10.0, 0.0, 0.0, 0.0),
glutin::VirtualKeyCode::Comma => (0.0, 0.0, 0.1, 0.0),
glutin::VirtualKeyCode::Period => (0.0, 0.0, -0.1, 0.0),
glutin::VirtualKeyCode::Z => (0.0, 0.0, 0.0, -0.1),
glutin::VirtualKeyCode::X => (0.0, 0.0, 0.0, 0.1),
_ => return false,
};
// Update the transform based on the keyboard input and push it to
// webrender using the generate_frame API. This will recomposite with
// the updated transform.
self.opacity += delta_opacity;
let new_transform = self.transform
.pre_rotate(0.0, 0.0, 1.0, Radians::new(angle))
.post_translate(LayoutVector3D::new(offset_x, offset_y, 0.0));
@ -92,7 +101,12 @@ impl Example for App {
value: new_transform,
},
],
floats: vec![],
floats: vec![
PropertyValue {
key: self.opacity_key,
value: self.opacity,
}
],
}),
);
self.transform = new_transform;
@ -107,7 +121,9 @@ impl Example for App {
fn main() {
let mut app = App {
property_key: PropertyBindingKey::new(42), // arbitrary magic number
opacity_key: PropertyBindingKey::new(43),
transform: LayoutTransform::create_translation(0.0, 0.0, 0.0),
opacity: 0.5,
};
boilerplate::main_wrapper(&mut app, None);
}

Просмотреть файл

@ -182,8 +182,7 @@ pub fn main_wrapper(example: &mut Example, options: Option<webrender::RendererOp
for event in events {
match event {
glutin::Event::Closed |
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) => break 'outer,
glutin::Event::KeyboardInput(
glutin::ElementState::Pressed,
@ -221,6 +220,13 @@ pub fn main_wrapper(example: &mut Example, options: Option<webrender::RendererOp
flags.toggle(webrender::DebugFlags::ALPHA_PRIM_DBG);
renderer.set_debug_flags(flags);
}
glutin::Event::KeyboardInput(
glutin::ElementState::Pressed,
_,
Some(glutin::VirtualKeyCode::Q),
) => {
renderer.toggle_queries_enabled();
}
glutin::Event::KeyboardInput(
glutin::ElementState::Pressed,
_,

Просмотреть файл

@ -90,39 +90,23 @@ void main(void) {
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
vec4 Blur(float radius, vec2 direction) {
// TODO(gw): Support blur in WR2!
return vec4(1.0);
}
vec4 Contrast(vec4 Cs, float amount) {
return vec4(Cs.rgb * amount - 0.5 * amount + 0.5, 1.0);
return vec4(Cs.rgb * amount - 0.5 * amount + 0.5, Cs.a);
}
vec4 Invert(vec4 Cs, float amount) {
Cs.rgb /= Cs.a;
vec3 color = mix(Cs.rgb, vec3(1.0) - Cs.rgb, amount);
// Pre-multiply the alpha into the output value.
return vec4(color.rgb * Cs.a, Cs.a);
return vec4(mix(Cs.rgb, vec3(1.0) - Cs.rgb, amount), Cs.a);
}
vec4 Brightness(vec4 Cs, float amount) {
// Un-premultiply the input.
Cs.rgb /= Cs.a;
// Apply the brightness factor.
// Resulting color needs to be clamped to output range
// since we are pre-multiplying alpha in the shader.
vec3 color = clamp(Cs.rgb * amount, vec3(0.0), vec3(1.0));
// Pre-multiply the alpha into the output value.
return vec4(color.rgb * Cs.a, Cs.a);
return vec4(clamp(Cs.rgb * amount, vec3(0.0), vec3(1.0)), Cs.a);
}
vec4 Opacity(vec4 Cs, float amount) {
return Cs * amount;
return vec4(Cs.rgb, Cs.a * amount);
}
void main(void) {
@ -133,10 +117,12 @@ void main(void) {
discard;
}
// Un-premultiply the input.
Cs.rgb /= Cs.a;
switch (vOp) {
case 0:
// Gaussian blur is specially handled:
oFragColor = Cs;// Blur(vAmount, vec2(0,0));
oFragColor = Cs;
break;
case 1:
oFragColor = Contrast(Cs, vAmount);
@ -153,5 +139,8 @@ void main(void) {
default:
oFragColor = vColorMat * Cs;
}
// Pre-multiply the alpha into the output value.
oFragColor.rgb *= oFragColor.a;
}
#endif

Просмотреть файл

@ -4,7 +4,8 @@
use api::{BorderRadiusKind, ColorF, LayerPoint, LayerRect, LayerSize, LayerVector2D};
use api::{BorderRadius, BoxShadowClipMode, LayoutSize, LayerPrimitiveInfo};
use api::{ClipMode, ComplexClipRegion, EdgeAaSegmentMask, LocalClip, ClipAndScrollInfo};
use api::{ClipMode, ClipAndScrollInfo, ComplexClipRegion, EdgeAaSegmentMask, LocalClip};
use api::{PipelineId};
use clip::ClipSource;
use frame_builder::FrameBuilder;
use prim_store::{PrimitiveContainer, RectangleContent, RectanglePrimitive};
@ -24,6 +25,7 @@ pub const MASK_CORNER_PADDING: f32 = 4.0;
impl FrameBuilder {
pub fn add_box_shadow(
&mut self,
pipeline_id: PipelineId,
clip_and_scroll: ClipAndScrollInfo,
prim_info: &LayerPrimitiveInfo,
box_offset: &LayerVector2D,
@ -185,13 +187,12 @@ impl FrameBuilder {
Vec::new(),
clip_mode,
radii_kind,
pipeline_id,
);
pic_prim.add_primitive(
brush_prim_index,
&brush_rect,
clip_and_scroll
);
pic_prim.build();
// TODO(gw): Right now, we always use a clip out
// mask for outset shadows. We can make this
@ -264,13 +265,12 @@ impl FrameBuilder {
BoxShadowClipMode::Inset,
// TODO(gw): Make use of optimization for inset.
BorderRadiusKind::NonUniform,
pipeline_id,
);
pic_prim.add_primitive(
brush_prim_index,
&brush_rect,
clip_and_scroll
);
pic_prim.build();
// Draw the picture one pixel outside the original
// rect to account for the inflate above. This

Просмотреть файл

@ -13,7 +13,7 @@ use prim_store::{ClipData, ImageMaskData};
use resource_cache::ResourceCache;
use util::{extract_inner_rect_safe, TransformedRect};
const MAX_CLIP: f32 = 1000000.0;
pub const MAX_CLIP: f32 = 1000000.0;
pub type ClipStore = FreeList<ClipSources>;
pub type ClipSourcesHandle = FreeListHandle<ClipSources>;

Просмотреть файл

@ -5,7 +5,7 @@
use api::{ClipId, DeviceIntRect, LayerPixel, LayerPoint, LayerRect, LayerSize};
use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, LayoutVector2D, PipelineId};
use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity};
use api::{StickyOffsetBounds, WorldPoint};
use api::{LayoutTransform, PropertyBinding, StickyOffsetBounds, WorldPoint};
use clip::{ClipSourcesHandle, ClipStore};
use clip_scroll_tree::{CoordinateSystemId, TransformUpdateState};
use euclid::SideOffsets2D;
@ -14,6 +14,7 @@ use gpu_cache::GpuCache;
use gpu_types::{ClipScrollNodeIndex, ClipScrollNodeData};
use render_task::{ClipChain, ClipChainNode, ClipWorkItem};
use resource_cache::ResourceCache;
use scene::SceneProperties;
use spring::{DAMPING, STIFFNESS, Spring};
use std::rc::Rc;
use util::{MatrixHelpers, MaxRect};
@ -185,12 +186,16 @@ impl ClipScrollNode {
pub fn new_reference_frame(
parent_id: Option<ClipId>,
frame_rect: &LayerRect,
transform: &LayerToScrollTransform,
source_transform: Option<PropertyBinding<LayoutTransform>>,
source_perspective: Option<LayoutTransform>,
origin_in_parent_reference_frame: LayerVector2D,
pipeline_id: PipelineId,
) -> Self {
let identity = LayoutTransform::identity();
let info = ReferenceFrameInfo {
transform: *transform,
resolved_transform: LayerToScrollTransform::identity(),
source_transform: source_transform.unwrap_or(PropertyBinding::Value(identity)),
source_perspective: source_perspective.unwrap_or(identity),
origin_in_parent_reference_frame,
};
Self::new(pipeline_id, parent_id, frame_rect, NodeType::ReferenceFrame(info))
@ -273,12 +278,13 @@ impl ClipScrollNode {
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
scene_properties: &SceneProperties,
) {
// We set this earlier so that we can use it before we have all the data necessary
// to populate the ClipScrollNodeData.
self.node_data_index = ClipScrollNodeIndex(node_data.len() as u32);
self.update_transform(state);
self.update_transform(state, scene_properties);
self.update_clip_work_item(
state,
device_pixel_ratio,
@ -309,6 +315,7 @@ impl ClipScrollNode {
}
None => {
state.combined_outer_clip_bounds = DeviceIntRect::zero();
self.combined_clip_outer_bounds = DeviceIntRect::zero();
ClipScrollNodeData::invalid()
}
};
@ -365,7 +372,11 @@ impl ClipScrollNode {
state.parent_clip_chain = self.clip_chain_node.clone();
}
pub fn update_transform(&mut self, state: &mut TransformUpdateState) {
pub fn update_transform(
&mut self,
state: &mut TransformUpdateState,
scene_properties: &SceneProperties,
) {
// We calculate this here to avoid a double-borrow later.
let sticky_offset = self.calculate_sticky_offset(
&state.nearest_scrolling_ancestor_offset,
@ -373,12 +384,21 @@ impl ClipScrollNode {
);
let (local_transform, accumulated_scroll_offset) = match self.node_type {
NodeType::ReferenceFrame(ref info) => {
self.combined_local_viewport_rect = info.transform
NodeType::ReferenceFrame(ref mut info) => {
// Resolve the transform against any property bindings.
let source_transform = scene_properties.resolve_layout_transform(&info.source_transform);
info.resolved_transform = LayerToScrollTransform::create_translation(
info.origin_in_parent_reference_frame.x,
info.origin_in_parent_reference_frame.y,
0.0
).pre_mul(&source_transform)
.pre_mul(&info.source_perspective);
self.combined_local_viewport_rect = info.resolved_transform
.with_destination::<LayerPixel>()
.inverse_rect_footprint(&state.parent_combined_viewport_rect);
self.reference_frame_relative_scroll_offset = LayerVector2D::zero();
(info.transform, state.parent_accumulated_scroll_offset)
(info.resolved_transform, state.parent_accumulated_scroll_offset)
}
NodeType::Clip(_) | NodeType::ScrollFrame(_) => {
// Move the parent's viewport into the local space (of the node origin)
@ -436,7 +456,7 @@ impl ClipScrollNode {
state.nearest_scrolling_ancestor_viewport
.translate(&info.origin_in_parent_reference_frame);
if !info.transform.preserves_2d_axis_alignment() {
if !info.resolved_transform.preserves_2d_axis_alignment() {
state.current_coordinate_system_id = state.next_coordinate_system_id;
state.next_coordinate_system_id = state.next_coordinate_system_id.next();
}
@ -787,7 +807,14 @@ impl ScrollingState {
pub struct ReferenceFrameInfo {
/// The transformation that establishes this reference frame, relative to the parent
/// reference frame. The origin of the reference frame is included in the transformation.
pub transform: LayerToScrollTransform,
pub resolved_transform: LayerToScrollTransform,
/// The source transform and perspective matrices provided by the stacking context
/// that forms this reference frame. We maintain the property binding information
/// here so that we can resolve the animated transform and update the tree each
/// frame.
pub source_transform: PropertyBinding<LayoutTransform>,
pub source_perspective: LayoutTransform,
/// The original, not including the transform and relative to the parent reference frame,
/// origin of this reference frame. This is already rolled into the `transform' property, but

Просмотреть файл

@ -2,9 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{ClipId, DeviceIntRect, LayerPoint, LayerRect, LayerToScrollTransform};
use api::{ClipId, DeviceIntRect, LayerPoint, LayerRect};
use api::{LayerToWorldTransform, LayerVector2D, PipelineId, ScrollClamping, ScrollEventPhase};
use api::{ScrollLayerState, ScrollLocation, WorldPoint};
use api::{PropertyBinding, LayoutTransform, ScrollLayerState, ScrollLocation, WorldPoint};
use clip::ClipStore;
use clip_scroll_node::{ClipScrollNode, NodeType, ScrollingState, StickyFrameInfo};
use gpu_cache::GpuCache;
@ -13,6 +13,7 @@ use internal_types::{FastHashMap, FastHashSet};
use print_tree::{PrintTree, PrintTreePrinter};
use render_task::ClipChain;
use resource_cache::ResourceCache;
use scene::SceneProperties;
pub type ScrollStates = FastHashMap<ClipId, ScrollingState>;
@ -193,9 +194,8 @@ impl ClipScrollTree {
return false;
}
let point_in_clips = transformed_point - node.local_clip_rect.origin.to_vector();
for &(ref clip, _) in clip_store.get(&clip_sources_handle).clips() {
if !clip.contains(&point_in_clips) {
if !clip.contains(&transformed_point) {
cache.insert(*node_id, None);
return false;
}
@ -334,6 +334,7 @@ impl ClipScrollTree {
gpu_cache: &mut GpuCache,
pan: LayerPoint,
node_data: &mut Vec<ClipScrollNodeData>,
scene_properties: &SceneProperties,
) {
if self.nodes.is_empty() {
return;
@ -365,6 +366,7 @@ impl ClipScrollTree {
resource_cache,
gpu_cache,
node_data,
scene_properties,
);
}
@ -377,6 +379,7 @@ impl ClipScrollTree {
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
node_data: &mut Vec<ClipScrollNodeData>,
scene_properties: &SceneProperties,
) {
// TODO(gw): This is an ugly borrow check workaround to clone these.
// Restructure this to avoid the clones!
@ -394,6 +397,7 @@ impl ClipScrollTree {
clip_store,
resource_cache,
gpu_cache,
scene_properties,
);
node.children.clone()
@ -408,6 +412,7 @@ impl ClipScrollTree {
resource_cache,
gpu_cache,
node_data,
scene_properties,
);
}
}
@ -441,7 +446,8 @@ impl ClipScrollTree {
pub fn add_reference_frame(
&mut self,
rect: &LayerRect,
transform: &LayerToScrollTransform,
source_transform: Option<PropertyBinding<LayoutTransform>>,
source_perspective: Option<LayoutTransform>,
origin_in_parent_reference_frame: LayerVector2D,
pipeline_id: PipelineId,
parent_id: Option<ClipId>,
@ -456,7 +462,8 @@ impl ClipScrollTree {
let node = ClipScrollNode::new_reference_frame(
parent_id,
rect,
transform,
source_transform,
source_perspective,
origin_in_parent_reference_frame,
pipeline_id,
);
@ -516,7 +523,7 @@ impl ClipScrollTree {
pt.end_level();
}
NodeType::ReferenceFrame(ref info) => {
pt.new_level(format!("ReferenceFrame {:?}", info.transform));
pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform));
pt.add_item(format!("id: {:?}", id));
}
NodeType::ScrollFrame(scrolling_info) => {

Просмотреть файл

@ -4,7 +4,7 @@
use api::{ColorU, DeviceIntRect, DeviceUintSize, ImageFormat};
use debug_font_data;
use device::{Device, GpuMarker, Program, Texture, TextureSlot, VertexDescriptor, VAO};
use device::{Device, Program, Texture, TextureSlot, VertexDescriptor, VAO};
use device::{TextureFilter, TextureTarget, VertexAttribute, VertexAttributeKind, VertexUsageHint};
use euclid::{Point2D, Rect, Size2D, Transform3D};
use internal_types::{ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE};
@ -105,7 +105,7 @@ pub struct DebugRenderer {
}
impl DebugRenderer {
pub fn new(device: &mut Device) -> DebugRenderer {
pub fn new(device: &mut Device) -> Self {
let font_program = device.create_program("debug_font", "", &DESC_FONT).unwrap();
device.bind_shader_samplers(&font_program, &[("sColor0", DebugSampler::Font)]);
@ -263,7 +263,6 @@ impl DebugRenderer {
}
pub fn render(&mut self, device: &mut Device, viewport_size: &DeviceUintSize) {
let _gm = GpuMarker::new(device.rc_gl(), "debug");
device.disable_depth();
device.set_blend(true);
device.set_blend_mode_premultiplied_alpha();

Просмотреть файл

@ -53,6 +53,10 @@ impl ws::Handler for Server {
"disable_render_target_debug" => DebugCommand::EnableRenderTargetDebug(false),
"enable_alpha_rects_debug" => DebugCommand::EnableAlphaRectsDebug(true),
"disable_alpha_rects_debug" => DebugCommand::EnableAlphaRectsDebug(false),
"enable_gpu_time_queries" => DebugCommand::EnableGpuTimeQueries(true),
"disable_gpu_time_queries" => DebugCommand::EnableGpuTimeQueries(false),
"enable_gpu_sample_queries" => DebugCommand::EnableGpuSampleQueries(true),
"disable_gpu_sample_queries" => DebugCommand::EnableGpuSampleQueries(false),
"fetch_passes" => DebugCommand::FetchPasses,
"fetch_documents" => DebugCommand::FetchDocuments,
"fetch_clipscrolltree" => DebugCommand::FetchClipScrollTree,

Просмотреть файл

@ -485,308 +485,6 @@ pub struct VBOId(gl::GLuint);
#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
struct IBOId(gl::GLuint);
#[cfg(feature = "query")]
const MAX_PROFILE_FRAMES: usize = 4;
pub trait NamedTag {
fn get_label(&self) -> &str;
}
#[derive(Debug, Clone)]
pub struct GpuTimer<T> {
pub tag: T,
pub time_ns: u64,
}
#[derive(Debug, Clone)]
pub struct GpuSampler<T> {
pub tag: T,
pub count: u64,
}
#[cfg(feature = "query")]
pub struct QuerySet<T> {
set: Vec<gl::GLuint>,
data: Vec<T>,
pending: gl::GLuint,
}
#[cfg(feature = "query")]
impl<T> QuerySet<T> {
fn new(set: Vec<gl::GLuint>) -> Self {
QuerySet {
set,
data: Vec::new(),
pending: 0,
}
}
fn reset(&mut self) {
self.data.clear();
self.pending = 0;
}
fn add(&mut self, value: T) -> Option<gl::GLuint> {
assert_eq!(self.pending, 0);
self.set.get(self.data.len()).cloned().map(|query_id| {
self.data.push(value);
self.pending = query_id;
query_id
})
}
fn take<F: Fn(&mut T, gl::GLuint)>(&mut self, fun: F) -> Vec<T> {
let mut data = mem::replace(&mut self.data, Vec::new());
for (value, &query) in data.iter_mut().zip(self.set.iter()) {
fun(value, query)
}
data
}
}
#[cfg(feature = "query")]
pub struct GpuFrameProfile<T> {
gl: Rc<gl::Gl>,
timers: QuerySet<GpuTimer<T>>,
samplers: QuerySet<GpuSampler<T>>,
frame_id: FrameId,
inside_frame: bool,
}
#[cfg(feature = "query")]
impl<T> GpuFrameProfile<T> {
const MAX_TIMERS_PER_FRAME: usize = 256;
// disable samplers on OSX due to driver bugs
#[cfg(target_os = "macos")]
const MAX_SAMPLERS_PER_FRAME: usize = 0;
#[cfg(not(target_os = "macos"))]
const MAX_SAMPLERS_PER_FRAME: usize = 16;
fn new(gl: Rc<gl::Gl>) -> Self {
assert_eq!(gl.get_type(), gl::GlType::Gl);
let time_queries = gl.gen_queries(Self::MAX_TIMERS_PER_FRAME as _);
let sample_queries = gl.gen_queries(Self::MAX_SAMPLERS_PER_FRAME as _);
GpuFrameProfile {
gl,
timers: QuerySet::new(time_queries),
samplers: QuerySet::new(sample_queries),
frame_id: FrameId(0),
inside_frame: false,
}
}
fn begin_frame(&mut self, frame_id: FrameId) {
self.frame_id = frame_id;
self.timers.reset();
self.samplers.reset();
self.inside_frame = true;
}
fn end_frame(&mut self) {
self.done_marker();
self.done_sampler();
self.inside_frame = false;
}
fn done_marker(&mut self) {
debug_assert!(self.inside_frame);
if self.timers.pending != 0 {
self.gl.end_query(gl::TIME_ELAPSED);
self.timers.pending = 0;
}
}
fn add_marker(&mut self, tag: T) -> GpuMarker
where
T: NamedTag,
{
self.done_marker();
let marker = GpuMarker::new(&self.gl, tag.get_label());
if let Some(query) = self.timers.add(GpuTimer { tag, time_ns: 0 }) {
self.gl.begin_query(gl::TIME_ELAPSED, query);
}
marker
}
fn done_sampler(&mut self) {
debug_assert!(self.inside_frame);
if self.samplers.pending != 0 {
self.gl.end_query(gl::SAMPLES_PASSED);
self.samplers.pending = 0;
}
}
fn add_sampler(&mut self, tag: T)
where
T: NamedTag,
{
self.done_sampler();
if let Some(query) = self.samplers.add(GpuSampler { tag, count: 0 }) {
self.gl.begin_query(gl::SAMPLES_PASSED, query);
}
}
fn is_valid(&self) -> bool {
!self.timers.set.is_empty() || !self.samplers.set.is_empty()
}
fn build_samples(&mut self) -> (Vec<GpuTimer<T>>, Vec<GpuSampler<T>>) {
debug_assert!(!self.inside_frame);
let gl = &self.gl;
(
self.timers.take(|timer, query| {
timer.time_ns = gl.get_query_object_ui64v(query, gl::QUERY_RESULT)
}),
self.samplers.take(|sampler, query| {
sampler.count = gl.get_query_object_ui64v(query, gl::QUERY_RESULT)
}),
)
}
}
#[cfg(feature = "query")]
impl<T> Drop for GpuFrameProfile<T> {
fn drop(&mut self) {
if !self.timers.set.is_empty() {
self.gl.delete_queries(&self.timers.set);
}
if !self.samplers.set.is_empty() {
self.gl.delete_queries(&self.samplers.set);
}
}
}
#[cfg(feature = "query")]
pub struct GpuProfiler<T> {
frames: [GpuFrameProfile<T>; MAX_PROFILE_FRAMES],
next_frame: usize,
}
#[cfg(feature = "query")]
impl<T> GpuProfiler<T> {
pub fn new(gl: &Rc<gl::Gl>) -> Self {
GpuProfiler {
next_frame: 0,
frames: [
GpuFrameProfile::new(Rc::clone(gl)),
GpuFrameProfile::new(Rc::clone(gl)),
GpuFrameProfile::new(Rc::clone(gl)),
GpuFrameProfile::new(Rc::clone(gl)),
],
}
}
pub fn build_samples(&mut self) -> Option<(FrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>)> {
let frame = &mut self.frames[self.next_frame];
if frame.is_valid() {
let (timers, samplers) = frame.build_samples();
Some((frame.frame_id, timers, samplers))
} else {
None
}
}
pub fn begin_frame(&mut self, frame_id: FrameId) {
let frame = &mut self.frames[self.next_frame];
frame.begin_frame(frame_id);
}
pub fn end_frame(&mut self) {
let frame = &mut self.frames[self.next_frame];
frame.end_frame();
self.next_frame = (self.next_frame + 1) % MAX_PROFILE_FRAMES;
}
pub fn add_marker(&mut self, tag: T) -> GpuMarker
where
T: NamedTag,
{
self.frames[self.next_frame].add_marker(tag)
}
pub fn add_sampler(&mut self, tag: T)
where
T: NamedTag,
{
self.frames[self.next_frame].add_sampler(tag)
}
pub fn done_sampler(&mut self) {
self.frames[self.next_frame].done_sampler()
}
}
#[cfg(not(feature = "query"))]
pub struct GpuProfiler<T>(Option<T>);
#[cfg(not(feature = "query"))]
impl<T> GpuProfiler<T> {
pub fn new(_: &Rc<gl::Gl>) -> Self {
GpuProfiler(None)
}
pub fn build_samples(&mut self) -> Option<(FrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>)> {
None
}
pub fn begin_frame(&mut self, _: FrameId) {}
pub fn end_frame(&mut self) {}
pub fn add_marker(&mut self, _: T) -> GpuMarker {
GpuMarker {}
}
pub fn add_sampler(&mut self, _: T) {}
pub fn done_sampler(&mut self) {}
}
#[must_use]
pub struct GpuMarker {
#[cfg(feature = "query")]
gl: Rc<gl::Gl>,
}
#[cfg(feature = "query")]
impl GpuMarker {
pub fn new(gl: &Rc<gl::Gl>, message: &str) -> Self {
debug_assert_eq!(gl.get_type(), gl::GlType::Gl);
gl.push_group_marker_ext(message);
GpuMarker { gl: Rc::clone(gl) }
}
pub fn fire(gl: &gl::Gl, message: &str) {
debug_assert_eq!(gl.get_type(), gl::GlType::Gl);
gl.insert_event_marker_ext(message);
}
}
#[cfg(feature = "query")]
impl Drop for GpuMarker {
fn drop(&mut self) {
self.gl.pop_group_marker_ext();
}
}
#[cfg(not(feature = "query"))]
impl GpuMarker {
#[inline]
pub fn new(_: &Rc<gl::Gl>, _: &str) -> Self {
GpuMarker{}
}
#[inline]
pub fn fire(_: &gl::Gl, _: &str) {}
}
#[derive(Debug, Copy, Clone)]
pub enum VertexUsageHint {
Static,
@ -1138,15 +836,14 @@ impl Device {
let (internal_format, gl_format) = gl_texture_formats_for_image_format(self.gl(), format);
let type_ = gl_type_for_texture_format(format);
self.bind_texture(DEFAULT_TEXTURE, texture);
self.set_texture_parameters(texture.target, filter);
match mode {
RenderTargetMode::RenderTarget => {
self.bind_texture(DEFAULT_TEXTURE, texture);
self.set_texture_parameters(texture.target, filter);
self.update_texture_storage(texture, layer_count, resized);
self.update_texture_storage(texture, resized);
}
RenderTargetMode::None => {
self.bind_texture(DEFAULT_TEXTURE, texture);
self.set_texture_parameters(texture.target, filter);
let expanded_data: Vec<u8>;
let actual_pixels = if pixels.is_some() && format == ImageFormat::A8 &&
cfg!(any(target_arch = "arm", target_arch = "aarch64"))
@ -1197,13 +894,13 @@ impl Device {
/// Updates the texture storage for the texture, creating
/// FBOs as required.
fn update_texture_storage(&mut self, texture: &mut Texture, layer_count: i32, resized: bool) {
assert!(layer_count > 0);
fn update_texture_storage(&mut self, texture: &mut Texture, resized: bool) {
assert!(texture.layer_count > 0);
assert_eq!(texture.target, gl::TEXTURE_2D_ARRAY);
let current_layer_count = texture.fbo_ids.len() as i32;
let needed_layer_count = texture.layer_count - texture.fbo_ids.len() as i32;
// If the texture is already the required size skip.
if current_layer_count == layer_count && !resized {
if needed_layer_count == 0 && !resized {
return;
}
@ -1217,35 +914,37 @@ impl Device {
internal_format as gl::GLint,
texture.width as gl::GLint,
texture.height as gl::GLint,
layer_count,
texture.layer_count,
0,
gl_format,
type_,
None,
);
let needed_layer_count = layer_count - current_layer_count;
if needed_layer_count > 0 {
// Create more framebuffers to fill the gap
let new_fbos = self.gl.gen_framebuffers(needed_layer_count);
texture
.fbo_ids
.extend(new_fbos.into_iter().map(|id| FBOId(id)));
.extend(new_fbos.into_iter().map(FBOId));
} else if needed_layer_count < 0 {
// Remove extra framebuffers
for old in texture.fbo_ids.drain(layer_count as usize ..) {
for old in texture.fbo_ids.drain(texture.layer_count as usize ..) {
self.gl.delete_framebuffers(&[old.0]);
}
}
let depth_rb = if let Some(rbo) = texture.depth_rb {
rbo.0
} else {
let (depth_rb, depth_alloc) = match texture.depth_rb {
Some(rbo) => (rbo.0, resized),
None => {
let renderbuffer_ids = self.gl.gen_renderbuffers(1);
let depth_rb = renderbuffer_ids[0];
texture.depth_rb = Some(RBOId(depth_rb));
depth_rb
(depth_rb, true)
}
};
if depth_alloc {
self.gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
self.gl.renderbuffer_storage(
gl::RENDERBUFFER,
@ -1253,6 +952,7 @@ impl Device {
texture.width as gl::GLsizei,
texture.height as gl::GLsizei,
);
}
for (fbo_index, fbo_id) in texture.fbo_ids.iter().enumerate() {
self.gl.bind_framebuffer(gl::FRAMEBUFFER, fbo_id.0);

Просмотреть файл

@ -6,8 +6,8 @@
use api::{BuiltDisplayListIter, ClipAndScrollInfo, ClipId, ColorF, ComplexClipRegion};
use api::{DeviceUintRect, DeviceUintSize, DisplayItemRef, Epoch, FilterOp};
use api::{ImageDisplayItem, ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect};
use api::{LayerSize, LayerToScrollTransform, LayerVector2D};
use api::{LayoutRect, LayoutSize, LayoutTransform};
use api::{LayerSize, LayerVector2D};
use api::{LayoutRect, LayoutSize};
use api::{LocalClip, PipelineId, ScrollClamping, ScrollEventPhase, ScrollLayerState};
use api::{ScrollLocation, ScrollPolicy, ScrollSensitivity, SpecificDisplayItem, StackingContext};
use api::{ClipMode, TileOffset, TransformStyle, WorldPoint};
@ -21,7 +21,7 @@ use internal_types::{FastHashMap, FastHashSet, RendererFrame};
use prim_store::RectangleContent;
use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
use resource_cache::{FontInstanceMap,ResourceCache, TiledImageMap};
use scene::{Scene, StackingContextHelpers, ScenePipeline};
use scene::{Scene, StackingContextHelpers, ScenePipeline, SceneProperties};
use tiling::{CompositeOps, Frame};
use util::ComplexClipRegionHelpers;
@ -48,6 +48,7 @@ struct FlattenContext<'a> {
opaque_parts: Vec<LayoutRect>,
/// Same for the transparent rectangles.
transparent_parts: Vec<LayoutRect>,
output_pipelines: &'a FastHashSet<PipelineId>,
}
impl<'a> FlattenContext<'a> {
@ -88,21 +89,18 @@ impl<'a> FlattenContext<'a> {
root_reference_frame_id: ClipId,
root_scroll_frame_id: ClipId,
) {
let clip_id = ClipId::root_scroll_node(pipeline_id);
self.builder.push_stacking_context(
&LayerVector2D::zero(),
pipeline_id,
CompositeOps::default(),
TransformStyle::Flat,
true,
true,
ClipAndScrollInfo::simple(clip_id),
self.output_pipelines,
);
// We do this here, rather than above because we want any of the top-level
// stacking contexts in the display list to be treated like root stacking contexts.
// FIXME(mrobinson): Currently only the first one will, which for the moment is
// sufficient for all our use cases.
self.builder.notify_waiting_for_root_stacking_context();
// For the root pipeline, there's no need to add a full screen rectangle
// here, as it's handled by the framebuffer clear.
if self.scene.root_pipeline_id != Some(pipeline_id) {
@ -121,7 +119,11 @@ impl<'a> FlattenContext<'a> {
}
self.flatten_items(traversal, pipeline_id, LayerVector2D::zero());
self.flatten_items(
traversal,
pipeline_id,
LayerVector2D::zero(),
);
if self.builder.config.enable_scrollbars {
let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0));
@ -154,7 +156,11 @@ impl<'a> FlattenContext<'a> {
return;
}
self.flatten_item(item, pipeline_id, reference_frame_relative_offset)
self.flatten_item(
item,
pipeline_id,
reference_frame_relative_offset,
)
};
// If flatten_item created a sub-traversal, we need `traversal` to have the
@ -240,7 +246,6 @@ impl<'a> FlattenContext<'a> {
stacking_context.filter_ops_for_compositing(
display_list,
filters,
&self.scene.properties,
),
stacking_context.mix_blend_mode_for_compositing(),
)
@ -258,23 +263,15 @@ impl<'a> FlattenContext<'a> {
let is_reference_frame =
stacking_context.transform.is_some() || stacking_context.perspective.is_some();
if is_reference_frame {
let transform = stacking_context.transform.as_ref();
let transform = self.scene.properties.resolve_layout_transform(transform);
let perspective = stacking_context
.perspective
.unwrap_or_else(LayoutTransform::identity);
let origin = reference_frame_relative_offset + bounds.origin.to_vector();
let transform = LayerToScrollTransform::create_translation(origin.x, origin.y, 0.0)
.pre_mul(&transform)
.pre_mul(&perspective);
let reference_frame_bounds = LayerRect::new(LayerPoint::zero(), bounds.size);
let mut clip_id = self.apply_scroll_frame_id_replacement(context_scroll_node_id);
clip_id = self.builder.push_reference_frame(
Some(clip_id),
pipeline_id,
&reference_frame_bounds,
&transform,
stacking_context.transform,
stacking_context.perspective,
origin,
false,
self.clip_scroll_tree,
@ -286,15 +283,18 @@ impl<'a> FlattenContext<'a> {
reference_frame_relative_offset.x + bounds.origin.x,
reference_frame_relative_offset.y + bounds.origin.y,
);
}
};
let sc_scroll_node_id = self.apply_scroll_frame_id_replacement(context_scroll_node_id);
self.builder.push_stacking_context(
&reference_frame_relative_offset,
pipeline_id,
composition_operations,
stacking_context.transform_style,
is_backface_visible,
false,
ClipAndScrollInfo::simple(sc_scroll_node_id),
self.output_pipelines,
);
self.flatten_items(
@ -347,12 +347,12 @@ impl<'a> FlattenContext<'a> {
let iframe_rect = LayerRect::new(LayerPoint::zero(), bounds.size);
let origin = reference_frame_relative_offset + bounds.origin.to_vector();
let transform = LayerToScrollTransform::create_translation(origin.x, origin.y, 0.0);
let iframe_reference_frame_id = self.builder.push_reference_frame(
Some(clip_id),
pipeline_id,
&iframe_rect,
&transform,
None,
None,
origin,
true,
self.clip_scroll_tree,
@ -518,6 +518,7 @@ impl<'a> FlattenContext<'a> {
let mut prim_info = prim_info.clone();
prim_info.rect = bounds;
self.builder.add_box_shadow(
pipeline_id,
clip_and_scroll,
&prim_info,
&box_shadow_info.offset,
@ -1088,6 +1089,7 @@ impl FrameContext {
window_size: DeviceUintSize,
inner_rect: DeviceUintRect,
device_pixel_ratio: f32,
output_pipelines: &FastHashSet<PipelineId>,
) -> Option<FrameBuilder> {
let root_pipeline_id = match scene.root_pipeline_id {
Some(root_pipeline_id) => root_pipeline_id,
@ -1128,6 +1130,7 @@ impl FrameContext {
replacements: Vec::new(),
opaque_parts: Vec::new(),
transparent_parts: Vec::new(),
output_pipelines,
};
roller.builder.push_root(
@ -1154,6 +1157,8 @@ impl FrameContext {
scroll_frame_id,
);
debug_assert!(roller.builder.picture_stack.is_empty());
self.pipeline_epoch_map.extend(roller.pipeline_epochs.drain(..));
roller.builder
};
@ -1180,9 +1185,9 @@ impl FrameContext {
pipelines: &FastHashMap<PipelineId, ScenePipeline>,
device_pixel_ratio: f32,
pan: LayerPoint,
output_pipelines: &FastHashSet<PipelineId>,
texture_cache_profile: &mut TextureCacheProfileCounters,
gpu_cache_profile: &mut GpuCacheProfileCounters,
scene_properties: &SceneProperties,
) -> RendererFrame {
let frame = frame_builder.build(
resource_cache,
@ -1192,9 +1197,9 @@ impl FrameContext {
pipelines,
device_pixel_ratio,
pan,
output_pipelines,
texture_cache_profile,
gpu_cache_profile,
scene_properties,
);
self.get_renderer_frame_impl(Some(frame))

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -9,7 +9,6 @@ use api::DebugCommand;
use device::TextureFilter;
use fxhash::FxHasher;
use profiler::BackendProfileCounters;
use renderer::BlendMode;
use std::{usize, i32};
use std::collections::{HashMap, HashSet};
use std::f32;
@ -187,24 +186,8 @@ pub enum ResultMsg {
},
}
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
pub struct StackingContextIndex(pub usize);
#[derive(Clone, Copy, Debug)]
pub struct UvRect {
pub uv0: DevicePoint,
pub uv1: DevicePoint,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum HardwareCompositeOp {
PremultipliedAlpha,
}
impl HardwareCompositeOp {
pub fn to_blend_mode(&self) -> BlendMode {
match *self {
HardwareCompositeOp::PremultipliedAlpha => BlendMode::PremultipliedAlpha,
}
}
}

Просмотреть файл

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

Просмотреть файл

@ -2,14 +2,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{BorderRadiusKind, ColorF, ClipAndScrollInfo};
use api::{device_length, DeviceIntSize};
use api::{BorderRadiusKind, ColorF, ClipAndScrollInfo, FilterOp, MixBlendMode};
use api::{device_length, DeviceIntRect, DeviceIntSize, PipelineId};
use api::{BoxShadowClipMode, LayerPoint, LayerRect, LayerSize, LayerVector2D, Shadow};
use api::{ClipId, PremultipliedColorF};
use box_shadow::BLUR_SAMPLE_SCALE;
use frame_builder::PrimitiveContext;
use gpu_cache::GpuDataRequest;
use prim_store::{PrimitiveIndex, PrimitiveRun};
use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect};
use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskTree};
use scene::{FilterOpHelpers, SceneProperties};
use tiling::RenderTargetKind;
/*
@ -22,12 +24,26 @@ use tiling::RenderTargetKind;
this picture (e.g. in screen space or local space).
*/
/// Specifies how this Picture should be composited
/// onto the target it belongs to.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum PictureCompositeMode {
/// Apply CSS mix-blend-mode effect.
MixBlend(MixBlendMode),
/// Apply a CSS filter.
Filter(FilterOp),
/// Draw to intermediate surface, copy straight across. This
/// is used for CSS isolation, and plane splitting.
Blit,
}
#[derive(Debug)]
pub enum PictureKind {
TextShadow {
offset: LayerVector2D,
color: ColorF,
blur_radius: f32,
content_rect: LayerRect,
},
BoxShadow {
blur_radius: f32,
@ -35,15 +51,49 @@ pub enum PictureKind {
blur_regions: Vec<LayerRect>,
clip_mode: BoxShadowClipMode,
radii_kind: BorderRadiusKind,
content_rect: LayerRect,
},
Image {
// If a mix-blend-mode, contains the render task for
// the readback of the framebuffer that we use to sample
// from in the mix-blend-mode shader.
readback_render_task_id: Option<RenderTaskId>,
/// How this picture should be composited.
/// If None, don't composite - just draw directly on parent surface.
composite_mode: Option<PictureCompositeMode>,
// If true, this picture is part of a 3D context.
is_in_3d_context: bool,
// If requested as a frame output (for rendering
// pages to a texture), this is the pipeline this
// picture is the root of.
frame_output_pipeline_id: Option<PipelineId>,
// The original reference frame ID for this picture.
// It is only different if this is part of a 3D
// rendering context.
reference_frame_id: ClipId,
real_local_rect: LayerRect,
},
}
#[derive(Debug)]
pub struct PicturePrimitive {
pub prim_runs: Vec<PrimitiveRun>,
// If this picture is drawn to an intermediate surface,
// the associated render task.
pub render_task_id: Option<RenderTaskId>,
// Details specific to this type of picture.
pub kind: PictureKind,
pub content_rect: LayerRect,
// List of primitive runs that make up this picture.
pub runs: Vec<PrimitiveRun>,
// The pipeline that the primitives on this picture belong to.
pub pipeline_id: PipelineId,
// If true, apply visibility culling to primitives on this
// picture. For text shadows and box shadows, we want to
// unconditionally draw them.
pub cull_children: bool,
// TODO(gw): Add a mode that specifies if this
// picture should be rasterized in
@ -51,16 +101,39 @@ pub struct PicturePrimitive {
}
impl PicturePrimitive {
pub fn new_text_shadow(shadow: Shadow) -> Self {
pub fn new_text_shadow(shadow: Shadow, pipeline_id: PipelineId) -> Self {
PicturePrimitive {
prim_runs: Vec::new(),
runs: Vec::new(),
render_task_id: None,
content_rect: LayerRect::zero(),
kind: PictureKind::TextShadow {
offset: shadow.offset,
color: shadow.color,
blur_radius: shadow.blur_radius,
content_rect: LayerRect::zero(),
},
pipeline_id,
cull_children: false,
}
}
pub fn resolve_scene_properties(&mut self, properties: &SceneProperties) -> bool {
match self.kind {
PictureKind::Image { ref mut composite_mode, .. } => {
match composite_mode {
&mut Some(PictureCompositeMode::Filter(ref mut filter)) => {
match filter {
&mut FilterOp::Opacity(ref binding, ref mut value) => {
*value = properties.resolve_float(binding, *value);
}
_ => {}
}
filter.is_visible()
}
_ => true,
}
}
_ => true
}
}
@ -70,38 +143,53 @@ impl PicturePrimitive {
blur_regions: Vec<LayerRect>,
clip_mode: BoxShadowClipMode,
radii_kind: BorderRadiusKind,
pipeline_id: PipelineId,
) -> Self {
PicturePrimitive {
prim_runs: Vec::new(),
runs: Vec::new(),
render_task_id: None,
content_rect: LayerRect::zero(),
kind: PictureKind::BoxShadow {
blur_radius,
color,
blur_regions,
clip_mode,
radii_kind,
content_rect: LayerRect::zero(),
},
pipeline_id,
cull_children: false,
}
}
pub fn new_image(
composite_mode: Option<PictureCompositeMode>,
is_in_3d_context: bool,
pipeline_id: PipelineId,
reference_frame_id: ClipId,
frame_output_pipeline_id: Option<PipelineId>,
) -> PicturePrimitive {
PicturePrimitive {
runs: Vec::new(),
render_task_id: None,
kind: PictureKind::Image {
readback_render_task_id: None,
composite_mode,
is_in_3d_context,
frame_output_pipeline_id,
reference_frame_id,
real_local_rect: LayerRect::zero(),
},
pipeline_id,
cull_children: true,
}
}
pub fn add_primitive(
&mut self,
prim_index: PrimitiveIndex,
local_rect: &LayerRect,
clip_and_scroll: ClipAndScrollInfo
) {
// TODO(gw): Accumulating the primitive local rect
// into the content rect here is fine, for now.
// The only way pictures are currently used,
// all the items added to a picture are known
// to be in the same local space. Once we start
// using pictures for other uses, we will need
// to consider the space of a primitive in order
// to build a correct contect rect!
self.content_rect = self.content_rect.union(local_rect);
if let Some(ref mut run) = self.prim_runs.last_mut() {
if let Some(ref mut run) = self.runs.last_mut() {
if run.clip_and_scroll == clip_and_scroll &&
run.base_prim_index.0 + run.count == prim_index.0 {
run.count += 1;
@ -109,26 +197,44 @@ impl PicturePrimitive {
}
}
self.prim_runs.push(PrimitiveRun {
self.runs.push(PrimitiveRun {
base_prim_index: prim_index,
count: 1,
clip_and_scroll,
});
}
pub fn build(&mut self) -> LayerRect {
pub fn update_local_rect(&mut self,
prim_local_rect: LayerRect,
prim_run_rect: PrimitiveRunLocalRect,
) -> LayerRect {
let local_content_rect = prim_run_rect.local_rect_in_actual_parent_space;
match self.kind {
PictureKind::TextShadow { offset, blur_radius, .. } => {
PictureKind::Image { composite_mode, ref mut real_local_rect, .. } => {
*real_local_rect = prim_run_rect.local_rect_in_original_parent_space;
match composite_mode {
Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
let inflate_size = blur_radius * BLUR_SAMPLE_SCALE;
local_content_rect.inflate(inflate_size, inflate_size)
}
_ => {
local_content_rect
}
}
}
PictureKind::TextShadow { offset, blur_radius, ref mut content_rect, .. } => {
let blur_offset = blur_radius * BLUR_SAMPLE_SCALE;
self.content_rect = self.content_rect.inflate(
*content_rect = local_content_rect.inflate(
blur_offset,
blur_offset,
);
self.content_rect.translate(&offset)
content_rect.translate(&offset)
}
PictureKind::BoxShadow { blur_radius, clip_mode, radii_kind, .. } => {
PictureKind::BoxShadow { blur_radius, clip_mode, radii_kind, ref mut content_rect, .. } => {
// We need to inflate the content rect if outset.
match clip_mode {
BoxShadowClipMode::Outset => {
@ -141,29 +247,31 @@ impl PicturePrimitive {
match radii_kind {
BorderRadiusKind::Uniform => {
let origin = LayerPoint::new(
self.content_rect.origin.x - blur_offset,
self.content_rect.origin.y - blur_offset,
local_content_rect.origin.x - blur_offset,
local_content_rect.origin.y - blur_offset,
);
let size = LayerSize::new(
self.content_rect.size.width + blur_offset,
self.content_rect.size.height + blur_offset,
local_content_rect.size.width + blur_offset,
local_content_rect.size.height + blur_offset,
);
self.content_rect = LayerRect::new(origin, size);
*content_rect = LayerRect::new(origin, size);
}
BorderRadiusKind::NonUniform => {
// For a non-uniform radii, we need to expand
// the content rect on all sides for the blur.
self.content_rect = self.content_rect.inflate(
*content_rect = local_content_rect.inflate(
blur_offset,
blur_offset,
);
}
}
}
BoxShadowClipMode::Inset => {}
BoxShadowClipMode::Inset => {
*content_rect = local_content_rect;
}
}
self.content_rect
prim_local_rect
}
}
}
@ -173,27 +281,93 @@ impl PicturePrimitive {
prim_index: PrimitiveIndex,
prim_context: &PrimitiveContext,
render_tasks: &mut RenderTaskTree,
screen_rect: &DeviceIntRect,
child_tasks: Vec<RenderTaskId>,
parent_tasks: &mut Vec<RenderTaskId>,
) {
match self.kind {
PictureKind::Image {
ref mut readback_render_task_id,
composite_mode,
frame_output_pipeline_id,
..
} => {
match composite_mode {
Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
let picture_task = RenderTask::new_dynamic_alpha_batch(
screen_rect,
prim_index,
None,
child_tasks,
);
let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
let blur_std_deviation = blur_radius.0 as f32;
let picture_task_id = render_tasks.add(picture_task);
let blur_render_task = RenderTask::new_blur(
blur_std_deviation,
picture_task_id,
render_tasks,
RenderTargetKind::Color,
&[],
ClearMode::Transparent,
PremultipliedColorF::TRANSPARENT,
);
let blur_render_task_id = render_tasks.add(blur_render_task);
self.render_task_id = Some(blur_render_task_id);
}
Some(PictureCompositeMode::MixBlend(..)) => {
let picture_task = RenderTask::new_dynamic_alpha_batch(
screen_rect,
prim_index,
None,
child_tasks,
);
let readback_task_id = render_tasks.add(RenderTask::new_readback(*screen_rect));
*readback_render_task_id = Some(readback_task_id);
parent_tasks.push(readback_task_id);
self.render_task_id = Some(render_tasks.add(picture_task));
}
Some(PictureCompositeMode::Filter(..)) | Some(PictureCompositeMode::Blit) => {
let picture_task = RenderTask::new_dynamic_alpha_batch(
screen_rect,
prim_index,
frame_output_pipeline_id,
child_tasks,
);
self.render_task_id = Some(render_tasks.add(picture_task));
}
None => {
parent_tasks.extend(child_tasks);
self.render_task_id = None;
}
}
}
PictureKind::TextShadow { blur_radius, color, content_rect, .. } => {
// This is a shadow element. Create a render task that will
// render the text run to a target, and then apply a gaussian
// blur to that text run in order to build the actual primitive
// which will be blitted to the framebuffer.
let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
// TODO(gw): Rounding the content rect here to device pixels is not
// technically correct. Ideally we should ceil() here, and ensure that
// the extra part pixel in the case of fractional sizes is correctly
// handled. For now, just use rounding which passes the existing
// Gecko tests.
let cache_width =
(self.content_rect.size.width * prim_context.device_pixel_ratio).round() as i32;
(content_rect.size.width * prim_context.device_pixel_ratio).round() as i32;
let cache_height =
(self.content_rect.size.height * prim_context.device_pixel_ratio).round() as i32;
(content_rect.size.height * prim_context.device_pixel_ratio).round() as i32;
let cache_size = DeviceIntSize::new(cache_width, cache_height);
match self.kind {
PictureKind::TextShadow { blur_radius, color, .. } => {
let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
// "the image that would be generated by applying to the shadow a
// Gaussian blur with a standard deviation equal to half the blur radius."
@ -203,7 +377,7 @@ impl PicturePrimitive {
cache_size,
prim_index,
RenderTargetKind::Color,
self.content_rect.origin,
content_rect.origin,
color.premultiplied(),
ClearMode::Transparent,
);
@ -222,9 +396,20 @@ impl PicturePrimitive {
self.render_task_id = Some(render_tasks.add(render_task));
}
PictureKind::BoxShadow { blur_radius, clip_mode, ref blur_regions, color, .. } => {
PictureKind::BoxShadow { blur_radius, clip_mode, ref blur_regions, color, content_rect, .. } => {
let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
// TODO(gw): Rounding the content rect here to device pixels is not
// technically correct. Ideally we should ceil() here, and ensure that
// the extra part pixel in the case of fractional sizes is correctly
// handled. For now, just use rounding which passes the existing
// Gecko tests.
let cache_width =
(content_rect.size.width * prim_context.device_pixel_ratio).round() as i32;
let cache_height =
(content_rect.size.height * prim_context.device_pixel_ratio).round() as i32;
let cache_size = DeviceIntSize::new(cache_width, cache_height);
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
// "the image that would be generated by applying to the shadow a
// Gaussian blur with a standard deviation equal to half the blur radius."
@ -243,7 +428,7 @@ impl PicturePrimitive {
cache_size,
prim_index,
RenderTargetKind::Alpha,
self.content_rect.origin,
content_rect.origin,
color.premultiplied(),
ClearMode::Zero,
);
@ -263,6 +448,10 @@ impl PicturePrimitive {
self.render_task_id = Some(render_tasks.add(render_task));
}
}
if let Some(render_task_id) = self.render_task_id {
parent_tasks.push(render_task_id);
}
}
pub fn write_gpu_blocks(&self, mut _request: GpuDataRequest) {
@ -275,6 +464,7 @@ impl PicturePrimitive {
match self.kind {
PictureKind::TextShadow { .. } => RenderTargetKind::Color,
PictureKind::BoxShadow { .. } => RenderTargetKind::Alpha,
PictureKind::Image { .. } => RenderTargetKind::Color,
}
}
}

Просмотреть файл

@ -40,11 +40,9 @@ fn dwrite_measure_mode(
render_mode: FontRenderMode,
options: Option<FontInstancePlatformOptions>,
) -> dwrote::DWRITE_MEASURING_MODE {
if let Some(FontInstancePlatformOptions {
force_gdi_rendering: true,
..
}) = options
{
let FontInstancePlatformOptions { force_gdi_rendering, use_embedded_bitmap, .. } =
options.unwrap_or_default();
if force_gdi_rendering || use_embedded_bitmap {
return dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC;
}
@ -61,19 +59,18 @@ fn dwrite_render_mode(
measure_mode: dwrote::DWRITE_MEASURING_MODE,
options: Option<FontInstancePlatformOptions>,
) -> dwrote::DWRITE_RENDERING_MODE {
if let Some(FontInstancePlatformOptions {
force_gdi_rendering: true,
..
}) = options
{
return dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC;
}
let FontInstancePlatformOptions { force_gdi_rendering, use_embedded_bitmap, .. } =
options.unwrap_or_default();
let dwrite_render_mode = match render_mode {
FontRenderMode::Mono | FontRenderMode::Bitmap => dwrote::DWRITE_RENDERING_MODE_ALIASED,
FontRenderMode::Alpha | FontRenderMode::Subpixel => {
if force_gdi_rendering || use_embedded_bitmap {
dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC
} else {
font_face.get_recommended_rendering_mode_default_params(em_size, 1.0, measure_mode)
}
}
};
if dwrite_render_mode == dwrote::DWRITE_RENDERING_MODE_OUTLINE {

Просмотреть файл

@ -7,35 +7,33 @@ use api::{DevicePoint, ExtendMode, GlyphInstance, GlyphKey};
use api::{GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerRect};
use api::{ClipMode, LayerSize, LayerVector2D, LineOrientation, LineStyle};
use api::{ClipAndScrollInfo, EdgeAaSegmentMask, PremultipliedColorF, TileOffset};
use api::{YuvColorSpace, YuvFormat};
use api::{ClipId, LayerTransform, PipelineId, YuvColorSpace, YuvFormat};
use border::BorderCornerInstance;
use clip::{ClipSourcesHandle, ClipStore, Geometry};
use clip_scroll_tree::ClipScrollTree;
use clip::{ClipSourcesHandle, ClipStore};
use frame_builder::PrimitiveContext;
use glyph_rasterizer::FontInstance;
use internal_types::FastHashMap;
use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
ToGpuBlocks};
use picture::PicturePrimitive;
use render_task::{ClipWorkItem, ClipChainNode, RenderTask, RenderTaskId, RenderTaskTree};
use picture::{PictureKind, PicturePrimitive};
use profiler::FrameProfileCounters;
use render_task::{ClipWorkItem, ClipChainNode};
use render_task::{RenderTask, RenderTaskId, RenderTaskTree};
use renderer::MAX_VERTEX_TEXTURE_WIDTH;
use resource_cache::{ImageProperties, ResourceCache};
use scene::{ScenePipeline, SceneProperties};
use std::{mem, usize};
use std::rc::Rc;
use util::{pack_as_float, recycle_vec, MatrixHelpers, TransformedRect, TransformedRectKind};
#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct PrimitiveRun {
pub base_prim_index: PrimitiveIndex,
pub count: usize,
pub clip_and_scroll: ClipAndScrollInfo,
}
#[derive(Debug)]
pub struct PrimitiveRunResult {
pub local_rect: LayerRect,
pub device_rect: DeviceIntRect,
pub visible_primitives: usize,
}
#[derive(Debug, Copy, Clone)]
pub struct PrimitiveOpacity {
pub is_opaque: bool,
@ -61,6 +59,27 @@ impl PrimitiveOpacity {
}
}
// Represents the local space rect of a list of
// primitive runs. For most primitive runs, the
// primitive runs are attached to the parent they
// are declared in. However, when a primitive run
// is part of a 3d rendering context, it may get
// hoisted to a higher level in the picture tree.
// When this happens, we need to also calculate the
// local space rects in the original space. This
// allows constructing the true world space polygons
// for the primitive, to enable the plane splitting
// logic to work correctly.
// TODO(gw) In the future, we can probably simplify
// this - perhaps calculate the world space
// polygons directly and store internally
// in the picture structure.
#[derive(Debug)]
pub struct PrimitiveRunLocalRect {
pub local_rect_in_actual_parent_space: LayerRect,
pub local_rect_in_original_parent_space: LayerRect,
}
/// Stores two coordinates in texel space. The coordinates
/// are stored in texel coordinates because the texture atlas
/// may grow. Storing them as texel coords and normalizing
@ -1062,37 +1081,6 @@ impl PrimitiveStore {
self.cpu_metadata.len()
}
/// Add any task dependencies for this primitive to the provided task.
pub fn add_render_tasks_for_prim(&self, prim_index: PrimitiveIndex, task: &mut RenderTask) {
// Add any dynamic render tasks needed to render this primitive
let metadata = &self.cpu_metadata[prim_index.0];
let render_task_id = match metadata.prim_kind {
PrimitiveKind::Picture => {
let picture = &self.cpu_pictures[metadata.cpu_prim_index.0];
picture.render_task_id
}
PrimitiveKind::Rectangle |
PrimitiveKind::TextRun |
PrimitiveKind::Image |
PrimitiveKind::AlignedGradient |
PrimitiveKind::YuvImage |
PrimitiveKind::Border |
PrimitiveKind::AngleGradient |
PrimitiveKind::RadialGradient |
PrimitiveKind::Line |
PrimitiveKind::Brush => None,
};
if let Some(render_task_id) = render_task_id {
task.children.push(render_task_id);
}
if let Some(clip_task_id) = metadata.clip_task_id {
task.children.push(clip_task_id);
}
}
fn prepare_prim_for_render_inner(
&mut self,
prim_index: PrimitiveIndex,
@ -1100,6 +1088,8 @@ impl PrimitiveStore {
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
child_tasks: Vec<RenderTaskId>,
parent_tasks: &mut Vec<RenderTaskId>,
) {
let metadata = &mut self.cpu_metadata[prim_index.0];
match metadata.prim_kind {
@ -1109,7 +1099,10 @@ impl PrimitiveStore {
.prepare_for_render(
prim_index,
prim_context,
render_tasks
render_tasks,
metadata.screen_rect.as_ref().expect("bug: trying to draw an off-screen picture!?"),
child_tasks,
parent_tasks,
);
}
PrimitiveKind::TextRun => {
@ -1226,8 +1219,10 @@ impl PrimitiveStore {
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
clip_store: &mut ClipStore,
tasks: &mut Vec<RenderTaskId>,
) -> bool {
let metadata = &mut self.cpu_metadata[prim_index.0];
metadata.clip_task_id = None;
let transform = &prim_context.scroll_node.world_content_transform;
clip_store.get_mut(&metadata.clip_sources).update(
@ -1283,7 +1278,13 @@ impl PrimitiveStore {
None
};
metadata.clip_task_id = clip_task.map(|clip_task| render_tasks.add(clip_task));
if let Some(clip_task) = clip_task {
let clip_task_id = render_tasks.add(clip_task);
metadata.clip_task_id = Some(clip_task_id);
tasks.push(clip_task_id);
}
true
}
@ -1295,19 +1296,89 @@ impl PrimitiveStore {
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
clip_store: &mut ClipStore,
) -> Option<Geometry> {
let (geometry, dependent_primitives) = {
clip_scroll_tree: &ClipScrollTree,
pipelines: &FastHashMap<PipelineId, ScenePipeline>,
perform_culling: bool,
parent_tasks: &mut Vec<RenderTaskId>,
scene_properties: &SceneProperties,
profile_counters: &mut FrameProfileCounters,
) -> Option<LayerRect> {
// Reset the visibility of this primitive.
// Do some basic checks first, that can early out
// without even knowing the local rect.
let (cpu_prim_index, dependencies, cull_children) = {
let metadata = &mut self.cpu_metadata[prim_index.0];
metadata.screen_rect = None;
if metadata.local_rect.size.width <= 0.0 ||
metadata.local_rect.size.height <= 0.0 {
warn!("invalid primitive rect {:?}", metadata.local_rect);
if perform_culling &&
!metadata.is_backface_visible &&
prim_context.scroll_node.world_content_transform.is_backface_visible() {
return None;
}
if !metadata.is_backface_visible &&
prim_context.scroll_node.world_content_transform.is_backface_visible() {
let (dependencies, cull_children) = match metadata.prim_kind {
PrimitiveKind::Picture => {
let pic = &mut self.cpu_pictures[metadata.cpu_prim_index.0];
if !pic.resolve_scene_properties(scene_properties) {
return None;
}
let rfid = match pic.kind {
PictureKind::Image { reference_frame_id, .. } => Some(reference_frame_id),
_ => None,
};
(Some((pic.pipeline_id, mem::replace(&mut pic.runs, Vec::new()), rfid)), pic.cull_children)
}
_ => {
(None, true)
}
};
(metadata.cpu_prim_index, dependencies, cull_children)
};
// If we have dependencies, we need to prepare them first, in order
// to know the actual rect of this primitive.
// For example, scrolling may affect the location of an item in
// local space, which may force us to render this item on a larger
// picture target, if being composited.
let mut child_tasks = Vec::new();
if let Some((pipeline_id, dependencies, rfid)) = dependencies {
let result = self.prepare_prim_runs(
&dependencies,
pipeline_id,
gpu_cache,
resource_cache,
render_tasks,
clip_store,
clip_scroll_tree,
pipelines,
prim_context,
cull_children,
&mut child_tasks,
profile_counters,
rfid,
scene_properties,
);
let metadata = &mut self.cpu_metadata[prim_index.0];
// Restore the dependencies (borrow check dance)
let pic = &mut self.cpu_pictures[cpu_prim_index.0];
pic.runs = dependencies;
metadata.local_rect = pic.update_local_rect(
metadata.local_rect,
result,
);
}
let (local_rect, device_rect) = {
let metadata = &mut self.cpu_metadata[prim_index.0];
if metadata.local_rect.size.width <= 0.0 ||
metadata.local_rect.size.height <= 0.0 {
warn!("invalid primitive rect {:?}", metadata.local_rect);
return None;
}
@ -1317,7 +1388,8 @@ impl PrimitiveStore {
let local_rect = match local_rect {
Some(local_rect) => local_rect,
None => return None,
None if perform_culling => return None,
None => LayerRect::zero(),
};
let xf_rect = TransformedRect::new(
@ -1330,50 +1402,30 @@ impl PrimitiveStore {
metadata.screen_rect = xf_rect.bounding_rect
.intersection(clip_bounds);
let geometry = match metadata.screen_rect {
Some(device_rect) => Geometry {
local_rect,
device_rect,
},
None => return None,
};
let dependencies = match metadata.prim_kind {
PrimitiveKind::Picture =>
self.cpu_pictures[metadata.cpu_prim_index.0].prim_runs.clone(),
_ => Vec::new(),
};
(geometry, dependencies)
};
// Recurse into any sub primitives and prepare them for rendering first.
// TODO(gw): This code is a bit hacky to work around the borrow checker.
// Specifically, the clone() below on the primitive list for
// text shadow primitives. Consider restructuring this code to
// avoid borrow checker issues.
for run in dependent_primitives {
for i in 0 .. run.count {
let sub_prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
self.prepare_prim_for_render_inner(
sub_prim_index,
prim_context,
resource_cache,
gpu_cache,
render_tasks,
);
let device_rect = match metadata.screen_rect {
Some(device_rect) => device_rect,
None => {
if perform_culling {
return None
} else {
DeviceIntRect::zero()
}
}
};
(local_rect, device_rect)
};
if !self.update_clip_task(
prim_index,
prim_context,
geometry.device_rect,
device_rect,
resource_cache,
gpu_cache,
render_tasks,
clip_store,
) {
parent_tasks,
) && perform_culling {
return None;
}
@ -1383,40 +1435,117 @@ impl PrimitiveStore {
resource_cache,
gpu_cache,
render_tasks,
child_tasks,
parent_tasks,
);
Some(geometry)
Some(local_rect)
}
pub fn prepare_prim_run(
// TODO(gw): Make this simpler / more efficient by tidying
// up the logic that early outs from prepare_prim_for_render.
pub fn reset_prim_visibility(&mut self) {
for md in &mut self.cpu_metadata {
md.screen_rect = None;
}
}
pub fn prepare_prim_runs(
&mut self,
run: &PrimitiveRun,
prim_context: &PrimitiveContext,
runs: &[PrimitiveRun],
pipeline_id: PipelineId,
gpu_cache: &mut GpuCache,
resource_cache: &mut ResourceCache,
render_tasks: &mut RenderTaskTree,
clip_store: &mut ClipStore,
) -> PrimitiveRunResult {
let mut result = PrimitiveRunResult {
local_rect: LayerRect::zero(),
device_rect: DeviceIntRect::zero(),
visible_primitives: 0,
clip_scroll_tree: &ClipScrollTree,
pipelines: &FastHashMap<PipelineId, ScenePipeline>,
parent_prim_context: &PrimitiveContext,
perform_culling: bool,
parent_tasks: &mut Vec<RenderTaskId>,
profile_counters: &mut FrameProfileCounters,
original_reference_frame_id: Option<ClipId>,
scene_properties: &SceneProperties,
) -> PrimitiveRunLocalRect {
let mut result = PrimitiveRunLocalRect {
local_rect_in_actual_parent_space: LayerRect::zero(),
local_rect_in_original_parent_space: LayerRect::zero(),
};
for run in runs {
// TODO(gw): Perhaps we can restructure this to not need to create
// a new primitive context for every run (if the hash
// lookups ever show up in a profile).
let scroll_node = &clip_scroll_tree.nodes[&run.clip_and_scroll.scroll_node_id];
let clip_node = &clip_scroll_tree.nodes[&run.clip_and_scroll.clip_node_id()];
if perform_culling && !clip_node.is_visible() {
debug!("{:?} of clipped out {:?}", run.base_prim_index, pipeline_id);
continue;
}
let parent_relative_transform = parent_prim_context
.scroll_node
.world_content_transform
.inverse()
.map(|inv_parent| {
inv_parent.pre_mul(&scroll_node.world_content_transform)
});
let original_relative_transform = original_reference_frame_id
.and_then(|original_reference_frame_id| {
let parent = clip_scroll_tree
.nodes[&original_reference_frame_id]
.world_content_transform;
parent.inverse()
.map(|inv_parent| {
inv_parent.pre_mul(&scroll_node.world_content_transform)
})
});
let display_list = &pipelines
.get(&pipeline_id)
.expect("No display list?")
.display_list;
let child_prim_context = PrimitiveContext::new(
parent_prim_context.device_pixel_ratio,
display_list,
clip_node,
scroll_node,
);
for i in 0 .. run.count {
let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
if let Some(prim_geom) = self.prepare_prim_for_render(
if let Some(prim_local_rect) = self.prepare_prim_for_render(
prim_index,
prim_context,
&child_prim_context,
resource_cache,
gpu_cache,
render_tasks,
clip_store,
clip_scroll_tree,
pipelines,
perform_culling,
parent_tasks,
scene_properties,
profile_counters,
) {
result.local_rect = result.local_rect.union(&prim_geom.local_rect);
result.device_rect = result.device_rect.union(&prim_geom.device_rect);
result.visible_primitives += 1;
profile_counters.visible_primitives.inc();
if let Some(ref matrix) = original_relative_transform {
let bounds = get_local_bounding_rect(&prim_local_rect, matrix);
result.local_rect_in_original_parent_space =
result.local_rect_in_original_parent_space.union(&bounds);
}
if let Some(ref matrix) = parent_relative_transform {
let bounds = get_local_bounding_rect(&prim_local_rect, matrix);
result.local_rect_in_actual_parent_space =
result.local_rect_in_actual_parent_space.union(&bounds);
}
}
}
}
@ -1448,3 +1577,29 @@ impl InsideTest<ComplexClipRegion> for ComplexClipRegion {
clip.radii.bottom_right.height >= self.radii.bottom_right.height - delta_bottom
}
}
fn get_local_bounding_rect(
local_rect: &LayerRect,
matrix: &LayerTransform
) -> LayerRect {
let vertices = [
matrix.transform_point3d(&local_rect.origin.to_3d()),
matrix.transform_point3d(&local_rect.bottom_left().to_3d()),
matrix.transform_point3d(&local_rect.bottom_right().to_3d()),
matrix.transform_point3d(&local_rect.top_right().to_3d()),
];
let mut x0 = vertices[0].x;
let mut y0 = vertices[0].y;
let mut x1 = vertices[0].x;
let mut y1 = vertices[0].y;
for v in &vertices[1..] {
x0 = x0.min(v.x);
y0 = y0.min(v.y);
x1 = x1.max(v.x);
y1 = y1.max(v.y);
}
LayerRect::new(LayerPoint::new(x0, y0), LayerSize::new(x1 - x0, y1 - y0))
}

Просмотреть файл

@ -4,11 +4,10 @@
use api::{ColorF, ColorU};
use debug_render::DebugRenderer;
use device::{Device, GpuMarker, GpuSampler, GpuTimer, NamedTag};
use euclid::{Point2D, Rect, Size2D, vec2};
use query::{GpuSampler, GpuTimer, NamedTag};
use std::collections::vec_deque::VecDeque;
use std::f32;
use std::mem;
use std::{f32, mem};
use time::precise_time_ns;
const GRAPH_WIDTH: f32 = 1024.0;
@ -794,7 +793,6 @@ impl Profiler {
pub fn draw_profile(
&mut self,
device: &mut Device,
frame_profile: &FrameProfileCounters,
backend_profile: &BackendProfileCounters,
renderer_profile: &RendererProfileCounters,
@ -803,15 +801,14 @@ impl Profiler {
screen_fraction: f32,
debug_renderer: &mut DebugRenderer,
) {
let _gm = GpuMarker::new(device.rc_gl(), "profile");
self.x_left = 20.0;
self.y_left = 40.0;
self.x_right = 400.0;
self.y_right = 40.0;
let mut gpu_time = 0;
let gpu_samples = mem::replace(&mut renderer_timers.gpu_samples, Vec::new());
for sample in &gpu_samples {
let gpu_timers = mem::replace(&mut renderer_timers.gpu_samples, Vec::new());
for sample in &gpu_timers {
gpu_time += sample.time_ns;
}
renderer_timers.gpu_time.set(gpu_time);
@ -882,6 +879,7 @@ impl Profiler {
false,
);
if !gpu_samplers.is_empty() {
let mut samplers = Vec::<FloatProfileCounter>::new();
// Gathering unique GPU samplers. This has O(N^2) complexity,
// but we only have a few samplers per target.
@ -898,6 +896,7 @@ impl Profiler {
}
}
self.draw_counters(&samplers, debug_renderer, false);
}
self.backend_time
.push(backend_profile.total_time.nanoseconds);
@ -906,7 +905,7 @@ impl Profiler {
self.ipc_time
.push(backend_profile.ipc.total_time.nanoseconds);
self.gpu_time.push(gpu_time);
self.gpu_frames.push(gpu_time, gpu_samples);
self.gpu_frames.push(gpu_time, gpu_timers);
let rect =

319
gfx/webrender/src/query.rs Normal 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;

Просмотреть файл

@ -93,6 +93,7 @@ impl Document {
self.window_size,
self.inner_rect,
accumulated_scale_factor,
&self.output_pipelines,
);
}
@ -116,9 +117,9 @@ impl Document {
&self.scene.pipelines,
accumulated_scale_factor,
pan,
&self.output_pipelines,
&mut resource_profile.texture_cache,
&mut resource_profile.gpu_cache,
&self.scene.properties,
)
}
None => {
@ -410,19 +411,8 @@ impl RenderBackend {
profile_scope!("GenerateFrame");
let _timer = profile_counters.total_time.timer();
// Ideally, when there are property bindings present,
// we won't need to rebuild the entire frame here.
// However, to avoid conflicts with the ongoing work to
// refactor how scroll roots + transforms work, this
// just rebuilds the frame if there are animated property
// bindings present for now.
// TODO(gw): Once the scrolling / reference frame changes
// are completed, optimize the internals of
// animated properties to not require a full
// rebuild of the frame!
if let Some(property_bindings) = property_bindings {
doc.scene.properties.set_properties(property_bindings);
doc.build_scene(&mut self.resource_cache);
}
if let Some(ref mut ros) = doc.render_on_scroll {

Просмотреть файл

@ -3,18 +3,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{ClipId, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use api::{FilterOp, LayerPoint, LayerRect, MixBlendMode};
use api::{LayerPoint, LayerRect};
use api::{PipelineId, PremultipliedColorF};
use clip::{ClipSource, ClipSourcesWeakHandle, ClipStore};
use clip_scroll_tree::CoordinateSystemId;
use gpu_cache::GpuCacheHandle;
use gpu_types::{ClipScrollNodeIndex};
use internal_types::HardwareCompositeOp;
use prim_store::PrimitiveIndex;
use prim_store::{PrimitiveIndex};
use std::{cmp, usize, f32, i32};
use std::rc::Rc;
use tiling::{RenderPass, RenderTargetIndex};
use tiling::{RenderTargetKind, StackingContextIndex};
use tiling::{RenderTargetKind};
const FLOATS_PER_RENDER_TASK_INFO: usize = 12;
pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0;
@ -152,32 +150,10 @@ pub enum RenderTaskLocation {
Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize),
}
#[derive(Debug)]
pub enum AlphaRenderItem {
Primitive(ClipScrollNodeIndex, ClipScrollNodeIndex, PrimitiveIndex, i32),
Blend(StackingContextIndex, RenderTaskId, FilterOp, i32),
Composite(
StackingContextIndex,
RenderTaskId,
RenderTaskId,
MixBlendMode,
i32,
),
SplitComposite(StackingContextIndex, RenderTaskId, GpuCacheHandle, i32),
HardwareComposite(
StackingContextIndex,
RenderTaskId,
HardwareCompositeOp,
DeviceIntPoint,
i32,
DeviceIntSize,
),
}
#[derive(Debug)]
pub struct AlphaRenderTask {
pub screen_origin: DeviceIntPoint,
pub items: Vec<AlphaRenderItem>,
pub prim_index: PrimitiveIndex,
// If this render task is a registered frame output, this
// contains the pipeline ID it maps to.
pub frame_output_pipeline_id: Option<PipelineId>,
@ -312,18 +288,23 @@ pub struct RenderTask {
}
impl RenderTask {
// TODO(gw): In the future we'll remove this
// completely and convert everything
// that is an alpha task to a Picture.
pub fn new_alpha_batch(
screen_origin: DeviceIntPoint,
location: RenderTaskLocation,
prim_index: PrimitiveIndex,
frame_output_pipeline_id: Option<PipelineId>,
children: Vec<RenderTaskId>,
) -> Self {
RenderTask {
cache_key: None,
children: Vec::new(),
children,
location,
kind: RenderTaskKind::Alpha(AlphaRenderTask {
screen_origin,
items: Vec::new(),
prim_index,
frame_output_pipeline_id,
}),
clear_mode: ClearMode::Transparent,
@ -332,10 +313,18 @@ impl RenderTask {
pub fn new_dynamic_alpha_batch(
rect: &DeviceIntRect,
prim_index: PrimitiveIndex,
frame_output_pipeline_id: Option<PipelineId>,
children: Vec<RenderTaskId>,
) -> Self {
let location = RenderTaskLocation::Dynamic(None, rect.size);
Self::new_alpha_batch(rect.origin, location, frame_output_pipeline_id)
Self::new_alpha_batch(
rect.origin,
location,
prim_index,
frame_output_pipeline_id,
children,
)
}
pub fn new_picture(
@ -553,19 +542,6 @@ impl RenderTask {
}
}
pub fn as_alpha_batch_mut<'a>(&'a mut self) -> &'a mut AlphaRenderTask {
match self.kind {
RenderTaskKind::Alpha(ref mut task) => task,
RenderTaskKind::Picture(..) |
RenderTaskKind::CacheMask(..) |
RenderTaskKind::VerticalBlur(..) |
RenderTaskKind::Readback(..) |
RenderTaskKind::HorizontalBlur(..) |
RenderTaskKind::Alias(..) |
RenderTaskKind::Scaling(..) => unreachable!(),
}
}
pub fn as_alpha_batch<'a>(&'a self) -> &'a AlphaRenderTask {
match self.kind {
RenderTaskKind::Alpha(ref task) => task,
@ -693,34 +669,6 @@ impl RenderTask {
}
}
pub fn inflate(&mut self, device_radius: i32) {
match self.kind {
RenderTaskKind::Alpha(ref mut info) => {
match self.location {
RenderTaskLocation::Fixed => {
panic!("bug: inflate only supported for dynamic tasks");
}
RenderTaskLocation::Dynamic(_, ref mut size) => {
size.width += device_radius * 2;
size.height += device_radius * 2;
info.screen_origin.x -= device_radius;
info.screen_origin.y -= device_radius;
}
}
}
RenderTaskKind::Readback(..) |
RenderTaskKind::CacheMask(..) |
RenderTaskKind::VerticalBlur(..) |
RenderTaskKind::HorizontalBlur(..) |
RenderTaskKind::Picture(..) |
RenderTaskKind::Alias(..) |
RenderTaskKind::Scaling(..) => {
panic!("bug: inflate only supported for alpha tasks");
}
}
}
pub fn get_dynamic_size(&self) -> DeviceIntSize {
match self.location {
RenderTaskLocation::Fixed => DeviceIntSize::zero(),
@ -730,12 +678,29 @@ impl RenderTask {
pub fn get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex) {
match self.location {
RenderTaskLocation::Fixed => (DeviceIntRect::zero(), RenderTargetIndex(0)),
RenderTaskLocation::Dynamic(origin_and_target_index, size) => {
let (origin, target_index) =
origin_and_target_index.expect("Should have been allocated by now!");
RenderTaskLocation::Fixed => {
(DeviceIntRect::zero(), RenderTargetIndex(0))
}
// Previously, we only added render tasks after the entire
// primitive chain was determined visible. This meant that
// we could assert any render task in the list was also
// allocated (assigned to passes). Now, we add render
// tasks earlier, and the picture they belong to may be
// culled out later, so we can't assert that the task
// has been allocated.
// Render tasks that are created but not assigned to
// passes consume a row in the render task texture, but
// don't allocate any space in render targets nor
// draw any pixels.
// TODO(gw): Consider some kind of tag or other method
// to mark a task as unused explicitly. This
// would allow us to restore this debug check.
RenderTaskLocation::Dynamic(Some((origin, target_index)), size) => {
(DeviceIntRect::new(origin, size), target_index)
}
RenderTaskLocation::Dynamic(None, _) => {
(DeviceIntRect::zero(), RenderTargetIndex(0))
}
}
}

Просмотреть файл

@ -24,11 +24,11 @@ use debug_colors;
use debug_render::DebugRenderer;
#[cfg(feature = "debugger")]
use debug_server::{self, DebugServer};
use device::{DepthFunction, Device, FrameId, GpuMarker, GpuProfiler, Program, Texture,
use device::{DepthFunction, Device, FrameId, Program, Texture,
VertexDescriptor, PBO};
use device::{get_gl_format_bgra, ExternalTexture, FBOId, TextureSlot, VertexAttribute,
VertexAttributeKind};
use device::{FileWatcherHandler, GpuTimer, ShaderError, TextureFilter, TextureTarget,
use device::{FileWatcherHandler, ShaderError, TextureFilter, TextureTarget,
VertexUsageHint, VAO};
use euclid::{rect, Transform3D};
use frame_builder::FrameBuilderConfig;
@ -41,6 +41,7 @@ use internal_types::{CacheTextureId, FastHashMap, RendererFrame, ResultMsg, Text
use internal_types::{DebugOutput, RenderTargetMode, TextureUpdateList, TextureUpdateSource};
use profiler::{BackendProfileCounters, Profiler};
use profiler::{GpuProfileTag, RendererProfileCounters, RendererProfileTimers};
use query::{GpuProfiler, GpuTimer};
use rayon::Configuration as ThreadPoolConfig;
use rayon::ThreadPool;
use record::ApiRecordingReceiver;
@ -555,10 +556,9 @@ impl SourceTextureResolver {
fn end_pass(
&mut self,
pass_index: usize,
pass_count: usize,
mut a8_texture: Option<Texture>,
mut rgba8_texture: Option<Texture>,
is_last: bool,
a8_texture: Option<Texture>,
rgba8_texture: Option<Texture>,
a8_pool: &mut Vec<Texture>,
rgba8_pool: &mut Vec<Texture>,
) {
@ -566,19 +566,15 @@ impl SourceTextureResolver {
rgba8_pool.extend(self.cache_rgba8_texture.take());
a8_pool.extend(self.cache_a8_texture.take());
if pass_index == pass_count - 1 {
if is_last {
// On the last pass, return the textures from this pass to the pool.
if let Some(texture) = rgba8_texture.take() {
rgba8_pool.push(texture);
}
if let Some(texture) = a8_texture.take() {
a8_pool.push(texture);
}
rgba8_pool.extend(rgba8_texture);
a8_pool.extend(a8_texture);
} else {
// We have another pass to process, make these textures available
// as inputs to the next pass.
self.cache_rgba8_texture = rgba8_texture.take();
self.cache_a8_texture = a8_texture.take();
self.cache_rgba8_texture = rgba8_texture;
self.cache_a8_texture = a8_texture;
}
}
@ -1838,8 +1834,7 @@ impl Renderer {
};
let gpu_cache_texture = CacheTexture::new(&mut device);
let gpu_profile = GpuProfiler::new(device.rc_gl());
let gpu_profile = GpuProfiler::new(Rc::clone(device.rc_gl()));
let renderer = Renderer {
result_rx,
@ -2153,6 +2148,16 @@ impl Renderer {
} else {
self.debug_flags.remove(DebugFlags::ALPHA_PRIM_DBG);
},
DebugCommand::EnableGpuTimeQueries(enable) => if enable {
self.gpu_profile.enable_timers();
} else {
self.gpu_profile.disable_timers();
},
DebugCommand::EnableGpuSampleQueries(enable) => if enable {
self.gpu_profile.enable_samplers();
} else {
self.gpu_profile.disable_samplers();
},
DebugCommand::FetchDocuments => {}
DebugCommand::FetchClipScrollTree => {}
DebugCommand::FetchPasses => {
@ -2162,6 +2167,11 @@ impl Renderer {
}
}
pub fn toggle_queries_enabled(&mut self) {
self.gpu_profile.toggle_timers_enabled();
self.gpu_profile.toggle_samplers_enabled();
}
/// Set a callback for handling external images.
pub fn set_external_image_handler(&mut self, handler: Box<ExternalImageHandler>) {
self.external_image_handler = Some(handler);
@ -2189,15 +2199,12 @@ impl Renderer {
if let Some(mut frame) = self.current_frame.take() {
if let Some(ref mut frame) = frame.frame {
let mut profile_timers = RendererProfileTimers::new();
let mut profile_samplers = Vec::new();
{
//Note: avoiding `self.gpu_profile.add_marker` - it would block here
let _gm = GpuMarker::new(self.device.rc_gl(), "build samples");
let profile_samplers = {
let _gm = self.gpu_profile.start_marker("build samples");
// Block CPU waiting for last frame's GPU profiles to arrive.
// In general this shouldn't block unless heavily GPU limited.
if let Some((gpu_frame_id, timers, samplers)) = self.gpu_profile.build_samples()
{
let (gpu_frame_id, timers, samplers) = self.gpu_profile.build_samples();
if self.max_recorded_profiles > 0 {
while self.gpu_profiles.len() >= self.max_recorded_profiles {
self.gpu_profiles.pop_front();
@ -2206,13 +2213,12 @@ impl Renderer {
.push_back(GpuProfile::new(gpu_frame_id, &timers));
}
profile_timers.gpu_samples = timers;
profile_samplers = samplers;
}
}
samplers
};
let cpu_frame_id = profile_timers.cpu_time.profile(|| {
let cpu_frame_id = {
let _gm = GpuMarker::new(self.device.rc_gl(), "begin frame");
let _gm = self.gpu_profile.start_marker("begin frame");
let frame_id = self.device.begin_frame(frame.device_pixel_ratio);
self.gpu_profile.begin_frame(frame_id);
@ -2257,10 +2263,10 @@ impl Renderer {
}
if self.debug_flags.contains(DebugFlags::PROFILER_DBG) {
let _gm = self.gpu_profile.start_marker("profile");
let screen_fraction = 1.0 / //TODO: take device/pixel ratio into equation?
(framebuffer_size.width as f32 * framebuffer_size.height as f32);
self.profiler.draw_profile(
&mut self.device,
&frame.profile_counters,
&self.backend_profile_counters,
&self.profile_counters,
@ -2274,13 +2280,16 @@ impl Renderer {
self.profile_counters.reset();
self.profile_counters.frame_counter.inc();
{
let _gm = self.gpu_profile.start_marker("debug");
let debug_size = DeviceUintSize::new(
framebuffer_size.width as u32,
framebuffer_size.height as u32,
);
self.debug.render(&mut self.device, &debug_size);
}
{
let _gm = GpuMarker::new(self.device.rc_gl(), "end frame");
let _gm = self.gpu_profile.start_marker("end frame");
self.device.end_frame();
}
self.last_time = current_time;
@ -2304,7 +2313,7 @@ impl Renderer {
}
fn update_gpu_cache(&mut self, frame: &mut Frame) {
let _gm = GpuMarker::new(self.device.rc_gl(), "gpu cache update");
let _gm = self.gpu_profile.start_marker("gpu cache update");
for update_list in self.pending_gpu_cache_updates.drain(..) {
self.gpu_cache_texture
.update(&mut self.device, &update_list);
@ -2314,7 +2323,7 @@ impl Renderer {
}
fn update_texture_cache(&mut self) {
let _gm = GpuMarker::new(self.device.rc_gl(), "texture cache update");
let _gm = self.gpu_profile.start_marker("texture cache update");
let mut pending_texture_updates = mem::replace(&mut self.pending_texture_updates, vec![]);
for update_list in pending_texture_updates.drain(..) {
@ -2711,7 +2720,7 @@ impl Renderer {
_ => {}
}
let _gm = self.gpu_profile.add_marker(marker);
let _timer = self.gpu_profile.start_timer(marker);
self.draw_instanced_batch(instances, VertexArrayKind::Primitive, &key.textures);
}
@ -2750,7 +2759,7 @@ impl Renderer {
frame_id: FrameId,
) {
{
let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET);
let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_TARGET);
self.device
.bind_draw_target(render_target, Some(target_size));
self.device.disable_depth();
@ -2781,7 +2790,7 @@ impl Renderer {
// fast path blur shaders for common
// blur radii with fixed weights.
if !target.vertical_blurs.is_empty() || !target.horizontal_blurs.is_empty() {
let _gm = self.gpu_profile.add_marker(GPU_TAG_BLUR);
let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR);
self.device.set_blend(false);
self.cs_blur_rgba8
@ -2816,7 +2825,7 @@ impl Renderer {
self.device.set_blend(true);
self.device.set_blend_mode_premultiplied_alpha();
let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_TEXT_RUN);
let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_TEXT_RUN);
self.cs_text_run
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
for (texture_id, instances) in &target.text_run_cache_prims {
@ -2833,7 +2842,7 @@ impl Renderer {
self.device.set_blend(true);
self.device.set_blend_mode_premultiplied_alpha();
let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_LINE);
let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_LINE);
self.cs_line
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
self.draw_instanced_batch(
@ -2846,11 +2855,11 @@ impl Renderer {
//TODO: record the pixel count for cached primitives
if !target.alpha_batcher.is_empty() {
let _gm2 = GpuMarker::new(self.device.rc_gl(), "alpha batches");
let _gl = self.gpu_profile.start_marker("alpha batches");
self.device.set_blend(false);
let mut prev_blend_mode = BlendMode::None;
self.gpu_profile.add_sampler(GPU_SAMPLER_TAG_OPAQUE);
let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE);
//Note: depth equality is needed for split planes
self.device.set_depth_func(DepthFunction::LessEqual);
@ -2878,7 +2887,8 @@ impl Renderer {
}
self.device.disable_depth_write();
self.gpu_profile.add_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
self.gpu_profile.finish_sampler(opaque_sampler);
let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
for batch in &target.alpha_batcher.batch_list.alpha_batch_list.batches {
if self.debug_flags.contains(DebugFlags::ALPHA_PRIM_DBG) {
@ -2904,7 +2914,7 @@ impl Renderer {
// 1) Use dual source blending where available (almost all recent hardware).
// 2) Use frame buffer fetch where available (most modern hardware).
// 3) Consider the old constant color blend method where no clip is applied.
let _gm = self.gpu_profile.add_marker(GPU_TAG_PRIM_TEXT_RUN);
let _timer = self.gpu_profile.start_timer(GPU_TAG_PRIM_TEXT_RUN);
self.device.set_blend(true);
@ -3078,7 +3088,7 @@ impl Renderer {
self.device.disable_depth();
self.device.set_blend(false);
self.gpu_profile.done_sampler();
self.gpu_profile.finish_sampler(transparent_sampler);
}
// For any registered image outputs on this render target,
@ -3123,10 +3133,10 @@ impl Renderer {
projection: &Transform3D<f32>,
render_tasks: &RenderTaskTree,
) {
self.gpu_profile.add_sampler(GPU_SAMPLER_TAG_ALPHA);
let alpha_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_ALPHA);
{
let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET);
let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_TARGET);
self.device
.bind_draw_target(Some(render_target), Some(target_size));
self.device.disable_depth();
@ -3157,7 +3167,7 @@ impl Renderer {
// fast path blur shaders for common
// blur radii with fixed weights.
if !target.vertical_blurs.is_empty() || !target.horizontal_blurs.is_empty() {
let _gm = self.gpu_profile.add_marker(GPU_TAG_BLUR);
let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR);
self.device.set_blend(false);
self.cs_blur_a8
@ -3185,7 +3195,7 @@ impl Renderer {
if !target.brush_mask_corners.is_empty() {
self.device.set_blend(false);
let _gm = self.gpu_profile.add_marker(GPU_TAG_BRUSH_MASK);
let _timer = self.gpu_profile.start_timer(GPU_TAG_BRUSH_MASK);
self.brush_mask_corner
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
self.draw_instanced_batch(
@ -3198,7 +3208,7 @@ impl Renderer {
if !target.brush_mask_rounded_rects.is_empty() {
self.device.set_blend(false);
let _gm = self.gpu_profile.add_marker(GPU_TAG_BRUSH_MASK);
let _timer = self.gpu_profile.start_timer(GPU_TAG_BRUSH_MASK);
self.brush_mask_rounded_rect
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
self.draw_instanced_batch(
@ -3210,13 +3220,13 @@ impl Renderer {
// Draw the clip items into the tiled alpha mask.
{
let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_CLIP);
let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_CLIP);
// If we have border corner clips, the first step is to clear out the
// area in the clip mask. This allows drawing multiple invididual clip
// in regions below.
if !target.clip_batcher.border_clears.is_empty() {
let _gm2 = GpuMarker::new(self.device.rc_gl(), "clip borders [clear]");
let _gm = self.gpu_profile.start_marker("clip borders [clear]");
self.device.set_blend(false);
self.cs_clip_border
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
@ -3229,7 +3239,7 @@ impl Renderer {
// Draw any dots or dashes for border corners.
if !target.clip_batcher.borders.is_empty() {
let _gm2 = GpuMarker::new(self.device.rc_gl(), "clip borders");
let _gm = self.gpu_profile.start_marker("clip borders");
// We are masking in parts of the corner (dots or dashes) here.
// Blend mode is set to max to allow drawing multiple dots.
// The individual dots and dashes in a border never overlap, so using
@ -3251,7 +3261,7 @@ impl Renderer {
// draw rounded cornered rectangles
if !target.clip_batcher.rectangles.is_empty() {
let _gm2 = GpuMarker::new(self.device.rc_gl(), "clip rectangles");
let _gm = self.gpu_profile.start_marker("clip rectangles");
self.cs_clip_rectangle.bind(
&mut self.device,
projection,
@ -3266,7 +3276,7 @@ impl Renderer {
}
// draw image masks
for (mask_texture_id, items) in target.clip_batcher.images.iter() {
let _gm2 = GpuMarker::new(self.device.rc_gl(), "clip images");
let _gm = self.gpu_profile.start_marker("clip images");
let textures = BatchTextures {
colors: [
mask_texture_id.clone(),
@ -3280,7 +3290,7 @@ impl Renderer {
}
}
self.gpu_profile.done_sampler();
self.gpu_profile.finish_sampler(alpha_sampler);
}
fn update_deferred_resolves(&mut self, frame: &mut Frame) {
@ -3294,7 +3304,7 @@ impl Renderer {
.expect("Found external image, but no handler set!");
for deferred_resolve in &frame.deferred_resolves {
GpuMarker::fire(self.device.gl(), "deferred resolve");
self.gpu_profile.place_marker("deferred resolve");
let props = &deferred_resolve.image_properties;
let ext_image = props
.external_image
@ -3365,7 +3375,7 @@ impl Renderer {
}
fn start_frame(&mut self, frame: &mut Frame) {
let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_DATA);
let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_DATA);
// Assign render targets to the passes.
for pass in &mut frame.passes {
@ -3447,7 +3457,7 @@ impl Renderer {
framebuffer_size: DeviceUintSize,
frame_id: FrameId,
) {
let _gm = GpuMarker::new(self.device.rc_gl(), "tile frame draw");
let _gm = self.gpu_profile.start_marker("tile frame draw");
// Some tests use a restricted viewport smaller than the main screen size.
// Ensure we clear the framebuffer in these tests.
@ -3464,7 +3474,10 @@ impl Renderer {
.clear_target(Some(self.clear_color.to_array()), Some(1.0));
} else {
self.start_frame(frame);
let pass_count = frame.passes.len();
let base_color_target_count = self.color_render_targets.len();
let base_alpha_target_count = self.alpha_render_targets.len();
for (pass_index, pass) in frame.passes.iter_mut().enumerate() {
self.texture_resolver.bind(
@ -3549,8 +3562,7 @@ impl Renderer {
}
self.texture_resolver.end_pass(
pass_index,
pass_count,
pass_index == pass_count - 1,
pass.alpha_texture.take(),
pass.color_texture.take(),
&mut self.alpha_render_targets,
@ -3569,8 +3581,8 @@ impl Renderer {
}
}
self.color_render_targets.reverse();
self.alpha_render_targets.reverse();
self.color_render_targets[base_color_target_count..].reverse();
self.alpha_render_targets[base_alpha_target_count..].reverse();
self.draw_render_target_debug(framebuffer_size);
self.draw_texture_cache_debug(framebuffer_size);

Просмотреть файл

@ -42,36 +42,39 @@ impl SceneProperties {
/// Get the current value for a transform property.
pub fn resolve_layout_transform(
&self,
property: Option<&PropertyBinding<LayoutTransform>>,
property: &PropertyBinding<LayoutTransform>,
) -> LayoutTransform {
let property = match property {
Some(property) => property,
None => return LayoutTransform::identity(),
};
match *property {
PropertyBinding::Value(matrix) => matrix,
PropertyBinding::Binding(ref key) => self.transform_properties
PropertyBinding::Value(value) => value,
PropertyBinding::Binding(ref key) => {
self.transform_properties
.get(&key.id)
.cloned()
.unwrap_or_else(|| {
warn!("Property binding {:?} has an invalid value.", key);
LayoutTransform::identity()
}),
})
}
}
}
/// Get the current value for a float property.
pub fn resolve_float(&self, property: &PropertyBinding<f32>, default_value: f32) -> f32 {
pub fn resolve_float(
&self,
property: &PropertyBinding<f32>,
default_value: f32
) -> f32 {
match *property {
PropertyBinding::Value(value) => value,
PropertyBinding::Binding(ref key) => self.float_properties
PropertyBinding::Binding(ref key) => {
self.float_properties
.get(&key.id)
.cloned()
.unwrap_or_else(|| {
warn!("Property binding {:?} has an invalid value.", key);
default_value
}),
})
}
}
}
}
@ -141,19 +144,28 @@ impl Scene {
}
}
/// An arbitrary number which we assume opacity is invisible below.
pub const OPACITY_EPSILON: f32 = 0.001;
pub trait FilterOpHelpers {
fn resolve(self, properties: &SceneProperties) -> FilterOp;
fn is_visible(&self) -> bool;
fn is_noop(&self) -> bool;
}
impl FilterOpHelpers for FilterOp {
fn resolve(self, properties: &SceneProperties) -> FilterOp {
match self {
FilterOp::Opacity(ref value) => {
let amount = properties.resolve_float(value, 1.0);
FilterOp::Opacity(PropertyBinding::Value(amount))
fn is_visible(&self) -> bool {
match *self {
FilterOp::Blur(..) |
FilterOp::Brightness(..) |
FilterOp::Contrast(..) |
FilterOp::Grayscale(..) |
FilterOp::HueRotate(..) |
FilterOp::Invert(..) |
FilterOp::Saturate(..) |
FilterOp::Sepia(..) => true,
FilterOp::Opacity(_, amount) => {
amount > OPACITY_EPSILON
}
_ => self,
}
}
@ -165,12 +177,7 @@ impl FilterOpHelpers for FilterOp {
FilterOp::Grayscale(amount) => amount == 0.0,
FilterOp::HueRotate(amount) => amount == 0.0,
FilterOp::Invert(amount) => amount == 0.0,
FilterOp::Opacity(value) => match value {
PropertyBinding::Value(amount) => amount == 1.0,
PropertyBinding::Binding(..) => {
panic!("bug: binding value should be resolved");
}
},
FilterOp::Opacity(_, amount) => amount >= 1.0,
FilterOp::Saturate(amount) => amount == 1.0,
FilterOp::Sepia(amount) => amount == 0.0,
}
@ -183,7 +190,6 @@ pub trait StackingContextHelpers {
&self,
display_list: &BuiltDisplayList,
input_filters: ItemRange<FilterOp>,
properties: &SceneProperties,
) -> Vec<FilterOp>;
}
@ -199,14 +205,12 @@ impl StackingContextHelpers for StackingContext {
&self,
display_list: &BuiltDisplayList,
input_filters: ItemRange<FilterOp>,
properties: &SceneProperties,
) -> Vec<FilterOp> {
// TODO(gw): Now that we resolve these later on,
// we could probably make it a bit
// more efficient than cloning these here.
let mut filters = vec![];
for filter in display_list.get(input_filters) {
let filter = filter.resolve(properties);
if filter.is_noop() {
continue;
}
filters.push(filter);
}
filters

Просмотреть файл

@ -5,12 +5,14 @@
use api::{BorderRadiusKind, ClipId, ColorF, DeviceIntPoint, ImageKey};
use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize};
use api::{ExternalImageType, FilterOp, FontRenderMode, ImageRendering, LayerRect};
use api::{MixBlendMode, PipelineId, PropertyBinding, TransformStyle};
use api::{LayerVector2D, TileOffset, YuvColorSpace, YuvFormat};
use api::{MixBlendMode, PipelineId};
use api::{TileOffset, YuvColorSpace, YuvFormat};
use api::{LayerToWorldTransform, WorldPixel};
use border::{BorderCornerInstance, BorderCornerSide};
use clip::{ClipSource, ClipStore};
use clip_scroll_tree::CoordinateSystemId;
use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId};
use device::Texture;
use euclid::{TypedTransform3D, vec3};
use glyph_rasterizer::GlyphFormat;
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuCacheUpdateList};
use gpu_types::{BlurDirection, BlurInstance, BrushInstance, BrushImageKind, ClipMaskInstance};
@ -18,11 +20,12 @@ use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveIn
use gpu_types::{BRUSH_FLAG_USES_PICTURE, ClipScrollNodeIndex, ClipScrollNodeData};
use internal_types::{FastHashMap, SourceTexture};
use internal_types::BatchTextures;
use picture::PictureKind;
use picture::{PictureCompositeMode, PictureKind, PicturePrimitive};
use plane_split::{BspSplitter, Polygon, Splitter};
use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
use prim_store::{BrushMaskKind, BrushKind, DeferredResolve, PrimitiveRun, RectangleContent};
use profiler::FrameProfileCounters;
use render_task::{AlphaRenderItem, ClipWorkItem, MaskGeometryKind, MaskSegment};
use render_task::{ClipWorkItem, MaskGeometryKind, MaskSegment};
use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKey, RenderTaskKind};
use render_task::{BlurTask, ClearMode, RenderTaskLocation, RenderTaskTree};
use renderer::BlendMode;
@ -37,6 +40,52 @@ use util::{MatrixHelpers, TransformedRectKind};
const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(i32::MAX as u32);
const MIN_TARGET_SIZE: u32 = 2048;
// Helper to add an entire primitive run to a batch list.
// TODO(gw): Restructure this so the param list isn't quite
// so daunting!
impl PrimitiveRun {
fn add_to_batch(
&self,
clip_id: ClipScrollNodeIndex,
scroll_id: ClipScrollNodeIndex,
batch_list: &mut BatchList,
ctx: &RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &RenderTaskTree,
task_id: RenderTaskId,
task_address: RenderTaskAddress,
deferred_resolves: &mut Vec<DeferredResolve>,
glyph_fetch_buffer: &mut Vec<GlyphFetchResult>,
splitter: &mut BspSplitter<f64, WorldPixel>,
) {
for i in 0 .. self.count {
let prim_index = PrimitiveIndex(self.base_prim_index.0 + i);
let md = &ctx.prim_store.cpu_metadata[prim_index.0];
// Now that we walk the primitive runs in order to add
// items to batches, we need to check if they are
// visible here.
if md.screen_rect.is_some() {
add_to_batch(
clip_id,
scroll_id,
prim_index,
batch_list,
ctx,
gpu_cache,
render_tasks,
task_id,
task_address,
deferred_resolves,
glyph_fetch_buffer,
splitter,
);
}
}
}
}
trait AlphaBatchHelpers {
fn get_blend_mode(
&self,
@ -111,13 +160,6 @@ pub struct ScrollbarPrimitive {
pub frame_rect: LayerRect,
}
#[derive(Debug)]
pub enum PrimitiveRunCmd {
PushStackingContext(StackingContextIndex),
PopStackingContext,
PrimitiveRun(PrimitiveRun),
}
#[derive(Debug, Copy, Clone)]
pub struct RenderTargetIndex(pub usize);
@ -208,25 +250,45 @@ impl AlphaBatchList {
}
pub struct OpaqueBatchList {
pub pixel_area_threshold_for_new_batch: i32,
pub batches: Vec<OpaquePrimitiveBatch>,
}
impl OpaqueBatchList {
fn new() -> OpaqueBatchList {
fn new(pixel_area_threshold_for_new_batch: i32) -> OpaqueBatchList {
OpaqueBatchList {
batches: Vec::new(),
pixel_area_threshold_for_new_batch,
}
}
fn get_suitable_batch(&mut self, key: BatchKey) -> &mut Vec<PrimitiveInstance> {
fn get_suitable_batch(
&mut self,
key: BatchKey,
item_bounding_rect: &DeviceIntRect
) -> &mut Vec<PrimitiveInstance> {
let mut selected_batch_index = None;
let item_area = item_bounding_rect.size.area();
// If the area of this primitive is larger than the given threshold,
// then it is large enough to warrant breaking a batch for. In this
// case we just see if it can be added to the existing batch or
// create a new one.
if item_area > self.pixel_area_threshold_for_new_batch {
if let Some(ref batch) = self.batches.last() {
if batch.key.is_compatible_with(&key) {
selected_batch_index = Some(self.batches.len() - 1);
}
}
} else {
// Otherwise, look back through a reasonable number of batches.
for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
if batch.key.is_compatible_with(&key) {
selected_batch_index = Some(batch_index);
break;
}
}
}
if selected_batch_index.is_none() {
let new_batch = OpaquePrimitiveBatch::new(key);
@ -258,10 +320,14 @@ pub struct BatchList {
}
impl BatchList {
fn new() -> BatchList {
fn new(screen_size: DeviceIntSize) -> BatchList {
// The threshold for creating a new batch is
// one quarter the screen size.
let batch_area_threshold = screen_size.width * screen_size.height / 4;
BatchList {
alpha_batch_list: AlphaBatchList::new(),
opaque_batch_list: OpaqueBatchList::new(),
opaque_batch_list: OpaqueBatchList::new(batch_area_threshold),
}
}
@ -271,7 +337,10 @@ impl BatchList {
item_bounding_rect: &DeviceIntRect,
) -> &mut Vec<PrimitiveInstance> {
match key.blend_mode {
BlendMode::None => self.opaque_batch_list.get_suitable_batch(key),
BlendMode::None => {
self.opaque_batch_list
.get_suitable_batch(key, item_bounding_rect)
}
BlendMode::PremultipliedAlpha |
BlendMode::PremultipliedDestOut |
BlendMode::SubpixelConstantTextColor(..) |
@ -295,9 +364,14 @@ pub struct AlphaBatcher {
glyph_fetch_buffer: Vec<GlyphFetchResult>,
}
impl AlphaRenderItem {
fn add_to_batch(
&self,
// A free function that adds a primitive to a batch.
// It can recursively call itself in some situations, for
// example if it encounters a picture where the items
// in that picture are being drawn into the same target.
fn add_to_batch(
clip_id: ClipScrollNodeIndex,
scroll_id: ClipScrollNodeIndex,
prim_index: PrimitiveIndex,
batch_list: &mut BatchList,
ctx: &RenderTargetContext,
gpu_cache: &mut GpuCache,
@ -306,110 +380,9 @@ impl AlphaRenderItem {
task_address: RenderTaskAddress,
deferred_resolves: &mut Vec<DeferredResolve>,
glyph_fetch_buffer: &mut Vec<GlyphFetchResult>,
) {
match *self {
AlphaRenderItem::Blend(stacking_context_index, src_id, filter, z) => {
let stacking_context = &ctx.stacking_context_store[stacking_context_index.0];
let key = BatchKey::new(
BatchKind::Blend,
BlendMode::PremultipliedAlpha,
BatchTextures::no_texture(),
);
let src_task_address = render_tasks.get_task_address(src_id);
let (filter_mode, amount) = match filter {
FilterOp::Blur(..) => (0, 0.0),
FilterOp::Contrast(amount) => (1, amount),
FilterOp::Grayscale(amount) => (2, amount),
FilterOp::HueRotate(angle) => (3, angle),
FilterOp::Invert(amount) => (4, amount),
FilterOp::Saturate(amount) => (5, amount),
FilterOp::Sepia(amount) => (6, amount),
FilterOp::Brightness(amount) => (7, amount),
FilterOp::Opacity(PropertyBinding::Value(amount)) => (8, amount),
FilterOp::Opacity(_) => unreachable!(),
};
let amount = (amount * 65535.0).round() as i32;
let batch = batch_list.get_suitable_batch(key, &stacking_context.screen_bounds);
let instance = CompositePrimitiveInstance::new(
task_address,
src_task_address,
RenderTaskAddress(0),
filter_mode,
amount,
z,
0,
0,
);
batch.push(PrimitiveInstance::from(instance));
}
AlphaRenderItem::HardwareComposite(
stacking_context_index,
src_id,
composite_op,
screen_origin,
z,
dest_rect,
) => {
let stacking_context = &ctx.stacking_context_store[stacking_context_index.0];
let src_task_address = render_tasks.get_task_address(src_id);
let key = BatchKey::new(
BatchKind::HardwareComposite,
composite_op.to_blend_mode(),
BatchTextures::no_texture(),
);
let batch = batch_list.get_suitable_batch(key, &stacking_context.screen_bounds);
let dest_rect = if dest_rect.width > 0 && dest_rect.height > 0 {
dest_rect
} else {
render_tasks.get(src_id).get_dynamic_size()
};
let instance = CompositePrimitiveInstance::new(
task_address,
src_task_address,
RenderTaskAddress(0),
screen_origin.x,
screen_origin.y,
z,
dest_rect.width,
dest_rect.height,
);
batch.push(PrimitiveInstance::from(instance));
}
AlphaRenderItem::Composite(stacking_context_index, source_id, backdrop_id, mode, z) => {
let stacking_context = &ctx.stacking_context_store[stacking_context_index.0];
let key = BatchKey::new(
BatchKind::Composite {
task_id,
source_id,
backdrop_id,
},
BlendMode::PremultipliedAlpha,
BatchTextures::no_texture(),
);
let batch = batch_list.get_suitable_batch(key, &stacking_context.screen_bounds);
let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
let source_task_address = render_tasks.get_task_address(source_id);
let instance = CompositePrimitiveInstance::new(
task_address,
source_task_address,
backdrop_task_address,
mode as u32 as i32,
0,
z,
0,
0,
);
batch.push(PrimitiveInstance::from(instance));
}
AlphaRenderItem::Primitive(clip_id, scroll_id, prim_index, z) => {
splitter: &mut BspSplitter<f64, WorldPixel>,
) {
let z = prim_index.0 as i32;
let prim_metadata = ctx.prim_store.get_metadata(prim_index);
let scroll_node = &ctx.node_data[scroll_id.0 as usize];
// TODO(gw): Calculating this for every primitive is a bit
@ -609,29 +582,49 @@ impl AlphaRenderItem {
PrimitiveKind::Picture => {
let picture =
&ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0];
let cache_task_id = picture.render_task_id.expect("no render task!");
match picture.render_task_id {
Some(cache_task_id) => {
let cache_task_address = render_tasks.get_task_address(cache_task_id);
let textures = BatchTextures::render_target_cache();
match picture.kind {
PictureKind::TextShadow { .. } => {
let kind = BatchKind::Brush(
BrushBatchKind::Image(picture.target_kind()),
);
let key = BatchKey::new(kind, blend_mode, textures);
let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
let image_kind = match picture.kind {
PictureKind::TextShadow { .. } => {
BrushImageKind::Simple
let instance = BrushInstance {
picture_address: task_address,
prim_address: prim_cache_address,
clip_id,
scroll_id,
clip_task_address,
z,
flags: 0,
user_data0: cache_task_address.0 as i32,
user_data1: BrushImageKind::Simple as i32,
};
batch.push(PrimitiveInstance::from(instance));
}
PictureKind::BoxShadow { radii_kind, .. } => {
match radii_kind {
let kind = BatchKind::Brush(
BrushBatchKind::Image(picture.target_kind()),
);
let key = BatchKey::new(kind, blend_mode, textures);
let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
let image_kind = match radii_kind {
BorderRadiusKind::Uniform => {
BrushImageKind::Mirror
}
BorderRadiusKind::NonUniform => {
BrushImageKind::NinePatch
}
}
}
};
let instance = BrushInstance {
picture_address: task_address,
prim_address: prim_cache_address,
@ -645,6 +638,172 @@ impl AlphaRenderItem {
};
batch.push(PrimitiveInstance::from(instance));
}
PictureKind::Image {
composite_mode,
readback_render_task_id,
is_in_3d_context,
reference_frame_id,
real_local_rect,
..
} => {
// If this picture is participating in a 3D rendering context,
// then don't add it to any batches here. Instead, create a polygon
// for it and add it to the current plane splitter.
if is_in_3d_context {
// Push into parent plane splitter.
let real_xf = &ctx.clip_scroll_tree.nodes[&reference_frame_id].world_content_transform;
let polygon = make_polygon(
real_local_rect,
&real_xf,
prim_index.0,
);
splitter.add(polygon);
return;
}
// Depending on the composite mode of the picture, we generate the
// old style Composite primitive instances. In the future, we'll
// remove these and pass them through the brush batching pipeline.
// This will allow us to unify some of the shaders, apply clip masks
// when compositing pictures, and also correctly apply pixel snapping
// to picture compositing operations.
let source_id = picture.render_task_id.expect("no source!?");
match composite_mode.expect("bug: only composites here") {
PictureCompositeMode::Filter(filter) => {
match filter {
FilterOp::Blur(..) => {
let src_task_address = render_tasks.get_task_address(source_id);
let key = BatchKey::new(
BatchKind::HardwareComposite,
BlendMode::PremultipliedAlpha,
BatchTextures::no_texture(),
);
let batch = batch_list.get_suitable_batch(key, &item_bounding_rect);
let instance = CompositePrimitiveInstance::new(
task_address,
src_task_address,
RenderTaskAddress(0),
item_bounding_rect.origin.x,
item_bounding_rect.origin.y,
z,
item_bounding_rect.size.width,
item_bounding_rect.size.height,
);
batch.push(PrimitiveInstance::from(instance));
}
_ => {
let key = BatchKey::new(
BatchKind::Blend,
BlendMode::PremultipliedAlpha,
BatchTextures::no_texture(),
);
let src_task_address = render_tasks.get_task_address(source_id);
let (filter_mode, amount) = match filter {
FilterOp::Blur(..) => (0, 0.0),
FilterOp::Contrast(amount) => (1, amount),
FilterOp::Grayscale(amount) => (2, amount),
FilterOp::HueRotate(angle) => (3, angle),
FilterOp::Invert(amount) => (4, amount),
FilterOp::Saturate(amount) => (5, amount),
FilterOp::Sepia(amount) => (6, amount),
FilterOp::Brightness(amount) => (7, amount),
FilterOp::Opacity(_, amount) => (8, amount),
};
let amount = (amount * 65535.0).round() as i32;
let batch = batch_list.get_suitable_batch(key, &item_bounding_rect);
let instance = CompositePrimitiveInstance::new(
task_address,
src_task_address,
RenderTaskAddress(0),
filter_mode,
amount,
z,
0,
0,
);
batch.push(PrimitiveInstance::from(instance));
}
}
}
PictureCompositeMode::MixBlend(mode) => {
let backdrop_id = readback_render_task_id.expect("no backdrop!?");
let key = BatchKey::new(
BatchKind::Composite {
task_id,
source_id,
backdrop_id,
},
BlendMode::PremultipliedAlpha,
BatchTextures::no_texture(),
);
let batch = batch_list.get_suitable_batch(key, &item_bounding_rect);
let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
let source_task_address = render_tasks.get_task_address(source_id);
let instance = CompositePrimitiveInstance::new(
task_address,
source_task_address,
backdrop_task_address,
mode as u32 as i32,
0,
z,
0,
0,
);
batch.push(PrimitiveInstance::from(instance));
}
PictureCompositeMode::Blit => {
let src_task_address = render_tasks.get_task_address(source_id);
let key = BatchKey::new(
BatchKind::HardwareComposite,
BlendMode::PremultipliedAlpha,
BatchTextures::no_texture(),
);
let batch = batch_list.get_suitable_batch(key, &item_bounding_rect);
let instance = CompositePrimitiveInstance::new(
task_address,
src_task_address,
RenderTaskAddress(0),
item_bounding_rect.origin.x,
item_bounding_rect.origin.y,
z,
item_bounding_rect.size.width,
item_bounding_rect.size.height,
);
batch.push(PrimitiveInstance::from(instance));
}
}
}
}
}
None => {
// If this picture is being drawn into an existing target (i.e. with
// no composition operation), recurse and add to the current batch list.
picture.add_to_batch(
task_id,
ctx,
gpu_cache,
render_tasks,
deferred_resolves,
batch_list,
glyph_fetch_buffer,
);
}
}
}
PrimitiveKind::AlignedGradient => {
let gradient_cpu =
&ctx.prim_store.cpu_gradients[prim_metadata.cpu_prim_index.0];
@ -759,16 +918,69 @@ impl AlphaRenderItem {
));
}
}
}
impl PicturePrimitive {
fn add_to_batch(
&self,
task_id: RenderTaskId,
ctx: &RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
batch_list: &mut BatchList,
glyph_fetch_buffer: &mut Vec<GlyphFetchResult>,
) {
let task_address = render_tasks.get_task_address(task_id);
// Even though most of the time a splitter isn't used or needed,
// they are cheap to construct so we will always pass one down.
let mut splitter = BspSplitter::new();
// Add each run in this picture to the batch.
for run in &self.runs {
let clip_node = &ctx.clip_scroll_tree.nodes[&run.clip_and_scroll.clip_node_id()];
let clip_id = clip_node.node_data_index;
let scroll_node = &ctx.clip_scroll_tree.nodes[&run.clip_and_scroll.scroll_node_id];
let scroll_id = scroll_node.node_data_index;
run.add_to_batch(
clip_id,
scroll_id,
batch_list,
ctx,
gpu_cache,
render_tasks,
task_id,
task_address,
deferred_resolves,
glyph_fetch_buffer,
&mut splitter,
);
}
AlphaRenderItem::SplitComposite(sc_index, task_id, gpu_handle, z) => {
// Flush the accumulated plane splits onto the task tree.
// Z axis is directed at the screen, `sort` is ascending, and we need back-to-front order.
for poly in splitter.sort(vec3(0.0, 0.0, 1.0)) {
let prim_index = PrimitiveIndex(poly.anchor);
debug!("process sorted poly {:?} {:?}", prim_index, poly.points);
let pp = &poly.points;
let gpu_blocks = [
[pp[0].x as f32, pp[0].y as f32, pp[0].z as f32, pp[1].x as f32].into(),
[pp[1].y as f32, pp[1].z as f32, pp[2].x as f32, pp[2].y as f32].into(),
[pp[2].z as f32, pp[3].x as f32, pp[3].y as f32, pp[3].z as f32].into(),
];
let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
let key = BatchKey::new(
BatchKind::SplitComposite,
BlendMode::PremultipliedAlpha,
BatchTextures::no_texture(),
);
let stacking_context = &ctx.stacking_context_store[sc_index.0];
let batch = batch_list.get_suitable_batch(key, &stacking_context.screen_bounds);
let source_task_address = render_tasks.get_task_address(task_id);
let pic_metadata = &ctx.prim_store.cpu_metadata[prim_index.0];
let pic = &ctx.prim_store.cpu_pictures[pic_metadata.cpu_prim_index.0];
let batch = batch_list.get_suitable_batch(key, pic_metadata.screen_rect.as_ref().expect("bug"));
let source_task_address = render_tasks.get_task_address(pic.render_task_id.expect("bug"));
let gpu_address = gpu_handle.as_int(gpu_cache);
let instance = CompositePrimitiveInstance::new(
@ -777,7 +989,7 @@ impl AlphaRenderItem {
RenderTaskAddress(0),
gpu_address,
0,
z,
prim_index.0 as i32,
0,
0,
);
@ -785,14 +997,13 @@ impl AlphaRenderItem {
batch.push(PrimitiveInstance::from(instance));
}
}
}
}
impl AlphaBatcher {
fn new() -> AlphaBatcher {
fn new(screen_size: DeviceIntSize) -> AlphaBatcher {
AlphaBatcher {
tasks: Vec::new(),
batch_list: BatchList::new(),
batch_list: BatchList::new(screen_size),
glyph_fetch_buffer: Vec::new(),
}
}
@ -811,21 +1022,17 @@ impl AlphaBatcher {
for task_id in &self.tasks {
let task_id = *task_id;
let task = render_tasks.get(task_id).as_alpha_batch();
let task_address = render_tasks.get_task_address(task_id);
for item in &task.items {
item.add_to_batch(
&mut self.batch_list,
let pic = &ctx.prim_store.cpu_pictures[ctx.prim_store.cpu_metadata[task.prim_index.0].cpu_prim_index.0];
pic.add_to_batch(
task_id,
ctx,
gpu_cache,
render_tasks,
task_id,
task_address,
deferred_resolves,
&mut self.glyph_fetch_buffer,
&mut self.batch_list,
&mut self.glyph_fetch_buffer
);
}
}
self.batch_list.finalize();
}
@ -962,10 +1169,10 @@ impl ClipBatcher {
pub struct RenderTargetContext<'a> {
pub device_pixel_ratio: f32,
pub stacking_context_store: &'a [StackingContext],
pub prim_store: &'a PrimitiveStore,
pub resource_cache: &'a ResourceCache,
pub node_data: &'a [ClipScrollNodeData],
pub clip_scroll_tree: &'a ClipScrollTree,
}
struct TextureAllocator {
@ -1007,7 +1214,10 @@ impl TextureAllocator {
}
pub trait RenderTarget {
fn new(size: Option<DeviceUintSize>) -> Self;
fn new(
size: Option<DeviceUintSize>,
screen_size: DeviceIntSize,
) -> Self;
fn allocate(&mut self, size: DeviceUintSize) -> Option<DeviceUintPoint>;
fn build(
&mut self,
@ -1035,17 +1245,24 @@ pub enum RenderTargetKind {
}
pub struct RenderTargetList<T> {
screen_size: DeviceIntSize,
pub targets: Vec<T>,
}
impl<T: RenderTarget> RenderTargetList<T> {
fn new(create_initial_target: bool) -> RenderTargetList<T> {
fn new(
create_initial_target: bool,
screen_size: DeviceIntSize
) -> RenderTargetList<T> {
let mut targets = Vec::new();
if create_initial_target {
targets.push(T::new(None));
targets.push(T::new(None, screen_size));
}
RenderTargetList { targets }
RenderTargetList {
targets,
screen_size,
}
}
pub fn target_count(&self) -> usize {
@ -1093,7 +1310,7 @@ impl<T: RenderTarget> RenderTargetList<T> {
let origin = match existing_origin {
Some(origin) => origin,
None => {
let mut new_target = T::new(Some(target_size));
let mut new_target = T::new(Some(target_size), self.screen_size);
let origin = new_target.allocate(alloc_size).expect(&format!(
"Each render task must allocate <= size of one target! ({:?})",
alloc_size
@ -1146,9 +1363,12 @@ impl RenderTarget for ColorRenderTarget {
.allocate(&size)
}
fn new(size: Option<DeviceUintSize>) -> ColorRenderTarget {
fn new(
size: Option<DeviceUintSize>,
screen_size: DeviceIntSize,
) -> Self {
ColorRenderTarget {
alpha_batcher: AlphaBatcher::new(),
alpha_batcher: AlphaBatcher::new(screen_size),
text_run_cache_prims: FastHashMap::default(),
line_cache_prims: Vec::new(),
vertical_blurs: Vec::new(),
@ -1231,7 +1451,7 @@ impl RenderTarget for ColorRenderTarget {
let task_index = render_tasks.get_task_address(task_id);
for run in &prim.prim_runs {
for run in &prim.runs {
for i in 0 .. run.count {
let sub_prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
@ -1328,7 +1548,10 @@ impl RenderTarget for AlphaRenderTarget {
self.allocator.allocate(&size)
}
fn new(size: Option<DeviceUintSize>) -> AlphaRenderTarget {
fn new(
size: Option<DeviceUintSize>,
_: DeviceIntSize,
) -> Self {
AlphaRenderTarget {
clip_batcher: ClipBatcher::new(),
brush_mask_corners: Vec::new(),
@ -1400,7 +1623,7 @@ impl RenderTarget for AlphaRenderTarget {
let task_index = render_tasks.get_task_address(task_id);
for run in &prim.prim_runs {
for run in &prim.runs {
for i in 0 .. run.count {
let sub_prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
@ -1490,11 +1713,14 @@ pub struct RenderPass {
}
impl RenderPass {
pub fn new(is_framebuffer: bool) -> RenderPass {
pub fn new(
is_framebuffer: bool,
screen_size: DeviceIntSize
) -> RenderPass {
RenderPass {
is_framebuffer,
color_targets: RenderTargetList::new(is_framebuffer),
alpha_targets: RenderTargetList::new(false),
color_targets: RenderTargetList::new(is_framebuffer, screen_size),
alpha_targets: RenderTargetList::new(false, screen_size),
tasks: vec![],
color_texture: None,
alpha_texture: None,
@ -1730,111 +1956,6 @@ impl OpaquePrimitiveBatch {
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct StackingContextIndex(pub usize);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum ContextIsolation {
/// No isolation - the content is mixed up with everything else.
None,
/// Items are isolated and drawn into a separate render target.
/// Child contexts are exposed.
Items,
/// All the content inside is isolated and drawn into a separate target.
Full,
}
#[derive(Debug)]
pub struct StackingContext {
pub pipeline_id: PipelineId,
/// Offset in the parent reference frame to the origin of this stacking
/// context's coordinate system.
pub reference_frame_offset: LayerVector2D,
/// The `ClipId` of the owning reference frame.
pub reference_frame_id: ClipId,
/// Screen space bounding rectangle for this stacking context,
/// calculated based on the size and position of all its children.
pub screen_bounds: DeviceIntRect,
/// Local bounding rectangle of this stacking context,
/// computed as the union of all contained items that are not
/// `ContextIsolation::Items` on their own
pub isolated_items_bounds: LayerRect,
pub composite_ops: CompositeOps,
/// Type of the isolation of the content.
pub isolation: ContextIsolation,
/// Set for the root stacking context of a display list or an iframe. Used for determining
/// when to isolate a mix-blend-mode composite.
pub is_page_root: bool,
/// Set to true if this is the root stacking context for a pipeline.
pub is_pipeline_root: bool,
/// Whether or not this stacking context has any visible components, calculated
/// based on the size and position of all children and how they are clipped.
pub is_visible: bool,
/// Current stacking context visibility of backface.
pub is_backface_visible: bool,
/// Allow subpixel AA for text runs on this stacking context.
/// This is a temporary hack while we don't support subpixel AA
/// on transparent stacking contexts.
pub allow_subpixel_aa: bool,
/// Indicate that if any pritimive contained in this stacking context.
pub has_any_primitive: bool,
/// Union of all stacking context bounds of all children.
pub children_sc_bounds: LayerRect,
}
impl StackingContext {
pub fn new(
pipeline_id: PipelineId,
reference_frame_offset: LayerVector2D,
is_page_root: bool,
is_pipeline_root: bool,
reference_frame_id: ClipId,
transform_style: TransformStyle,
composite_ops: CompositeOps,
is_backface_visible: bool,
) -> StackingContext {
let isolation = match transform_style {
TransformStyle::Flat => ContextIsolation::None,
TransformStyle::Preserve3D => ContextIsolation::Items,
};
let allow_subpixel_aa = composite_ops.count() == 0 &&
isolation == ContextIsolation::None;
StackingContext {
pipeline_id,
reference_frame_offset,
reference_frame_id,
screen_bounds: DeviceIntRect::zero(),
isolated_items_bounds: LayerRect::zero(),
composite_ops,
isolation,
is_page_root,
is_pipeline_root,
is_visible: false,
is_backface_visible,
allow_subpixel_aa,
has_any_primitive: false,
children_sc_bounds: LayerRect::zero(),
}
}
pub fn can_contribute_to_scene(&self) -> bool {
!self.composite_ops.will_make_invisible()
}
}
#[derive(Debug, Clone, Default)]
pub struct CompositeOps {
// Requires only a single texture as input (e.g. most filters)
@ -1855,15 +1976,6 @@ impl CompositeOps {
pub fn count(&self) -> usize {
self.filters.len() + if self.mix_blend_mode.is_some() { 1 } else { 0 }
}
pub fn will_make_invisible(&self) -> bool {
for op in &self.filters {
if op == &FilterOp::Opacity(PropertyBinding::Value(0.0)) {
return true;
}
}
false
}
}
/// A rendering-oriented representation of frame::Frame built by the render backend
@ -1956,3 +2068,31 @@ impl BlurTask {
}
}
}
/// Construct a polygon from stacking context boundaries.
/// `anchor` here is an index that's going to be preserved in all the
/// splits of the polygon.
fn make_polygon(
rect: LayerRect,
transform: &LayerToWorldTransform,
anchor: usize,
) -> Polygon<f64, WorldPixel> {
let mat = TypedTransform3D::row_major(
transform.m11 as f64,
transform.m12 as f64,
transform.m13 as f64,
transform.m14 as f64,
transform.m21 as f64,
transform.m22 as f64,
transform.m23 as f64,
transform.m24 as f64,
transform.m31 as f64,
transform.m32 as f64,
transform.m33 as f64,
transform.m34 as f64,
transform.m41 as f64,
transform.m42 as f64,
transform.m43 as f64,
transform.m44 as f64);
Polygon::from_transformed_rect(rect.cast().unwrap(), mat, anchor)
}

Просмотреть файл

@ -243,19 +243,23 @@ impl fmt::Debug for DocumentMsg {
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum DebugCommand {
// Display the frame profiler on screen.
/// Display the frame profiler on screen.
EnableProfiler(bool),
// Display all texture cache pages on screen.
/// Display all texture cache pages on screen.
EnableTextureCacheDebug(bool),
// Display intermediate render targets on screen.
/// Display intermediate render targets on screen.
EnableRenderTargetDebug(bool),
// Display alpha primitive rects.
/// Display alpha primitive rects.
EnableAlphaRectsDebug(bool),
// Fetch current documents and display lists.
/// Display GPU timing results.
EnableGpuTimeQueries(bool),
/// Display GPU overdraw results
EnableGpuSampleQueries(bool),
/// Fetch current documents and display lists.
FetchDocuments,
// Fetch current passes and batches.
/// Fetch current passes and batches.
FetchPasses,
// Fetch clip-scroll tree.
/// Fetch clip-scroll tree.
FetchClipScrollTree,
}

Просмотреть файл

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

Просмотреть файл

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Generated with cbindgen:0.2.0 */
/* Generated with cbindgen:0.2.2 */
/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
* To generate this file: