зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1690396 - Refactor mix-blend-mode impl to work correctly with raster roots. r=nical
This fixes incorrect rendering when either the source or backdrop tasks establish a raster root. By design, it also changes mix-blend backdrop readbacks to work in a way that can handle readbacks from picture cache tiles, which is a follow up optimization being worked on. Differential Revision: https://phabricator.services.mozilla.com/D103853
This commit is contained in:
Родитель
9302cabffc
Коммит
5a2bff1c55
|
@ -7,16 +7,40 @@
|
|||
|
||||
#include shared,prim_shared,brush
|
||||
|
||||
// xy: uv coorinates.
|
||||
// UV and bounds for the source image
|
||||
varying vec2 v_src_uv;
|
||||
flat varying vec4 v_src_uv_sample_bounds;
|
||||
|
||||
// xy: uv coorinates.
|
||||
// UV and bounds for the backdrop image
|
||||
varying vec2 v_backdrop_uv;
|
||||
flat varying vec4 v_backdrop_uv_sample_bounds;
|
||||
|
||||
// mix-blend op, and perspective interpolation control
|
||||
flat varying float v_perspective;
|
||||
flat varying int v_op;
|
||||
|
||||
#ifdef WR_VERTEX_SHADER
|
||||
|
||||
void get_uv(
|
||||
int res_address,
|
||||
vec2 f,
|
||||
ivec2 texture_size,
|
||||
float perspective_f,
|
||||
out vec2 out_uv,
|
||||
out vec4 out_uv_sample_bounds
|
||||
) {
|
||||
ImageResource res = fetch_image_resource(res_address);
|
||||
vec2 uv0 = res.uv_rect.p0;
|
||||
vec2 uv1 = res.uv_rect.p1;
|
||||
|
||||
vec2 inv_texture_size = vec2(1.0) / vec2(texture_size);
|
||||
f = get_image_quad_uv(res_address, f);
|
||||
vec2 uv = mix(uv0, uv1, f);
|
||||
|
||||
out_uv = uv * inv_texture_size * perspective_f;
|
||||
out_uv_sample_bounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5)) * inv_texture_size.xyxy;
|
||||
}
|
||||
|
||||
void brush_vs(
|
||||
VertexInfo vi,
|
||||
int prim_address,
|
||||
|
@ -29,25 +53,29 @@ void brush_vs(
|
|||
int brush_flags,
|
||||
vec4 unused
|
||||
) {
|
||||
//Note: this is unsafe for `vi.world_pos.w <= 0.0`
|
||||
vec2 device_pos = vi.world_pos.xy * pic_task.device_pixel_scale / max(0.0, vi.world_pos.w);
|
||||
vec2 backdrop_texture_size = vec2(textureSize(sColor0, 0));
|
||||
vec2 src_texture_size = vec2(textureSize(sColor1, 0));
|
||||
vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
|
||||
float perspective_interpolate = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0;
|
||||
float perspective_f = mix(vi.world_pos.w, 1.0, perspective_interpolate);
|
||||
v_perspective = perspective_interpolate;
|
||||
v_op = prim_user_data.x;
|
||||
|
||||
PictureTask src_task = fetch_picture_task(prim_user_data.z);
|
||||
vec2 src_device_pos = vi.world_pos.xy * (src_task.device_pixel_scale / max(0.0, vi.world_pos.w));
|
||||
vec2 src_uv = src_device_pos +
|
||||
src_task.common_data.task_rect.p0 -
|
||||
src_task.content_origin;
|
||||
v_src_uv = src_uv / src_texture_size;
|
||||
get_uv(
|
||||
prim_user_data.y,
|
||||
f,
|
||||
textureSize(sColor0, 0).xy,
|
||||
1.0,
|
||||
v_backdrop_uv,
|
||||
v_backdrop_uv_sample_bounds
|
||||
);
|
||||
|
||||
RenderTaskCommonData backdrop_task = fetch_render_task_common_data(prim_user_data.y);
|
||||
float src_to_backdrop_scale = pic_task.device_pixel_scale / src_task.device_pixel_scale;
|
||||
vec2 backdrop_uv = device_pos +
|
||||
backdrop_task.task_rect.p0 -
|
||||
src_task.content_origin * src_to_backdrop_scale;
|
||||
v_backdrop_uv = backdrop_uv / backdrop_texture_size;
|
||||
get_uv(
|
||||
prim_user_data.z,
|
||||
f,
|
||||
textureSize(sColor1, 0).xy,
|
||||
perspective_f,
|
||||
v_src_uv,
|
||||
v_src_uv_sample_bounds
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -210,8 +238,15 @@ const int MixBlendMode_Color = 14;
|
|||
const int MixBlendMode_Luminosity = 15;
|
||||
|
||||
Fragment brush_fs() {
|
||||
vec4 Cb = texture(sColor0, v_backdrop_uv);
|
||||
vec4 Cs = texture(sColor1, v_src_uv);
|
||||
float perspective_divisor = mix(gl_FragCoord.w, 1.0, v_perspective);
|
||||
|
||||
vec2 src_uv = v_src_uv * perspective_divisor;
|
||||
src_uv = clamp(src_uv, v_src_uv_sample_bounds.xy, v_src_uv_sample_bounds.zw);
|
||||
|
||||
vec2 backdrop_uv = clamp(v_backdrop_uv, v_backdrop_uv_sample_bounds.xy, v_backdrop_uv_sample_bounds.zw);
|
||||
|
||||
vec4 Cb = texture(sColor0, backdrop_uv);
|
||||
vec4 Cs = texture(sColor1, src_uv);
|
||||
|
||||
// The mix-blend-mode functions assume no premultiplied alpha
|
||||
if (Cb.a != 0.0) {
|
||||
|
|
|
@ -59,7 +59,6 @@ pub enum BrushBatchKind {
|
|||
Blend,
|
||||
MixBlend {
|
||||
task_id: RenderTaskId,
|
||||
source_id: RenderTaskId,
|
||||
backdrop_id: RenderTaskId,
|
||||
},
|
||||
YuvImage(ImageBufferKind, YuvFormat, ColorDepth, YuvColorSpace, ColorRange),
|
||||
|
@ -1979,7 +1978,6 @@ impl BatchBuilder {
|
|||
BatchKind::Brush(
|
||||
BrushBatchKind::MixBlend {
|
||||
task_id: self.batchers[0].render_task_id,
|
||||
source_id: cache_task_id,
|
||||
backdrop_id,
|
||||
},
|
||||
),
|
||||
|
@ -2001,12 +1999,12 @@ impl BatchBuilder {
|
|||
clip_mask: clip_mask_texture_id,
|
||||
},
|
||||
);
|
||||
let backdrop_task_address: RenderTaskAddress = backdrop_id.into();
|
||||
let source_task_address: RenderTaskAddress = cache_task_id.into();
|
||||
let src_uv_address = render_tasks[cache_task_id].get_texture_address(gpu_cache);
|
||||
let readback_uv_address = render_tasks[backdrop_id].get_texture_address(gpu_cache);
|
||||
let prim_header_index = prim_headers.push(&prim_header, z_id, [
|
||||
mode as u32 as i32,
|
||||
backdrop_task_address.0 as i32,
|
||||
source_task_address.0 as i32,
|
||||
readback_uv_address.as_int(),
|
||||
src_uv_address.as_int(),
|
||||
0,
|
||||
]);
|
||||
|
||||
|
|
|
@ -5415,10 +5415,50 @@ impl PicturePrimitive {
|
|||
device_pixel_scale,
|
||||
);
|
||||
|
||||
let parent_surface = &frame_state.surfaces[parent_surface_index.0];
|
||||
let parent_raster_spatial_node_index = parent_surface.raster_spatial_node_index;
|
||||
let parent_device_pixel_scale = parent_surface.device_pixel_scale;
|
||||
|
||||
// Create a space mapper that will allow mapping from the local rect
|
||||
// of the mix-blend primitive into the space of the surface that we
|
||||
// need to read back from. Note that we use the parent's raster spatial
|
||||
// node here, so that we are in the correct device space of the parent
|
||||
// surface, whether it establishes a raster root or not.
|
||||
let map_pic_to_parent = SpaceMapper::new_with_target(
|
||||
parent_raster_spatial_node_index,
|
||||
self.spatial_node_index,
|
||||
RasterRect::max_rect(), // TODO(gw): May need a conservative estimate?
|
||||
frame_context.spatial_tree,
|
||||
);
|
||||
let pic_in_raster_space = map_pic_to_parent
|
||||
.map(&pic_rect)
|
||||
.expect("bug: unable to map mix-blend content into parent");
|
||||
|
||||
// Apply device pixel ratio for parent surface to get into device
|
||||
// pixels for that surface.
|
||||
let backdrop_rect = raster_rect_to_device_pixels(
|
||||
pic_in_raster_space,
|
||||
parent_device_pixel_scale,
|
||||
);
|
||||
|
||||
// Calculate the UV coords necessary for the shader to sampler
|
||||
// from the primitive rect within the readback region. This is
|
||||
// 0..1 for aligned surfaces, but doing it this way allows
|
||||
// accurate sampling if the primitive bounds have fractional values.
|
||||
let backdrop_uv = calculate_uv_rect_kind(
|
||||
&pic_rect,
|
||||
&map_pic_to_parent.get_transform(),
|
||||
&backdrop_rect,
|
||||
parent_device_pixel_scale,
|
||||
);
|
||||
|
||||
let readback_task_id = frame_state.rg_builder.add().init(
|
||||
RenderTask::new_dynamic(
|
||||
clipped.size.to_i32(),
|
||||
RenderTaskKind::new_readback(),
|
||||
backdrop_rect.size.to_i32(),
|
||||
RenderTaskKind::new_readback(
|
||||
backdrop_rect.origin.to_i32(),
|
||||
backdrop_uv,
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -408,7 +408,7 @@ impl RenderTarget for ColorRenderTarget {
|
|||
RenderTaskKind::LineDecoration(..) => {
|
||||
panic!("Should not be added to color target!");
|
||||
}
|
||||
RenderTaskKind::Readback => {}
|
||||
RenderTaskKind::Readback(..) => {}
|
||||
RenderTaskKind::Scaling(ref info) => {
|
||||
add_scaling_instances(
|
||||
info,
|
||||
|
@ -530,7 +530,7 @@ impl RenderTarget for AlphaRenderTarget {
|
|||
let (target_rect, _) = task.get_target_rect();
|
||||
|
||||
match task.kind {
|
||||
RenderTaskKind::Readback |
|
||||
RenderTaskKind::Readback(..) |
|
||||
RenderTaskKind::Picture(..) |
|
||||
RenderTaskKind::Blit(..) |
|
||||
RenderTaskKind::Border(..) |
|
||||
|
@ -752,7 +752,7 @@ impl TextureCacheRenderTarget {
|
|||
RenderTaskKind::Picture(..) |
|
||||
RenderTaskKind::ClipRegion(..) |
|
||||
RenderTaskKind::CacheMask(..) |
|
||||
RenderTaskKind::Readback |
|
||||
RenderTaskKind::Readback(..) |
|
||||
RenderTaskKind::Scaling(..) |
|
||||
RenderTaskKind::SvgFilter(..) => {
|
||||
panic!("BUG: unexpected task kind for texture cache target");
|
||||
|
|
|
@ -297,6 +297,18 @@ pub struct SvgFilterTask {
|
|||
pub uv_rect_kind: UvRectKind,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct ReadbackTask {
|
||||
// The offset of the rect that needs to be read back, in the
|
||||
// device space of the surface that will be read back from
|
||||
pub readback_origin: DeviceIntPoint,
|
||||
// UV rect of the readback rect within the readback task area
|
||||
pub uv_rect_kind: UvRectKind,
|
||||
// GPU cache handle that provides uv_rect_kind to shaders
|
||||
pub uv_rect_handle: GpuCacheHandle,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
|
@ -312,7 +324,7 @@ pub enum RenderTaskKind {
|
|||
ClipRegion(ClipRegionTask),
|
||||
VerticalBlur(BlurTask),
|
||||
HorizontalBlur(BlurTask),
|
||||
Readback,
|
||||
Readback(ReadbackTask),
|
||||
Scaling(ScalingTask),
|
||||
Blit(BlitTask),
|
||||
Border(BorderTask),
|
||||
|
@ -331,7 +343,7 @@ impl RenderTaskKind {
|
|||
RenderTaskKind::ClipRegion(..) => "ClipRegion",
|
||||
RenderTaskKind::VerticalBlur(..) => "VerticalBlur",
|
||||
RenderTaskKind::HorizontalBlur(..) => "HorizontalBlur",
|
||||
RenderTaskKind::Readback => "Readback",
|
||||
RenderTaskKind::Readback(..) => "Readback",
|
||||
RenderTaskKind::Scaling(..) => "Scaling",
|
||||
RenderTaskKind::Blit(..) => "Blit",
|
||||
RenderTaskKind::Border(..) => "Border",
|
||||
|
@ -346,7 +358,7 @@ impl RenderTaskKind {
|
|||
pub fn target_kind(&self) -> RenderTargetKind {
|
||||
match *self {
|
||||
RenderTaskKind::LineDecoration(..) |
|
||||
RenderTaskKind::Readback |
|
||||
RenderTaskKind::Readback(..) |
|
||||
RenderTaskKind::Border(..) |
|
||||
RenderTaskKind::Gradient(..) |
|
||||
RenderTaskKind::Picture(..) |
|
||||
|
@ -419,8 +431,17 @@ impl RenderTaskKind {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn new_readback() -> Self {
|
||||
RenderTaskKind::Readback
|
||||
pub fn new_readback(
|
||||
readback_origin: DeviceIntPoint,
|
||||
uv_rect_kind: UvRectKind,
|
||||
) -> Self {
|
||||
RenderTaskKind::Readback(
|
||||
ReadbackTask {
|
||||
readback_origin,
|
||||
uv_rect_kind,
|
||||
uv_rect_handle: GpuCacheHandle::new(),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_line_decoration(
|
||||
|
@ -628,7 +649,7 @@ impl RenderTaskKind {
|
|||
task.blur_region.height as f32,
|
||||
]
|
||||
}
|
||||
RenderTaskKind::Readback |
|
||||
RenderTaskKind::Readback(..) |
|
||||
RenderTaskKind::Scaling(..) |
|
||||
RenderTaskKind::Border(..) |
|
||||
RenderTaskKind::LineDecoration(..) |
|
||||
|
@ -685,7 +706,9 @@ impl RenderTaskKind {
|
|||
RenderTaskKind::SvgFilter(ref mut info) => {
|
||||
(&mut info.uv_rect_handle, info.uv_rect_kind)
|
||||
}
|
||||
RenderTaskKind::Readback |
|
||||
RenderTaskKind::Readback(ref mut info) => {
|
||||
(&mut info.uv_rect_handle, info.uv_rect_kind)
|
||||
}
|
||||
RenderTaskKind::Scaling(..) |
|
||||
RenderTaskKind::Blit(..) |
|
||||
RenderTaskKind::ClipRegion(..) |
|
||||
|
@ -1359,7 +1382,7 @@ impl RenderTask {
|
|||
pub fn uv_rect_kind(&self) -> UvRectKind {
|
||||
match self.kind {
|
||||
RenderTaskKind::CacheMask(..) |
|
||||
RenderTaskKind::Readback => {
|
||||
RenderTaskKind::Readback(..) => {
|
||||
unreachable!("bug: unexpected render task");
|
||||
}
|
||||
|
||||
|
@ -1407,8 +1430,10 @@ impl RenderTask {
|
|||
RenderTaskKind::SvgFilter(ref info) => {
|
||||
gpu_cache.get_address(&info.uv_rect_handle)
|
||||
}
|
||||
RenderTaskKind::Readback(ref info) => {
|
||||
gpu_cache.get_address(&info.uv_rect_handle)
|
||||
}
|
||||
RenderTaskKind::ClipRegion(..) |
|
||||
RenderTaskKind::Readback |
|
||||
RenderTaskKind::Scaling(..) |
|
||||
RenderTaskKind::Blit(..) |
|
||||
RenderTaskKind::Border(..) |
|
||||
|
@ -1508,7 +1533,7 @@ impl RenderTask {
|
|||
pt.new_level("HorizontalBlur".to_owned());
|
||||
task.print_with(pt);
|
||||
}
|
||||
RenderTaskKind::Readback => {
|
||||
RenderTaskKind::Readback(..) => {
|
||||
pt.new_level("Readback".to_owned());
|
||||
}
|
||||
RenderTaskKind::Scaling(ref kind) => {
|
||||
|
|
|
@ -2489,7 +2489,6 @@ impl Renderer {
|
|||
&mut self,
|
||||
draw_target: DrawTarget,
|
||||
uses_scissor: bool,
|
||||
source: &RenderTask,
|
||||
backdrop: &RenderTask,
|
||||
readback: &RenderTask,
|
||||
) {
|
||||
|
@ -2504,16 +2503,19 @@ impl Renderer {
|
|||
let (cache_texture, _) = self.texture_resolver
|
||||
.resolve(&texture_source).expect("bug: no source texture");
|
||||
|
||||
// Extract the rectangle in the backdrop surface's device space of where
|
||||
// we need to read from.
|
||||
let readback_origin = match readback.kind {
|
||||
RenderTaskKind::Readback(ref info) => info.readback_origin,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// Before submitting the composite batch, do the
|
||||
// framebuffer readbacks that are needed for each
|
||||
// composite operation in this batch.
|
||||
let (readback_rect, readback_layer) = readback.get_target_rect();
|
||||
let (backdrop_rect, _) = backdrop.get_target_rect();
|
||||
let (backdrop_screen_origin, backdrop_scale) = match backdrop.kind {
|
||||
RenderTaskKind::Picture(ref task_info) => (task_info.content_origin, task_info.device_pixel_scale),
|
||||
_ => panic!("bug: composite on non-picture?"),
|
||||
};
|
||||
let (source_screen_origin, source_scale) = match source.kind {
|
||||
let (backdrop_screen_origin, _) = match backdrop.kind {
|
||||
RenderTaskKind::Picture(ref task_info) => (task_info.content_origin, task_info.device_pixel_scale),
|
||||
_ => panic!("bug: composite on non-picture?"),
|
||||
};
|
||||
|
@ -2528,28 +2530,22 @@ impl Renderer {
|
|||
false,
|
||||
);
|
||||
|
||||
let source_in_backdrop_space = source_screen_origin * (backdrop_scale.0 / source_scale.0);
|
||||
|
||||
let mut src = DeviceIntRect::new(
|
||||
(source_in_backdrop_space + (backdrop_rect.origin.to_f32() - backdrop_screen_origin)).to_i32(),
|
||||
let src = DeviceIntRect::new(
|
||||
readback_origin +
|
||||
backdrop_rect.origin.to_vector() -
|
||||
backdrop_screen_origin.to_i32().to_vector(),
|
||||
readback_rect.size,
|
||||
);
|
||||
let mut dest = readback_rect.to_i32();
|
||||
let device_to_framebuffer = Scale::new(1i32);
|
||||
|
||||
// Need to invert the y coordinates and flip the image vertically when
|
||||
// reading back from the framebuffer.
|
||||
if draw_target.is_default() {
|
||||
src.origin.y = draw_target.dimensions().height as i32 - src.size.height - src.origin.y;
|
||||
dest.origin.y += dest.size.height;
|
||||
dest.size.height = -dest.size.height;
|
||||
}
|
||||
// Should always be drawing to picture cache tiles or off-screen surface!
|
||||
debug_assert!(!draw_target.is_default());
|
||||
let device_to_framebuffer = Scale::new(1i32);
|
||||
|
||||
self.device.blit_render_target(
|
||||
draw_target.into(),
|
||||
src * device_to_framebuffer,
|
||||
cache_draw_target,
|
||||
dest * device_to_framebuffer,
|
||||
readback_rect * device_to_framebuffer,
|
||||
TextureFilter::Linear,
|
||||
);
|
||||
|
||||
|
@ -2899,14 +2895,13 @@ impl Renderer {
|
|||
}
|
||||
|
||||
// Handle special case readback for composites.
|
||||
if let BatchKind::Brush(BrushBatchKind::MixBlend { task_id, source_id, backdrop_id }) = batch.key.kind {
|
||||
if let BatchKind::Brush(BrushBatchKind::MixBlend { task_id, backdrop_id }) = batch.key.kind {
|
||||
// composites can't be grouped together because
|
||||
// they may overlap and affect each other.
|
||||
debug_assert_eq!(batch.instances.len(), 1);
|
||||
self.handle_readback_composite(
|
||||
draw_target,
|
||||
uses_scissor,
|
||||
&render_tasks[source_id],
|
||||
&render_tasks[task_id],
|
||||
&render_tasks[backdrop_id],
|
||||
);
|
||||
|
|
|
@ -266,7 +266,6 @@ impl Wrench {
|
|||
testing: true,
|
||||
max_texture_size: Some(8196), // Needed for rawtest::test_resize_image.
|
||||
allow_dual_source_blending: !disable_dual_source_blending,
|
||||
allow_advanced_blend_equation: true,
|
||||
dump_shader_source,
|
||||
// SWGL doesn't support the GL_ALWAYS depth comparison function used by
|
||||
// `clear_caches_with_quads`, but scissored clears work well.
|
||||
|
|
|
@ -52,6 +52,8 @@ fn string_to_color(color: &str) -> Option<ColorF> {
|
|||
"white" => Some(ColorF::new(1.0, 1.0, 1.0, 1.0)),
|
||||
"black" => Some(ColorF::new(0.0, 0.0, 0.0, 1.0)),
|
||||
"yellow" => Some(ColorF::new(1.0, 1.0, 0.0, 1.0)),
|
||||
"cyan" => Some(ColorF::new(0.0, 1.0, 1.0, 1.0)),
|
||||
"magenta" => Some(ColorF::new(1.0, 0.0, 1.0, 1.0)),
|
||||
"transparent" => Some(ColorF::new(1.0, 1.0, 1.0, 0.0)),
|
||||
s => {
|
||||
let items: Vec<f32> = s.split_whitespace()
|
||||
|
|
Загрузка…
Ссылка в новой задаче