/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const STYLE_SOLID: i32 = ((BorderStyle::Solid as i32) << 8) | ((BorderStyle::Solid as i32) << 16); const STYLE_MASK: i32 = 0x00FF_FF00;
/// A tag used to identify the output format of a `RenderTarget`. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pubenum RenderTargetKind {
Color, // RGBA8
Alpha, // R8
}
/// A series of `RenderTarget` instances, serving as the high-level container /// into which `RenderTasks` are assigned. /// /// During the build phase, we iterate over the tasks in each `RenderPass`. For /// each task, we invoke `allocate()` on the `RenderTargetList`, which in turn /// attempts to allocate an output region in the last `RenderTarget` in the /// list. If allocation fails (or if the list is empty), a new `RenderTarget` is /// created and appended to the list. The build phase then assign the task into /// the target associated with the final allocation. /// /// The result is that each `RenderPass` is associated with one or two /// `RenderTargetLists`, depending on whether we have all our tasks have the /// same `RenderTargetKind`. The lists are then shipped to the `Renderer`, which /// allocates a device texture array, with one slice per render target in the /// list. /// /// The upshot of this scheme is that it maximizes batching. In a given pass, /// we need to do a separate batch for each individual render target. But with /// the texture array, we can expose the entirety of the previous pass to each /// task in the current pass in a single batch, which generally allows each /// task to be drawn in a single batch regardless of how many results from the /// previous pass it depends on. /// /// Note that in some cases (like drop-shadows), we can depend on the output of /// a pass earlier than the immediately-preceding pass. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pubstruct RenderTargetList { pub targets: FrameVec<RenderTarget>,
}
for target in &mutself.targets {
target.build(
ctx,
render_tasks,
prim_headers,
transforms,
z_generator,
prim_instances,
cmd_buffers,
gpu_buffer_builder,
);
}
}
}
const NUM_PATTERNS: usize = crate::pattern::NUM_PATTERNS as usize;
/// Contains the work (in the form of instance arrays) needed to fill a color /// color (RGBA8) or alpha output surface. /// /// In graphics parlance, a "render target" usually means "a surface (texture or /// framebuffer) bound to the output of a shader". This struct has a slightly /// different meaning, in that it represents the operations on that surface /// _before_ it's actually bound and rendered. So a `RenderTarget` is built by /// the `RenderBackend` by inserting tasks, and then shipped over to the /// `Renderer` where a device surface is resolved and the tasks are transformed /// into draw commands on that surface. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pubstruct RenderTarget { pub target_kind: RenderTargetKind, pub cached: bool,
screen_size: DeviceIntSize, pub texture_id: CacheTextureId,
pub alpha_batch_containers: FrameVec<AlphaBatchContainer>, // List of blur operations to apply for this render target. pub vertical_blurs: FastHashMap<TextureSource, FrameVec<BlurInstance>>, pub horizontal_blurs: FastHashMap<TextureSource, FrameVec<BlurInstance>>, pub scalings: FastHashMap<TextureSource, FrameVec<ScalingInstance>>, pub svg_nodes: FrameVec<(BatchTextures, FrameVec<SVGFEFilterInstance>)>, pub blits: FrameVec<BlitJob>,
alpha_tasks: FrameVec<RenderTaskId>, pub resolve_ops: FrameVec<ResolveOp>,
// Clearing render targets has a fair amount of special cases. // The general rules are: // - Depth (for at least the used potion of the target) is always cleared if it // is used by the target. The rest of this explaination focuses on clearing // color/alpha textures. // - For non-cached targets we either clear the entire target or the used portion // (unless clear_color is None). // - Cached render targets require precise partial clears which are specified // via the vectors below (if clearing is needed at all). // // See also: Renderer::clear_render_target
// Areas that *must* be cleared. // Even if a global target clear is done, we try to honor clearing the rects that // have a different color than the global clear color. pub clears: FrameVec<(DeviceIntRect, ColorF)>,
// Optionally track the used rect of the render target, to give the renderer // an opportunity to only clear the used portion of the target as an optimization. // Note: We make the simplifying assumption that if clear vectors AND used_rect // are specified, then the rects from the clear vectors are contained in // used_rect. pub used_rect: Option<DeviceIntRect>, // The global clear color is Some(TRANSPARENT) by default. If we are drawing // a single render task in this target, it can be set to something else. // If clear_color is None, only the clears/zero_clears/one_clears are done. pub clear_color: Option<ColorF>,
}
// TODO(gw): The type names of AlphaBatchBuilder and BatchBuilder // are still confusing. Once more of the picture caching // improvement code lands, the AlphaBatchBuilder and // AlphaBatchList types will be collapsed into one, which // should simplify coming up with better type names. let alpha_batch_builder = AlphaBatchBuilder::new( self.screen_size,
ctx.break_advanced_blend_batches,
ctx.batch_lookback_count,
*task_id,
(*task_id).into(),
&ctx.frame_memory,
);
letmut batch_builder = BatchBuilder::new(alpha_batch_builder); let cmd_buffer = cmd_buffers.get(pic_task.cmd_buffer_index);
let task_origin = target_rect.min.to_f32(); // TODO(gw): Clone here instead of a move of this vec, since the frame // graph is immutable by this point. It's rare that borders // are drawn since they are persisted in the texture cache, // but perhaps this could be improved in future. let instances = task_info.instances.clone(); formut instance in instances { // TODO(gw): It may be better to store the task origin in // the render task data instead of per instance.
instance.task_origin = task_origin; if instance.flags & STYLE_MASK == STYLE_SOLID { self.border_segments_solid.push(instance);
} else { self.border_segments_complex.push(instance);
}
}
}
RenderTaskKind::Image(..) |
RenderTaskKind::Cached(..) |
RenderTaskKind::TileComposite(..) => {
panic!("Should not be added to color target!");
}
RenderTaskKind::Readback(..) => {} #[cfg(test)]
RenderTaskKind::Test(..) => {}
}
let task_address = task_id.into(); for sub_task_id in task.sub_tasks.clone() { let sub_task = &render_tasks[sub_task_id]; match sub_task {
SubTask::RectangleClip(clip_task) => {
add_rect_clip_task_to_batch(
clip_task,
&target_rect,
task_address,
&ctx.frame_memory,
render_tasks,
gpu_buffer_builder,
&mutself.clip_masks
);
}
SubTask::ImageClip(clip_task) => {
add_image_clip_task_to_batch(
clip_task,
&target_rect,
task_address,
&ctx.frame_memory,
render_tasks,
gpu_buffer_builder,
&mutself.clip_masks
);
}
}
}
}
/// Generates SVGFEFilterInstances from a single SVGFEFilterTask, this is what /// prepares vertex data for the shader, and adds it to the appropriate batch. /// /// The interesting parts of the handling of SVG filters are: /// * scene_building.rs : wrap_prim_with_filters /// * picture.rs : get_coverage_svgfe /// * render_task.rs : new_svg_filter_graph /// * render_target.rs : add_svg_filter_node_instances (you are here) fn add_svg_filter_node_instances(
instances: &mut FrameVec<(BatchTextures, FrameVec<SVGFEFilterInstance>)>,
render_tasks: &RenderTaskGraph,
task_info: &SVGFEFilterTask,
target_task: &RenderTask,
input_1_task: Option<RenderTaskId>,
input_2_task: Option<RenderTaskId>,
extra_data_address: Option<GpuBufferAddress>,
memory: &FrameMemory,
) { let node = &task_info.node; let op = &task_info.op; letmut textures = BatchTextures::empty();
// We have to undo the inflate here as the inflated target rect is meant to // have a blank border let target_rect = target_task
.get_target_rect()
.inner_box(DeviceIntSideOffsets::new(node.inflate as i32, node.inflate as i32, node.inflate as i32, node.inflate as i32))
.to_f32();
// Must match FILTER_* in cs_svg_filter_node.glsl
instance.kind = match op { // Identity does not modify color, no linear case
FilterGraphOp::SVGFEIdentity => 0, // SourceGraphic does not have its own shader mode, it uses Identity.
FilterGraphOp::SVGFESourceGraphic => 0, // SourceAlpha does not have its own shader mode, it uses ToAlpha.
FilterGraphOp::SVGFESourceAlpha => 4, // Opacity scales the entire rgba color, so it does not need a linear // case as the rgb / a ratio does not change (sRGB is a curve on the RGB // before alpha multiply, not after)
FilterGraphOp::SVGFEOpacity{..} => 2,
FilterGraphOp::SVGFEToAlpha => 4,
FilterGraphOp::SVGFEBlendColor => {match node.linear {false => 6, true => 7}},
FilterGraphOp::SVGFEBlendColorBurn => {match node.linear {false => 8, true => 9}},
FilterGraphOp::SVGFEBlendColorDodge => {match node.linear {false => 10, true => 11}},
FilterGraphOp::SVGFEBlendDarken => {match node.linear {false => 12, true => 13}},
FilterGraphOp::SVGFEBlendDifference => {match node.linear {false => 14, true => 15}},
FilterGraphOp::SVGFEBlendExclusion => {match node.linear {false => 16, true => 17}},
FilterGraphOp::SVGFEBlendHardLight => {match node.linear {false => 18, true => 19}},
FilterGraphOp::SVGFEBlendHue => {match node.linear {false => 20, true => 21}},
FilterGraphOp::SVGFEBlendLighten => {match node.linear {false => 22, true => 23}},
FilterGraphOp::SVGFEBlendLuminosity => {match node.linear {false => 24, true => 25}},
FilterGraphOp::SVGFEBlendMultiply => {match node.linear {false => 26, true => 27}},
FilterGraphOp::SVGFEBlendNormal => {match node.linear {false => 28, true => 29}},
FilterGraphOp::SVGFEBlendOverlay => {match node.linear {false => 30, true => 31}},
FilterGraphOp::SVGFEBlendSaturation => {match node.linear {false => 32, true => 33}},
FilterGraphOp::SVGFEBlendScreen => {match node.linear {false => 34, true => 35}},
FilterGraphOp::SVGFEBlendSoftLight => {match node.linear {false => 36, true => 37}},
FilterGraphOp::SVGFEColorMatrix{..} => {match node.linear {false => 38, true => 39}},
FilterGraphOp::SVGFEComponentTransfer => unreachable!(),
FilterGraphOp::SVGFEComponentTransferInterned{..} => {match node.linear {false => 40, true => 41}},
FilterGraphOp::SVGFECompositeArithmetic{..} => {match node.linear {false => 42, true => 43}},
FilterGraphOp::SVGFECompositeATop => {match node.linear {false => 44, true => 45}},
FilterGraphOp::SVGFECompositeIn => {match node.linear {false => 46, true => 47}},
FilterGraphOp::SVGFECompositeLighter => {match node.linear {false => 48, true => 49}},
FilterGraphOp::SVGFECompositeOut => {match node.linear {false => 50, true => 51}},
FilterGraphOp::SVGFECompositeOver => {match node.linear {false => 52, true => 53}},
FilterGraphOp::SVGFECompositeXOR => {match node.linear {false => 54, true => 55}},
FilterGraphOp::SVGFEConvolveMatrixEdgeModeDuplicate{..} => {match node.linear {false => 56, true => 57}},
FilterGraphOp::SVGFEConvolveMatrixEdgeModeNone{..} => {match node.linear {false => 58, true => 59}},
FilterGraphOp::SVGFEConvolveMatrixEdgeModeWrap{..} => {match node.linear {false => 60, true => 61}},
FilterGraphOp::SVGFEDiffuseLightingDistant{..} => {match node.linear {false => 62, true => 63}},
FilterGraphOp::SVGFEDiffuseLightingPoint{..} => {match node.linear {false => 64, true => 65}},
FilterGraphOp::SVGFEDiffuseLightingSpot{..} => {match node.linear {false => 66, true => 67}},
FilterGraphOp::SVGFEDisplacementMap{..} => {match node.linear {false => 68, true => 69}},
FilterGraphOp::SVGFEDropShadow{..} => {match node.linear {false => 70, true => 71}}, // feFlood takes an sRGB color and does no math on it, no linear case
FilterGraphOp::SVGFEFlood{..} => 72,
FilterGraphOp::SVGFEGaussianBlur{..} => {match node.linear {false => 74, true => 75}}, // feImage does not meaningfully modify the color of its input, though a // case could be made for gamma-correct image scaling, that's a bit out // of scope for now
FilterGraphOp::SVGFEImage{..} => 76,
FilterGraphOp::SVGFEMorphologyDilate{..} => {match node.linear {false => 80, true => 81}},
FilterGraphOp::SVGFEMorphologyErode{..} => {match node.linear {false => 82, true => 83}},
FilterGraphOp::SVGFESpecularLightingDistant{..} => {match node.linear {false => 86, true => 87}},
FilterGraphOp::SVGFESpecularLightingPoint{..} => {match node.linear {false => 88, true => 89}},
FilterGraphOp::SVGFESpecularLightingSpot{..} => {match node.linear {false => 90, true => 91}}, // feTile does not modify color, no linear case
FilterGraphOp::SVGFETile => 92,
FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithNoStitching{..} => {match node.linear {false => 94, true => 95}},
FilterGraphOp::SVGFETurbulenceWithFractalNoiseWithStitching{..} => {match node.linear {false => 96, true => 97}},
FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithNoStitching{..} => {match node.linear {false => 98, true => 99}},
FilterGraphOp::SVGFETurbulenceWithTurbulenceNoiseWithStitching{..} => {match node.linear {false => 100, true => 101}},
};
// This is a bit of an ugly way to do this, but avoids code duplication. letmut resolve_input = |index: usize, src_task: Option<RenderTaskId>| -> (RenderTaskAddress, [f32; 4]) { letmut src_task_id = RenderTaskId::INVALID; letmut resolved_scale_and_offset: [f32; 4] = [0.0; 4]; iflet Some(input) = node.inputs.get(index) {
src_task_id = src_task.unwrap(); let src_task = &render_tasks[src_task_id];
textures.input.colors[index] = src_task.get_texture_source(); let src_task_size = src_task.location.size(); let src_scale_x = (src_task_size.width as f32 - input.inflate as f32 * 2.0) / input.subregion.width(); let src_scale_y = (src_task_size.height as f32 - input.inflate as f32 * 2.0) / input.subregion.height(); let scale_x = src_scale_x * node.subregion.width(); let scale_y = src_scale_y * node.subregion.height(); let offset_x = src_scale_x * (node.subregion.min.x - input.subregion.min.x) + input.inflate as f32; let offset_y = src_scale_y * (node.subregion.min.y - input.subregion.min.y) + input.inflate as f32;
resolved_scale_and_offset = [
scale_x,
scale_y,
offset_x,
offset_y];
} let address: RenderTaskAddress = src_task_id.into();
(address, resolved_scale_and_offset)
};
(instance.input_1_task_address, instance.input_1_content_scale_and_offset) = resolve_input(0, input_1_task);
(instance.input_2_task_address, instance.input_2_content_scale_and_offset) = resolve_input(1, input_2_task);
// Additional instance modifications for certain filters match op {
FilterGraphOp::SVGFEOpacity { valuebinding: _, value } => { // opacity only has one input so we can use the other // components to store the opacity value
instance.input_2_content_scale_and_offset = [*value, 0.0, 0.0, 0.0];
},
FilterGraphOp::SVGFEMorphologyDilate { radius_x, radius_y } |
FilterGraphOp::SVGFEMorphologyErode { radius_x, radius_y } => { // morphology filters only use one input, so we use the // second offset coord to store the radius values.
instance.input_2_content_scale_and_offset = [*radius_x, *radius_y, 0.0, 0.0];
},
FilterGraphOp::SVGFEFlood { color } => { // flood filters don't use inputs, so we store color here. // We can't do the same trick on DropShadow because it does have two // inputs.
instance.input_2_content_scale_and_offset = [color.r, color.g, color.b, color.a];
},
_ => {},
}
for (refmut batch_textures, refmut batch) in instances.iter_mut() { iflet Some(combined_textures) = batch_textures.combine_textures(textures) {
batch.push(instance); // Update the batch textures to the newly combined batch textures
*batch_textures = combined_textures; // is this really the intended behavior? return;
}
}
// Information required to do a blit from a source to a target. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pubstruct BlitJob { pub source: RenderTaskId, // Normalized region within the source task to blit from pub source_rect: DeviceIntRect, pub target_rect: DeviceIntRect,
}
fn add_image_clip_task_to_batch(
task: &ImageClipSubTask,
target_rect: &DeviceIntRect,
masked_task_address: RenderTaskAddress,
memory: &FrameMemory,
render_tasks: &RenderTaskGraph,
gpu_buffers: &mut GpuBufferBuilder,
results: &mut ClipMaskInstanceList,
) { // A current oddity of the quads infrastructure is that image sources are encoded in // quad segment data so we need to have at least one segment if we have a uv rect as // input. let segment_index = 0;
let pattern = Pattern::texture(task.src_task, false);
quad::add_to_batch(
pattern.kind,
pattern.shader_input,
masked_task_address,
task.quad_transform_id,
task.quad_address,
task.quad_flags,
EdgeMask::empty(),
segment_index,
task.src_task,
ZBufferId(0),
BlendMode::None, // This parameter is ignored.
render_tasks,
gpu_buffers,
|_, prim| { let texture = render_tasks
.resolve_texture(task.src_task)
.expect("bug: texture not found for tile");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.