Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Picture caching support, part 1 (of 2). #3332

Merged
merged 10 commits into from Nov 23, 2018

Picture caching support, part 1 (of 2).

This patch introduces a picture composite mode that caches the
surface in a grid of tiles in the texture cache. This is enabled
by setting the picture composite mode to PictureCompositeMode::TileCache.

Basic overview:
 * During initial picture traversal, any tile caches encountered
   are pushed / popped onto the PictureUpdateState stack.
 * If a tile cache is present, and if the transforms have changed
   since last frame build, then the primitive instances are walked,
   mapping each prim instance to a set of tiles in the cache. This
   allows dependencies for each tile (transforms, images, animated
   property bindings etc) to be set. Although this does involve an
   extra pass of primitive instances, it's only used when the tile
   cache mappings (the relative transforms) have changed, so it's
   done quite rarely. In addition, in the future we can move much
   of the second primitive traversal (clip chain building) etc to
   also happen here, which will mean that doesn't get done each
   frame build.
 * During prim prepare, a picture with tile cache enabled checks
   the dependencies on each tile, to see if it needs to be invalidated.
   It builds a dirty rect that is the union of the set of dirty
   tiles for this picture. Right now, we support only a single
   dirty rect for simplicity, but this can easily be expanded in
   the future to support a number of regions as required. This
   dirty rect is then passed via the PictureContext to any child
   primitives, and is used as the culling rect for primitive, clip
   mask and child picture allocations.
 * The picture preparation step also maintains a list of texture
   cache handles per tile, and records a series of blit commands
   for the renderer to execute after drawing the picture. These
   blits are executed to cache the picture output in texture
   cache tiles.
 * On subsequent frames, if the tile(s) are not invalid, they are
   drawn as a series of images with the brush_image shader.
  • Loading branch information
gw3583 committed Nov 22, 2018
commit 7c3453f884a3c9a58d3815c7b41f7a3fe69ddceb
@@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use api::{AlphaType, ClipMode, DeviceIntRect, DeviceIntPoint, DeviceIntSize};
use api::{ExternalImageType, FilterOp, ImageRendering};
use api::{ExternalImageType, FilterOp, ImageRendering, LayoutRect};
use api::{YuvColorSpace, YuvFormat, PictureRect, ColorDepth};
use clip::{ClipDataStore, ClipNodeFlags, ClipNodeRange, ClipItem, ClipStore};
use clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
@@ -27,7 +27,7 @@ use scene::FilterOpHelpers;
use smallvec::SmallVec;
use std::{f32, i32, usize};
use tiling::{RenderTargetContext};
use util::{TransformedRectKind};
use util::{RectHelpers, TransformedRectKind};

// Special sentinel value recognized by the shader. It is considered to be
// a dummy task that doesn't mask out anything.
@@ -894,12 +894,98 @@ impl AlphaBatchBuilder {

match picture.raster_config {
Some(ref raster_config) => {
let surface = ctx.surfaces[raster_config.surface_index.0]
.surface
.as_ref()
.expect("bug: surface must be allocated by now");
match raster_config.composite_mode {
PictureCompositeMode::TileCache { .. } => {

// Step through each tile in the cache, and draw it with an image
// brush primitive if visible.

let kind = BatchKind::Brush(
BrushBatchKind::Image(ImageBufferKind::Texture2DArray)
);

let tile_cache = picture.tile_cache.as_ref().unwrap();

for y in 0 .. tile_cache.y_tiles {
for x in 0 .. tile_cache.x_tiles {
let i = y * tile_cache.x_tiles + x;
let tile = &tile_cache.tiles[i as usize];

// Check if the tile is visible.
if tile.is_visible {

This comment has been minimized.

@emilio

emilio Nov 21, 2018

Member

maybe:

if !tile.is_visible {
    continue;
}

// Get the local rect of the tile.
let tx0 = (tile_cache.tile_x0 + x) as f32 * tile_cache.local_tile_size.width;
let ty0 = (tile_cache.tile_y0 + y) as f32 * tile_cache.local_tile_size.height;
let tx1 = tx0 + tile_cache.local_tile_size.width;
let ty1 = ty0 + tile_cache.local_tile_size.height;
let tile_rect = LayoutRect::from_floats(tx0, ty0, tx1, ty1);

// Construct a local clip rect that ensures we only draw pixels where
// the local bounds of the picture extend to within the edge tiles.
let local_clip_rect = prim_instance
.combined_local_clip_rect
.intersection(&picture.local_rect)
.expect("bug: invalid picture local rect");

let prim_header = PrimitiveHeader {
local_rect: tile_rect,
local_clip_rect,
task_address,
specific_prim_address: prim_cache_address,
clip_task_address,
transform_id,
};

let prim_header_index = prim_headers.push(&prim_header, z_id, [
ShaderColorMode::Image as i32 | ((AlphaType::PremultipliedAlpha as i32) << 16),
RasterizationSpace::Local as i32,
get_shader_opacity(1.0),
]);

let cache_item = ctx
.resource_cache
.get_texture_cache_item(&tile.handle);

let key = BatchKey::new(
kind,
non_segmented_blend_mode,
BatchTextures::color(cache_item.texture_id),
);

let uv_rect_address = gpu_cache
.get_address(&cache_item.uv_rect_handle)
.as_int();

let instance = BrushInstance {
prim_header_index,
clip_task_address,
segment_index: 0xffff,
edge_flags: EdgeAaSegmentMask::empty(),
brush_flags: BrushFlags::empty(),
user_data: uv_rect_address,
};

// Instead of retrieving the batch once and adding each tile instance,
// use this API to get an appropriate batch for each tile, since
// the batch textures may be different. The batch list internally
// caches the current batch if the key hasn't changed.
let batch = self.batch_list.set_params_and_get_batch(
key,
bounding_rect,
z_id,
);

batch.push(PrimitiveInstanceData::from(instance));
}
}
}
}
PictureCompositeMode::Filter(filter) => {
let surface = ctx.surfaces[raster_config.surface_index.0]
.surface
.as_ref()
.expect("bug: surface must be allocated by now");
assert!(filter.is_visible());
match filter {
FilterOp::Blur(..) => {
@@ -1117,6 +1203,10 @@ impl AlphaBatchBuilder {
}
}
PictureCompositeMode::MixBlend(mode) => {
let surface = ctx.surfaces[raster_config.surface_index.0]
.surface
.as_ref()
.expect("bug: surface must be allocated by now");
let cache_task_id = surface.resolve_render_task_id();
let backdrop_id = picture.secondary_render_task_id.expect("no backdrop!?");

@@ -1156,6 +1246,10 @@ impl AlphaBatchBuilder {
);
}
PictureCompositeMode::Blit => {
let surface = ctx.surfaces[raster_config.surface_index.0]
.surface
.as_ref()
.expect("bug: surface must be allocated by now");
let cache_task_id = surface.resolve_render_task_id();
let kind = BatchKind::Brush(
BrushBatchKind::Image(ImageBufferKind::Texture2DArray)
@@ -203,7 +203,11 @@ impl<'a> DisplayListFlattener<'a> {
&root_pipeline.viewport_size,
&root_pipeline.content_size,
);
flattener.flatten_root(root_pipeline, &root_pipeline.viewport_size);

flattener.flatten_root(
root_pipeline,
&root_pipeline.viewport_size,
);

debug_assert!(flattener.sc_stack.is_empty());

@@ -241,7 +245,11 @@ impl<'a> DisplayListFlattener<'a> {
.get(items)
}

fn flatten_root(&mut self, pipeline: &'a ScenePipeline, frame_size: &LayoutSize) {
fn flatten_root(
&mut self,
pipeline: &'a ScenePipeline,
frame_size: &LayoutSize,
) {
let pipeline_id = pipeline.pipeline_id;
let reference_frame_info = self.simple_scroll_and_clip_chain(
&ClipId::root_reference_frame(pipeline_id),
@@ -262,6 +270,8 @@ impl<'a> DisplayListFlattener<'a> {

// For the root pipeline, there's no need to add a full screen rectangle
// here, as it's handled by the framebuffer clear.
// TODO(gw): In future, we can probably remove this code completely and handle
// it as part of the tile cache background color clearing.
if self.scene.root_pipeline_id != Some(pipeline_id) {
if let Some(pipeline) = self.scene.pipelines.get(&pipeline_id) {
if let Some(bg_color) = pipeline.background_color {
@@ -499,7 +509,10 @@ impl<'a> DisplayListFlattener<'a> {
ScrollSensitivity::ScriptAndInputEvents,
);

self.flatten_root(pipeline, &iframe_rect.size);
self.flatten_root(
pipeline,
&iframe_rect.size,
);

self.pipeline_clip_chain_stack.pop();
}
@@ -70,7 +70,7 @@ pub struct FrameBuildingContext<'a> {
pub device_pixel_scale: DevicePixelScale,
pub scene_properties: &'a SceneProperties,
pub pipelines: &'a FastHashMap<PipelineId, Arc<ScenePipeline>>,
pub world_rect: WorldRect,
pub screen_world_rect: WorldRect,
pub clip_scroll_tree: &'a ClipScrollTree,
pub max_local_clip: LayoutRect,
}
@@ -95,14 +95,14 @@ pub struct PictureContext {
pub pic_index: PictureIndex,
pub pipeline_id: PipelineId,
pub apply_local_clip_rect: bool,
pub inflation_factor: f32,
pub allow_subpixel_aa: bool,
pub is_passthrough: bool,
pub raster_space: RasterSpace,
pub surface_spatial_node_index: SpatialNodeIndex,
pub raster_spatial_node_index: SpatialNodeIndex,
/// The surface that this picture will render on.
pub surface_index: SurfaceIndex,
pub dirty_world_rect: WorldRect,
}

/// Mutable state of a picture that gets modified when
@@ -203,13 +203,13 @@ impl FrameBuilder {

const MAX_CLIP_COORD: f32 = 1.0e9;

let world_rect = (self.screen_rect.to_f32() / device_pixel_scale).round_out();
let screen_world_rect = (self.screen_rect.to_f32() / device_pixel_scale).round_out();

let frame_context = FrameBuildingContext {
device_pixel_scale,
scene_properties,
pipelines,
world_rect,
screen_world_rect,
clip_scroll_tree,
max_local_clip: LayoutRect::new(
LayoutPoint::new(-MAX_CLIP_COORD, -MAX_CLIP_COORD),
@@ -222,7 +222,8 @@ impl FrameBuilder {
let root_surface = SurfaceInfo::new(
ROOT_SPATIAL_NODE_INDEX,
ROOT_SPATIAL_NODE_INDEX,
world_rect,
0.0,
screen_world_rect,
clip_scroll_tree,
);
surfaces.push(root_surface);
@@ -240,6 +241,9 @@ impl FrameBuilder {
self.root_pic_index,
&mut pic_update_state,
&frame_context,
resource_cache,
&resources.prim_data_store,
&self.clip_store,
);

let mut frame_state = FrameBuildingState {
@@ -298,6 +302,8 @@ impl FrameBuilder {
child_tasks,
UvRectKind::Rect,
root_spatial_node_index,
None,
Vec::new(),
);

let render_task_id = frame_state.render_tasks.add(root_render_task);
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.