Bug 1658491 - Remove unused frame output functionality. r=kvark
authorGlenn Watson <git@intuitionlibrary.com>
Tue, 11 Aug 2020 21:06:58 +0000 (2020-08-11)
changeset 544305 361521e3c52324809553c555fb066d50f023d9bf
parent 544304 73d8b0959b4deedf5934f86c5ea6ff259d13fe0b
child 544306 367054bd65f01d4b364b7ebc8339aa9a0601d9d0
push id37692
push usercbrindusan@mozilla.com
push dateWed, 12 Aug 2020 03:44:18 +0000 (2020-08-12)
treeherdermozilla-central@08471023c834 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskvark
bugs1658491
milestone81.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1658491 - Remove unused frame output functionality. r=kvark This is unused by Gecko, and untested / unused in Servo. It was only added as an initial prototype, and would need a more efficient implementation if it was ever intended for production use. Differential Revision: https://phabricator.services.mozilla.com/D86682
gfx/wr/examples/common/boilerplate.rs
gfx/wr/examples/frame_output.rs
gfx/wr/examples/texture_cache_stress.rs
gfx/wr/examples/yuv.rs
gfx/wr/webrender/src/picture.rs
gfx/wr/webrender/src/render_target.rs
gfx/wr/webrender/src/renderer.rs
gfx/wr/webrender/src/scene_builder_thread.rs
gfx/wr/webrender/src/scene_building.rs
gfx/wr/webrender/src/tile_cache.rs
gfx/wr/webrender_api/src/api.rs
gfx/wr/webrender_api/src/image.rs
--- a/gfx/wr/examples/common/boilerplate.rs
+++ b/gfx/wr/examples/common/boilerplate.rs
@@ -83,22 +83,21 @@ pub trait Example {
     fn on_event(
         &mut self,
         _: winit::WindowEvent,
         _: &mut RenderApi,
         _: DocumentId,
     ) -> bool {
         false
     }
-    fn get_image_handlers(
+    fn get_image_handler(
         &mut self,
         _gl: &dyn gl::Gl,
-    ) -> (Option<Box<dyn ExternalImageHandler>>,
-          Option<Box<dyn OutputImageHandler>>) {
-        (None, None)
+    ) -> Option<Box<dyn ExternalImageHandler>> {
+        None
     }
     fn draw_custom(&mut self, _gl: &dyn gl::Gl) {
     }
 }
 
 pub fn main_wrapper<E: Example>(
     example: &mut E,
     options: Option<webrender::RendererOptions>,
@@ -183,21 +182,17 @@ pub fn main_wrapper<E: Example>(
         notifier,
         opts,
         None,
         device_size,
     ).unwrap();
     let mut api = sender.create_api();
     let document_id = api.add_document(device_size, 0);
 
-    let (external, output) = example.get_image_handlers(&*gl);
-
-    if let Some(output_image_handler) = output {
-        renderer.set_output_image_handler(output_image_handler);
-    }
+    let external = example.get_image_handler(&*gl);
 
     if let Some(external_image_handler) = external {
         renderer.set_external_image_handler(external_image_handler);
     }
 
     let epoch = Epoch(0);
     let pipeline_id = PipelineId(0, 0);
     let layout_size = device_size.to_f32() / euclid::Scale::new(device_pixel_ratio);
deleted file mode 100644
--- a/gfx/wr/examples/frame_output.rs
+++ /dev/null
@@ -1,235 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-extern crate euclid;
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use crate::boilerplate::{Example, HandyDandyRectBuilder};
-use euclid::Scale;
-use gleam::gl;
-use webrender::api::*;
-use webrender::api::units::*;
-
-
-// This example demonstrates using the frame output feature to copy
-// the output of a WR framebuffer to a custom texture.
-
-#[derive(Debug)]
-struct Document {
-    id: DocumentId,
-    pipeline_id: PipelineId,
-    content_rect: LayoutRect,
-    color: ColorF,
-}
-
-
-struct App {
-    external_image_key: Option<ImageKey>,
-    output_document: Option<Document>
-}
-
-struct OutputHandler {
-    texture_id: gl::GLuint
-}
-
-struct ExternalHandler {
-    texture_id: gl::GLuint
-}
-
-impl OutputImageHandler for OutputHandler {
-    fn lock(&mut self, _id: PipelineId) -> Option<(u32, FramebufferIntSize)> {
-        Some((self.texture_id, FramebufferIntSize::new(500, 500)))
-    }
-
-    fn unlock(&mut self, _id: PipelineId) {}
-}
-
-impl ExternalImageHandler for ExternalHandler {
-    fn lock(
-        &mut self,
-        _key: ExternalImageId,
-        _channel_index: u8,
-        _rendering: ImageRendering
-    ) -> ExternalImage {
-        ExternalImage {
-            uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
-            source: ExternalImageSource::NativeTexture(self.texture_id),
-        }
-    }
-    fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {}
-}
-
-impl App {
-    fn init_output_document(
-        &mut self,
-        api: &mut RenderApi,
-        device_size: DeviceIntSize,
-        device_pixel_ratio: f32,
-    ) {
-        // Generate the external image key that will be used to render the output document to the root document.
-        self.external_image_key = Some(api.generate_image_key());
-
-        let pipeline_id = PipelineId(1, 0);
-        let layer = 1;
-        let color = ColorF::new(1., 1., 0., 1.);
-        let document_id = api.add_document(device_size, layer);
-        api.enable_frame_output(document_id, pipeline_id, true);
-        api.set_document_view(
-            document_id,
-            device_size.into(),
-            device_pixel_ratio,
-        );
-
-        let document = Document {
-            id: document_id,
-            pipeline_id,
-            content_rect: LayoutRect::new(
-                LayoutPoint::zero(),
-                device_size.to_f32() / Scale::new(device_pixel_ratio),
-            ),
-            color,
-        };
-
-        let mut txn = Transaction::new();
-
-        txn.add_image(
-            self.external_image_key.unwrap(),
-            ImageDescriptor::new(100, 100, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
-            ImageData::External(ExternalImageData {
-                id: ExternalImageId(0),
-                channel_index: 0,
-                image_type: ExternalImageType::TextureHandle(TextureTarget::Default),
-            }),
-            None,
-        );
-
-        let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
-        let mut builder = DisplayListBuilder::new(
-            document.pipeline_id,
-        );
-
-        builder.push_simple_stacking_context(
-            document.content_rect.origin,
-            space_and_clip.spatial_id,
-            PrimitiveFlags::IS_BACKFACE_VISIBLE,
-        );
-
-        builder.push_rect(
-            &CommonItemProperties::new(document.content_rect, space_and_clip),
-            document.content_rect,
-            ColorF::new(1.0, 1.0, 0.0, 1.0)
-        );
-        builder.pop_stacking_context();
-
-        txn.set_root_pipeline(pipeline_id);
-        txn.set_display_list(
-            Epoch(0),
-            Some(document.color),
-            document.content_rect.size,
-            builder.finalize(),
-            true,
-        );
-        txn.generate_frame();
-        api.send_transaction(document.id, txn);
-        self.output_document = Some(document);
-    }
-}
-
-impl Example for App {
-    fn render(
-        &mut self,
-        api: &mut RenderApi,
-        builder: &mut DisplayListBuilder,
-        _txn: &mut Transaction,
-        _device_size: DeviceIntSize,
-        pipeline_id: PipelineId,
-        _document_id: DocumentId,
-    ) {
-        if self.output_document.is_none() {
-            self.init_output_document(api, DeviceIntSize::new(200, 200), 1.0);
-        }
-
-        let bounds = (100, 100).to(200, 200);
-        let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
-
-        builder.push_simple_stacking_context(
-            bounds.origin,
-            space_and_clip.spatial_id,
-            PrimitiveFlags::IS_BACKFACE_VISIBLE,
-        );
-
-        builder.push_image(
-            &CommonItemProperties::new(bounds, space_and_clip),
-            bounds,
-            ImageRendering::Auto,
-            AlphaType::PremultipliedAlpha,
-            self.external_image_key.unwrap(),
-            ColorF::WHITE,
-        );
-
-        builder.pop_stacking_context();
-    }
-
-    fn get_image_handlers(
-        &mut self,
-        gl: &dyn gl::Gl,
-    ) -> (Option<Box<dyn ExternalImageHandler>>,
-          Option<Box<dyn OutputImageHandler>>) {
-        let texture_id = gl.gen_textures(1)[0];
-
-        gl.bind_texture(gl::TEXTURE_2D, texture_id);
-        gl.tex_parameter_i(
-            gl::TEXTURE_2D,
-            gl::TEXTURE_MAG_FILTER,
-            gl::LINEAR as gl::GLint,
-        );
-        gl.tex_parameter_i(
-            gl::TEXTURE_2D,
-            gl::TEXTURE_MIN_FILTER,
-            gl::LINEAR as gl::GLint,
-        );
-        gl.tex_parameter_i(
-            gl::TEXTURE_2D,
-            gl::TEXTURE_WRAP_S,
-            gl::CLAMP_TO_EDGE as gl::GLint,
-        );
-        gl.tex_parameter_i(
-            gl::TEXTURE_2D,
-            gl::TEXTURE_WRAP_T,
-            gl::CLAMP_TO_EDGE as gl::GLint,
-        );
-        gl.tex_image_2d(
-            gl::TEXTURE_2D,
-            0,
-            gl::RGBA as gl::GLint,
-            100,
-            100,
-            0,
-            gl::BGRA,
-            gl::UNSIGNED_BYTE,
-            None,
-        );
-        gl.bind_texture(gl::TEXTURE_2D, 0);
-
-        (
-            Some(Box::new(ExternalHandler { texture_id })),
-            Some(Box::new(OutputHandler { texture_id }))
-        )
-    }
-}
-
-fn main() {
-    let mut app = App {
-        external_image_key: None,
-        output_document: None
-    };
-
-    boilerplate::main_wrapper(&mut app, None);
-}
--- a/gfx/wr/examples/texture_cache_stress.rs
+++ b/gfx/wr/examples/texture_cache_stress.rs
@@ -296,22 +296,21 @@ impl Example for App {
                 return true;
             }
             _ => {}
         }
 
         false
     }
 
-    fn get_image_handlers(
+    fn get_image_handler(
         &mut self,
         _gl: &dyn gl::Gl,
-    ) -> (Option<Box<dyn ExternalImageHandler>>,
-          Option<Box<dyn OutputImageHandler>>) {
-        (Some(Box::new(ImageGenerator::new())), None)
+    ) -> Option<Box<dyn ExternalImageHandler>> {
+        Some(Box::new(ImageGenerator::new()))
     }
 }
 
 fn main() {
     let mut app = App {
         image_key: None,
         stress_keys: Vec::new(),
         image_generator: ImageGenerator::new(),
--- a/gfx/wr/examples/yuv.rs
+++ b/gfx/wr/examples/yuv.rs
@@ -190,24 +190,23 @@ impl Example for App {
         &mut self,
         _event: winit::WindowEvent,
         _api: &mut RenderApi,
         _document_id: DocumentId,
     ) -> bool {
         false
     }
 
-    fn get_image_handlers(
+    fn get_image_handler(
         &mut self,
         gl: &dyn gl::Gl,
-    ) -> (Option<Box<dyn ExternalImageHandler>>,
-          Option<Box<dyn OutputImageHandler>>) {
+    ) -> Option<Box<dyn ExternalImageHandler>> {
         let provider = YuvImageProvider::new(gl);
         self.texture_id = provider.texture_ids[0];
-        (Some(Box::new(provider)), None)
+        Some(Box::new(provider))
     }
 
     fn draw_custom(&mut self, gl: &dyn gl::Gl) {
         init_gl_texture(self.texture_id, gl::RED, gl::RED, &[self.current_value; 100 * 100], gl);
         self.current_value = self.current_value.wrapping_add(1);
     }
 }
 
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -89,17 +89,17 @@
 //!
 //! Tiles are only drawn in overlay mode if there is content that exists on top
 //! of the compositor surface. Otherwise, we can draw the tiles in the normal fast
 //! path before the compositor surface is drawn. Use of the per-tile valid and
 //! dirty rects ensure that we do a minimal amount of per-pixel work here to
 //! blend the overlay tile (this is not always optimal right now, but will be
 //! improved as a follow up).
 
-use api::{MixBlendMode, PipelineId, PremultipliedColorF, FilterPrimitiveKind};
+use api::{MixBlendMode, PremultipliedColorF, FilterPrimitiveKind};
 use api::{PropertyBinding, PropertyBindingId, FilterPrimitive};
 use api::{DebugFlags, RasterSpace, ImageKey, ColorF, ColorU, PrimitiveFlags};
 use api::{ImageRendering, ColorDepth, YuvColorSpace, YuvFormat};
 use api::units::*;
 use crate::box_shadow::BLUR_SAMPLE_SCALE;
 use crate::clip::{ClipStore, ClipChainInstance, ClipChainId, ClipInstance};
 use crate::spatial_tree::{ROOT_SPATIAL_NODE_INDEX,
     SpatialTree, CoordinateSpaceMapping, SpatialNodeIndex, VisibleFace
@@ -4589,20 +4589,16 @@ pub struct PicturePrimitive {
     pub requested_composite_mode: Option<PictureCompositeMode>,
     /// Requested rasterization space for this picture. It is
     /// a performance hint only.
     pub requested_raster_space: RasterSpace,
 
     pub raster_config: Option<RasterConfig>,
     pub context_3d: Picture3DContext<OrderedPictureChild>,
 
-    // If requested as a frame output (for rendering
-    // pages to a texture), this is the pipeline this
-    // picture is the root of.
-    pub frame_output_pipeline_id: Option<PipelineId>,
     // Optional cache handles for storing extra data
     // in the GPU cache, depending on the type of
     // picture.
     pub extra_gpu_data_handles: SmallVec<[GpuCacheHandle; 1]>,
 
     /// The spatial node index of this picture when it is
     /// composited into the parent picture.
     pub spatial_node_index: SpatialNodeIndex,
@@ -4718,32 +4714,30 @@ impl PicturePrimitive {
 
     // TODO(gw): We have the PictureOptions struct available. We
     //           should move some of the parameter list in this
     //           method to be part of the PictureOptions, and
     //           avoid adding new parameters here.
     pub fn new_image(
         requested_composite_mode: Option<PictureCompositeMode>,
         context_3d: Picture3DContext<OrderedPictureChild>,
-        frame_output_pipeline_id: Option<PipelineId>,
         apply_local_clip_rect: bool,
         flags: PrimitiveFlags,
         requested_raster_space: RasterSpace,
         prim_list: PrimitiveList,
         spatial_node_index: SpatialNodeIndex,
         options: PictureOptions,
     ) -> Self {
         PicturePrimitive {
             prim_list,
             state: None,
             secondary_render_task_id: None,
             requested_composite_mode,
             raster_config: None,
             context_3d,
-            frame_output_pipeline_id,
             extra_gpu_data_handles: SmallVec::new(),
             apply_local_clip_rect,
             is_backface_visible: flags.contains(PrimitiveFlags::IS_BACKFACE_VISIBLE),
             requested_raster_space,
             spatial_node_index,
             estimated_local_rect: LayoutRect::zero(),
             precise_local_rect: LayoutRect::zero(),
             prev_precise_local_rect: LayoutRect::zero(),
--- a/gfx/wr/webrender/src/render_target.rs
+++ b/gfx/wr/webrender/src/render_target.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 use api::units::*;
-use api::{ColorF, PremultipliedColorF, ImageFormat, LineOrientation, BorderStyle, PipelineId};
+use api::{ColorF, PremultipliedColorF, ImageFormat, LineOrientation, BorderStyle};
 use crate::batch::{AlphaBatchBuilder, AlphaBatchContainer, BatchTextures, resolve_image};
 use crate::batch::{ClipBatcher, BatchBuilder};
 use crate::spatial_tree::{SpatialTree, ROOT_SPATIAL_NODE_INDEX};
 use crate::clip::ClipStore;
 use crate::composite::CompositeState;
 use crate::device::Texture;
 use crate::frame_builder::{FrameGlobalResources};
 use crate::gpu_cache::{GpuCache, GpuCacheAddress};
@@ -294,18 +294,16 @@ pub struct ColorRenderTarget {
     pub alpha_batch_containers: Vec<AlphaBatchContainer>,
     // List of blur operations to apply for this render target.
     pub vertical_blurs: Vec<BlurInstance>,
     pub horizontal_blurs: Vec<BlurInstance>,
     pub readbacks: Vec<DeviceIntRect>,
     pub scalings: FastHashMap<TextureSource, Vec<ScalingInstance>>,
     pub svg_filters: Vec<(BatchTextures, Vec<SvgFilterInstance>)>,
     pub blits: Vec<BlitJob>,
-    // List of frame buffer outputs for this render target.
-    pub outputs: Vec<FrameOutput>,
     alpha_tasks: Vec<RenderTaskId>,
     screen_size: DeviceIntSize,
     // Track the used rect of the render target, so that
     // we can set a scissor rect and only clear to the
     // used portion of the target as an optimization.
     pub used_rect: DeviceIntRect,
 }
 
@@ -317,17 +315,16 @@ impl RenderTarget for ColorRenderTarget 
         ColorRenderTarget {
             alpha_batch_containers: Vec::new(),
             vertical_blurs: Vec::new(),
             horizontal_blurs: Vec::new(),
             readbacks: Vec::new(),
             scalings: FastHashMap::default(),
             svg_filters: Vec::new(),
             blits: Vec::new(),
-            outputs: Vec::new(),
             alpha_tasks: Vec::new(),
             screen_size,
             used_rect: DeviceIntRect::zero(),
         }
     }
 
     fn build(
         &mut self,
@@ -471,28 +468,18 @@ impl RenderTarget for ColorRenderTarget 
             RenderTaskKind::HorizontalBlur(..) => {
                 add_blur_instances(
                     &mut self.horizontal_blurs,
                     BlurDirection::Horizontal,
                     render_tasks.get_task_address(task_id),
                     render_tasks.get_task_address(task.children[0]),
                 );
             }
-            RenderTaskKind::Picture(ref task_info) => {
-                let pic = &ctx.prim_store.pictures[task_info.pic_index.0];
+            RenderTaskKind::Picture(..) => {
                 self.alpha_tasks.push(task_id);
-
-                // If this pipeline is registered as a frame output
-                // store the information necessary to do the copy.
-                if let Some(pipeline_id) = pic.frame_output_pipeline_id {
-                    self.outputs.push(FrameOutput {
-                        pipeline_id,
-                        task_id,
-                    });
-                }
             }
             RenderTaskKind::SvgFilter(ref task_info) => {
                 add_svg_filter_instances(
                     &mut self.svg_filters,
                     render_tasks,
                     &task_info.info,
                     task_id,
                     task.children.get(0).cloned(),
@@ -1088,19 +1075,8 @@ pub struct LineDecorationJob {
 #[repr(C)]
 pub struct GradientJob {
     pub task_rect: DeviceRect,
     pub stops: [f32; GRADIENT_FP_STOPS],
     pub colors: [PremultipliedColorF; GRADIENT_FP_STOPS],
     pub axis_select: f32,
     pub start_stop: [f32; 2],
 }
-
-/// Frame output information for a given pipeline ID.
-/// Storing the task ID allows the renderer to find
-/// the target rect within the render target that this
-/// pipeline exists at.
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct FrameOutput {
-    pub task_id: RenderTaskId,
-    pub pipeline_id: PipelineId,
-}
--- a/gfx/wr/webrender/src/renderer.rs
+++ b/gfx/wr/webrender/src/renderer.rs
@@ -32,17 +32,17 @@
 //! the framebuffer rectangle provided by the document view, stored in `DrawTarget`
 //!   - all the direct framebuffer operations, like blitting, reading pixels, and setting
 //! up the scissor, are accepting already transformed coordinates, which we can get by
 //! calling `DrawTarget::to_framebuffer_rect`
 
 use api::{ApiMsg, BlobImageHandler, ColorF, ColorU, MixBlendMode};
 use api::{DocumentId, Epoch, ExternalImageHandler, ExternalImageId};
 use api::{ExternalImageSource, ExternalImageType, FontRenderMode, FrameMsg, ImageFormat};
-use api::{PipelineId, ImageRendering, Checkpoint, NotificationRequest, OutputImageHandler};
+use api::{PipelineId, ImageRendering, Checkpoint, NotificationRequest};
 use api::{DebugCommand, MemoryReport, VoidPtrToSizeFn, PremultipliedColorF};
 use api::{RenderApiSender, RenderNotifier, TextureTarget, SharedFontInstanceMap};
 #[cfg(feature = "replay")]
 use api::ExternalImage;
 use api::units::*;
 use api::channel::{unbounded_channel, Sender, Receiver};
 pub use api::DebugFlags;
 use core::time::Duration;
@@ -51,21 +51,23 @@ use crate::batch::{AlphaBatchContainer, 
 use crate::capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
 use crate::composite::{CompositeState, CompositeTileSurface, CompositeTile, ResolvedExternalSurface, CompositorSurfaceTransform};
 use crate::composite::{CompositorKind, Compositor, NativeTileId, CompositeSurfaceFormat, ResolvedExternalSurfaceColorData};
 use crate::composite::{CompositorConfig, NativeSurfaceOperationDetails, NativeSurfaceId, NativeSurfaceOperation};
 use crate::c_str;
 use crate::debug_colors;
 use crate::debug_render::{DebugItem, DebugRenderer};
 use crate::device::{DepthFunction, Device, GpuFrameId, Program, UploadMethod, Texture, PBO};
-use crate::device::{DrawTarget, ExternalTexture, FBOId, ReadTarget, TextureSlot};
+use crate::device::{DrawTarget, ExternalTexture, ReadTarget, TextureSlot};
 use crate::device::{ShaderError, TextureFilter, TextureFlags,
              VertexUsageHint, VAO, VBO, CustomVAO};
 use crate::device::ProgramCache;
 use crate::device::query::GpuTimer;
+#[cfg(feature = "capture")]
+use crate::device::FBOId;
 use euclid::{rect, Transform3D, Scale, default};
 use crate::frame_builder::{Frame, ChasePrimitive, FrameBuilderConfig};
 use gleam::gl;
 use crate::glyph_cache::GlyphCache;
 use crate::glyph_rasterizer::{GlyphFormat, GlyphRasterizer};
 use crate::gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
 use crate::gpu_cache::{GpuCacheDebugChunk, GpuCacheDebugCmd};
 use crate::gpu_types::{PrimitiveHeaderI, PrimitiveHeaderF, PrimitiveInstanceData, ScalingInstance, SvgFilterInstance};
@@ -96,16 +98,17 @@ use crate::render_target::{AlphaRenderTa
 use crate::render_target::{RenderTarget, TextureCacheRenderTarget, RenderTargetList};
 use crate::render_target::{RenderTargetKind, BlitJob, BlitJobSource};
 use crate::render_task_graph::RenderPassKind;
 use crate::util::drain_filter;
 
 use std;
 use std::cmp;
 use std::collections::VecDeque;
+#[cfg(any(feature = "capture", feature = "replay"))]
 use std::collections::hash_map::Entry;
 use std::f32;
 use std::marker::PhantomData;
 use std::mem;
 use std::os::raw::c_void;
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::sync::Arc;
@@ -1843,21 +1846,16 @@ impl<T> VertexDataTexture<T> {
     fn deinit(mut self, device: &mut Device) {
         device.delete_pbo(self.pbo);
         if let Some(t) = self.texture.take() {
             device.delete_texture(t);
         }
     }
 }
 
-struct FrameOutput {
-    last_access: GpuFrameId,
-    fbo_id: FBOId,
-}
-
 #[derive(PartialEq)]
 struct TargetSelector {
     size: DeviceIntSize,
     num_layers: usize,
     format: ImageFormat,
 }
 
 struct LazyInitializedDebugRenderer {
@@ -2085,28 +2083,20 @@ pub struct Renderer {
     texture_cache_upload_pbo: PBO,
 
     dither_matrix_texture: Option<Texture>,
 
     /// Optional trait object that allows the client
     /// application to provide external buffers for image data.
     external_image_handler: Option<Box<dyn ExternalImageHandler>>,
 
-    /// Optional trait object that allows the client
-    /// application to provide a texture handle to
-    /// copy the WR output to.
-    output_image_handler: Option<Box<dyn OutputImageHandler>>,
-
     /// Optional function pointers for measuring memory used by a given
     /// heap-allocated pointer.
     size_of_ops: Option<MallocSizeOfOps>,
 
-    // Currently allocated FBOs for output frames.
-    output_targets: FastHashMap<u32, FrameOutput>,
-
     pub renderer_errors: Vec<RendererError>,
 
     pub(in crate) async_frame_recorder: Option<AsyncScreenshotGrabber>,
     pub(in crate) async_screenshots: Option<AsyncScreenshotGrabber>,
 
     /// List of profile results from previous frames. Can be retrieved
     /// via get_frame_profiles().
     cpu_profiles: VecDeque<CpuProfile>,
@@ -2700,19 +2690,17 @@ impl Renderer {
                 composite_vao,
                 clear_vao,
             },
             vertex_data_textures,
             current_vertex_data_textures: 0,
             pipeline_info: PipelineInfo::default(),
             dither_matrix_texture,
             external_image_handler: None,
-            output_image_handler: None,
             size_of_ops: make_size_of_ops(),
-            output_targets: FastHashMap::default(),
             cpu_profiles: VecDeque::new(),
             gpu_profiles: VecDeque::new(),
             gpu_cache_texture,
             gpu_cache_debug_chunks: Vec::new(),
             gpu_cache_frame_id: FrameId::INVALID,
             gpu_cache_overflow: false,
             texture_cache_upload_pbo,
             texture_resolver,
@@ -3227,21 +3215,16 @@ impl Renderer {
         }
     }
 
     /// Set a callback for handling external images.
     pub fn set_external_image_handler(&mut self, handler: Box<dyn ExternalImageHandler>) {
         self.external_image_handler = Some(handler);
     }
 
-    /// Set a callback for handling external outputs.
-    pub fn set_output_image_handler(&mut self, handler: Box<dyn OutputImageHandler>) {
-        self.output_image_handler = Some(handler);
-    }
-
     /// Retrieve (and clear) the current list of recorded frame profiles.
     pub fn get_frame_profiles(&mut self) -> (Vec<CpuProfile>, Vec<GpuProfile>) {
         let cpu_profiles = self.cpu_profiles.drain(..).collect();
         let gpu_profiles = self.gpu_profiles.drain(..).collect();
         (cpu_profiles, gpu_profiles)
     }
 
     /// Reset the current partial present state. This forces the entire framebuffer
@@ -3514,17 +3497,16 @@ impl Renderer {
                 {
                     profile_scope!("gl.flush");
                     self.device.gl().flush();  // early start on gpu cache updates
                 }
 
                 self.draw_frame(
                     frame,
                     device_size,
-                    cpu_frame_id,
                     &mut results,
                     doc_index == 0,
                 );
 
                 // Profile marker for the number of invalidated picture cache
                 if thread_is_being_profiled() {
                     let duration = Duration::new(0,0);
                     let message = self.profile_counters.rendered_picture_cache_tiles.get_accum().to_string();
@@ -5117,17 +5099,16 @@ impl Renderer {
         &mut self,
         draw_target: DrawTarget,
         target: &ColorRenderTarget,
         content_origin: DeviceIntPoint,
         clear_color: Option<[f32; 4]>,
         clear_depth: Option<f32>,
         render_tasks: &RenderTaskGraph,
         projection: &default::Transform3D<f32>,
-        frame_id: GpuFrameId,
         stats: &mut RendererStats,
     ) {
         profile_scope!("draw_color_target");
 
         self.profile_counters.color_passes.inc();
         let _gm = self.gpu_profile.start_marker("color target");
 
         // sanity check for the depth buffer
@@ -5249,59 +5230,16 @@ impl Renderer {
                 draw_target,
                 content_origin,
                 framebuffer_kind,
                 projection,
                 render_tasks,
                 stats,
             );
         }
-
-        // For any registered image outputs on this render target,
-        // get the texture from caller and blit it.
-        for output in &target.outputs {
-            let handler = self.output_image_handler
-                .as_mut()
-                .expect("Found output image, but no handler set!");
-            if let Some((texture_id, output_size)) = handler.lock(output.pipeline_id) {
-                let fbo_id = match self.output_targets.entry(texture_id) {
-                    Entry::Vacant(entry) => {
-                        let fbo_id = self.device.create_fbo_for_external_texture(texture_id);
-                        entry.insert(FrameOutput {
-                            fbo_id,
-                            last_access: frame_id,
-                        });
-                        fbo_id
-                    }
-                    Entry::Occupied(mut entry) => {
-                        let target = entry.get_mut();
-                        target.last_access = frame_id;
-                        target.fbo_id
-                    }
-                };
-                let (src_rect, _) = render_tasks[output.task_id].get_target_rect();
-                if !self.device.surface_origin_is_top_left() {
-                    self.device.blit_render_target_invert_y(
-                        draw_target.into(),
-                        draw_target.to_framebuffer_rect(src_rect.translate(-content_origin.to_vector())),
-                        DrawTarget::External { fbo: fbo_id, size: output_size },
-                        output_size.into(),
-                    );
-                } else {
-                    self.device.blit_render_target(
-                        draw_target.into(),
-                        draw_target.to_framebuffer_rect(src_rect.translate(-content_origin.to_vector())),
-                        DrawTarget::External { fbo: fbo_id, size: output_size },
-                        output_size.into(),
-                        TextureFilter::Linear,
-                    );
-                }
-                handler.unlock(output.pipeline_id);
-            }
-        }
     }
 
     /// Draw all the instances in a clip batcher list to the current target.
     fn draw_clip_batch_list(
         &mut self,
         list: &ClipBatchList,
         projection: &default::Transform3D<f32>,
         stats: &mut RendererStats,
@@ -5933,17 +5871,16 @@ impl Renderer {
             }
         }
     }
 
     fn draw_frame(
         &mut self,
         frame: &mut Frame,
         device_size: Option<DeviceIntSize>,
-        frame_id: GpuFrameId,
         results: &mut RenderResults,
         clear_framebuffer: bool,
     ) {
         profile_scope!("draw_frame");
 
         // These markers seem to crash a lot on Android, see bug 1559834
         #[cfg(not(target_os = "android"))]
         let _gm = self.gpu_profile.start_marker("draw frame");
@@ -6098,17 +6035,16 @@ impl Renderer {
                             self.draw_color_target(
                                 draw_target,
                                 main_target,
                                 frame.content_origin,
                                 None,
                                 None,
                                 &frame.render_tasks,
                                 &projection,
-                                frame_id,
                                 &mut results.stats,
                             );
                         }
                     }
                 }
                 RenderPassKind::OffScreen {
                     ref mut alpha,
                     ref mut color,
@@ -6262,17 +6198,16 @@ impl Renderer {
                         self.draw_color_target(
                             draw_target,
                             target,
                             frame.content_origin,
                             Some([0.0, 0.0, 0.0, 0.0]),
                             clear_depth,
                             &frame.render_tasks,
                             &projection,
-                            frame_id,
                             &mut results.stats,
                         );
                     }
 
                     // Only end the pass here and invalidate previous textures for
                     // off-screen targets. Deferring return of the inputs to the
                     // frame buffer until the implicit end_pass in end_frame allows
                     // debug draw overlays to be added without triggering a copy
@@ -6294,26 +6229,16 @@ impl Renderer {
             self.draw_frame_debug_items(&frame.debug_items);
             self.draw_render_target_debug(device_size);
             self.draw_texture_cache_debug(device_size);
             self.draw_gpu_cache_debug(device_size);
             self.draw_zoom_debug(device_size);
         }
         self.draw_epoch_debug();
 
-        // Garbage collect any frame outputs that weren't used this frame.
-        let device = &mut self.device;
-        self.output_targets
-            .retain(|_, target| if target.last_access != frame_id {
-                device.delete_fbo(target.fbo_id);
-                false
-            } else {
-                true
-            });
-
         frame.has_been_rendered = true;
     }
 
     /// Initialize the PLS block, by reading the current framebuffer color.
     pub fn init_pixel_local_storage(
         &mut self,
         task_rect: DeviceIntRect,
         projection: &default::Transform3D<f32>,
@@ -6824,19 +6749,16 @@ impl Renderer {
         self.device.delete_vao(self.vaos.border_vao);
         self.device.delete_vao(self.vaos.scale_vao);
         self.device.delete_vao(self.vaos.svg_filter_vao);
         self.device.delete_vao(self.vaos.composite_vao);
         self.device.delete_vao(self.vaos.clear_vao);
 
         self.debug.deinit(&mut self.device);
 
-        for (_, target) in self.output_targets {
-            self.device.delete_fbo(target.fbo_id);
-        }
         if let Ok(shaders) = Rc::try_unwrap(self.shaders) {
             shaders.into_inner().deinit(&mut self.device);
         }
 
         if let Some(async_screenshots) = self.async_screenshots.take() {
             async_screenshots.deinit(&mut self.device);
         }
 
@@ -7289,29 +7211,16 @@ impl ExternalImageHandler for DummyExter
                 CapturedExternalImageData::NativeTexture(tid) => ExternalImageSource::NativeTexture(tid),
                 CapturedExternalImageData::Buffer(ref arc) => ExternalImageSource::RawData(&*arc),
             }
         }
     }
     fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {}
 }
 
-#[cfg(feature = "replay")]
-struct VoidHandler;
-
-#[cfg(feature = "replay")]
-impl OutputImageHandler for VoidHandler {
-    fn lock(&mut self, _: PipelineId) -> Option<(u32, FramebufferIntSize)> {
-        None
-    }
-    fn unlock(&mut self, _: PipelineId) {
-        unreachable!()
-    }
-}
-
 #[derive(Default)]
 pub struct PipelineInfo {
     pub epochs: FastHashMap<(PipelineId, DocumentId), Epoch>,
     pub removed_pipelines: Vec<(PipelineId, DocumentId)>,
 }
 
 impl Renderer {
     #[cfg(feature = "capture")]
@@ -7688,17 +7597,16 @@ impl Renderer {
 
             info!("loading gpu cache");
             if let Some(t) = self.gpu_cache_texture.texture.take() {
                 self.device.delete_texture(t);
             }
             self.device.end_frame();
         }
 
-        self.output_image_handler = Some(Box::new(VoidHandler) as Box<_>);
         self.external_image_handler = Some(Box::new(image_handler) as Box<_>);
         info!("done.");
     }
 }
 
 fn get_vao(vertex_array_kind: VertexArrayKind, vaos: &RendererVAOs) -> &VAO {
     match vertex_array_kind {
         VertexArrayKind::Primitive => &vaos.prim_vao,
--- a/gfx/wr/webrender/src/scene_builder_thread.rs
+++ b/gfx/wr/webrender/src/scene_builder_thread.rs
@@ -213,28 +213,24 @@ enumerate_interners!(declare_interners);
 // as well as a persistent clip interner. This allows clips
 // to be de-duplicated, and persisted in the GPU cache between
 // display lists.
 struct Document {
     scene: Scene,
     interners: Interners,
     stats: SceneStats,
     view: SceneView,
-    /// A set of pipelines that the caller has requested be
-    /// made available as output textures.
-    output_pipelines: FastHashSet<PipelineId>,
 }
 
 impl Document {
     fn new(device_rect: DeviceIntRect, layer: DocumentLayer, device_pixel_ratio: f32) -> Self {
         Document {
             scene: Scene::new(),
             interners: Interners::default(),
             stats: SceneStats::empty(),
-            output_pipelines: FastHashSet::default(),
             view: SceneView {
                 device_rect,
                 layer,
                 device_pixel_ratio,
                 page_zoom_factor: 1.0,
                 quality_settings: QualitySettings::default(),
             },
         }
@@ -452,24 +448,21 @@ impl SceneBuilderThread {
         for mut item in scenes {
             self.config = item.config;
 
             let scene_build_start_time = precise_time_ns();
 
             let mut built_scene = None;
             let mut interner_updates = None;
 
-            let output_pipelines = FastHashSet::default();
-
             if item.scene.has_root_pipeline() {
                 built_scene = Some(SceneBuilder::build(
                     &item.scene,
                     item.font_instances,
                     &item.view,
-                    &output_pipelines,
                     &self.config,
                     &mut item.interners,
                     &SceneStats::empty(),
                 ));
 
                 interner_updates = Some(
                     item.interners.end_frame_and_get_pending_updates()
                 );
@@ -477,17 +470,16 @@ impl SceneBuilderThread {
 
             self.documents.insert(
                 item.document_id,
                 Document {
                     scene: item.scene,
                     interners: item.interners,
                     stats: SceneStats::empty(),
                     view: item.view.clone(),
-                    output_pipelines,
                 },
             );
 
             let txns = vec![Box::new(BuiltTransaction {
                 document_id: item.document_id,
                 render_frame: item.build_frame,
                 invalidate_rendered_frame: false,
                 built_scene,
@@ -683,37 +675,29 @@ impl SceneBuilderThread {
                         scene.set_root_pipeline_id(pipeline_id);
                     }
                 }
                 SceneMsg::RemovePipeline(pipeline_id) => {
                     scene.remove_pipeline(pipeline_id);
                     self.removed_pipelines.insert(pipeline_id);
                     removed_pipelines.push((pipeline_id, txn.document_id));
                 }
-                SceneMsg::EnableFrameOutput(pipeline_id, enable) => {
-                    if enable {
-                        doc.output_pipelines.insert(pipeline_id);
-                    } else {
-                        doc.output_pipelines.remove(&pipeline_id);
-                    }
-                }
             }
         }
 
         self.removed_pipelines.clear();
 
         let mut built_scene = None;
         let mut interner_updates = None;
         if scene.has_root_pipeline() && rebuild_scene {
 
             let built = SceneBuilder::build(
                 &scene,
                 self.font_instances.clone(),
                 &doc.view,
-                &doc.output_pipelines,
                 &self.config,
                 &mut doc.interners,
                 &doc.stats,
             );
 
             // Update the allocation stats for next scene
             doc.stats = built.get_stats();
 
--- a/gfx/wr/webrender/src/scene_building.rs
+++ b/gfx/wr/webrender/src/scene_building.rs
@@ -16,17 +16,17 @@ use api::image_tiling::simplify_repeated
 use api::units::*;
 use crate::clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore, ClipItemKeyKind};
 use crate::clip::{ClipInternData, ClipNodeKind, ClipInstance};
 use crate::spatial_tree::{ROOT_SPATIAL_NODE_INDEX, SpatialTree, SpatialNodeIndex};
 use crate::frame_builder::{ChasePrimitive, FrameBuilderConfig};
 use crate::glyph_rasterizer::FontInstance;
 use crate::hit_test::{HitTestingItem, HitTestingScene};
 use crate::intern::Interner;
-use crate::internal_types::{FastHashMap, FastHashSet, LayoutPrimitiveInfo, Filter};
+use crate::internal_types::{FastHashMap, LayoutPrimitiveInfo, Filter};
 use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PictureOptions};
 use crate::picture::{BlitReason, OrderedPictureChild, PrimitiveList};
 use crate::prim_store::{PrimitiveInstance, register_prim_chase_id};
 use crate::prim_store::{PrimitiveInstanceKind, NinePatchDescriptor, PrimitiveStore};
 use crate::prim_store::{InternablePrimitive, SegmentInstanceIndex, PictureIndex};
 use crate::prim_store::backdrop::Backdrop;
 use crate::prim_store::borders::{ImageBorder, NormalBorderPrim};
 use crate::prim_store::gradient::{GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams, ConicGradient, ConicGradientParams};
@@ -218,20 +218,16 @@ bitflags! {
 /// members are typically those that are destructured into the BuiltScene.
 pub struct SceneBuilder<'a> {
     /// The scene that we are currently building.
     scene: &'a Scene,
 
     /// The map of all font instances.
     font_instances: SharedFontInstanceMap,
 
-    /// A set of pipelines that the caller has requested be made available as
-    /// output textures.
-    output_pipelines: &'a FastHashSet<PipelineId>,
-
     /// The data structure that converts between ClipId/SpatialId and the various
     /// index types that the SpatialTree uses.
     id_to_index_mapper: NodeIdToIndexMapper,
 
     /// A stack of stacking context properties.
     sc_stack: Vec<FlattenedStackingContext>,
 
     /// Stack of spatial node indices forming containing block for 3d contexts
@@ -283,17 +279,16 @@ pub struct SceneBuilder<'a> {
     snap_to_device: SpaceSnapper,
 }
 
 impl<'a> SceneBuilder<'a> {
     pub fn build(
         scene: &Scene,
         font_instances: SharedFontInstanceMap,
         view: &SceneView,
-        output_pipelines: &FastHashSet<PipelineId>,
         frame_builder_config: &FrameBuilderConfig,
         interners: &mut Interners,
         stats: &SceneStats,
     ) -> BuiltScene {
         profile_scope!("build_scene");
 
         // We checked that the root pipeline is available on the render backend.
         let root_pipeline_id = scene.root_pipeline_id.unwrap();
@@ -311,17 +306,16 @@ impl<'a> SceneBuilder<'a> {
             device_pixel_scale,
         );
 
         let mut builder = SceneBuilder {
             scene,
             spatial_tree,
             font_instances,
             config: *frame_builder_config,
-            output_pipelines,
             id_to_index_mapper: NodeIdToIndexMapper::default(),
             hit_testing_scene: HitTestingScene::new(&stats.hit_test_stats),
             pending_shadow_items: VecDeque::new(),
             sc_stack: Vec::new(),
             containing_block_stack: Vec::new(),
             prim_store: PrimitiveStore::new(&stats.prim_store_stats),
             clip_store: ClipStore::new(),
             interners,
@@ -343,17 +337,16 @@ impl<'a> SceneBuilder<'a> {
             &mut builder.prim_store,
         );
 
         let root_pic_index = PictureIndex(builder.prim_store.pictures
             .alloc()
             .init(PicturePrimitive::new_image(
                 None,
                 Picture3DContext::Out,
-                None,
                 true,
                 PrimitiveFlags::IS_BACKFACE_VISIBLE,
                 RasterSpace::Screen,
                 prim_list,
                 ROOT_SPATIAL_NODE_INDEX,
                 PictureOptions::default(),
             ))
         );
@@ -447,17 +440,16 @@ impl<'a> SceneBuilder<'a> {
                         let composition_operations = CompositeOps::new(
                             filter_ops_for_compositing(item.filters()),
                             filter_datas_for_compositing(item.filter_datas()),
                             filter_primitives_for_compositing(item.filter_primitives()),
                             info.stacking_context.mix_blend_mode_for_compositing(),
                         );
 
                         let sc_info = self.push_stacking_context(
-                            bc.pipeline_id,
                             composition_operations,
                             info.stacking_context.transform_style,
                             info.prim_flags,
                             spatial_node_index,
                             info.stacking_context.clip_id,
                             info.stacking_context.raster_space,
                             info.stacking_context.flags,
                         );
@@ -1527,37 +1519,26 @@ impl<'a> SceneBuilder<'a> {
             spatial_node_index,
             info.flags,
         );
     }
 
     /// Push a new stacking context. Returns context that must be passed to pop_stacking_context().
     fn push_stacking_context(
         &mut self,
-        pipeline_id: PipelineId,
         composite_ops: CompositeOps,
         transform_style: TransformStyle,
         prim_flags: PrimitiveFlags,
         spatial_node_index: SpatialNodeIndex,
         clip_id: Option<ClipId>,
         requested_raster_space: RasterSpace,
         flags: StackingContextFlags,
     ) -> StackingContextInfo {
         profile_scope!("push_stacking_context");
 
-        // Check if this stacking context is the root of a pipeline, and the caller
-        // has requested it as an output frame.
-        let is_pipeline_root =
-            self.sc_stack.last().map_or(true, |sc| sc.pipeline_id != pipeline_id);
-        let frame_output_pipeline_id = if is_pipeline_root && self.output_pipelines.contains(&pipeline_id) {
-            Some(pipeline_id)
-        } else {
-            None
-        };
-
         let clip_chain_id = match clip_id {
             Some(clip_id) => self.clip_store.get_or_build_clip_chain_id(clip_id),
             None => ClipChainId::NONE,
         };
 
         // Get the transform-style of the parent stacking context,
         // which determines if we *might* need to draw this on
         // an intermediate surface for plane splitting purposes.
@@ -1694,22 +1675,20 @@ impl<'a> SceneBuilder<'a> {
         // If not redundant, create a stacking context to hold primitive clusters
         if !is_redundant {
             sc_info.pop_stacking_context = true;
 
             // Push the SC onto the stack, so we know how to handle things in
             // pop_stacking_context.
             self.sc_stack.push(FlattenedStackingContext {
                 prim_list: PrimitiveList::empty(),
-                pipeline_id,
                 prim_flags,
                 requested_raster_space,
                 spatial_node_index,
                 clip_chain_id,
-                frame_output_pipeline_id,
                 composite_ops,
                 blit_reason,
                 transform_style,
                 context_3d,
                 is_redundant,
                 is_backdrop_root: flags.contains(StackingContextFlags::IS_BACKDROP_ROOT),
             });
         }
@@ -1743,50 +1722,47 @@ impl<'a> SceneBuilder<'a> {
         let parent_is_empty = match self.sc_stack.last() {
             Some(parent_sc) => {
                 assert!(!stacking_context.is_redundant);
                 parent_sc.prim_list.is_empty()
             },
             None => true,
         };
 
-        let (leaf_context_3d, leaf_composite_mode, leaf_output_pipeline_id) = match stacking_context.context_3d {
+        let (leaf_context_3d, leaf_composite_mode) = match stacking_context.context_3d {
             // TODO(gw): For now, as soon as this picture is in
             //           a 3D context, we draw it to an intermediate
             //           surface and apply plane splitting. However,
             //           there is a large optimization opportunity here.
             //           During culling, we can check if there is actually
             //           perspective present, and skip the plane splitting
             //           completely when that is not the case.
             Picture3DContext::In { ancestor_index, .. } => (
                 Picture3DContext::In { root_data: None, ancestor_index },
                 Some(PictureCompositeMode::Blit(BlitReason::PRESERVE3D | stacking_context.blit_reason)),
-                None,
             ),
             Picture3DContext::Out => (
                 Picture3DContext::Out,
                 if stacking_context.blit_reason.is_empty() {
                     // By default, this picture will be collapsed into
                     // the owning target.
                     None
                 } else {
                     // Add a dummy composite filter if the SC has to be isolated.
                     Some(PictureCompositeMode::Blit(stacking_context.blit_reason))
                 },
-                stacking_context.frame_output_pipeline_id
             ),
         };
 
         // Add picture for this actual stacking context contents to render into.
         let leaf_pic_index = PictureIndex(self.prim_store.pictures
             .alloc()
             .init(PicturePrimitive::new_image(
                 leaf_composite_mode.clone(),
                 leaf_context_3d,
-                leaf_output_pipeline_id,
                 true,
                 stacking_context.prim_flags,
                 stacking_context.requested_raster_space,
                 stacking_context.prim_list,
                 stacking_context.spatial_node_index,
                 PictureOptions::default(),
             ))
         );
@@ -1830,17 +1806,16 @@ impl<'a> SceneBuilder<'a> {
             current_pic_index = PictureIndex(self.prim_store.pictures
                 .alloc()
                 .init(PicturePrimitive::new_image(
                     None,
                     Picture3DContext::In {
                         root_data: Some(Vec::new()),
                         ancestor_index,
                     },
-                    stacking_context.frame_output_pipeline_id,
                     true,
                     stacking_context.prim_flags,
                     stacking_context.requested_raster_space,
                     prim_list,
                     stacking_context.spatial_node_index,
                     PictureOptions::default(),
                 ))
             );
@@ -1897,17 +1872,16 @@ impl<'a> SceneBuilder<'a> {
                     stacking_context.prim_flags,
                 );
 
                 let blend_pic_index = PictureIndex(self.prim_store.pictures
                     .alloc()
                     .init(PicturePrimitive::new_image(
                         composite_mode.clone(),
                         Picture3DContext::Out,
-                        None,
                         true,
                         stacking_context.prim_flags,
                         stacking_context.requested_raster_space,
                         prim_list,
                         stacking_context.spatial_node_index,
                         PictureOptions::default(),
                     ))
                 );
@@ -2367,17 +2341,16 @@ impl<'a> SceneBuilder<'a> {
                         };
 
                         // Create the primitive to draw the shadow picture into the scene.
                         let shadow_pic_index = PictureIndex(self.prim_store.pictures
                             .alloc()
                             .init(PicturePrimitive::new_image(
                                 composite_mode,
                                 Picture3DContext::Out,
-                                None,
                                 is_passthrough,
                                 PrimitiveFlags::IS_BACKFACE_VISIBLE,
                                 raster_space,
                                 prim_list,
                                 pending_shadow.spatial_node_index,
                                 options,
                             ))
                         );
@@ -3111,17 +3084,16 @@ impl<'a> SceneBuilder<'a> {
                 prim_flags,
             );
 
             backdrop_pic_index = PictureIndex(self.prim_store.pictures
                 .alloc()
                 .init(PicturePrimitive::new_image(
                     composite_mode.clone(),
                     Picture3DContext::Out,
-                    None,
                     true,
                     prim_flags,
                     requested_raster_space,
                     prim_list,
                     backdrop_spatial_node_index,
                     PictureOptions {
                        inflate_if_required: false,
                     },
@@ -3301,17 +3273,16 @@ impl<'a> SceneBuilder<'a> {
                 flags,
             );
 
             let filter_pic_index = PictureIndex(self.prim_store.pictures
                 .alloc()
                 .init(PicturePrimitive::new_image(
                     composite_mode.clone(),
                     Picture3DContext::Out,
-                    None,
                     true,
                     flags,
                     requested_raster_space,
                     prim_list,
                     spatial_node_index,
                     PictureOptions {
                        inflate_if_required,
                     },
@@ -3366,17 +3337,16 @@ impl<'a> SceneBuilder<'a> {
                 flags,
             );
 
             let filter_pic_index = PictureIndex(self.prim_store.pictures
                 .alloc()
                 .init(PicturePrimitive::new_image(
                     Some(composite_mode.clone()),
                     Picture3DContext::Out,
-                    None,
                     true,
                     flags,
                     requested_raster_space,
                     prim_list,
                     spatial_node_index,
                     PictureOptions {
                         inflate_if_required,
                     },
@@ -3443,31 +3413,24 @@ struct FlattenedStackingContext {
     requested_raster_space: RasterSpace,
 
     /// The positioning node for this stacking context
     spatial_node_index: SpatialNodeIndex,
 
     /// The clip chain for this stacking context
     clip_chain_id: ClipChainId,
 
-    /// If set, this should be provided to caller
-    /// as an output texture.
-    frame_output_pipeline_id: Option<PipelineId>,
-
     /// The list of filters / mix-blend-mode for this
     /// stacking context.
     composite_ops: CompositeOps,
 
     /// Bitfield of reasons this stacking context needs to
     /// be an offscreen surface.
     blit_reason: BlitReason,
 
-    /// Pipeline this stacking context belongs to.
-    pipeline_id: PipelineId,
-
     /// CSS transform-style property.
     transform_style: TransformStyle,
 
     /// Defines the relationship to a preserve-3D hiearachy.
     context_3d: Picture3DContext<ExtendedPrimitiveInstance>,
 
     /// True if this stacking context is a backdrop root.
     is_backdrop_root: bool,
@@ -3567,17 +3530,16 @@ impl FlattenedStackingContext {
             return None
         }
 
         let pic_index = PictureIndex(prim_store.pictures
             .alloc()
             .init(PicturePrimitive::new_image(
                 composite_mode.clone(),
                 flat_items_context_3d,
-                None,
                 true,
                 self.prim_flags,
                 self.requested_raster_space,
                 mem::replace(&mut self.prim_list, PrimitiveList::empty()),
                 self.spatial_node_index,
                 PictureOptions::default(),
             ))
         );
--- a/gfx/wr/webrender/src/tile_cache.rs
+++ b/gfx/wr/webrender/src/tile_cache.rs
@@ -416,17 +416,16 @@ fn create_tile_cache(
         shared_clips,
         shared_clip_chain: parent_clip_chain_id,
         virtual_surface_size: frame_builder_config.compositor_kind.get_virtual_surface_size(),
     });
 
     let pic_index = prim_store.pictures.alloc().init(PicturePrimitive::new_image(
         Some(PictureCompositeMode::TileCache { slice_id }),
         Picture3DContext::Out,
-        None,
         true,
         PrimitiveFlags::IS_BACKFACE_VISIBLE,
         RasterSpace::Screen,
         prim_list,
         scroll_root,
         PictureOptions::default(),
     ));
 
--- a/gfx/wr/webrender_api/src/api.rs
+++ b/gfx/wr/webrender_api/src/api.rs
@@ -302,22 +302,16 @@ impl Transaction {
         self.scene_ops.push(
             SceneMsg::SetDocumentView {
                 device_rect,
                 device_pixel_ratio,
             },
         );
     }
 
-    /// Enable copying of the output of this pipeline id to
-    /// an external texture for callers to consume.
-    pub fn enable_frame_output(&mut self, pipeline_id: PipelineId, enable: bool) {
-        self.scene_ops.push(SceneMsg::EnableFrameOutput(pipeline_id, enable));
-    }
-
     /// Scrolls the scrolling layer under the `cursor`
     ///
     /// WebRender looks for the layer closest to the user
     /// which has `ScrollPolicy::Scrollable` set.
     pub fn scroll(&mut self, scroll_location: ScrollLocation, cursor: WorldPoint) {
         self.frame_ops.push(FrameMsg::Scroll(scroll_location, cursor));
     }
 
@@ -794,18 +788,16 @@ pub enum SceneMsg {
     UpdateEpoch(PipelineId, Epoch),
     ///
     SetPageZoom(ZoomFactor),
     ///
     SetRootPipeline(PipelineId),
     ///
     RemovePipeline(PipelineId),
     ///
-    EnableFrameOutput(PipelineId, bool),
-    ///
     SetDisplayList {
         ///
         display_list: BuiltDisplayList,
         ///
         epoch: Epoch,
         ///
         pipeline_id: PipelineId,
         ///
@@ -857,17 +849,16 @@ pub enum FrameMsg {
 
 impl fmt::Debug for SceneMsg {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.write_str(match *self {
             SceneMsg::UpdateEpoch(..) => "SceneMsg::UpdateEpoch",
             SceneMsg::SetDisplayList { .. } => "SceneMsg::SetDisplayList",
             SceneMsg::SetPageZoom(..) => "SceneMsg::SetPageZoom",
             SceneMsg::RemovePipeline(..) => "SceneMsg::RemovePipeline",
-            SceneMsg::EnableFrameOutput(..) => "SceneMsg::EnableFrameOutput",
             SceneMsg::SetDocumentView { .. } => "SceneMsg::SetDocumentView",
             SceneMsg::SetRootPipeline(..) => "SceneMsg::SetRootPipeline",
             SceneMsg::SetQualitySettings { .. } => "SceneMsg::SetQualitySettings",
         })
     }
 }
 
 impl fmt::Debug for FrameMsg {
@@ -1722,31 +1713,16 @@ impl RenderApi {
         assert!(device_pixel_ratio > 0.0);
         window_size_sanity_check(device_rect.size);
         self.send_scene_msg(
             document_id,
             SceneMsg::SetDocumentView { device_rect, device_pixel_ratio },
         );
     }
 
-    /// Setup the output region in the framebuffer for a given document.
-    /// Enable copying of the output of this pipeline id to
-    /// an external texture for callers to consume.
-    pub fn enable_frame_output(
-        &self,
-        document_id: DocumentId,
-        pipeline_id: PipelineId,
-        enable: bool,
-    ) {
-        self.send_scene_msg(
-            document_id,
-            SceneMsg::EnableFrameOutput(pipeline_id, enable),
-        );
-    }
-
     ///
     pub fn get_scroll_node_state(&self, document_id: DocumentId) -> Vec<ScrollNodeState> {
         let (tx, rx) = single_msg_channel();
         self.send_frame_msg(document_id, FrameMsg::GetScrollNodeState(tx));
         rx.recv().unwrap()
     }
 
     // Some internal scheduling magic that leaked into the API.
--- a/gfx/wr/webrender_api/src/image.rs
+++ b/gfx/wr/webrender_api/src/image.rs
@@ -4,17 +4,17 @@
 
 #![deny(missing_docs)]
 
 use euclid::{size2, Rect, num::Zero};
 use peek_poke::PeekPoke;
 use std::ops::{Add, Sub};
 use std::sync::Arc;
 // local imports
-use crate::api::{IdNamespace, PipelineId, TileSize};
+use crate::api::{IdNamespace, TileSize};
 use crate::display_item::ImageRendering;
 use crate::font::{FontInstanceKey, FontInstanceData, FontKey, FontTemplate};
 use crate::units::*;
 
 /// The default tile size for blob images and regular images larger than
 /// the maximum texture size.
 pub const DEFAULT_TILE_SIZE: TileSize = 512;
 
@@ -95,26 +95,16 @@ pub trait ExternalImageHandler {
     /// The WR client should not change the image content until the unlock()
     /// call. Provide ImageRendering for NativeTexture external images.
     fn lock(&mut self, key: ExternalImageId, channel_index: u8, rendering: ImageRendering) -> ExternalImage;
     /// Unlock the external image. WR should not read the image content
     /// after this call.
     fn unlock(&mut self, key: ExternalImageId, channel_index: u8);
 }
 
-/// Allows callers to receive a texture with the contents of a specific
-/// pipeline copied to it.
-pub trait OutputImageHandler {
-    /// Return the native texture handle and the size of the texture.
-    fn lock(&mut self, pipeline_id: PipelineId) -> Option<(u32, FramebufferIntSize)>;
-    /// Unlock will only be called if the lock() call succeeds, when WR has issued
-    /// the GL commands to copy the output to the texture handle.
-    fn unlock(&mut self, pipeline_id: PipelineId);
-}
-
 /// Specifies the type of texture target in driver terms.
 #[repr(u8)]
 #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
 pub enum TextureTarget {
     /// Standard texture. This maps to GL_TEXTURE_2D in OpenGL.
     Default = 0,
     /// Array texture. This maps to GL_TEXTURE_2D_ARRAY in OpenGL. See
     /// https://www.khronos.org/opengl/wiki/Array_Texture for background