/* 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/. */
use api::units::*; use api::{ColorF, ExtendMode, GradientStop}; usecrate::pattern::{Pattern, PatternKind, PatternShaderInput, PatternTextureInput}; usecrate::renderer::{BlendMode, GpuBufferBuilder, GpuBufferWriterF};
fn write_gpu_gradient_stops_header_and_colors(
stops: &[GradientStop],
kind: GradientKind,
extend_mode: ExtendMode,
writer: &mut GpuBufferWriterF,
) -> bool { // Write the header.
writer.push_one([
(kind as u8) as f32,
stops.len() as f32, if extend_mode == ExtendMode::Repeat { 1.0 } else { 0.0 }, 0.0
]);
// Write the stop colors. letmut is_opaque = true; for stop in stops {
writer.push_one(stop.color.premultiplied());
is_opaque &= stop.color.a == 1.0;
}
is_opaque
}
// Push stop offsets in rearranged order so that the search can be carried // out as an implicit tree traversal. // // The structure of the tree is: // - Each level is plit into 5 partitions. // - The root level has one node (4 offsets -> 5 partitions). // - Each level has 5 more nodes than the previous one. // - Levels are pushed one by one starting from the root // // ```ascii // level : indices // ------:--------- // 0 : 24 ... // 1 : 4 9 14 19 | ... // 2 : 0,1,2,3,|,5,6,7,8,|10,11,12,13,| ,15,16,17,18,| ,20,21,22,23,| ,25, ... // ``` // // In the example above: // - The first (root) contains a single block containing the stop offsets from // indices [24, 49, 74, 99]. // - The second level contains blocks of offsets from indices [4, 9, 14, 19], // [29, 34, 39, 44], etc. // - The third (leaf) level contains blocks from indices [0,1,2,3], [5,6,7,8], // [15, 16, 17, 18], etc. // // Placeholder offsets (1.0) are used when a level has more capacity than the // input number of stops. // // Conceptually, blocks [0,1,2,3] and [5,6,7,8] are the first two children of // the node [4,9,14,19], separated by the offset from index 4. // Links are not explicitly represented via pointers or indices. Instead the // position in the buffer is sufficient to represent the level and index of the // stop (at the expense of having to store extra padding to round up each tree // level to its power-of-5-aligned size). // // This scheme is meant to make the traversal efficient loading offsets in // blocks of 4. The shader can converge to the leaf in very few loads. pubfn write_gpu_gradient_stops_tree(
stops: &[GradientStop],
kind: GradientKind,
extend_mode: ExtendMode,
writer: &mut GpuBufferWriterF,
) -> bool { let is_opaque = write_gpu_gradient_stops_header_and_colors(
stops,
kind,
extend_mode,
writer
);
let num_stops = stops.len(); letmut num_levels = 1; letmut index_stride = 5; letmut next_index_stride = 1; // Number of 4-offsets blocks for the current level. // The root has 1, then each level has 5 more than the previous one. letmut num_blocks_for_level = 1; letmut offset_blocks = 1; while offset_blocks * 4 < num_stops {
num_blocks_for_level *= 5;
offset_blocks += num_blocks_for_level;
// Fix offset_blocks up to account for the fact that we don't // store the entirety of the last level; let num_blocks_for_last_level = num_blocks_for_level.min(num_stops / 5 + 1);
// Reset num_blocks_for_level for the traversal.
num_blocks_for_level = 1;
// Go over each level, starting from the root. for level in0..num_levels { // This scheme rounds up the number of offsets to store for each // level to the next power of 5, which can represent a lot of wasted // space, especially for the last levels. We need each level to start // at a specific power-of-5-aligned offset so we can't get around the // wasted space for all levels except the last one (which has the most // waste). let is_last_level = level == num_levels - 1; let num_blocks = if is_last_level {
num_blocks_for_last_level
} else {
num_blocks_for_level
};
for block_idx in0..num_blocks { letmut block = [1.0; 4]; for i in0..4 { let linear_idx = block_idx * index_stride
+ i * next_index_stride
+ next_index_stride - 1;
fn gpu_gradient_stops_blocks(num_stops: usize) -> usize { let header_blocks = 1; let color_blocks = num_stops;
// If this is changed, matching changes should be made to the // equivalent code in write_gpu_gradient_stops_tree. letmut num_blocks_for_level = 1; letmut offset_blocks = 1; while offset_blocks * 4 < num_stops {
num_blocks_for_level *= 5;
offset_blocks += num_blocks_for_level;
}
// Fix the capacity up to account for the fact that we don't // store the entirety of the last level; let num_blocks_for_last_level = num_blocks_for_level.min(num_stops / 5 + 1);
offset_blocks -= num_blocks_for_level;
offset_blocks += num_blocks_for_last_level;
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.