diff --git a/webrender/res/brush.glsl b/webrender/res/brush.glsl index c59992c5f6..0d04a1a4d5 100644 --- a/webrender/res/brush.glsl +++ b/webrender/res/brush.glsl @@ -70,7 +70,6 @@ void main(void) { #endif } else { bvec4 edge_mask = notEqual(edge_flags & ivec4(1, 2, 4, 8), ivec4(0)); - bool do_perspective_interpolation = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0; vi = write_transform_vertex( local_segment_rect, @@ -79,8 +78,7 @@ void main(void) { mix(vec4(0.0), vec4(1.0), edge_mask), ph.z, transform, - pic_task, - do_perspective_interpolation + pic_task ); } @@ -92,7 +90,7 @@ void main(void) { // implies the other, for now. #ifdef WR_FEATURE_ALPHA_PASS write_clip( - vi.screen_pos, + vi.world_pos, clip_area ); #endif diff --git a/webrender/res/brush_blend.glsl b/webrender/res/brush_blend.glsl index 5f2e4a8fd8..5445e7d70d 100644 --- a/webrender/res/brush_blend.glsl +++ b/webrender/res/brush_blend.glsl @@ -29,9 +29,9 @@ void brush_vs( ) { PictureTask src_task = fetch_picture_task(user_data.x); vec2 texture_size = vec2(textureSize(sColor0, 0).xy); - vec2 uv = vi.snapped_device_pos + - src_task.common_data.task_rect.p0 - - src_task.content_origin; + vec2 uv = snap_device_pos(vi) + + src_task.common_data.task_rect.p0 - + src_task.content_origin; vUv = vec3(uv / texture_size, src_task.common_data.texture_layer_index); vec2 uv0 = src_task.common_data.task_rect.p0; diff --git a/webrender/res/brush_image.glsl b/webrender/res/brush_image.glsl index 6fb7685b99..5356036cb4 100644 --- a/webrender/res/brush_image.glsl +++ b/webrender/res/brush_image.glsl @@ -11,7 +11,8 @@ varying vec2 vLocalPos; #endif // Interpolated uv coordinates in xy, and layer in z. -varying vec3 vUv; +// W is 1 when perspective interpolation is enabled. +varying vec4 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 @@ -106,6 +107,7 @@ 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). @@ -151,6 +153,10 @@ 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))); @@ -196,12 +202,14 @@ 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(vUv.xy, vec2(0.0)); + vec2 local_uv = max(base_uv, vec2(0.0)); // Handle horizontal and vertical repetitions. vec2 repeated_uv = mod(local_uv, uv_size) + vUvBounds.xy; @@ -217,7 +225,7 @@ Fragment brush_fs() { } #else // Handle horizontal and vertical repetitions. - vec2 repeated_uv = mod(vUv.xy, uv_size) + vUvBounds.xy; + vec2 repeated_uv = mod(base_uv, uv_size) + vUvBounds.xy; #endif // Clamp the uvs to avoid sampling artifacts. diff --git a/webrender/res/brush_mix_blend.glsl b/webrender/res/brush_mix_blend.glsl index 0494974443..aec357a54b 100644 --- a/webrender/res/brush_mix_blend.glsl +++ b/webrender/res/brush_mix_blend.glsl @@ -23,17 +23,18 @@ void brush_vs( int brush_flags, vec4 unused ) { + vec2 snapped_device_pos = snap_device_pos(vi); vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0)); vOp = user_data.x; PictureTask src_task = fetch_picture_task(user_data.z); - vec2 src_uv = vi.snapped_device_pos + + vec2 src_uv = snapped_device_pos + src_task.common_data.task_rect.p0 - src_task.content_origin; vSrcUv = vec3(src_uv / texture_size, src_task.common_data.texture_layer_index); RenderTaskCommonData backdrop_task = fetch_render_task_common_data(user_data.y); - vec2 backdrop_uv = vi.snapped_device_pos + + vec2 backdrop_uv = snapped_device_pos + backdrop_task.task_rect.p0 - src_task.content_origin; vBackdropUv = vec3(backdrop_uv / texture_size, backdrop_task.texture_layer_index); diff --git a/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index 5bad278de1..0b09753b7a 100644 --- a/webrender/res/prim_shared.glsl +++ b/webrender/res/prim_shared.glsl @@ -27,7 +27,8 @@ vec2 clamp_rect(vec2 pt, RectWithSize rect) { // TODO: convert back to RectWithEndPoint if driver issues are resolved, if ever. flat varying vec4 vClipMaskUvBounds; -varying vec3 vClipMaskUv; +// XY and W are homogeneous coordinates, Z is the layer index +varying vec4 vClipMaskUv; #ifdef WR_VERTEX_SHADER @@ -86,11 +87,15 @@ PrimitiveHeader fetch_prim_header(int index) { struct VertexInfo { vec2 local_pos; - vec2 screen_pos; - float w; - vec2 snapped_device_pos; + vec2 snap_offset; + vec4 world_pos; }; +//Note: this function is unsafe for `vi.world_pos.w <= 0.0` +vec2 snap_device_pos(VertexInfo vi) { + return vi.world_pos.xy * uDevicePixelRatio / max(0.0, vi.world_pos.w) + vi.snap_offset; +} + VertexInfo write_vertex(RectWithSize instance_rect, RectWithSize local_clip_rect, float z, @@ -119,8 +124,7 @@ VertexInfo write_vertex(RectWithSize instance_rect, vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio; // Apply offsets for the render task to get correct screen location. - vec2 snapped_device_pos = device_pos + snap_offset; - vec2 final_pos = snapped_device_pos - + vec2 final_pos = device_pos + snap_offset - task.content_origin + task.common_data.task_rect.p0; @@ -128,9 +132,8 @@ VertexInfo write_vertex(RectWithSize instance_rect, VertexInfo vi = VertexInfo( clamped_local_pos, - device_pos, - world_pos.w, - snapped_device_pos + snap_offset, + world_pos ); return vi; @@ -161,8 +164,7 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect, vec4 clip_edge_mask, float z, Transform transform, - PictureTask task, - bool do_perspective_interpolation) { + PictureTask task) { // Calculate a clip rect from local_rect + local clip RectWithEndpoint clip_rect = to_rect_with_endpoint(local_clip_rect); RectWithEndpoint segment_rect = to_rect_with_endpoint(local_segment_rect); @@ -192,23 +194,17 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect, // Select the corner of the local rect that we are processing. vec2 local_pos = local_segment_rect.p0 + local_segment_rect.size * aPosition.xy; - // Transform the current vertex to the world cpace. - vec4 world_pos = transform.m * vec4(local_pos, 0.0, 1.0); - // Convert the world positions to device pixel space. - vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio; vec2 task_offset = task.common_data.task_rect.p0 - task.content_origin; - // Force w = 1, if we don't want perspective interpolation (for - // example, drawing a screen-space quad on an element with a - // perspective transform). - world_pos.w = mix(1.0, world_pos.w, do_perspective_interpolation); + // Transform the current vertex to the world cpace. + vec4 world_pos = transform.m * vec4(local_pos, 0.0, 1.0); + vec4 final_pos = vec4( + world_pos.xy * uDevicePixelRatio + task_offset * world_pos.w, + z * world_pos.w, + world_pos.w + ); - // We want the world space coords to be perspective divided by W. - // We also want that to apply to any interpolators. However, we - // want a constant Z across the primitive, since we're using it - // for draw ordering - so scale by the W coord to ensure this. - vec4 final_pos = vec4(device_pos + task_offset, z, 1.0) * world_pos.w; gl_Position = uTransform * final_pos; init_transform_vs(mix( @@ -219,36 +215,44 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect, VertexInfo vi = VertexInfo( local_pos, - device_pos, - world_pos.w, - device_pos + vec2(0.0), + world_pos ); return vi; } -void write_clip(vec2 global_pos, ClipArea area) { - vec2 uv = global_pos + - area.common_data.task_rect.p0 - - area.screen_origin; +void write_clip(vec4 world_pos, ClipArea area) { + vec2 uv = world_pos.xy * uDevicePixelRatio + + world_pos.w * (area.common_data.task_rect.p0 - area.screen_origin); vClipMaskUvBounds = vec4( area.common_data.task_rect.p0, area.common_data.task_rect.p0 + area.common_data.task_rect.size ); - vClipMaskUv = vec3(uv, area.common_data.texture_layer_index); + vClipMaskUv = vec4(uv, area.common_data.texture_layer_index, world_pos.w); } #endif //WR_VERTEX_SHADER #ifdef WR_FRAGMENT_SHADER float do_clip() { + // check for the dummy bounds, which are given to the opaque objects + if (vClipMaskUvBounds.xy == vClipMaskUvBounds.zw) { + return 1.0; + } // anything outside of the mask is considered transparent + //Note: we assume gl_FragCoord.w == interpolated(1 / vClipMaskUv.w) + vec2 mask_uv = vClipMaskUv.xy * gl_FragCoord.w; bvec4 inside = lessThanEqual( - vec4(vClipMaskUvBounds.xy, vClipMaskUv.xy), - vec4(vClipMaskUv.xy, vClipMaskUvBounds.zw)); - // check for the dummy bounds, which are given to the opaque objects - return vClipMaskUvBounds.xy == vClipMaskUvBounds.zw ? 1.0: - all(inside) ? texelFetch(sCacheA8, ivec3(vClipMaskUv), 0).r : 0.0; + vec4(vClipMaskUvBounds.xy, mask_uv), + vec4(mask_uv, vClipMaskUvBounds.zw)); + // bail out if the pixel is outside the valid bounds + if (!all(inside)) { + return 0.0; + } + // finally, the slow path - fetch the mask value from an image + ivec3 tc = ivec3(mask_uv, vClipMaskUv.z); + return texelFetch(sCacheA8, tc, 0).r; } #ifdef WR_FEATURE_DITHERING diff --git a/webrender/res/ps_text_run.glsl b/webrender/res/ps_text_run.glsl index 81770befb9..49730e84a9 100644 --- a/webrender/res/ps_text_run.glsl +++ b/webrender/res/ps_text_run.glsl @@ -73,37 +73,33 @@ VertexInfo write_text_vertex(vec2 clamped_local_pos, // Convert the world positions to device pixel space. float device_scale = uDevicePixelRatio / world_pos.w; vec2 device_pos = world_pos.xy * device_scale; - - // Apply offsets for the render task to get correct screen location. - vec2 final_pos = device_pos - - task.content_origin + - task.common_data.task_rect.p0; + vec2 snap_offset = vec2(0.0); #if defined(WR_FEATURE_GLYPH_TRANSFORM) bool remove_subpx_offset = true; #else - // Compute the snapping offset only if the scroll node transform is axis-aligned. bool remove_subpx_offset = transform.is_axis_aligned; #endif + // Compute the snapping offset only if the scroll node transform is axis-aligned. if (remove_subpx_offset) { // Ensure the transformed text offset does not contain a subpixel translation // such that glyph snapping is stable for equivalent glyph subpixel positions. vec2 world_text_offset = mat2(transform.m) * text_offset; vec2 device_text_pos = (transform.m[3].xy + world_text_offset) * device_scale; - final_pos += floor(device_text_pos + 0.5) - device_text_pos; + snap_offset += floor(device_text_pos + 0.5) - device_text_pos; #ifdef WR_FEATURE_GLYPH_TRANSFORM // For transformed subpixels, we just need to align the glyph origin to a device pixel. // The transformed text offset has already been snapped, so remove it from the glyph // origin when snapping the glyph. - vec2 snap_offset = snap_rect.p0 - world_text_offset * device_scale; - final_pos += floor(snap_offset + snap_bias) - snap_offset; + vec2 rough_offset = snap_rect.p0 - world_text_offset * device_scale; + snap_offset += floor(rough_offset + snap_bias) - rough_offset; #else // The transformed text offset has already been snapped, so remove it from the transform // when snapping the glyph. mat4 snap_transform = transform.m; snap_transform[3].xy = -world_text_offset; - final_pos += compute_snap_offset( + snap_offset += compute_snap_offset( clamped_local_pos, snap_transform, snap_rect, @@ -112,13 +108,17 @@ VertexInfo write_text_vertex(vec2 clamped_local_pos, #endif } + // Apply offsets for the render task to get correct screen location. + vec2 final_pos = device_pos + snap_offset - + task.content_origin + + task.common_data.task_rect.p0; + gl_Position = uTransform * vec4(final_pos, z, 1.0); VertexInfo vi = VertexInfo( clamped_local_pos, - device_pos, - world_pos.w, - final_pos + snap_offset, + world_pos ); return vi; @@ -223,7 +223,7 @@ void main(void) { vec2 f = (vi.local_pos - glyph_rect.p0) / glyph_rect.size; #endif - write_clip(vi.screen_pos, clip_area); + write_clip(vi.world_pos, clip_area); switch (color_mode) { case COLOR_MODE_ALPHA: diff --git a/wrench/reftests/transforms/coord-system.png b/wrench/reftests/transforms/coord-system.png index a930274d98..cc6a9c2955 100644 Binary files a/wrench/reftests/transforms/coord-system.png and b/wrench/reftests/transforms/coord-system.png differ diff --git a/wrench/reftests/transforms/near-plane-clip.png b/wrench/reftests/transforms/near-plane-clip.png index 13c9c7a50e..f7ae87e3cf 100644 Binary files a/wrench/reftests/transforms/near-plane-clip.png and b/wrench/reftests/transforms/near-plane-clip.png differ diff --git a/wrench/reftests/transforms/perspective-origin.png b/wrench/reftests/transforms/perspective-origin.png index 69d72ea8c8..f51f6b83eb 100644 Binary files a/wrench/reftests/transforms/perspective-origin.png and b/wrench/reftests/transforms/perspective-origin.png differ