diff --git a/webrender/res/brush_blend.glsl b/webrender/res/brush_blend.glsl index 9f925a067b..a8dd5854b2 100644 --- a/webrender/res/brush_blend.glsl +++ b/webrender/res/brush_blend.glsl @@ -27,18 +27,20 @@ void brush_vs( int brush_flags, vec4 unused ) { - PictureTask src_task = fetch_picture_task(user_data.x); + ImageResource res = fetch_image_resource(user_data.x); + vec2 uv0 = res.uv_rect.p0; + vec2 uv1 = res.uv_rect.p1; + + // PictureTask src_task = fetch_picture_task(user_data.x); vec2 texture_size = vec2(textureSize(sColor0, 0).xy); - vec2 uv = snap_device_pos(vi) + - src_task.common_data.task_rect.p0 - - src_task.content_origin; - vUv = vec3( - uv * gl_Position.w / texture_size, // multiply by W to compensate for perspective interpolation - src_task.common_data.texture_layer_index - ); - - vec2 uv0 = src_task.common_data.task_rect.p0; - vec2 uv1 = uv0 + src_task.common_data.task_rect.size; + vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size; + ImageResourceExtra extra_data = fetch_image_resource_extra(user_data.x); + vec2 x = mix(extra_data.st_tl, extra_data.st_tr, f.x); + vec2 y = mix(extra_data.st_bl, extra_data.st_br, f.x); + f = mix(x, y, f.y); + vec2 uv = mix(uv0, uv1, f); + vUv = vec3(uv / texture_size, res.layer); + vUvClipBounds = vec4(uv0, uv1) / texture_size.xyxy; float lumR = 0.2126; @@ -127,8 +129,7 @@ vec3 Brightness(vec3 Cs, float amount) { } Fragment brush_fs() { - vec2 base_uv = vUv.xy * gl_FragCoord.w; - vec4 Cs = texture(sColor0, vec3(base_uv, vUv.z)); + vec4 Cs = texture(sColor0, vUv); if (Cs.a == 0.0) { return Fragment(vec4(0.0)); // could also `discard` @@ -159,7 +160,7 @@ Fragment brush_fs() { // Fail-safe to ensure that we don't sample outside the rendered // portion of a blend source. - alpha *= point_inside_rect(base_uv, vUvClipBounds.xy, vUvClipBounds.zw); + alpha *= point_inside_rect(vUv.xy, vUvClipBounds.xy, vUvClipBounds.zw); // Pre-multiply the alpha into the output value. return Fragment(alpha * vec4(color, 1.0)); diff --git a/webrender/res/brush_image.glsl b/webrender/res/brush_image.glsl index 1ebc5a1da8..1ad36a238c 100644 --- a/webrender/res/brush_image.glsl +++ b/webrender/res/brush_image.glsl @@ -11,8 +11,7 @@ varying vec2 vLocalPos; #endif // Interpolated uv coordinates in xy, and layer in z. -// W is 1 when perspective interpolation is enabled. -varying vec4 vUv; +varying vec3 vUv; // Normalized bounds of the source image in the texture. flat varying vec4 vUvBounds; // Normalized bounds of the source image in the texture, adjusted to avoid @@ -107,7 +106,6 @@ void brush_vs( } vUv.z = res.layer; - vUv.w = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0; // Handle case where the UV coords are inverted (e.g. from an // external image). @@ -153,10 +151,6 @@ void brush_vs( vUv.xy = mix(uv0, uv1, f) - min_uv; vUv.xy /= texture_size; vUv.xy *= repeat.xy; - if ((brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) == 0) { - // Multiply by W to compensate for perspective interpolation. - vUv.xy *= gl_Position.w; - } #ifdef WR_FEATURE_TEXTURE_RECT vUvBounds = vec4(0.0, 0.0, vec2(textureSize(sColor0))); @@ -203,14 +197,12 @@ void brush_vs( Fragment brush_fs() { vec2 uv_size = vUvBounds.zw - vUvBounds.xy; - // Unapply the W scaler when no perspective interpolation is enabled. - vec2 base_uv = vUv.xy * mix(gl_FragCoord.w, 1.0, vUv.w); #ifdef WR_FEATURE_ALPHA_PASS // This prevents the uv on the top and left parts of the primitive that was inflated // for anti-aliasing purposes from going beyound the range covered by the regular // (non-inflated) primitive. - vec2 local_uv = max(base_uv, vec2(0.0)); + vec2 local_uv = max(vUv.xy, vec2(0.0)); // Handle horizontal and vertical repetitions. vec2 repeated_uv = mod(local_uv, uv_size) + vUvBounds.xy; @@ -226,7 +218,7 @@ Fragment brush_fs() { } #else // Handle horizontal and vertical repetitions. - vec2 repeated_uv = mod(base_uv, uv_size) + vUvBounds.xy; + vec2 repeated_uv = mod(vUv.xy, uv_size) + vUvBounds.xy; #endif // Clamp the uvs to avoid sampling artifacts. diff --git a/webrender/res/clip_shared.glsl b/webrender/res/clip_shared.glsl index debe5bc4cc..5580b34d93 100644 --- a/webrender/res/clip_shared.glsl +++ b/webrender/res/clip_shared.glsl @@ -13,13 +13,15 @@ #define SEGMENT_CORNER_BR 4 in int aClipRenderTaskAddress; -in int aScrollNodeId; +in int aClipTransformId; +in int aPrimTransformId; in int aClipSegment; in ivec4 aClipDataResourceAddress; struct ClipMaskInstance { int render_task_address; - int transform_id; + int clip_transform_id; + int prim_transform_id; int segment; ivec2 clip_data_address; ivec2 resource_address; @@ -29,7 +31,8 @@ ClipMaskInstance fetch_clip_item() { ClipMaskInstance cmi; cmi.render_task_address = aClipRenderTaskAddress; - cmi.transform_id = aScrollNodeId; + cmi.clip_transform_id = aClipTransformId; + cmi.prim_transform_id = aPrimTransformId; cmi.segment = aClipSegment; cmi.clip_data_address = aClipDataResourceAddress.xy; cmi.resource_address = aClipDataResourceAddress.zw; @@ -39,7 +42,6 @@ ClipMaskInstance fetch_clip_item() { struct ClipVertexInfo { vec3 local_pos; - vec2 screen_pos; RectWithSize clipped_local_rect; }; @@ -51,49 +53,53 @@ RectWithSize intersect_rect(RectWithSize a, RectWithSize b) { // The transformed vertex function that always covers the whole clip area, // which is the intersection of all clip instances of a given primitive ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect, - Transform transform, + Transform prim_transform, + Transform clip_transform, ClipArea area) { - vec2 device_pos = area.screen_origin + aPosition.xy * area.common_data.task_rect.size; - vec2 actual_pos = device_pos; - - if (transform.is_axis_aligned) { + vec2 device_pos = area.screen_origin + + aPosition.xy * area.common_data.task_rect.size; + + // TODO(gw): We only check the clip transform matrix here. We should + // probably also be checking the prim_transform matrix. I + // have left it as is for now, since that matches the + // previous behavior. + if (clip_transform.is_axis_aligned) { vec4 snap_positions = compute_snap_positions( - transform.m, + clip_transform.m, local_clip_rect ); vec2 snap_offsets = compute_snap_offset_impl( device_pos, - transform.m, + clip_transform.m, local_clip_rect, RectWithSize(snap_positions.xy, snap_positions.zw - snap_positions.xy), snap_positions, vec2(0.5) ); - actual_pos -= snap_offsets; + device_pos -= snap_offsets; } - vec4 node_pos; + vec2 world_pos = device_pos / uDevicePixelRatio; - // Select the local position, based on whether we are rasterizing this - // clip mask in local- or sccreen-space. - if (area.local_space) { - node_pos = vec4(actual_pos / uDevicePixelRatio, 0.0, 1.0); - } else { - node_pos = get_node_pos(actual_pos / uDevicePixelRatio, transform); - } + vec4 pos = prim_transform.m * vec4(world_pos, 0.0, 1.0); + pos.xyz /= pos.w; + + vec4 p = get_node_pos(pos.xy, clip_transform); + vec3 local_pos = p.xyw * pos.w; - // compute the point position inside the scroll node, in CSS space - vec2 vertex_pos = device_pos + - area.common_data.task_rect.p0 - - area.screen_origin; + vec4 vertex_pos = vec4( + area.common_data.task_rect.p0 + aPosition.xy * area.common_data.task_rect.size, + 0.0, + 1.0 + ); - gl_Position = uTransform * vec4(vertex_pos, 0.0, 1); + gl_Position = uTransform * vertex_pos; init_transform_vs(vec4(local_clip_rect.p0, local_clip_rect.p0 + local_clip_rect.size)); - ClipVertexInfo vi = ClipVertexInfo(node_pos.xyw, actual_pos, local_clip_rect); + ClipVertexInfo vi = ClipVertexInfo(local_pos, local_clip_rect); return vi; } diff --git a/webrender/res/cs_clip_box_shadow.glsl b/webrender/res/cs_clip_box_shadow.glsl index b431e4262c..5ed7d69278 100644 --- a/webrender/res/cs_clip_box_shadow.glsl +++ b/webrender/res/cs_clip_box_shadow.glsl @@ -4,7 +4,7 @@ #include shared,clip_shared -varying vec3 vPos; +varying vec3 vLocalPos; varying vec2 vUv; flat varying vec4 vUvBounds; flat varying float vLayer; @@ -41,23 +41,26 @@ BoxShadowData fetch_data(ivec2 address) { void main(void) { ClipMaskInstance cmi = fetch_clip_item(); ClipArea area = fetch_clip_area(cmi.render_task_address); - Transform transform = fetch_transform(cmi.transform_id); + Transform clip_transform = fetch_transform(cmi.clip_transform_id); + Transform prim_transform = fetch_transform(cmi.prim_transform_id); BoxShadowData bs_data = fetch_data(cmi.clip_data_address); ImageResource res = fetch_image_resource_direct(cmi.resource_address); - ClipVertexInfo vi = write_clip_tile_vertex(bs_data.dest_rect, - transform, - area); - + ClipVertexInfo vi = write_clip_tile_vertex( + bs_data.dest_rect, + prim_transform, + clip_transform, + area + ); + vLocalPos = vi.local_pos; vLayer = res.layer; - vPos = vi.local_pos; vClipMode = bs_data.clip_mode; vec2 uv0 = res.uv_rect.p0; vec2 uv1 = res.uv_rect.p1; vec2 texture_size = vec2(textureSize(sColor0, 0)); - vec2 local_pos = vPos.xy / vPos.z; + vec2 local_pos = vLocalPos.xy / vLocalPos.z; switch (bs_data.stretch_mode_x) { case MODE_STRETCH: { @@ -96,7 +99,7 @@ void main(void) { #ifdef WR_FRAGMENT_SHADER void main(void) { - vec2 local_pos = vPos.xy / vPos.z; + vec2 local_pos = vLocalPos.xy / vLocalPos.z; vec2 uv = clamp(vUv.xy, vec2(0.0), vEdge.xy); uv += max(vec2(0.0), vUv.xy - vEdge.zw); diff --git a/webrender/res/cs_clip_image.glsl b/webrender/res/cs_clip_image.glsl index 5c9221c12c..7c5425aae4 100644 --- a/webrender/res/cs_clip_image.glsl +++ b/webrender/res/cs_clip_image.glsl @@ -4,7 +4,7 @@ #include shared,clip_shared -varying vec3 vPos; +varying vec3 vLocalPos; varying vec3 vClipMaskImageUv; flat varying vec4 vClipMaskUvRect; @@ -26,19 +26,24 @@ ImageMaskData fetch_mask_data(ivec2 address) { void main(void) { ClipMaskInstance cmi = fetch_clip_item(); ClipArea area = fetch_clip_area(cmi.render_task_address); - Transform transform = fetch_transform(cmi.transform_id); + Transform clip_transform = fetch_transform(cmi.clip_transform_id); + Transform prim_transform = fetch_transform(cmi.prim_transform_id); ImageMaskData mask = fetch_mask_data(cmi.clip_data_address); RectWithSize local_rect = mask.local_rect; ImageResource res = fetch_image_resource_direct(cmi.resource_address); - ClipVertexInfo vi = write_clip_tile_vertex(local_rect, - transform, - area); - - vPos = vi.local_pos; + ClipVertexInfo vi = write_clip_tile_vertex( + local_rect, + prim_transform, + clip_transform, + area + ); + vLocalPos = vi.local_pos; vLayer = res.layer; - vClipMaskImageUv = vec3((vPos.xy / vPos.z - local_rect.p0) / local_rect.size, 0.0); + vec2 local_pos = vLocalPos.xy / vLocalPos.z; + + vClipMaskImageUv = vec3((local_pos - local_rect.p0) / local_rect.size, 0.0); vec2 texture_size = vec2(textureSize(sColor0, 0)); vClipMaskUvRect = vec4(res.uv_rect.p0, res.uv_rect.p1 - res.uv_rect.p0) / texture_size.xyxy; // applying a half-texel offset to the UV boundaries to prevent linear samples from the outside @@ -49,7 +54,9 @@ void main(void) { #ifdef WR_FRAGMENT_SHADER void main(void) { - float alpha = init_transform_fs(vPos.xy / vPos.z); + vec2 local_pos = vLocalPos.xy / vLocalPos.z; + + float alpha = init_transform_fs(local_pos); bool repeat_mask = false; //TODO vec2 clamped_mask_uv = repeat_mask ? fract(vClipMaskImageUv.xy) : diff --git a/webrender/res/cs_clip_line.glsl b/webrender/res/cs_clip_line.glsl index 16091767d9..4e2c151fc9 100644 --- a/webrender/res/cs_clip_line.glsl +++ b/webrender/res/cs_clip_line.glsl @@ -10,7 +10,6 @@ #define LINE_STYLE_WAVY 3 varying vec3 vLocalPos; - flat varying int vStyle; flat varying float vAxisSelect; flat varying vec4 vParams; @@ -43,14 +42,16 @@ LineDecorationData fetch_data(ivec2 address) { void main(void) { ClipMaskInstance cmi = fetch_clip_item(); ClipArea area = fetch_clip_area(cmi.render_task_address); - Transform transform = fetch_transform(cmi.transform_id); + Transform clip_transform = fetch_transform(cmi.clip_transform_id); + Transform prim_transform = fetch_transform(cmi.prim_transform_id); LineDecorationData data = fetch_data(cmi.clip_data_address); - ClipVertexInfo vi = write_clip_tile_vertex(data.local_rect, - transform, - area); - - + ClipVertexInfo vi = write_clip_tile_vertex( + data.local_rect, + prim_transform, + clip_transform, + area + ); vLocalPos = vi.local_pos; vec2 pos, size; diff --git a/webrender/res/cs_clip_rectangle.glsl b/webrender/res/cs_clip_rectangle.glsl index 410ded9bbf..3232f7f810 100644 --- a/webrender/res/cs_clip_rectangle.glsl +++ b/webrender/res/cs_clip_rectangle.glsl @@ -4,7 +4,7 @@ #include shared,clip_shared,ellipse -varying vec3 vPos; +varying vec3 vLocalPos; flat varying float vClipMode; flat varying vec4 vClipCenter_Radius_TL; flat varying vec4 vClipCenter_Radius_TR; @@ -60,13 +60,19 @@ ClipData fetch_clip(ivec2 address) { void main(void) { ClipMaskInstance cmi = fetch_clip_item(); ClipArea area = fetch_clip_area(cmi.render_task_address); - Transform transform = fetch_transform(cmi.transform_id); + Transform clip_transform = fetch_transform(cmi.clip_transform_id); + Transform prim_transform = fetch_transform(cmi.prim_transform_id); ClipData clip = fetch_clip(cmi.clip_data_address); RectWithSize local_rect = clip.rect.rect; - ClipVertexInfo vi = write_clip_tile_vertex(local_rect, transform, area); - vPos = vi.local_pos; + ClipVertexInfo vi = write_clip_tile_vertex( + local_rect, + prim_transform, + clip_transform, + area + ); + vLocalPos = vi.local_pos; vClipMode = clip.rect.mode.x; RectWithEndpoint clip_rect = to_rect_with_endpoint(local_rect); @@ -92,12 +98,13 @@ void main(void) { #ifdef WR_FRAGMENT_SHADER void main(void) { - vec2 local_pos = vPos.xy / vPos.z; - float alpha = init_transform_fs(local_pos); + vec2 local_pos = vLocalPos.xy / vLocalPos.z; - float aa_range = compute_aa_range(local_pos); + float alpha = init_transform_fs(local_pos.xy); - float clip_alpha = rounded_rect(local_pos, + float aa_range = compute_aa_range(local_pos.xy); + + float clip_alpha = rounded_rect(local_pos.xy, vClipCenter_Radius_TL, vClipCenter_Radius_TR, vClipCenter_Radius_BR, diff --git a/webrender/res/ps_split_composite.glsl b/webrender/res/ps_split_composite.glsl index 2a90e9f28c..dae9677d8b 100644 --- a/webrender/res/ps_split_composite.glsl +++ b/webrender/res/ps_split_composite.glsl @@ -5,12 +5,12 @@ #include shared,prim_shared varying vec3 vUv; -flat varying vec4 vUvTaskBounds; flat varying vec4 vUvSampleBounds; #ifdef WR_VERTEX_SHADER struct SplitGeometry { - vec3 points[4]; + vec2 local[4]; + RectWithSize local_rect; }; SplitGeometry fetch_split_geometry(int address) { @@ -21,22 +21,25 @@ SplitGeometry fetch_split_geometry(int address) { vec4 data2 = TEXEL_FETCH(sResourceCache, uv, 0, ivec2(2, 0)); SplitGeometry geo; - geo.points = vec3[4]( - data0.xyz, vec3(data0.w, data1.xy), - vec3(data1.zw, data2.x), data2.yzw + geo.local = vec2[4]( + data0.xy, + data0.zw, + data1.xy, + data1.zw ); + geo.local_rect = RectWithSize(data2.xy, data2.zw); + return geo; } -vec3 bilerp(vec3 a, vec3 b, vec3 c, vec3 d, float s, float t) { - vec3 x = mix(a, b, t); - vec3 y = mix(c, d, t); +vec2 bilerp(vec2 a, vec2 b, vec2 c, vec2 d, float s, float t) { + vec2 x = mix(a, b, t); + vec2 y = mix(c, d, t); return mix(x, y, s); } struct SplitCompositeInstance { - int render_task_index; - int src_task_index; + int prim_header_index; int polygons_address; float z; }; @@ -44,10 +47,9 @@ struct SplitCompositeInstance { SplitCompositeInstance fetch_composite_instance() { SplitCompositeInstance ci; - ci.render_task_index = aData.x; - ci.src_task_index = aData.y; - ci.polygons_address = aData.z; - ci.z = float(aData.w); + ci.prim_header_index = aData.x; + ci.polygons_address = aData.y; + ci.z = float(aData.z); return ci; } @@ -55,37 +57,63 @@ SplitCompositeInstance fetch_composite_instance() { void main(void) { SplitCompositeInstance ci = fetch_composite_instance(); SplitGeometry geometry = fetch_split_geometry(ci.polygons_address); - PictureTask src_task = fetch_picture_task(ci.src_task_index); - PictureTask dest_task = fetch_picture_task(ci.render_task_index); + PrimitiveHeader ph = fetch_prim_header(ci.prim_header_index); + PictureTask dest_task = fetch_picture_task(ph.render_task_index); + Transform transform = fetch_transform(ph.transform_id); + ImageResource res = fetch_image_resource(ph.user_data.x); + ImageResourceExtra extra_data = fetch_image_resource_extra(ph.user_data.x); + ClipArea clip_area = fetch_clip_area(ph.clip_task_index); vec2 dest_origin = dest_task.common_data.task_rect.p0 - dest_task.content_origin; - vec3 world_pos = bilerp(geometry.points[0], geometry.points[1], - geometry.points[3], geometry.points[2], + vec2 local_pos = bilerp(geometry.local[0], geometry.local[1], + geometry.local[3], geometry.local[2], aPosition.y, aPosition.x); - vec4 final_pos = vec4((world_pos.xy + dest_origin) * uDevicePixelRatio, ci.z, 1.0); + vec4 world_pos = transform.m * vec4(local_pos, 0.0, 1.0); + + vec4 final_pos = vec4( + dest_origin * world_pos.w + world_pos.xy * uDevicePixelRatio, + world_pos.w * ci.z, + world_pos.w + ); + + write_clip( + world_pos, + clip_area + ); gl_Position = uTransform * final_pos; - vec2 uv_origin = src_task.common_data.task_rect.p0; - vec2 uv_pos = uv_origin + world_pos.xy - src_task.content_origin; vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0)); - vUv = vec3(uv_pos / texture_size, src_task.common_data.texture_layer_index); - vUvTaskBounds = vec4(uv_origin, uv_origin + src_task.common_data.task_rect.size) / texture_size.xyxy; - vUvSampleBounds = vec4(uv_origin + 0.5, uv_origin + src_task.common_data.task_rect.size - 0.5) / texture_size.xyxy; + vec2 uv0 = res.uv_rect.p0; + vec2 uv1 = res.uv_rect.p1; + + vec2 min_uv = min(uv0, uv1); + vec2 max_uv = max(uv0, uv1); + + vUvSampleBounds = vec4( + min_uv + vec2(0.5), + max_uv - vec2(0.5) + ) / texture_size.xyxy; + + vec2 f = (local_pos - geometry.local_rect.p0) / geometry.local_rect.size; + + f = bilerp( + extra_data.st_tl, extra_data.st_tr, + extra_data.st_bl, extra_data.st_br, + f.y, f.x + ); + vec2 uv = mix(uv0, uv1, f); + + vUv = vec3(uv / texture_size, res.layer); } #endif #ifdef WR_FRAGMENT_SHADER void main(void) { - bvec4 inside = lessThanEqual(vec4(vUvTaskBounds.xy, vUv.xy), - vec4(vUv.xy, vUvTaskBounds.zw)); - if (all(inside)) { - vec2 uv = clamp(vUv.xy, vUvSampleBounds.xy, vUvSampleBounds.zw); - oFragColor = textureLod(sCacheRGBA8, vec3(uv, vUv.z), 0.0); - } else { - oFragColor = vec4(0.0); - } + float alpha = do_clip(); + vec2 uv = clamp(vUv.xy, vUvSampleBounds.xy, vUvSampleBounds.zw); + oFragColor = alpha * textureLod(sCacheRGBA8, vec3(uv, vUv.z), 0.0); } #endif diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index 72635aa610..d5e79007b3 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -6,6 +6,7 @@ use api::{AlphaType, ClipMode, DeviceIntRect, DeviceIntSize}; use api::{DeviceUintRect, DeviceUintPoint, ExternalImageType, FilterOp, ImageRendering}; use api::{YuvColorSpace, YuvFormat, WorldPixel, WorldRect}; use clip::{ClipNodeFlags, ClipNodeRange, ClipItem, ClipStore}; +use clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex}; use euclid::vec3; use glyph_rasterizer::GlyphFormat; use gpu_cache::{GpuCache, GpuCacheHandle, GpuCacheAddress}; @@ -27,7 +28,7 @@ use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache}; use scene::FilterOpHelpers; use std::{f32, i32}; use tiling::{RenderTargetContext}; -use util::{TransformedRectKind}; +use util::{MatrixHelpers, TransformedRectKind}; // Special sentinel value recognized by the shader. It is considered to be // a dummy task that doesn't mask out anything. @@ -211,7 +212,7 @@ impl OpaqueBatchList { pub fn get_suitable_batch( &mut self, key: BatchKey, - bounding_rect: &WorldRect + bounding_rect: &WorldRect, ) -> &mut Vec { let mut selected_batch_index = None; let item_area = bounding_rect.size.area(); @@ -439,6 +440,8 @@ impl AlphaBatchBuilder { render_tasks: &RenderTaskTree, deferred_resolves: &mut Vec, prim_headers: &mut PrimitiveHeaders, + transforms: &mut TransformPalette, + root_spatial_node_index: SpatialNodeIndex, ) { let task_address = render_tasks.get_task_address(task_id); @@ -458,6 +461,8 @@ impl AlphaBatchBuilder { deferred_resolves, &mut splitter, prim_headers, + transforms, + root_spatial_node_index, ); } @@ -465,44 +470,79 @@ impl AlphaBatchBuilder { // 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); + let pic_metadata = &ctx.prim_store.primitives[prim_index.0].metadata; if cfg!(debug_assertions) && ctx.prim_store.chase_id == Some(prim_index) { println!("\t\tsplit polygon {:?}", poly.points); } - debug!("process sorted poly {:?} {:?}", prim_index, poly.points); - let pp = &poly.points; + let transform = transforms.get_world_transform(pic_metadata.spatial_node_index).inverse().unwrap(); + let transform_id = transforms.get_id( + pic_metadata.spatial_node_index, + ROOT_SPATIAL_NODE_INDEX, + ctx.clip_scroll_tree, + ); + + let clip_task_address = pic_metadata + .clip_task_id + .map_or(OPAQUE_TASK_ADDRESS, |id| render_tasks.get_task_address(id)); + + let prim_header = PrimitiveHeader { + local_rect: pic_metadata.local_rect, + local_clip_rect: pic_metadata.combined_local_clip_rect, + task_address, + specific_prim_address: GpuCacheAddress::invalid(), + clip_task_address, + transform_id, + }; + + let pic = ctx.prim_store.get_pic(prim_index); + + let (uv_rect_address, _) = pic + .raster_config + .as_ref() + .expect("BUG: no raster config") + .surface + .as_ref() + .expect("BUG: no surface") + .resolve( + render_tasks, + ctx.resource_cache, + gpu_cache, + ); + + let prim_header_index = prim_headers.push(&prim_header, [ + uv_rect_address.as_int(), + 0, + 0, + ]); + + let mut local_points = [ + transform.transform_point3d(&poly.points[0].cast()).unwrap(), + transform.transform_point3d(&poly.points[1].cast()).unwrap(), + transform.transform_point3d(&poly.points[2].cast()).unwrap(), + transform.transform_point3d(&poly.points[3].cast()).unwrap(), + ]; 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(), + [local_points[0].x, local_points[0].y, local_points[1].x, local_points[1].y].into(), + [local_points[2].x, local_points[2].y, local_points[3].x, local_points[3].y].into(), + pic_metadata.local_rect.into(), ]; + let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks); let key = BatchKey::new( BatchKind::SplitComposite, BlendMode::PremultipliedAlpha, BatchTextures::no_texture(), ); - let pic_metadata = &ctx.prim_store.primitives[prim_index.0].metadata; - let pic = ctx.prim_store.get_pic(prim_index); let batch = self.batch_list .get_suitable_batch( key, &pic_metadata.clipped_world_rect.as_ref().expect("bug"), ); - let source_task_id = pic - .raster_config - .as_ref() - .expect("BUG: no raster config") - .surface - .as_ref() - .expect("BUG: unexpected surface in splitting") - .resolve_render_task_id(); - let source_task_address = render_tasks.get_task_address(source_task_id); let gpu_address = gpu_cache.get_address(&gpu_handle); let instance = SplitCompositeInstance::new( - task_address, - source_task_address, + prim_header_index, gpu_address, prim_headers.z_generator.next(), ); @@ -525,15 +565,20 @@ impl AlphaBatchBuilder { deferred_resolves: &mut Vec, splitter: &mut BspSplitter, prim_headers: &mut PrimitiveHeaders, + transforms: &mut TransformPalette, + root_spatial_node_index: SpatialNodeIndex, ) { for i in 0 .. run.count { let prim_index = PrimitiveIndex(run.base_prim_index.0 + i); let metadata = &ctx.prim_store.primitives[prim_index.0].metadata; if metadata.clipped_world_rect.is_some() { - let transform_id = ctx - .transforms - .get_id(metadata.spatial_node_index); + let transform_id = transforms + .get_id( + metadata.spatial_node_index, + root_spatial_node_index, + ctx.clip_scroll_tree, + ); self.add_prim_to_batch( transform_id, @@ -546,6 +591,8 @@ impl AlphaBatchBuilder { deferred_resolves, splitter, prim_headers, + transforms, + root_spatial_node_index, ); } } @@ -567,6 +614,8 @@ impl AlphaBatchBuilder { deferred_resolves: &mut Vec, splitter: &mut BspSplitter, prim_headers: &mut PrimitiveHeaders, + transforms: &mut TransformPalette, + root_spatial_node_index: SpatialNodeIndex, ) { let prim = &ctx.prim_store.primitives[prim_index.0]; let prim_metadata = &prim.metadata; @@ -640,29 +689,39 @@ impl AlphaBatchBuilder { if picture.is_in_3d_context { // Push into parent plane splitter. debug_assert!(picture.raster_config.is_some()); - let transform = &ctx.transforms - .get_transform_by_id(transform_id); - - match transform.transform_kind { - TransformedRectKind::AxisAligned => { - let polygon = Polygon::from_transformed_rect( - prim_metadata.local_rect.cast(), - transform.m.cast(), - prim_index.0, - ).unwrap(); - splitter.add(polygon); - } - TransformedRectKind::Complex => { - let mut clipper = Clipper::new(); - let matrix = transform.m.cast(); - let results = clipper.clip_transformed( - Polygon::from_rect(prim_metadata.local_rect.cast(), prim_index.0), - &matrix, - Some(bounding_rect.to_f64()), - ); - if let Ok(results) = results { - for poly in results { - splitter.add(poly); + let transform = transforms.get_world_transform(prim_metadata.spatial_node_index); + + // Apply the local clip rect here, before splitting. This is + // because the local clip rect can't be applied in the vertex + // shader for split composites, since we are drawing polygons + // rather that rectangles. The interpolation still works correctly + // since we determine the UVs by doing a bilerp with a factor + // from the original local rect. + let local_rect = prim_metadata.local_rect + .intersection(&prim_metadata.combined_local_clip_rect); + + if let Some(local_rect) = local_rect { + match transform.transform_kind() { + TransformedRectKind::AxisAligned => { + let polygon = Polygon::from_transformed_rect( + local_rect.cast(), + transform.cast(), + prim_index.0, + ).unwrap(); + splitter.add(polygon); + } + TransformedRectKind::Complex => { + let mut clipper = Clipper::new(); + let matrix = transform.cast(); + let results = clipper.clip_transformed( + Polygon::from_rect(local_rect.cast(), prim_index.0), + &matrix, + Some(bounding_rect.to_f64()), + ); + if let Ok(results) = results { + for poly in results { + splitter.add(poly); + } } } } @@ -801,12 +860,6 @@ impl AlphaBatchBuilder { .push(PrimitiveInstance::from(content_instance)); } _ => { - let key = BatchKey::new( - BatchKind::Brush(BrushBatchKind::Blend), - BlendMode::PremultipliedAlpha, - BatchTextures::render_target_cache(), - ); - let filter_mode = match filter { FilterOp::Identity => 1, // matches `Contrast(1)` FilterOp::Blur(..) => 0, @@ -846,10 +899,21 @@ impl AlphaBatchBuilder { } }; - let cache_task_id = surface.resolve_render_task_id(); - let cache_task_address = render_tasks.get_task_address(cache_task_id); + let (uv_rect_address, textures) = surface + .resolve( + render_tasks, + ctx.resource_cache, + gpu_cache, + ); + + let key = BatchKey::new( + BatchKind::Brush(BrushBatchKind::Blend), + BlendMode::PremultipliedAlpha, + textures, + ); + let prim_header_index = prim_headers.push(&prim_header, [ - cache_task_address.0 as i32, + uv_rect_address.as_int(), filter_mode, user_data, ]); @@ -948,6 +1012,8 @@ impl AlphaBatchBuilder { render_tasks, deferred_resolves, prim_headers, + transforms, + root_spatial_node_index, ); } } @@ -1665,7 +1731,8 @@ impl ClipBatcher { ) { let instance = ClipMaskInstance { render_task_address: task_address, - transform_id: TransformPaletteId::IDENTITY, + clip_transform_id: TransformPaletteId::IDENTITY, + prim_transform_id: TransformPaletteId::IDENTITY, segment: 0, clip_data_address, resource_address: GpuCacheAddress::invalid(), @@ -1678,17 +1745,32 @@ impl ClipBatcher { &mut self, task_address: RenderTaskAddress, clip_node_range: ClipNodeRange, + root_spatial_node_index: SpatialNodeIndex, resource_cache: &ResourceCache, gpu_cache: &GpuCache, clip_store: &ClipStore, - transforms: &TransformPalette, + clip_scroll_tree: &ClipScrollTree, + transforms: &mut TransformPalette, ) { for i in 0 .. clip_node_range.count { let (clip_node, flags) = clip_store.get_node_from_range(&clip_node_range, i); + let clip_transform_id = transforms.get_id( + clip_node.spatial_node_index, + ROOT_SPATIAL_NODE_INDEX, + clip_scroll_tree, + ); + + let prim_transform_id = transforms.get_id( + root_spatial_node_index, + ROOT_SPATIAL_NODE_INDEX, + clip_scroll_tree, + ); + let instance = ClipMaskInstance { render_task_address: task_address, - transform_id: transforms.get_id(clip_node.spatial_node_index), + clip_transform_id, + prim_transform_id, segment: 0, clip_data_address: GpuCacheAddress::invalid(), resource_address: GpuCacheAddress::invalid(), diff --git a/webrender/src/clip.rs b/webrender/src/clip.rs index fa5378ee51..d7993a45dc 100644 --- a/webrender/src/clip.rs +++ b/webrender/src/clip.rs @@ -8,10 +8,11 @@ use api::{BoxShadowClipMode, LayoutToWorldScale, LineOrientation, LineStyle, Pic use api::{PictureRect, LayoutPixel, WorldPoint, WorldSize, WorldRect, LayoutToWorldTransform}; use border::{ensure_no_corner_overlap}; use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey}; -use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId, SpatialNodeIndex}; +use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex}; use ellipse::Ellipse; use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks}; use gpu_types::{BoxShadowStretchMode}; +use internal_types::FastHashSet; use prim_store::{ClipData, ImageMaskData, SpaceMapper}; use render_task::to_cache_size; use resource_cache::{ImageRequest, ResourceCache}; @@ -314,6 +315,7 @@ pub struct ClipStore { pub clip_chain_nodes: Vec, clip_node_indices: Vec, clip_node_info: Vec, + clip_node_collectors: Vec, } // A clip chain instance is what gets built for a given clip @@ -338,6 +340,7 @@ impl ClipStore { clip_chain_nodes: Vec::new(), clip_node_indices: Vec::new(), clip_node_info: Vec::new(), + clip_node_collectors: Vec::new(), } } @@ -347,6 +350,7 @@ impl ClipStore { clip_chain_nodes: recycle_vec(self.clip_chain_nodes), clip_node_indices: recycle_vec(self.clip_node_indices), clip_node_info: recycle_vec(self.clip_node_info), + clip_node_collectors: recycle_vec(self.clip_node_collectors), } } @@ -411,6 +415,25 @@ impl ClipStore { (&mut self.clip_nodes[instance.index()], instance.flags()) } + // Notify the clip store that a new rasterization root has been created. + // This means any clips from an earlier root should be collected rather + // than applied on the primitive itself. + pub fn push_raster_root( + &mut self, + raster_spatial_node_index: SpatialNodeIndex, + ) { + self.clip_node_collectors.push( + ClipNodeCollector::new(raster_spatial_node_index), + ); + } + + // Mark the end of a rasterization root. + pub fn pop_raster_root( + &mut self, + ) -> ClipNodeCollector { + self.clip_node_collectors.pop().unwrap() + } + // The main interface other code uses. Given a local primitive, positioning // information, and a clip chain id, build an optimized clip chain instance. pub fn build_clip_chain_instance( @@ -426,15 +449,14 @@ impl ClipStore { resource_cache: &mut ResourceCache, device_pixel_scale: DevicePixelScale, world_rect: &WorldRect, + clip_node_collector: &Option, ) -> Option { let mut local_clip_rect = local_prim_clip_rect; - let spatial_nodes = &clip_scroll_tree.spatial_nodes; // Walk the clip chain to build local rects, and collect the // smallest possible local/device clip area. self.clip_node_info.clear(); - let ref_spatial_node = &spatial_nodes[spatial_node_index.0]; let mut current_clip_chain_id = clip_chain_id; // for each clip chain node @@ -446,67 +468,52 @@ impl ClipStore { for i in 0 .. node_count { let clip_node_index = ClipNodeIndex(clip_chain_node.clip_item_range.index.0 + i); let clip_node = &self.clip_nodes[clip_node_index.0 as usize]; - let clip_spatial_node = &spatial_nodes[clip_node.spatial_node_index.0 as usize]; - - // Determine the most efficient way to convert between coordinate - // systems of the primitive and clip node. - let conversion = if spatial_node_index == clip_node.spatial_node_index { - Some(ClipSpaceConversion::Local) - } else if ref_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id { - let offset = clip_spatial_node.coordinate_system_relative_offset - - ref_spatial_node.coordinate_system_relative_offset; - Some(ClipSpaceConversion::Offset(offset)) - } else { - let xf = clip_scroll_tree.get_relative_transform( - clip_node.spatial_node_index, - SpatialNodeIndex(0), - ); - xf.map(|xf| { - ClipSpaceConversion::Transform(xf.with_destination::()) - }) - }; - - // If we can convert spaces, try to reduce the size of the region - // requested, and cache the conversion information for the next step. - if let Some(conversion) = conversion { - if let Some(clip_rect) = clip_node.item.get_local_clip_rect() { - match conversion { - ClipSpaceConversion::Local => { - local_clip_rect = local_clip_rect.intersection(&clip_rect)?; - } - ClipSpaceConversion::Offset(ref offset) => { - let clip_rect = clip_rect.translate(offset); - local_clip_rect = local_clip_rect.intersection(&clip_rect)?; - } - ClipSpaceConversion::Transform(..) => { - // TODO(gw): In the future, we can reduce the size - // of the pic_clip_rect here. To do this, - // we can use project_rect or the - // inverse_rect_footprint method, depending - // on the relationship of the clip, pic - // and primitive spatial nodes. - // I have left this for now until we - // find some good test cases where this - // would be a worthwhile perf win. - } + // Check if any clip node index should actually be + // handled during compositing of a rasterization root. + match self.clip_node_collectors.iter_mut().find(|c| { + clip_node.spatial_node_index < c.raster_root + }) { + Some(collector) => { + collector.insert(clip_node_index); + } + None => { + if !add_clip_node_to_current_chain( + clip_node_index, + spatial_node_index, + &mut local_clip_rect, + &mut self.clip_node_info, + &self.clip_nodes, + clip_scroll_tree, + ) { + return None; } } - self.clip_node_info.push(ClipNodeInfo { - conversion, - node_index: clip_node_index, - has_non_root_coord_system: clip_spatial_node.coordinate_system_id != CoordinateSystemId::root(), - }) } } current_clip_chain_id = clip_chain_node.parent_clip_chain_id; } - let local_bounding_rect = local_prim_rect.intersection(&local_clip_rect)?; + // Add any collected clips from primitives that should be + // handled as part of this rasterization root. + if let Some(clip_node_collector) = clip_node_collector { + for clip_node_index in &clip_node_collector.clips { + if !add_clip_node_to_current_chain( + *clip_node_index, + spatial_node_index, + &mut local_clip_rect, + &mut self.clip_node_info, + &self.clip_nodes, + clip_scroll_tree, + ) { + return None; + } + } + } + let local_bounding_rect = local_prim_rect.intersection(&local_clip_rect)?; let pic_clip_rect = prim_to_pic_mapper.map(&local_bounding_rect)?; - let world_clip_rect = pic_to_world_mapper.map(&pic_clip_rect)?; // Now, we've collected all the clip nodes that *potentially* affect this @@ -1077,3 +1084,104 @@ pub fn project_inner_rect( WorldSize::new(xs[2] - xs[1], ys[2] - ys[1]), )) } + +// Collects a list of unique clips to be applied to a rasterization +// root at the end of primitive preparation. +#[derive(Debug)] +pub struct ClipNodeCollector { + raster_root: SpatialNodeIndex, + clips: FastHashSet, +} + +impl ClipNodeCollector { + pub fn new( + raster_root: SpatialNodeIndex, + ) -> Self { + ClipNodeCollector { + raster_root, + clips: FastHashSet::default(), + } + } + + pub fn insert( + &mut self, + clip_node_index: ClipNodeIndex, + ) { + self.clips.insert(clip_node_index); + } +} + +// Add a clip node into the list of clips to be processed +// for the current clip chain. Returns false if the clip +// results in the entire primitive being culled out. +fn add_clip_node_to_current_chain( + clip_node_index: ClipNodeIndex, + spatial_node_index: SpatialNodeIndex, + local_clip_rect: &mut LayoutRect, + clip_node_info: &mut Vec, + clip_nodes: &[ClipNode], + clip_scroll_tree: &ClipScrollTree, +) -> bool { + let clip_node = &clip_nodes[clip_node_index.0 as usize]; + let clip_spatial_node = &clip_scroll_tree.spatial_nodes[clip_node.spatial_node_index.0 as usize]; + let ref_spatial_node = &clip_scroll_tree.spatial_nodes[spatial_node_index.0]; + + // Determine the most efficient way to convert between coordinate + // systems of the primitive and clip node. + let conversion = if spatial_node_index == clip_node.spatial_node_index { + Some(ClipSpaceConversion::Local) + } else if ref_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id { + let offset = clip_spatial_node.coordinate_system_relative_offset - + ref_spatial_node.coordinate_system_relative_offset; + Some(ClipSpaceConversion::Offset(offset)) + } else { + let xf = clip_scroll_tree.get_relative_transform( + clip_node.spatial_node_index, + ROOT_SPATIAL_NODE_INDEX, + ); + + xf.map(|xf| { + ClipSpaceConversion::Transform(xf.with_destination::()) + }) + }; + + // If we can convert spaces, try to reduce the size of the region + // requested, and cache the conversion information for the next step. + if let Some(conversion) = conversion { + if let Some(clip_rect) = clip_node.item.get_local_clip_rect() { + match conversion { + ClipSpaceConversion::Local => { + *local_clip_rect = match local_clip_rect.intersection(&clip_rect) { + Some(rect) => rect, + None => return false, + }; + } + ClipSpaceConversion::Offset(ref offset) => { + let clip_rect = clip_rect.translate(offset); + *local_clip_rect = match local_clip_rect.intersection(&clip_rect) { + Some(rect) => rect, + None => return false, + }; + } + ClipSpaceConversion::Transform(..) => { + // TODO(gw): In the future, we can reduce the size + // of the pic_clip_rect here. To do this, + // we can use project_rect or the + // inverse_rect_footprint method, depending + // on the relationship of the clip, pic + // and primitive spatial nodes. + // I have left this for now until we + // find some good test cases where this + // would be a worthwhile perf win. + } + } + } + clip_node_info.push(ClipNodeInfo { + conversion, + node_index: clip_node_index, + has_non_root_coord_system: clip_spatial_node.coordinate_system_id != CoordinateSystemId::root(), + }) + } + + true +} diff --git a/webrender/src/clip_scroll_tree.rs b/webrender/src/clip_scroll_tree.rs index c36a47dac0..068bee0b6e 100644 --- a/webrender/src/clip_scroll_tree.rs +++ b/webrender/src/clip_scroll_tree.rs @@ -5,7 +5,6 @@ use api::{ExternalScrollId, LayoutPoint, LayoutRect, LayoutVector2D, LayoutVector3D}; use api::{PipelineId, ScrollClamping, ScrollNodeState, ScrollLocation}; use api::{LayoutSize, LayoutTransform, PropertyBinding, ScrollSensitivity, WorldPoint}; -use clip::{ClipStore}; use gpu_types::TransformPalette; use internal_types::{FastHashMap, FastHashSet}; use print_tree::{PrintTree, PrintTreePrinter}; @@ -44,12 +43,12 @@ impl CoordinateSystem { } } -#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, PartialOrd)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct SpatialNodeIndex(pub usize); -const ROOT_REFERENCE_FRAME_INDEX: SpatialNodeIndex = SpatialNodeIndex(0); +pub const ROOT_SPATIAL_NODE_INDEX: SpatialNodeIndex = SpatialNodeIndex(0); const TOPMOST_SCROLL_NODE_INDEX: SpatialNodeIndex = SpatialNodeIndex(1); impl CoordinateSystemId { @@ -168,7 +167,7 @@ impl ClipScrollTree { pub fn root_reference_frame_index(&self) -> SpatialNodeIndex { // TODO(mrobinson): We should eventually make this impossible to misuse. debug_assert!(!self.spatial_nodes.is_empty()); - ROOT_REFERENCE_FRAME_INDEX + ROOT_SPATIAL_NODE_INDEX } /// The root scroll node which is the first child of the root reference frame. @@ -415,7 +414,6 @@ impl ClipScrollTree { &self, index: SpatialNodeIndex, pt: &mut T, - clip_store: &ClipStore ) { let node = &self.spatial_nodes[index.0]; match node.node_type { @@ -442,23 +440,23 @@ impl ClipScrollTree { pt.add_item(format!("coordinate_system_id: {:?}", node.coordinate_system_id)); for child_index in &node.children { - self.print_node(*child_index, pt, clip_store); + self.print_node(*child_index, pt); } pt.end_level(); } #[allow(dead_code)] - pub fn print(&self, clip_store: &ClipStore) { + pub fn print(&self) { if !self.spatial_nodes.is_empty() { let mut pt = PrintTree::new("clip_scroll tree"); - self.print_with(clip_store, &mut pt); + self.print_with(&mut pt); } } - pub fn print_with(&self, clip_store: &ClipStore, pt: &mut T) { + pub fn print_with(&self, pt: &mut T) { if !self.spatial_nodes.is_empty() { - self.print_node(self.root_reference_frame_index(), pt, clip_store); + self.print_node(self.root_reference_frame_index(), pt); } } } diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index 69c4eb8c83..ce777a462c 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -2,18 +2,18 @@ * 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::{ColorF, DeviceIntPoint, DevicePixelScale, LayoutPixel, PicturePixel}; +use api::{ColorF, DeviceIntPoint, DevicePixelScale, LayoutPixel, PicturePixel, RasterPixel}; use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentLayer, FontRenderMode, PictureRect}; use api::{LayoutPoint, LayoutRect, LayoutSize, PipelineId, WorldPoint, WorldRect, WorldPixel}; -use clip::{ClipStore}; -use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex}; +use clip::ClipStore; +use clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex}; use display_list_flattener::{DisplayListFlattener}; use gpu_cache::GpuCache; use gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind}; use hit_test::{HitTester, HitTestingRun}; use internal_types::{FastHashMap}; use picture::{PictureCompositeMode, PictureSurface, RasterConfig}; -use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveStore, Transform, SpaceMapper}; +use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveStore, SpaceMapper}; use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters}; use render_backend::FrameId; use render_task::{RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree}; @@ -24,7 +24,7 @@ use std::f32; use std::sync::Arc; use tiling::{Frame, RenderPass, RenderPassKind, RenderTargetContext}; use tiling::{ScrollbarPrimitive, SpecialRenderPasses}; -use util::{self, MaxRect}; +use util; #[derive(Clone, Copy, Debug, PartialEq)] @@ -73,7 +73,6 @@ pub struct FrameBuildingContext<'a> { pub pipelines: &'a FastHashMap>, pub world_rect: WorldRect, pub clip_scroll_tree: &'a ClipScrollTree, - pub transforms: &'a TransformPalette, pub max_local_clip: LayoutRect, } @@ -84,6 +83,7 @@ pub struct FrameBuildingState<'a> { pub resource_cache: &'a mut ResourceCache, pub gpu_cache: &'a mut GpuCache, pub special_render_passes: &'a mut SpecialRenderPasses, + pub transforms: &'a mut TransformPalette, } pub struct PictureContext { @@ -92,7 +92,8 @@ pub struct PictureContext { pub apply_local_clip_rect: bool, pub inflation_factor: f32, pub allow_subpixel_aa: bool, - pub has_surface: bool, + pub is_passthrough: bool, + pub establishes_raster_root: bool, } #[derive(Debug)] @@ -102,57 +103,25 @@ pub struct PictureState { pub local_rect_changed: bool, pub map_local_to_pic: SpaceMapper, pub map_pic_to_world: SpaceMapper, -} - -impl PictureState { - pub fn new( - ref_spatial_node_index: SpatialNodeIndex, - clip_scroll_tree: &ClipScrollTree, - world_rect: WorldRect, - ) -> Self { - let mut map_pic_to_world = SpaceMapper::new( - SpatialNodeIndex(0), - world_rect, - ); - map_pic_to_world.set_target_spatial_node( - ref_spatial_node_index, - clip_scroll_tree, - ); - let pic_bounds = map_pic_to_world.unmap( - &world_rect, - ).unwrap_or(PictureRect::max_rect()); - - let map_local_to_pic = SpaceMapper::new( - ref_spatial_node_index, - pic_bounds, - ); - - PictureState { - tasks: Vec::new(), - has_non_root_coord_system: false, - local_rect_changed: false, - map_local_to_pic, - map_pic_to_world, - } - } + pub map_pic_to_raster: SpaceMapper, + pub map_raster_to_world: SpaceMapper, + pub surface_spatial_node_index: SpatialNodeIndex, + pub raster_spatial_node_index: SpatialNodeIndex, } pub struct PrimitiveContext<'a> { pub spatial_node: &'a SpatialNode, pub spatial_node_index: SpatialNodeIndex, - pub transform: Transform, } impl<'a> PrimitiveContext<'a> { pub fn new( spatial_node: &'a SpatialNode, spatial_node_index: SpatialNodeIndex, - transform: Transform, ) -> Self { PrimitiveContext { spatial_node, spatial_node_index, - transform, } } } @@ -213,7 +182,7 @@ impl FrameBuilder { profile_counters: &mut FrameProfileCounters, device_pixel_scale: DevicePixelScale, scene_properties: &SceneProperties, - transform_palette: &TransformPalette, + transform_palette: &mut TransformPalette, ) -> Option { profile_scope!("cull"); @@ -237,7 +206,6 @@ impl FrameBuilder { pipelines, world_rect, clip_scroll_tree, - transforms: transform_palette, max_local_clip: LayoutRect::new( LayoutPoint::new(-MAX_CLIP_COORD, -MAX_CLIP_COORD), LayoutSize::new(2.0 * MAX_CLIP_COORD, 2.0 * MAX_CLIP_COORD), @@ -251,20 +219,24 @@ impl FrameBuilder { resource_cache, gpu_cache, special_render_passes, + transforms: transform_palette, }; - let mut pic_state = PictureState::new( + let prim_context = PrimitiveContext::new( + &clip_scroll_tree.spatial_nodes[root_spatial_node_index.0], root_spatial_node_index, - &frame_context.clip_scroll_tree, - frame_context.world_rect, ); - let pic_context = self + let (pic_context, mut pic_state) = self .prim_store .get_pic_mut(root_prim_index) .take_context( + &prim_context, + root_spatial_node_index, + root_spatial_node_index, true, - scene_properties, + &mut frame_state, + &frame_context, false, ) .unwrap(); @@ -276,7 +248,6 @@ impl FrameBuilder { &mut pic_state, &frame_context, &mut frame_state, - root_spatial_node_index, &mut pic_rect, ); @@ -287,6 +258,7 @@ impl FrameBuilder { pic_context, pic_state, Some(pic_rect), + &mut frame_state, ); let pic_state = pic.take_state(); @@ -298,12 +270,14 @@ impl FrameBuilder { DeviceIntPoint::zero(), pic_state.tasks, UvRectKind::Rect, + root_spatial_node_index, ); let render_task_id = frame_state.render_tasks.add(root_render_task); pic.raster_config = Some(RasterConfig { composite_mode: PictureCompositeMode::Blit, surface: Some(PictureSurface::RenderTask(render_task_id)), + raster_spatial_node_index: ROOT_SPATIAL_NODE_INDEX, }); Some(render_task_id) } @@ -365,7 +339,7 @@ impl FrameBuilder { resource_cache.begin_frame(frame_id); gpu_cache.begin_frame(); - let transform_palette = clip_scroll_tree.update_tree( + let mut transform_palette = clip_scroll_tree.update_tree( pan, scene_properties, ); @@ -387,7 +361,7 @@ impl FrameBuilder { &mut profile_counters, device_pixel_scale, scene_properties, - &transform_palette, + &mut transform_palette, ); resource_cache.block_until_all_resources_added(gpu_cache, @@ -430,7 +404,7 @@ impl FrameBuilder { prim_store: &self.prim_store, resource_cache, use_dual_source_blending, - transforms: &transform_palette, + clip_scroll_tree, }; pass.build( @@ -439,6 +413,7 @@ impl FrameBuilder { &mut render_tasks, &mut deferred_resolves, &self.clip_store, + &mut transform_palette, &mut prim_headers, ); diff --git a/webrender/src/gpu_types.rs b/webrender/src/gpu_types.rs index 893263f0a4..c2c0d8ffd9 100644 --- a/webrender/src/gpu_types.rs +++ b/webrender/src/gpu_types.rs @@ -2,13 +2,14 @@ * 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::{DevicePoint, DeviceSize, DeviceRect, LayoutRect, LayoutToWorldTransform}; -use api::{PremultipliedColorF, WorldToLayoutTransform}; -use clip_scroll_tree::SpatialNodeIndex; +use api::{DevicePoint, DeviceSize, DeviceRect, LayoutRect, LayoutToWorldTransform, LayoutTransform}; +use api::{PremultipliedColorF, LayoutToPictureTransform, PictureToLayoutTransform, PicturePixel, WorldPixel}; +use clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex}; use gpu_cache::{GpuCacheAddress, GpuDataRequest}; -use prim_store::{EdgeAaSegmentMask, Transform}; +use internal_types::FastHashMap; +use prim_store::EdgeAaSegmentMask; use render_task::RenderTaskAddress; -use util::{LayoutToWorldFastTransform, TransformedRectKind}; +use util::{TransformedRectKind, MatrixHelpers}; // Contains type that must exactly match the same structures declared in GLSL. @@ -115,7 +116,8 @@ pub struct BorderInstance { #[repr(C)] pub struct ClipMaskInstance { pub render_task_address: RenderTaskAddress, - pub transform_id: TransformPaletteId, + pub clip_transform_id: TransformPaletteId, + pub prim_transform_id: TransformPaletteId, pub segment: i32, pub clip_data_address: GpuCacheAddress, pub resource_address: GpuCacheAddress, @@ -259,22 +261,19 @@ impl GlyphInstance { } pub struct SplitCompositeInstance { - pub task_address: RenderTaskAddress, - pub src_task_address: RenderTaskAddress, + pub prim_header_index: PrimitiveHeaderIndex, pub polygons_address: GpuCacheAddress, pub z: ZBufferId, } impl SplitCompositeInstance { pub fn new( - task_address: RenderTaskAddress, - src_task_address: RenderTaskAddress, + prim_header_index: PrimitiveHeaderIndex, polygons_address: GpuCacheAddress, z: ZBufferId, ) -> Self { SplitCompositeInstance { - task_address, - src_task_address, + prim_header_index, polygons_address, z, } @@ -285,10 +284,10 @@ impl From for PrimitiveInstance { fn from(instance: SplitCompositeInstance) -> Self { PrimitiveInstance { data: [ - instance.task_address.0 as i32, - instance.src_task_address.0 as i32, + instance.prim_header_index.0, instance.polygons_address.as_int(), instance.z.0, + 0, ], } } @@ -353,11 +352,6 @@ impl TransformPaletteId { /// Identity transform ID. pub const IDENTITY: Self = TransformPaletteId(0); - /// Extract the spatial node index from the id. - pub fn spatial_node_index(&self) -> SpatialNodeIndex { - SpatialNodeIndex(self.0 as usize & 0xFFFFFF) - } - /// Extract the transform kind from the id. pub fn transform_kind(&self) -> TransformedRectKind { if (self.0 >> 24) == 0 { @@ -369,29 +363,44 @@ impl TransformPaletteId { } // The GPU data payload for a transform palette entry. -#[derive(Debug)] +#[derive(Debug, Clone)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[repr(C)] pub struct TransformData { - transform: LayoutToWorldTransform, - inv_transform: WorldToLayoutTransform, + transform: LayoutToPictureTransform, + inv_transform: PictureToLayoutTransform, } impl TransformData { fn invalid() -> Self { TransformData { - transform: LayoutToWorldTransform::identity(), - inv_transform: WorldToLayoutTransform::identity(), + transform: LayoutToPictureTransform::identity(), + inv_transform: PictureToLayoutTransform::identity(), } } } // Extra data stored about each transform palette entry. +#[derive(Clone)] pub struct TransformMetadata { transform_kind: TransformedRectKind, } +impl TransformMetadata { + pub fn invalid() -> Self { + TransformMetadata { + transform_kind: TransformedRectKind::AxisAligned, + } + } +} + +#[derive(Debug, Hash, Eq, PartialEq)] +struct RelativeTransformKey { + from_index: SpatialNodeIndex, + to_index: SpatialNodeIndex, +} + // Stores a contiguous list of TransformData structs, that // are ready for upload to the GPU. // TODO(gw): For now, this only stores the complete local @@ -402,89 +411,80 @@ pub struct TransformMetadata { pub struct TransformPalette { pub transforms: Vec, metadata: Vec, + map: FastHashMap, } impl TransformPalette { pub fn new(spatial_node_count: usize) -> Self { TransformPalette { - transforms: Vec::with_capacity(spatial_node_count), - metadata: Vec::with_capacity(spatial_node_count), + transforms: vec![TransformData::invalid(); spatial_node_count], + metadata: vec![TransformMetadata::invalid(); spatial_node_count], + map: FastHashMap::default(), } } - #[inline] - fn grow(&mut self, index: SpatialNodeIndex) { - // Pad the vectors out if they are not long enough to - // account for this index. This can occur, for instance, - // when we stop recursing down the CST due to encountering - // a node with an invalid transform. - while self.transforms.len() <= index.0 as usize { - self.transforms.push(TransformData::invalid()); - self.metadata.push(TransformMetadata { - transform_kind: TransformedRectKind::AxisAligned, - }); - } - } - - pub fn invalidate(&mut self, index: SpatialNodeIndex) { - self.grow(index); - self.metadata[index.0 as usize] = TransformMetadata { - transform_kind: TransformedRectKind::AxisAligned, - }; - self.transforms[index.0 as usize] = TransformData::invalid(); + pub fn set_world_transform( + &mut self, + index: SpatialNodeIndex, + transform: LayoutToWorldTransform, + ) { + register_transform( + &mut self.metadata, + &mut self.transforms, + index, + ROOT_SPATIAL_NODE_INDEX, + // We know the root picture space == world space + transform.with_destination::(), + ); } - // Set the local -> world transform for a given spatial - // node in the transform palette. - pub fn set( - &mut self, index: SpatialNodeIndex, fast_transform: &LayoutToWorldFastTransform, - ) -> bool { - self.grow(index); - - match fast_transform.inverse() { - Some(inverted) => { - // Store the transform itself, along with metadata about it. - self.metadata[index.0 as usize] = TransformMetadata { - transform_kind: fast_transform.kind() - }; - // Write the data that will be made available to the GPU for this node. - self.transforms[index.0 as usize] = TransformData { - transform: fast_transform.to_transform().into_owned(), - inv_transform: inverted.to_transform().into_owned(), - }; - true - } - None => { - self.invalidate(index); - false - } + fn get_index( + &mut self, + from_index: SpatialNodeIndex, + to_index: SpatialNodeIndex, + clip_scroll_tree: &ClipScrollTree, + ) -> usize { + if to_index == ROOT_SPATIAL_NODE_INDEX { + from_index.0 + } else if from_index == to_index { + 0 + } else { + let key = RelativeTransformKey { + from_index, + to_index, + }; + + let metadata = &mut self.metadata; + let transforms = &mut self.transforms; + + *self.map + .entry(key) + .or_insert_with(|| { + let transform = clip_scroll_tree.get_relative_transform( + from_index, + to_index, + ) + .unwrap_or(LayoutTransform::identity()) + .with_destination::(); + + register_transform( + metadata, + transforms, + from_index, + to_index, + transform, + ) + }) } } - // Get the relevant information about a given transform that is - // used by the CPU code during culling and primitive prep pass. - // TODO(gw): In the future, it will be possible to specify - // a coordinate system id here, to allow retrieving - // transforms in the local space of a given spatial node. - pub fn get_transform( + pub fn get_world_transform( &self, index: SpatialNodeIndex, - ) -> Transform { - let data = &self.transforms[index.0 as usize]; - let metadata = &self.metadata[index.0 as usize]; - - Transform { - m: data.transform, - transform_kind: metadata.transform_kind, - backface_is_visible: data.transform.is_backface_visible(), - } - } - - pub fn get_transform_by_id( - &self, - id: TransformPaletteId, - ) -> Transform { - self.get_transform(id.spatial_node_index()) + ) -> LayoutToWorldTransform { + self.transforms[index.0] + .transform + .with_destination::() } // Get a transform palette id for the given spatial node. @@ -492,12 +492,19 @@ impl TransformPalette { // a coordinate system id here, to allow retrieving // transforms in the local space of a given spatial node. pub fn get_id( - &self, - index: SpatialNodeIndex, + &mut self, + from_index: SpatialNodeIndex, + to_index: SpatialNodeIndex, + clip_scroll_tree: &ClipScrollTree, ) -> TransformPaletteId { - let transform_kind = self.metadata[index.0 as usize].transform_kind as u32; + let index = self.get_index( + from_index, + to_index, + clip_scroll_tree, + ); + let transform_kind = self.metadata[index].transform_kind as u32; TransformPaletteId( - (index.0 as u32) | + (index as u32) | (transform_kind << 24) ) } @@ -570,3 +577,44 @@ impl ImageSource { } } } + +// Set the local -> world transform for a given spatial +// node in the transform palette. +fn register_transform( + metadatas: &mut Vec, + transforms: &mut Vec, + from_index: SpatialNodeIndex, + to_index: SpatialNodeIndex, + transform: LayoutToPictureTransform, +) -> usize { + // TODO(gw): This shouldn't ever happen - should be eliminated before + // we get an uninvertible transform here. But maybe do + // some investigation on if this ever happens? + let inv_transform = match transform.inverse() { + Some(inv_transform) => inv_transform, + None => { + error!("Unable to get inverse transform"); + PictureToLayoutTransform::identity() + } + }; + + let metadata = TransformMetadata { + transform_kind: transform.transform_kind() + }; + let data = TransformData { + transform, + inv_transform, + }; + + if to_index == ROOT_SPATIAL_NODE_INDEX { + let index = from_index.0 as usize; + metadatas[index] = metadata; + transforms[index] = data; + index + } else { + let index = transforms.len(); + metadatas.push(metadata); + transforms.push(data); + index + } +} diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 6b869ea722..c1fa948dc5 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -2,22 +2,26 @@ * 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::{DeviceRect, FilterOp, MixBlendMode, PipelineId, PremultipliedColorF, PictureRect}; -use api::{DeviceIntRect, DeviceIntSize, DevicePoint, LayoutPoint, LayoutRect}; -use api::{DevicePixelScale, PictureIntPoint, PictureIntRect, PictureIntSize}; +use api::{DeviceRect, FilterOp, MixBlendMode, PipelineId, PremultipliedColorF, PictureRect, PicturePoint}; +use api::{DeviceIntRect, DeviceIntSize, DevicePoint, LayoutRect, PictureToRasterTransform}; +use api::{DevicePixelScale, PictureIntPoint, PictureIntRect, PictureIntSize, RasterRect}; +use api::{PicturePixel, RasterPixel, WorldPixel}; use box_shadow::{BLUR_SAMPLE_SCALE}; +use clip::ClipNodeCollector; +use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex}; +use euclid::TypedScale; use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState}; use frame_builder::{PictureContext, PrimitiveContext}; use gpu_cache::{GpuCacheHandle}; use gpu_types::UvRectKind; -use prim_store::{PrimitiveIndex, PrimitiveRun}; -use prim_store::{PrimitiveMetadata, Transform}; +use prim_store::{PrimitiveIndex, PrimitiveRun, SpaceMapper}; +use prim_store::{PrimitiveMetadata, get_raster_rects}; use render_task::{ClearMode, RenderTask, RenderTaskCacheEntryHandle}; use render_task::{RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation}; use scene::{FilterOpHelpers, SceneProperties}; use std::mem; use tiling::RenderTargetKind; -use util::{TransformedRectKind, world_rect_to_device_pixels}; +use util::{TransformedRectKind, MatrixHelpers, MaxRect}; /* A picture represents a dynamically rendered image. It consists of: @@ -36,6 +40,10 @@ pub struct RasterConfig { // If this picture is drawn to an intermediate surface, // the associated target information. pub surface: Option, + + // The spatial node of the rasterization root + // for this picture. + pub raster_spatial_node_index: SpatialNodeIndex, } /// Specifies how this Picture should be composited @@ -204,11 +212,15 @@ impl PicturePrimitive { pub fn take_context( &mut self, + prim_context: &PrimitiveContext, + surface_spatial_node_index: SpatialNodeIndex, + raster_spatial_node_index: SpatialNodeIndex, parent_allows_subpixel_aa: bool, - scene_properties: &SceneProperties, + frame_state: &mut FrameBuildingState, + frame_context: &FrameBuildingContext, is_chased: bool, - ) -> Option { - if !self.resolve_scene_properties(scene_properties) { + ) -> Option<(PictureContext, PictureState)> { + if !self.resolve_scene_properties(frame_context.scene_properties) { if cfg!(debug_assertions) && is_chased { println!("\tculled for carrying an invisible composite filter"); } @@ -221,13 +233,73 @@ impl PicturePrimitive { mode => mode, }; + let has_surface = actual_composite_mode.is_some(); + + let surface_spatial_node_index = if has_surface { + prim_context.spatial_node_index + } else { + surface_spatial_node_index + }; + + let xf = frame_context.clip_scroll_tree.get_relative_transform( + raster_spatial_node_index, + surface_spatial_node_index, + ).expect("todo"); + + let establishes_raster_root = has_surface && xf.has_perspective_component(); + + let raster_spatial_node_index = if establishes_raster_root { + surface_spatial_node_index + } else { + raster_spatial_node_index + }; + + if establishes_raster_root { + frame_state.clip_store + .push_raster_root(raster_spatial_node_index); + } + + let map_pic_to_world = SpaceMapper::new_with_target( + ROOT_SPATIAL_NODE_INDEX, + surface_spatial_node_index, + frame_context.world_rect, + frame_context.clip_scroll_tree, + ); + + let pic_bounds = map_pic_to_world.unmap(&map_pic_to_world.bounds) + .unwrap_or(PictureRect::max_rect()); + + let map_local_to_pic = SpaceMapper::new( + surface_spatial_node_index, + pic_bounds, + ); + + let (map_raster_to_world, map_pic_to_raster) = create_raster_mappers( + surface_spatial_node_index, + raster_spatial_node_index, + frame_context, + ); + self.raster_config = actual_composite_mode.map(|composite_mode| { RasterConfig { composite_mode, surface: None, + raster_spatial_node_index, } }); + let state = PictureState { + tasks: Vec::new(), + has_non_root_coord_system: false, + local_rect_changed: false, + raster_spatial_node_index, + surface_spatial_node_index, + map_local_to_pic, + map_pic_to_world, + map_pic_to_raster, + map_raster_to_world, + }; + // Disallow subpixel AA if an intermediate surface is needed. // TODO(lsalzman): allow overriding parent if intermediate surface is opaque let allow_subpixel_aa = parent_allows_subpixel_aa && @@ -244,14 +316,17 @@ impl PicturePrimitive { } }; - Some(PictureContext { + let context = PictureContext { pipeline_id: self.pipeline_id, prim_runs: mem::replace(&mut self.runs, Vec::new()), apply_local_clip_rect: self.apply_local_clip_rect, inflation_factor, allow_subpixel_aa, - has_surface: self.raster_config.is_some(), - }) + is_passthrough: self.raster_config.is_none(), + establishes_raster_root, + }; + + Some((context, state)) } pub fn add_primitive( @@ -276,11 +351,12 @@ impl PicturePrimitive { context: PictureContext, state: PictureState, local_rect: Option, - ) -> LayoutRect { + frame_state: &mut FrameBuildingState, + ) -> (LayoutRect, Option) { self.runs = context.prim_runs; self.state = Some(state); - match local_rect { + let local_rect = match local_rect { Some(local_rect) => { let local_content_rect = LayoutRect::from_untyped(&local_rect.to_untyped()); @@ -312,7 +388,15 @@ impl PicturePrimitive { assert!(self.raster_config.is_none()); LayoutRect::zero() } - } + }; + + let clip_node_collector = if context.establishes_raster_root { + Some(frame_state.clip_store.pop_raster_root()) + } else { + None + }; + + (local_rect, clip_node_collector) } pub fn take_state(&mut self) -> PictureState { @@ -323,36 +407,32 @@ impl PicturePrimitive { &mut self, prim_index: PrimitiveIndex, prim_metadata: &mut PrimitiveMetadata, - prim_context: &PrimitiveContext, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, - ) { + ) -> bool { let mut pic_state_for_children = self.take_state(); match self.raster_config { Some(ref mut raster_config) => { - let clipped_world_rect = prim_metadata - .clipped_world_rect - .as_ref() - .expect("bug: trying to draw an off-screen picture!?"); - - let clipped = world_rect_to_device_pixels( - *clipped_world_rect, - frame_context.device_pixel_scale, - ).to_i32(); + let (map_raster_to_world, map_pic_to_raster) = create_raster_mappers( + prim_metadata.spatial_node_index, + raster_config.raster_spatial_node_index, + frame_context, + ); - let pic_rect = pic_state.map_local_to_pic - .map(&prim_metadata.local_rect) - .unwrap(); - let world_rect = pic_state.map_pic_to_world - .map(&pic_rect) - .unwrap(); + let pic_rect = PictureRect::from_untyped(&prim_metadata.local_rect.to_untyped()); - let unclipped = world_rect_to_device_pixels( - world_rect, + let (clipped, unclipped, transform) = match get_raster_rects( + pic_rect, + &map_pic_to_raster, + &map_raster_to_world, + prim_metadata.clipped_world_rect.expect("bug1"), frame_context.device_pixel_scale, - ); + ) { + Some(info) => info, + None => return false, + }; // TODO(gw): Almost all of the Picture types below use extra_gpu_cache_data // to store the same type of data. The exception is the filter @@ -380,8 +460,8 @@ impl PicturePrimitive { .unwrap(); let uv_rect_kind = calculate_uv_rect_kind( - &prim_metadata.local_rect, - &prim_context.transform, + &pic_rect, + &transform, &device_rect, frame_context.device_pixel_scale, ); @@ -400,6 +480,7 @@ impl PicturePrimitive { device_rect.origin, pic_state_for_children.tasks, uv_rect_kind, + pic_state_for_children.raster_spatial_node_index, ); let picture_task_id = frame_state.render_tasks.add(picture_task); @@ -457,6 +538,7 @@ impl PicturePrimitive { device_rect.origin, child_tasks, uv_rect_kind, + pic_state_for_children.raster_spatial_node_index, ); let picture_task_id = render_tasks.add(picture_task); @@ -500,8 +582,8 @@ impl PicturePrimitive { .unwrap(); let uv_rect_kind = calculate_uv_rect_kind( - &prim_metadata.local_rect, - &prim_context.transform, + &pic_rect, + &transform, &device_rect, frame_context.device_pixel_scale, ); @@ -513,6 +595,7 @@ impl PicturePrimitive { device_rect.origin, pic_state_for_children.tasks, uv_rect_kind, + pic_state_for_children.raster_spatial_node_index, ); picture_task.mark_for_saving(); @@ -570,8 +653,8 @@ impl PicturePrimitive { } PictureCompositeMode::MixBlend(..) => { let uv_rect_kind = calculate_uv_rect_kind( - &prim_metadata.local_rect, - &prim_context.transform, + &pic_rect, + &transform, &clipped, frame_context.device_pixel_scale, ); @@ -583,6 +666,7 @@ impl PicturePrimitive { clipped.origin, pic_state_for_children.tasks, uv_rect_kind, + pic_state_for_children.raster_spatial_node_index, ); let readback_task_id = frame_state.render_tasks.add( @@ -606,8 +690,8 @@ impl PicturePrimitive { } let uv_rect_kind = calculate_uv_rect_kind( - &prim_metadata.local_rect, - &prim_context.transform, + &pic_rect, + &transform, &clipped, frame_context.device_pixel_scale, ); @@ -619,6 +703,7 @@ impl PicturePrimitive { clipped.origin, pic_state_for_children.tasks, uv_rect_kind, + pic_state_for_children.raster_spatial_node_index, ); let render_task_id = frame_state.render_tasks.add(picture_task); @@ -627,8 +712,8 @@ impl PicturePrimitive { } PictureCompositeMode::Blit => { let uv_rect_kind = calculate_uv_rect_kind( - &prim_metadata.local_rect, - &prim_context.transform, + &pic_rect, + &transform, &clipped, frame_context.device_pixel_scale, ); @@ -640,6 +725,7 @@ impl PicturePrimitive { clipped.origin, pic_state_for_children.tasks, uv_rect_kind, + pic_state_for_children.raster_spatial_node_index, ); let render_task_id = frame_state.render_tasks.add(picture_task); @@ -652,17 +738,19 @@ impl PicturePrimitive { pic_state.tasks.extend(pic_state_for_children.tasks); } } + + true } } // Calculate a single screen-space UV for a picture. fn calculate_screen_uv( - local_pos: &LayoutPoint, - transform: &Transform, + local_pos: &PicturePoint, + transform: &PictureToRasterTransform, rendered_rect: &DeviceRect, device_pixel_scale: DevicePixelScale, ) -> DevicePoint { - let world_pos = match transform.m.transform_point2d(local_pos) { + let raster_pos = match transform.transform_point2d(local_pos) { Some(pos) => pos, None => { //Warning: this is incorrect and needs to be fixed properly. @@ -673,10 +761,12 @@ fn calculate_screen_uv( } }; - let mut device_pos = world_pos * device_pixel_scale; + let raster_to_device_space = TypedScale::new(1.0) * device_pixel_scale; + + let mut device_pos = raster_pos * raster_to_device_space; // Apply snapping for axis-aligned scroll nodes, as per prim_shared.glsl. - if transform.transform_kind == TransformedRectKind::AxisAligned { + if transform.transform_kind() == TransformedRectKind::AxisAligned { device_pos.x = (device_pos.x + 0.5).floor(); device_pos.y = (device_pos.y + 0.5).floor(); } @@ -690,36 +780,36 @@ fn calculate_screen_uv( // Calculate a UV rect within an image based on the screen space // vertex positions of a picture. fn calculate_uv_rect_kind( - local_rect: &LayoutRect, - transform: &Transform, + pic_rect: &PictureRect, + transform: &PictureToRasterTransform, rendered_rect: &DeviceIntRect, device_pixel_scale: DevicePixelScale, ) -> UvRectKind { let rendered_rect = rendered_rect.to_f32(); let top_left = calculate_screen_uv( - &local_rect.origin, + &pic_rect.origin, transform, &rendered_rect, device_pixel_scale, ); let top_right = calculate_screen_uv( - &local_rect.top_right(), + &pic_rect.top_right(), transform, &rendered_rect, device_pixel_scale, ); let bottom_left = calculate_screen_uv( - &local_rect.bottom_left(), + &pic_rect.bottom_left(), transform, &rendered_rect, device_pixel_scale, ); let bottom_right = calculate_screen_uv( - &local_rect.bottom_right(), + &pic_rect.bottom_right(), transform, &rendered_rect, device_pixel_scale, @@ -732,3 +822,28 @@ fn calculate_uv_rect_kind( bottom_right, } } + +fn create_raster_mappers( + surface_spatial_node_index: SpatialNodeIndex, + raster_spatial_node_index: SpatialNodeIndex, + frame_context: &FrameBuildingContext, +) -> (SpaceMapper, SpaceMapper) { + let map_raster_to_world = SpaceMapper::new_with_target( + ROOT_SPATIAL_NODE_INDEX, + raster_spatial_node_index, + frame_context.world_rect, + frame_context.clip_scroll_tree, + ); + + let raster_bounds = map_raster_to_world.unmap(&frame_context.world_rect) + .unwrap_or(RasterRect::max_rect()); + + let map_pic_to_raster = SpaceMapper::new_with_target( + raster_spatial_node_index, + surface_spatial_node_index, + raster_bounds, + frame_context.clip_scroll_tree, + ); + + (map_raster_to_world, map_pic_to_raster) +} diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 0438ea5b04..83a9c43d7e 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -3,15 +3,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{AlphaType, BorderRadius, BuiltDisplayList, ClipMode, ColorF, PictureRect}; -use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, ExtendMode}; +use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, ExtendMode, DeviceRect, PictureToRasterTransform}; use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, TileOffset}; use api::{GlyphRasterSpace, LayoutPoint, LayoutRect, LayoutSize, LayoutToWorldTransform, LayoutVector2D}; -use api::{PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat, DeviceIntSideOffsets}; -use api::{BorderWidths, BoxShadowClipMode, LayoutToWorldScale, NormalBorder, WorldRect}; +use api::{PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat, DeviceIntSideOffsets, WorldPixel}; +use api::{BorderWidths, BoxShadowClipMode, LayoutToWorldScale, NormalBorder, WorldRect, PicturePixel, RasterPixel}; use app_units::Au; use border::{BorderCacheKey, BorderRenderTaskInfo}; use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId, SpatialNodeIndex}; -use clip::{ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem}; +use clip::{ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem, ClipNodeCollector}; use euclid::{TypedVector2D, TypedTransform3D, TypedRect}; use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState}; use frame_builder::PrimitiveContext; @@ -30,8 +30,7 @@ use resource_cache::{ImageProperties, ImageRequest, ResourceCache}; use scene::SceneProperties; use segment::SegmentBuilder; use std::{cmp, fmt, mem, usize}; -use util::{MatrixHelpers, pack_as_float, recycle_vec, project_rect}; -use util::{TransformedRectKind, world_rect_to_device_pixels}; +use util::{MatrixHelpers, pack_as_float, recycle_vec, project_rect, raster_rect_to_device_pixels}; const MIN_BRUSH_SPLIT_AREA: f32 = 256.0 * 256.0; @@ -55,17 +54,6 @@ impl ScrollNodeAndClipChain { } } -// This is CPU-side information about a transform, that is relevant -// during culling and primitive prep pass. Often it is the same as -// the information in the clip-scroll tree. However, if we decide -// to rasterize a picture in local space, then this will be the -// transform relative to that picture's coordinate system. -pub struct Transform { - pub m: LayoutToWorldTransform, - pub backface_is_visible: bool, - pub transform_kind: TransformedRectKind, -} - #[derive(Debug)] pub struct PrimitiveRun { pub base_prim_index: PrimitiveIndex, @@ -115,8 +103,8 @@ pub enum CoordinateSpaceMapping { pub struct SpaceMapper { kind: CoordinateSpaceMapping, pub ref_spatial_node_index: SpatialNodeIndex, - current_target_spatial_node_index: SpatialNodeIndex, - bounds: TypedRect, + pub current_target_spatial_node_index: SpatialNodeIndex, + pub bounds: TypedRect, } impl SpaceMapper where F: fmt::Debug { @@ -132,6 +120,17 @@ impl SpaceMapper where F: fmt::Debug { } } + pub fn new_with_target( + ref_spatial_node_index: SpatialNodeIndex, + target_node_index: SpatialNodeIndex, + bounds: TypedRect, + clip_scroll_tree: &ClipScrollTree, + ) -> Self { + let mut mapper = SpaceMapper::new(ref_spatial_node_index, bounds); + mapper.set_target_spatial_node(target_node_index, clip_scroll_tree); + mapper + } + pub fn set_target_spatial_node( &mut self, target_node_index: SpatialNodeIndex, @@ -166,6 +165,20 @@ impl SpaceMapper where F: fmt::Debug { } } + pub fn get_transform(&self) -> TypedTransform3D { + match self.kind { + CoordinateSpaceMapping::Local => { + TypedTransform3D::identity() + } + CoordinateSpaceMapping::Offset(offset) => { + TypedTransform3D::create_translation(offset.x, offset.y, 0.0) + } + CoordinateSpaceMapping::Transform(transform) => { + transform + } + } + } + pub fn unmap(&self, rect: &TypedRect) -> Option> { match self.kind { CoordinateSpaceMapping::Local => { @@ -1587,7 +1600,6 @@ impl PrimitiveStore { frame_state: &mut FrameBuildingState, display_list: &BuiltDisplayList, is_chased: bool, - root_spatial_node_index: SpatialNodeIndex, current_pic_rect: &mut PictureRect, ) -> bool { // If we have dependencies, we need to prepare them first, in order @@ -1595,15 +1607,19 @@ impl PrimitiveStore { // 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 pic_context_for_children = { + let pic_info = { match self.primitives[prim_index.0].details { PrimitiveDetails::Brush(BrushPrimitive { kind: BrushKind::Picture(ref mut pic), .. }) => { match pic.take_context( + prim_context, + pic_state.surface_spatial_node_index, + pic_state.raster_spatial_node_index, pic_context.allow_subpixel_aa, - frame_context.scene_properties, + frame_state, + frame_context, is_chased, ) { - Some(pic_context_for_children) => Some(pic_context_for_children), + Some(info) => Some(info), None => return false, } } @@ -1614,23 +1630,10 @@ impl PrimitiveStore { } }; - let is_passthrough = match pic_context_for_children { - Some(pic_context_for_children) => { - let has_surface = pic_context_for_children.has_surface; - - let root_spatial_node_index = if has_surface { - prim_context.spatial_node_index - } else { - root_spatial_node_index - }; - - let mut pic_state_for_children = PictureState::new( - root_spatial_node_index, - frame_context.clip_scroll_tree, - frame_context.world_rect, - ); - + let (is_passthrough, clip_node_collector) = match pic_info { + Some((pic_context_for_children, mut pic_state_for_children)) => { // Mark whether this picture has a complex coordinate system. + let is_passthrough = pic_context_for_children.is_passthrough; pic_state_for_children.has_non_root_coord_system |= prim_context.spatial_node.coordinate_system_id != CoordinateSystemId::root(); @@ -1640,25 +1643,25 @@ impl PrimitiveStore { &mut pic_state_for_children, frame_context, frame_state, - root_spatial_node_index, &mut pic_rect, ); - let pic_rect = if has_surface { - Some(pic_rect) - } else { + let pic_rect = if is_passthrough { *current_pic_rect = current_pic_rect.union(&pic_rect); None + } else { + Some(pic_rect) }; // Restore the dependencies (borrow check dance) let prim = &mut self.primitives[prim_index.0]; - let new_local_rect = prim + let (new_local_rect, clip_node_collector) = prim .as_pic_mut() .restore_context( pic_context_for_children, pic_state_for_children, pic_rect, + frame_state, ); if new_local_rect != prim.metadata.local_rect { @@ -1667,17 +1670,17 @@ impl PrimitiveStore { pic_state.local_rect_changed = true; } - !has_surface + (is_passthrough, clip_node_collector) } None => { - false + (false, None) } }; let prim = &mut self.primitives[prim_index.0]; if is_passthrough { - prim.metadata.clipped_world_rect = Some(frame_context.world_rect); + prim.metadata.clipped_world_rect = Some(pic_state.map_pic_to_world.bounds); } else { if prim.metadata.local_rect.size.width <= 0.0 || prim.metadata.local_rect.size.height <= 0.0 { @@ -1721,6 +1724,7 @@ impl PrimitiveStore { frame_state.resource_cache, frame_context.device_pixel_scale, &frame_context.world_rect, + &clip_node_collector, ); let clip_chain = match clip_chain { @@ -1780,11 +1784,13 @@ impl PrimitiveStore { prim.update_clip_task( prim_context, clipped_world_rect, + pic_state.raster_spatial_node_index, &clip_chain, pic_state, frame_context, frame_state, is_chased, + &clip_node_collector, ); if cfg!(debug_assertions) && is_chased { @@ -1822,7 +1828,6 @@ impl PrimitiveStore { pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, - root_spatial_node_index: SpatialNodeIndex, current_pic_rect: &mut PictureRect, ) { let display_list = &frame_context @@ -1853,22 +1858,17 @@ impl PrimitiveStore { .clip_scroll_tree .spatial_nodes[spatial_node_index.0]; - let transform = frame_context - .transforms - .get_transform(spatial_node_index); - // TODO(gw): Although constructing these is cheap, they are often // the same for many consecutive primitives, so it may // be worth caching the most recent context. let prim_context = PrimitiveContext::new( spatial_node, spatial_node_index, - transform, ); // Do some basic checks first, that can early out // without even knowing the local rect. - if !is_backface_visible && prim_context.transform.backface_is_visible { + if !is_backface_visible && spatial_node.world_content_transform.is_backface_visible() { if cfg!(debug_assertions) && is_chased { println!("\tculled for not having visible back faces"); } @@ -1889,7 +1889,7 @@ impl PrimitiveStore { pic_state.map_local_to_pic.set_target_spatial_node( spatial_node_index, - &frame_context.clip_scroll_tree, + frame_context.clip_scroll_tree, ); if self.prepare_prim_for_render( @@ -1901,7 +1901,6 @@ impl PrimitiveStore { frame_state, display_list, is_chased, - root_spatial_node_index, current_pic_rect, ) { frame_state.profile_counters.visible_primitives.inc(); @@ -2197,12 +2196,14 @@ fn write_brush_segment_description( impl Primitive { fn update_clip_task_for_brush( &mut self, - clipped_world_rect: &WorldRect, + root_spatial_node_index: SpatialNodeIndex, + prim_bounding_rect: WorldRect, prim_context: &PrimitiveContext, prim_clip_chain: &ClipChainInstance, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, + clip_node_collector: &Option, ) -> bool { let brush = match self.details { PrimitiveDetails::Brush(ref mut brush) => brush, @@ -2239,6 +2240,7 @@ impl Primitive { frame_state.resource_cache, frame_context.device_pixel_scale, &frame_context.world_rect, + clip_node_collector, ); match segment_clip_chain { @@ -2249,30 +2251,24 @@ impl Primitive { continue; } - let world_clip_rect = match pic_state.map_pic_to_world.map(&segment_clip_chain.pic_clip_rect) { - Some(world_clip_rect) => world_clip_rect, - None => { - segment.clip_task_id = BrushSegmentTaskId::Empty; - continue; - } - }; - - let world_clip_rect = match world_clip_rect.intersection(clipped_world_rect) { - Some(world_clip_rect) => world_clip_rect, + let (device_rect, _, _) = match get_raster_rects( + segment_clip_chain.pic_clip_rect, + &pic_state.map_pic_to_raster, + &pic_state.map_raster_to_world, + prim_bounding_rect, + frame_context.device_pixel_scale, + ) { + Some(info) => info, None => { segment.clip_task_id = BrushSegmentTaskId::Empty; continue; } }; - let bounds = world_rect_to_device_pixels( - world_clip_rect, - frame_context.device_pixel_scale, - ); - let clip_task = RenderTask::new_mask( - bounds.to_i32(), + device_rect.to_i32(), segment_clip_chain.clips_range, + root_spatial_node_index, frame_state.clip_store, frame_state.gpu_cache, frame_state.resource_cache, @@ -2696,14 +2692,15 @@ impl Primitive { } } BrushKind::Picture(ref mut pic) => { - pic.prepare_for_render( + if !pic.prepare_for_render( prim_index, metadata, - prim_context, pic_state, frame_context, frame_state, - ); + ) { + metadata.clipped_world_rect = None; + } } BrushKind::Solid { ref color, ref mut opacity_binding, .. } => { // If the opacity changed, invalidate the GPU cache so that @@ -2762,12 +2759,14 @@ impl Primitive { fn update_clip_task( &mut self, prim_context: &PrimitiveContext, - clipped_world_rect: WorldRect, + prim_bounding_rect: WorldRect, + root_spatial_node_index: SpatialNodeIndex, clip_chain: &ClipChainInstance, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, is_chased: bool, + clip_node_collector: &Option, ) { if cfg!(debug_assertions) && is_chased { println!("\tupdating clip task with pic rect {:?}", clip_chain.pic_clip_rect); @@ -2780,12 +2779,14 @@ impl Primitive { // First try to render this primitive's mask using optimized brush rendering. if self.update_clip_task_for_brush( - &clipped_world_rect, + root_spatial_node_index, + prim_bounding_rect, prim_context, &clip_chain, pic_state, frame_context, frame_state, + clip_node_collector, ) { if cfg!(debug_assertions) && is_chased { println!("\tsegment tasks have been created for clipping"); @@ -2794,27 +2795,31 @@ impl Primitive { } if clip_chain.clips_range.count > 0 { - let device_rect = world_rect_to_device_pixels( - clipped_world_rect, + if let Some((device_rect, _, _)) = get_raster_rects( + clip_chain.pic_clip_rect, + &pic_state.map_pic_to_raster, + &pic_state.map_raster_to_world, + prim_bounding_rect, frame_context.device_pixel_scale, - ); - - let clip_task = RenderTask::new_mask( - device_rect.to_i32(), - clip_chain.clips_range, - frame_state.clip_store, - frame_state.gpu_cache, - frame_state.resource_cache, - frame_state.render_tasks, - ); + ) { + let clip_task = RenderTask::new_mask( + device_rect, + clip_chain.clips_range, + root_spatial_node_index, + frame_state.clip_store, + frame_state.gpu_cache, + frame_state.resource_cache, + frame_state.render_tasks, + ); - let clip_task_id = frame_state.render_tasks.add(clip_task); - if cfg!(debug_assertions) && is_chased { - println!("\tcreated task {:?} with world rect {:?}", - clip_task_id, clipped_world_rect); + let clip_task_id = frame_state.render_tasks.add(clip_task); + if cfg!(debug_assertions) && is_chased { + println!("\tcreated task {:?} with world rect {:?}", + clip_task_id, self.metadata.clipped_world_rect); + } + self.metadata.clip_task_id = Some(clip_task_id); + pic_state.tasks.push(clip_task_id); } - self.metadata.clip_task_id = Some(clip_task_id); - pic_state.tasks.push(clip_task_id); } } @@ -2899,3 +2904,35 @@ impl Primitive { } } } + +pub fn get_raster_rects( + pic_rect: PictureRect, + map_to_raster: &SpaceMapper, + map_to_world: &SpaceMapper, + prim_bounding_rect: WorldRect, + device_pixel_scale: DevicePixelScale, +) -> Option<(DeviceIntRect, DeviceRect, PictureToRasterTransform)> { + let unclipped_raster_rect = map_to_raster.map(&pic_rect)?; + + let unclipped = raster_rect_to_device_pixels( + unclipped_raster_rect, + device_pixel_scale, + ); + + let unclipped_world_rect = map_to_world.map(&unclipped_raster_rect)?; + + let clipped_world_rect = unclipped_world_rect.intersection(&prim_bounding_rect)?; + + let clipped_raster_rect = map_to_world.unmap(&clipped_world_rect)?; + + let clipped_raster_rect = clipped_raster_rect.intersection(&unclipped_raster_rect)?; + + let clipped = raster_rect_to_device_pixels( + clipped_raster_rect, + device_pixel_scale, + ); + + let transform = map_to_raster.get_transform(); + + Some((clipped.to_i32(), unclipped, transform)) +} diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 63cc470bd3..94442986e9 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -1098,10 +1098,7 @@ impl RenderBackend { let debug_node = debug_server::TreeNode::new("document clip-scroll tree"); let mut builder = debug_server::TreeNodeBuilder::new(debug_node); - // TODO(gw): Restructure the storage of clip-scroll tree, clip store - // etc so this isn't so untidy. - let clip_store = &doc.frame_builder.as_ref().unwrap().clip_store; - doc.clip_scroll_tree.print_with(clip_store, &mut builder); + doc.clip_scroll_tree.print_with(&mut builder); debug_root.add(builder.build()); } diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index 9b7f037696..539a5137f3 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -8,6 +8,7 @@ use api::FontRenderMode; use border::BorderCacheKey; use box_shadow::{BoxShadowCacheKey}; use clip::{ClipItem, ClipStore, ClipNodeRange}; +use clip_scroll_tree::SpatialNodeIndex; use device::TextureFilter; #[cfg(feature = "pathfinder")] use euclid::{TypedPoint2D, TypedVector2D}; @@ -187,6 +188,7 @@ pub enum RenderTaskLocation { #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct CacheMaskTask { actual_rect: DeviceIntRect, + pub root_spatial_node_index: SpatialNodeIndex, pub clip_node_range: ClipNodeRange, } @@ -205,6 +207,7 @@ pub struct PictureTask { pub can_merge: bool, pub content_origin: DeviceIntPoint, pub uv_rect_handle: GpuCacheHandle, + pub root_spatial_node_index: SpatialNodeIndex, uv_rect_kind: UvRectKind, } @@ -346,6 +349,7 @@ impl RenderTask { content_origin: DeviceIntPoint, children: Vec, uv_rect_kind: UvRectKind, + root_spatial_node_index: SpatialNodeIndex, ) -> Self { let size = match location { RenderTaskLocation::Dynamic(_, size) => size, @@ -367,6 +371,7 @@ impl RenderTask { can_merge, uv_rect_handle: GpuCacheHandle::new(), uv_rect_kind, + root_spatial_node_index, }), clear_mode: ClearMode::Transparent, saved_index: None, @@ -422,6 +427,7 @@ impl RenderTask { pub fn new_mask( outer_rect: DeviceIntRect, clip_node_range: ClipNodeRange, + root_spatial_node_index: SpatialNodeIndex, clip_store: &mut ClipStore, gpu_cache: &mut GpuCache, resource_cache: &mut ResourceCache, @@ -498,6 +504,7 @@ impl RenderTask { RenderTaskKind::CacheMask(CacheMaskTask { actual_rect: outer_rect, clip_node_range, + root_spatial_node_index, }), ClearMode::One, ) diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 7f49b78b9b..ce44f8d825 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -441,7 +441,12 @@ pub(crate) mod desc { kind: VertexAttributeKind::I32, }, VertexAttribute { - name: "aScrollNodeId", + name: "aClipTransformId", + count: 1, + kind: VertexAttributeKind::I32, + }, + VertexAttribute { + name: "aPrimTransformId", count: 1, kind: VertexAttributeKind::I32, }, diff --git a/webrender/src/spatial_node.rs b/webrender/src/spatial_node.rs index cd6e92be14..51385ac10e 100644 --- a/webrender/src/spatial_node.rs +++ b/webrender/src/spatial_node.rs @@ -203,12 +203,14 @@ impl SpatialNode { transform_palette: &mut TransformPalette, node_index: SpatialNodeIndex, ) { - if !self.invertible { - transform_palette.invalidate(node_index); - return; + if self.invertible { + transform_palette.set_world_transform( + node_index, + self.world_content_transform + .to_transform() + .into_owned() + ); } - - transform_palette.set(node_index, &self.world_content_transform); } pub fn update( diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index d63096d171..36d21a171b 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -7,7 +7,7 @@ use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentLayer, Filter use api::{LayoutRect, MixBlendMode, PipelineId}; use batch::{AlphaBatchBuilder, AlphaBatchContainer, ClipBatcher, resolve_image}; use clip::{ClipStore}; -use clip_scroll_tree::SpatialNodeIndex; +use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex}; use device::{FrameId, Texture}; #[cfg(feature = "pathfinder")] use euclid::{TypedPoint2D, TypedVector2D}; @@ -47,7 +47,7 @@ pub struct RenderTargetContext<'a, 'rc> { pub prim_store: &'a PrimitiveStore, pub resource_cache: &'rc mut ResourceCache, pub use_dual_source_blending: bool, - pub transforms: &'a TransformPalette, + pub clip_scroll_tree: &'a ClipScrollTree, } #[cfg_attr(feature = "capture", derive(Serialize))] @@ -103,6 +103,7 @@ pub trait RenderTarget { _render_tasks: &mut RenderTaskTree, _deferred_resolves: &mut Vec, _prim_headers: &mut PrimitiveHeaders, + _transforms: &mut TransformPalette, ) { } // TODO(gw): It's a bit odd that we need the deferred resolves and mutable @@ -119,6 +120,7 @@ pub trait RenderTarget { gpu_cache: &mut GpuCache, render_tasks: &RenderTaskTree, clip_store: &ClipStore, + transforms: &mut TransformPalette, deferred_resolves: &mut Vec, ); fn used_rect(&self) -> DeviceIntRect; @@ -167,6 +169,7 @@ impl RenderTargetList { deferred_resolves: &mut Vec, saved_index: Option, prim_headers: &mut PrimitiveHeaders, + transforms: &mut TransformPalette, ) { debug_assert_eq!(None, self.saved_index); self.saved_index = saved_index; @@ -178,6 +181,7 @@ impl RenderTargetList { render_tasks, deferred_resolves, prim_headers, + transforms, ); } } @@ -189,6 +193,7 @@ impl RenderTargetList { gpu_cache: &mut GpuCache, render_tasks: &mut RenderTaskTree, clip_store: &ClipStore, + transforms: &mut TransformPalette, deferred_resolves: &mut Vec, ) { self.targets.last_mut().unwrap().add_task( @@ -197,6 +202,7 @@ impl RenderTargetList { gpu_cache, render_tasks, clip_store, + transforms, deferred_resolves, ); } @@ -341,6 +347,7 @@ impl RenderTarget for ColorRenderTarget { render_tasks: &mut RenderTaskTree, deferred_resolves: &mut Vec, prim_headers: &mut PrimitiveHeaders, + transforms: &mut TransformPalette, ) { let mut merged_batches = AlphaBatchContainer::new(None); @@ -367,6 +374,8 @@ impl RenderTarget for ColorRenderTarget { render_tasks, deferred_resolves, prim_headers, + transforms, + pic_task.root_spatial_node_index, ); if let Some(batch_container) = batch_builder.build(&mut merged_batches) { @@ -389,6 +398,7 @@ impl RenderTarget for ColorRenderTarget { gpu_cache: &mut GpuCache, render_tasks: &RenderTaskTree, _: &ClipStore, + _: &mut TransformPalette, deferred_resolves: &mut Vec, ) { let task = &render_tasks[task_id]; @@ -539,6 +549,7 @@ impl RenderTarget for AlphaRenderTarget { gpu_cache: &mut GpuCache, render_tasks: &RenderTaskTree, clip_store: &ClipStore, + transforms: &mut TransformPalette, _: &mut Vec, ) { let task = &render_tasks[task_id]; @@ -582,10 +593,12 @@ impl RenderTarget for AlphaRenderTarget { self.clip_batcher.add( task_address, task_info.clip_node_range, + task_info.root_spatial_node_index, ctx.resource_cache, gpu_cache, clip_store, - ctx.transforms, + ctx.clip_scroll_tree, + transforms, ); } RenderTaskKind::ClipRegion(ref task) => { @@ -794,6 +807,7 @@ impl RenderPass { render_tasks: &mut RenderTaskTree, deferred_resolves: &mut Vec, clip_store: &ClipStore, + transforms: &mut TransformPalette, prim_headers: &mut PrimitiveHeaders, ) { profile_scope!("RenderPass::build"); @@ -808,6 +822,7 @@ impl RenderPass { gpu_cache, render_tasks, clip_store, + transforms, deferred_resolves, ); } @@ -817,6 +832,7 @@ impl RenderPass { render_tasks, deferred_resolves, prim_headers, + transforms, ); } RenderPassKind::OffScreen { ref mut color, ref mut alpha, ref mut texture_cache } => { @@ -901,6 +917,7 @@ impl RenderPass { gpu_cache, render_tasks, clip_store, + transforms, deferred_resolves, ), RenderTargetKind::Alpha => alpha.add_task( @@ -909,6 +926,7 @@ impl RenderPass { gpu_cache, render_tasks, clip_store, + transforms, deferred_resolves, ), } @@ -923,6 +941,7 @@ impl RenderPass { deferred_resolves, saved_color, prim_headers, + transforms, ); alpha.build( ctx, @@ -931,6 +950,7 @@ impl RenderPass { deferred_resolves, saved_alpha, prim_headers, + transforms, ); alpha.is_shared = is_shared_alpha; } diff --git a/webrender/src/util.rs b/webrender/src/util.rs index 846895497a..42025088cc 100644 --- a/webrender/src/util.rs +++ b/webrender/src/util.rs @@ -3,9 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderRadius, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale}; -use api::{LayoutPixel, DeviceRect, WorldPixel, WorldRect}; +use api::{LayoutPixel, DeviceRect, WorldPixel, RasterRect}; use euclid::{Point2D, Rect, Size2D, TypedPoint2D, TypedRect, TypedSize2D}; -use euclid::{TypedTransform2D, TypedTransform3D, TypedVector2D}; +use euclid::{TypedTransform2D, TypedTransform3D, TypedVector2D, TypedScale}; use num_traits::Zero; use plane_split::{Clipper, Polygon}; use std::{i32, f32, fmt}; @@ -62,7 +62,10 @@ impl MatrixHelpers for TypedTransform3D { } fn has_perspective_component(&self) -> bool { - self.m14 != 0.0 || self.m24 != 0.0 || self.m34 != 0.0 || self.m44 != 1.0 + self.m14.abs() > NEARLY_ZERO || + self.m24.abs() > NEARLY_ZERO || + self.m34.abs() > NEARLY_ZERO || + (self.m44 - 1.0).abs() > NEARLY_ZERO } fn has_2d_inverse(&self) -> bool { @@ -501,10 +504,11 @@ pub fn project_rect( } } -pub fn world_rect_to_device_pixels( - rect: WorldRect, +pub fn raster_rect_to_device_pixels( + rect: RasterRect, device_pixel_scale: DevicePixelScale, ) -> DeviceRect { - let device_rect = rect * device_pixel_scale; + let world_rect = rect * TypedScale::new(1.0); + let device_rect = world_rect * device_pixel_scale; device_rect.round_out() } diff --git a/webrender_api/src/units.rs b/webrender_api/src/units.rs index 213e701001..45ec7e8409 100644 --- a/webrender_api/src/units.rs +++ b/webrender_api/src/units.rs @@ -51,6 +51,22 @@ pub type PicturePoint3D = TypedPoint3D; pub type PictureVector2D = TypedVector2D; pub type PictureVector3D = TypedVector3D; +/// Geometry gets rasterized in a given root coordinate space. This +/// is often the root spatial node (world space), but may be a local +/// space for a variety of reasons (e.g. perspective). +#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub struct RasterPixel; + +pub type RasterIntRect = TypedRect; +pub type RasterIntPoint = TypedPoint2D; +pub type RasterIntSize = TypedSize2D; +pub type RasterRect = TypedRect; +pub type RasterPoint = TypedPoint2D; +pub type RasterSize = TypedSize2D; +pub type RasterPoint3D = TypedPoint3D; +pub type RasterVector2D = TypedVector2D; +pub type RasterVector3D = TypedVector3D; + /// Geometry in a stacking context's local coordinate space (logical pixels). #[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize)] pub struct LayoutPixel; @@ -94,6 +110,12 @@ pub type WorldToLayoutTransform = TypedTransform3D pub type LayoutToPictureTransform = TypedTransform3D; pub type PictureToLayoutTransform = TypedTransform3D; +pub type LayoutToRasterTransform = TypedTransform3D; +pub type RasterToLayoutTransform = TypedTransform3D; + +pub type PictureToRasterTransform = TypedTransform3D; +pub type RasterToPictureTransform = TypedTransform3D; + // Fixed position coordinates, to avoid float precision errors. pub type LayoutPointAu = TypedPoint2D; pub type LayoutRectAu = TypedRect; diff --git a/wrench/reftests/split/near-plane.png b/wrench/reftests/split/near-plane.png index 41c81d0827..c6e81c9983 100644 Binary files a/wrench/reftests/split/near-plane.png and b/wrench/reftests/split/near-plane.png differ diff --git a/wrench/reftests/text/shadow-transforms.png b/wrench/reftests/text/shadow-transforms.png index 490ce81510..5de93cfaa9 100644 Binary files a/wrench/reftests/text/shadow-transforms.png and b/wrench/reftests/text/shadow-transforms.png differ diff --git a/wrench/reftests/transforms/content-offset.png b/wrench/reftests/transforms/content-offset.png index f92d156b43..16a39791b2 100644 Binary files a/wrench/reftests/transforms/content-offset.png and b/wrench/reftests/transforms/content-offset.png differ diff --git a/wrench/reftests/transforms/nested-preserve-3d.png b/wrench/reftests/transforms/nested-preserve-3d.png index b99f0ea1b5..0c1f97d236 100644 Binary files a/wrench/reftests/transforms/nested-preserve-3d.png and b/wrench/reftests/transforms/nested-preserve-3d.png differ diff --git a/wrench/reftests/transforms/perspective-clip.png b/wrench/reftests/transforms/perspective-clip.png new file mode 100644 index 0000000000..9233e52765 Binary files /dev/null and b/wrench/reftests/transforms/perspective-clip.png differ diff --git a/wrench/reftests/transforms/perspective-clip.yaml b/wrench/reftests/transforms/perspective-clip.yaml new file mode 100644 index 0000000000..7c314dd22b --- /dev/null +++ b/wrench/reftests/transforms/perspective-clip.yaml @@ -0,0 +1,27 @@ +# Test that a local space clip is correctly applied to +# a primitive with a perspective transform. +--- +root: + items: + - type: clip + bounds: [225, 200, 150, 200] + complex: + - rect: [225, 200, 150, 200] + radius: 32 + items: + - + type: "stacking-context" + bounds: 0 0 1000 1000 + perspective: 100 + perspective-origin: 300 300 + items: + - + type: "stacking-context" + transform: rotate-x(10) + transform-origin: 300 300 + filters: identity + items: + - + bounds: [200, 200, 200, 200] + image: checkerboard(2, 16, 12) + stretch-size: 200 200 diff --git a/wrench/reftests/transforms/reftest.list b/wrench/reftests/transforms/reftest.list index c72bdb1ee6..93a36ce5f6 100644 --- a/wrench/reftests/transforms/reftest.list +++ b/wrench/reftests/transforms/reftest.list @@ -14,7 +14,7 @@ platform(linux,mac) == coord-system.yaml coord-system.png platform(linux,mac) zoom(4) == border-zoom.yaml border-zoom.png platform(linux) fuzzy(1,520) == perspective-origin.yaml perspective-origin.png platform(linux,mac) color_targets(2) alpha_targets(0) fuzzy(1,180) == screen-space-blit.yaml screen-space-blit.png -platform(linux,mac) color_targets(1) alpha_targets(0) fuzzy(128,800) == screen-space-blit-trivial.yaml screen-space-blit.png +platform(linux,mac) color_targets(1) alpha_targets(0) == screen-space-blit-trivial.yaml screen-space-blit-trivial.png platform(linux) fuzzy(11,4592) == screen-space-blur.yaml screen-space-blur.png platform(linux,mac) == nested-rotate-x.yaml nested-rotate-x.png platform(linux,mac) == nested-preserve-3d.yaml nested-preserve-3d.png @@ -22,3 +22,4 @@ platform(linux,mac) == near-plane-clip.yaml near-plane-clip.png platform(linux,mac) == perspective-mask.yaml perspective-mask.png rotate-clip.yaml rotate-clip-ref.yaml clip-translate.yaml clip-translate-ref.yaml +platform(linux,mac) == perspective-clip.yaml perspective-clip.png diff --git a/wrench/reftests/transforms/screen-space-blit-trivial.png b/wrench/reftests/transforms/screen-space-blit-trivial.png new file mode 100644 index 0000000000..0b1079ef92 Binary files /dev/null and b/wrench/reftests/transforms/screen-space-blit-trivial.png differ diff --git a/wrench/reftests/transforms/screen-space-blit.png b/wrench/reftests/transforms/screen-space-blit.png index f49bb3fd43..044a4cc505 100644 Binary files a/wrench/reftests/transforms/screen-space-blit.png and b/wrench/reftests/transforms/screen-space-blit.png differ diff --git a/wrench/reftests/transforms/screen-space-blur.png b/wrench/reftests/transforms/screen-space-blur.png index 59e3358752..c9956a2940 100644 Binary files a/wrench/reftests/transforms/screen-space-blur.png and b/wrench/reftests/transforms/screen-space-blur.png differ