Bug 1591346 - Remove EGL_ANGLE_experimental_present_path usage with WebRender r=gw
authorsotaro <sotaro.ikeda.g@gmail.com>
Mon, 28 Oct 2019 00:56:41 +0000
changeset 499396 9277be3613ca5109ae4007a512ee75856f8f1120
parent 499395 32af5afdcfe30752a0d896e1c9aae10b3f7d3d6e
child 499397 5e129785c9664543a48ad07cf6c96aed304671b9
push id114161
push userncsoregi@mozilla.com
push dateTue, 29 Oct 2019 21:34:24 +0000
treeherdermozilla-inbound@25bf8e097e60 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgw
bugs1591346
milestone72.0a1
first release with
nightly linux32
9277be3613ca / 72.0a1 / 20191028094851 / files
nightly linux64
9277be3613ca / 72.0a1 / 20191028094851 / files
nightly mac
9277be3613ca / 72.0a1 / 20191028094851 / files
nightly win32
9277be3613ca / 72.0a1 / 20191028094851 / files
nightly win64
9277be3613ca / 72.0a1 / 20191028094851 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1591346 - Remove EGL_ANGLE_experimental_present_path usage with WebRender r=gw EGL_ANGLE_experimental_present_path was enabled for fast rendering to SwapChain by ANGLE. But current gecko does not request ANGLE to render to SwapChain for WebRender. Then we do not need to use EGL_ANGLE_experimental_present_path anymore. But Its usage still has a side effect that y is flipped. But OS compositor implementation on Windows does not want it. And it seems not good to continue to use EGL_ANGLE_experimental_present_path since it is experimental feature. But when EGL_ANGLE_experimental_present_path is removed, rendering result of frame buffer is y flipped with ANGLE compared to other OpenGL implementation. It needs to be handled in WR. It is similar to chromium. Differential Revision: https://phabricator.services.mozilla.com/D50604
gfx/gl/GLLibraryEGL.cpp
gfx/layers/wr/WebRenderLayerManager.cpp
gfx/webrender_bindings/RenderCompositor.h
gfx/webrender_bindings/RenderCompositorANGLE.h
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/src/bindings.rs
gfx/wr/webrender/src/debug_render.rs
gfx/wr/webrender/src/device/gl.rs
gfx/wr/webrender/src/renderer.rs
gfx/wr/webrender/src/screen_capture.rs
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -155,19 +155,17 @@ static EGLDisplay GetAndInitWARPDisplay(
   if (!egl.fInitialize(display, nullptr, nullptr)) return EGL_NO_DISPLAY;
 
   return display;
 }
 
 static EGLDisplay GetAndInitDisplayForWebRender(GLLibraryEGL& egl,
                                                 void* displayType) {
 #ifdef XP_WIN
-  const EGLint attrib_list[] = {LOCAL_EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE,
-                                LOCAL_EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE,
-                                LOCAL_EGL_NONE};
+  const EGLint attrib_list[] = {LOCAL_EGL_NONE};
   RefPtr<ID3D11Device> d3d11Device =
       gfx::DeviceManagerDx::Get()->GetCompositorDevice();
   if (!d3d11Device) {
     gfxCriticalNote << "Failed to get compositor device for EGLDisplay";
     return EGL_NO_DISPLAY;
   }
   EGLDeviceEXT eglDevice = egl.fCreateDeviceANGLE(
       LOCAL_EGL_D3D11_DEVICE_ANGLE, reinterpret_cast<void*>(d3d11Device.get()),
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -537,19 +537,27 @@ void WebRenderLayerManager::MakeSnapshot
     gfxUtils::WriteAsPNG(snapshot, filename);
     */
 
   Rect dst(bounds.X(), bounds.Y(), bounds.Width(), bounds.Height());
   Rect src(0, 0, bounds.Width(), bounds.Height());
 
   // The data we get from webrender is upside down. So flip and translate up so
   // the image is rightside up. Webrender always does a full screen readback.
-  SurfacePattern pattern(
-      snapshot, ExtendMode::CLAMP,
-      Matrix::Scaling(1.0, -1.0).PostTranslate(0.0, aSize.height));
+#ifdef XP_WIN
+  // ANGLE with WR does not need y flip
+  const bool needsYFlip = !WrBridge()->GetCompositorUseANGLE();
+#else
+  const bool needsYFlip = true;
+#endif
+  Matrix m;
+  if (needsYFlip) {
+    m = Matrix::Scaling(1.0, -1.0).PostTranslate(0.0, aSize.height);
+  }
+  SurfacePattern pattern(snapshot, ExtendMode::CLAMP, m);
   DrawTarget* dt = mTarget->GetDrawTarget();
   MOZ_RELEASE_ASSERT(dt);
   dt->FillRect(dst, pattern);
 
   mTarget = nullptr;
 }
 
 void WebRenderLayerManager::DiscardImages() {
--- a/gfx/webrender_bindings/RenderCompositor.h
+++ b/gfx/webrender_bindings/RenderCompositor.h
@@ -73,16 +73,19 @@ class RenderCompositor {
   virtual void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntSize aSize) {
   }
   virtual void DestroySurface(NativeSurfaceId aId) {}
   virtual void AddSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aPosition,
                           wr::DeviceIntRect aClipRect) {}
 
   void wr_compositor_unbind(void* aCompositor) {}
 
+  // Whether the surface contents are flipped vertically
+  virtual bool SurfaceIsYFlipped() { return false; }
+
  protected:
   RefPtr<widget::CompositorWidget> mWidget;
   RefPtr<layers::SyncObjectHost> mSyncObject;
 };
 
 }  // namespace wr
 }  // namespace mozilla
 
--- a/gfx/webrender_bindings/RenderCompositorANGLE.h
+++ b/gfx/webrender_bindings/RenderCompositorANGLE.h
@@ -54,16 +54,18 @@ class RenderCompositorANGLE : public Ren
   bool UseDComp() const override { return !!mDCLayerTree; }
 
   bool UseTripleBuffering() const override { return mUseTripleBuffering; }
 
   LayoutDeviceIntSize GetBufferSize() override;
 
   bool IsContextLost() override;
 
+  bool SurfaceIsYFlipped() override { return true; }
+
  protected:
   void InsertPresentWaitQuery();
   bool WaitForPreviousPresentQuery();
   bool ResizeBufferIfNeeded();
   bool CreateEGLSurface();
   void DestroyEGLSurface();
   ID3D11Device* GetDeviceOfEGLDisplay();
   void CreateSwapChainForDCompIfPossible(IDXGIFactory2* aDXGIFactory2);
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -80,17 +80,17 @@ class NewRenderer : public RendererEvent
             supportLowPriorityTransactions, allow_texture_swizzling,
             StaticPrefs::gfx_webrender_picture_caching() &&
                 supportPictureCaching,
 #ifdef NIGHTLY_BUILD
             StaticPrefs::gfx_webrender_start_debug_server(),
 #else
             false,
 #endif
-            compositor->gl(),
+            compositor->gl(), compositor->SurfaceIsYFlipped(),
             aRenderThread.GetProgramCache()
                 ? aRenderThread.GetProgramCache()->Raw()
                 : nullptr,
             aRenderThread.GetShaders()
                 ? aRenderThread.GetShaders()->RawShaders()
                 : nullptr,
             aRenderThread.ThreadPool().Raw(), &WebRenderMallocSizeOf,
             &WebRenderMallocEnclosingSizeOf, (uint32_t)wr::RenderRoot::Default,
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1127,17 +1127,17 @@ fn wr_device_new(gl_context: *mut c_void
         }
     };
 
     let cached_programs = match pc {
       Some(cached_programs) => Some(Rc::clone(cached_programs.rc_get())),
       None => None,
     };
 
-    Device::new(gl, resource_override_path, upload_method, cached_programs, false, true, true, None)
+    Device::new(gl, resource_override_path, upload_method, cached_programs, false, true, true, None, false)
 }
 
 extern "C" {
     fn wr_compositor_create_surface(
         compositor: *mut c_void,
         id: NativeSurfaceId,
         size: DeviceIntSize,
     );
@@ -1254,16 +1254,17 @@ impl Compositor for WrCompositor {
 pub extern "C" fn wr_window_new(window_id: WrWindowId,
                                 window_width: i32,
                                 window_height: i32,
                                 support_low_priority_transactions: bool,
                                 allow_texture_swizzling: bool,
                                 enable_picture_caching: bool,
                                 start_debug_server: bool,
                                 gl_context: *mut c_void,
+                                surface_is_y_flipped: bool,
                                 program_cache: Option<&mut WrProgramCache>,
                                 shaders: Option<&mut WrShaders>,
                                 thread_pool: *mut WrThreadPool,
                                 size_of_op: VoidPtrToSizeFn,
                                 enclosing_size_of_op: VoidPtrToSizeFn,
                                 document_id: u32,
                                 compositor: *mut c_void,
                                 out_handle: &mut *mut DocumentHandle,
@@ -1354,16 +1355,17 @@ pub extern "C" fn wr_window_new(window_i
         max_texture_size: Some(8192), // Moz2D doesn't like textures bigger than this
         clear_color: Some(color),
         precache_flags,
         namespace_alloc_by_client: true,
         enable_picture_caching,
         allow_pixel_local_storage_support: false,
         start_debug_server,
         native_compositor,
+        surface_is_y_flipped,
         ..Default::default()
     };
 
     // Ensure the WR profiler callbacks are hooked up to the Gecko profiler.
     set_profiler_hooks(Some(&PROFILER_HOOKS));
 
     let window_size = DeviceIntSize::new(window_width, window_height);
     let notifier = Box::new(CppNotifier {
--- a/gfx/wr/webrender/src/debug_render.rs
+++ b/gfx/wr/webrender/src/debug_render.rs
@@ -316,21 +316,28 @@ impl DebugRenderer {
         viewport_size: Option<DeviceIntSize>,
         scale: f32,
     ) {
         if let Some(viewport_size) = viewport_size {
             device.disable_depth();
             device.set_blend(true);
             device.set_blend_mode_premultiplied_alpha();
 
+            let surface_is_y_flipped = device.surface_is_y_flipped();
+            let (bottom, top) = if surface_is_y_flipped {
+                (0.0, viewport_size.height as f32 * scale)
+            } else {
+                (viewport_size.height as f32 * scale, 0.0)
+            };
+
             let projection = Transform3D::ortho(
                 0.0,
                 viewport_size.width as f32 * scale,
-                viewport_size.height as f32 * scale,
-                0.0,
+                bottom,
+                top,
                 ORTHO_NEAR_PLANE,
                 ORTHO_FAR_PLANE,
             );
 
             // Triangles
             if !self.tri_vertices.is_empty() {
                 device.bind_program(&self.color_program);
                 device.set_uniforms(&self.color_program, &projection);
--- a/gfx/wr/webrender/src/device/gl.rs
+++ b/gfx/wr/webrender/src/device/gl.rs
@@ -1036,28 +1036,31 @@ pub struct Device {
 
     optimal_pbo_stride: NonZeroUsize,
 
     // GL extensions
     extensions: Vec<String>,
 
     /// Dumps the source of the shader with the given name
     dump_shader_source: Option<String>,
+
+    surface_is_y_flipped: bool,
 }
 
 /// Contains the parameters necessary to bind a draw target.
 #[derive(Clone, Copy, Debug)]
 pub enum DrawTarget {
     /// Use the device's default draw target, with the provided dimensions,
     /// which are used to set the viewport.
     Default {
         /// Target rectangle to draw.
         rect: FramebufferIntRect,
         /// Total size of the target.
         total_size: FramebufferIntSize,
+        surface_is_y_flipped: bool,
     },
     /// Use the provided texture.
     Texture {
         /// Size of the texture in pixels
         dimensions: DeviceIntSize,
         /// The slice within the texture array to draw to
         layer: LayerIndex,
         /// Whether to draw with the texture's associated depth target
@@ -1079,21 +1082,22 @@ pub enum DrawTarget {
     /// An OS compositor surface
     NativeSurface {
         offset: DeviceIntPoint,
         dimensions: DeviceIntSize,
     },
 }
 
 impl DrawTarget {
-    pub fn new_default(size: DeviceIntSize) -> Self {
+    pub fn new_default(size: DeviceIntSize, surface_is_y_flipped: bool) -> Self {
         let total_size = FramebufferIntSize::from_untyped(size.to_untyped());
         DrawTarget::Default {
             rect: total_size.into(),
             total_size,
+            surface_is_y_flipped,
         }
     }
 
     /// Returns true if this draw target corresponds to the default framebuffer.
     pub fn is_default(&self) -> bool {
         match *self {
             DrawTarget::Default {..} => true,
             _ => false,
@@ -1130,20 +1134,22 @@ impl DrawTarget {
             DrawTarget::External { size, .. } => DeviceIntSize::from_untyped(size.to_untyped()),
             DrawTarget::NativeSurface { dimensions, .. } => dimensions,
         }
     }
 
     pub fn to_framebuffer_rect(&self, device_rect: DeviceIntRect) -> FramebufferIntRect {
         let mut fb_rect = FramebufferIntRect::from_untyped(&device_rect.to_untyped());
         match *self {
-            DrawTarget::Default { ref rect, .. } => {
+            DrawTarget::Default { ref rect, surface_is_y_flipped, .. } => {
                 // perform a Y-flip here
-                fb_rect.origin.y = rect.origin.y + rect.size.height - fb_rect.origin.y - fb_rect.size.height;
-                fb_rect.origin.x += rect.origin.x;
+                if !surface_is_y_flipped {
+                    fb_rect.origin.y = rect.origin.y + rect.size.height - fb_rect.origin.y - fb_rect.size.height;
+                    fb_rect.origin.x += rect.origin.x;
+                }
             }
             DrawTarget::Texture { .. } | DrawTarget::External { .. } => (),
             DrawTarget::NativeSurface { .. } => {
                 panic!("bug: is this ever used for native surfaces?");
             }
         }
         fb_rect
     }
@@ -1229,16 +1235,17 @@ impl Device {
         mut gl: Rc<dyn gl::Gl>,
         resource_override_path: Option<PathBuf>,
         upload_method: UploadMethod,
         cached_programs: Option<Rc<ProgramCache>>,
         allow_pixel_local_storage_support: bool,
         allow_texture_storage_support: bool,
         allow_texture_swizzling: bool,
         dump_shader_source: Option<String>,
+        surface_is_y_flipped: bool,
     ) -> Device {
         let mut max_texture_size = [0];
         let mut max_texture_layers = [0];
         unsafe {
             gl.get_integer_v(gl::MAX_TEXTURE_SIZE, &mut max_texture_size);
             gl.get_integer_v(gl::MAX_ARRAY_TEXTURE_LAYERS, &mut max_texture_layers);
         }
 
@@ -1454,16 +1461,17 @@ impl Device {
             max_texture_layers,
             renderer_name,
             cached_programs,
             frame_id: GpuFrameId(0),
             extensions,
             texture_storage_usage,
             optimal_pbo_stride,
             dump_shader_source,
+            surface_is_y_flipped,
         }
     }
 
     pub fn gl(&self) -> &dyn gl::Gl {
         &*self.gl
     }
 
     pub fn rc_gl(&self) -> &Rc<dyn gl::Gl> {
@@ -1481,16 +1489,20 @@ impl Device {
         self.max_texture_size = self.max_texture_size.min(size);
     }
 
     /// Returns the limit on texture dimensions (width or height).
     pub fn max_texture_size(&self) -> i32 {
         self.max_texture_size
     }
 
+    pub fn surface_is_y_flipped(&self) -> bool {
+        self.surface_is_y_flipped
+    }
+
     /// Returns the limit on texture array layers.
     pub fn max_texture_layers(&self) -> usize {
         self.max_texture_layers as usize
     }
 
     pub fn get_capabilities(&self) -> &Capabilities {
         &self.capabilities
     }
--- a/gfx/wr/webrender/src/renderer.rs
+++ b/gfx/wr/webrender/src/renderer.rs
@@ -1945,16 +1945,17 @@ impl Renderer {
             gl,
             options.resource_override_path.clone(),
             options.upload_method.clone(),
             options.cached_programs.take(),
             options.allow_pixel_local_storage_support,
             options.allow_texture_storage_support,
             options.allow_texture_swizzling,
             options.dump_shader_source.take(),
+            options.surface_is_y_flipped,
         );
 
         let color_cache_formats = device.preferred_color_formats();
         let swizzle_settings = device.swizzle_settings();
         let supports_dual_source_blending = match gl_type {
             gl::GlType::Gl => device.supports_extension("GL_ARB_blend_func_extended") &&
                 device.supports_extension("GL_ARB_explicit_attrib_location"),
             gl::GlType::Gles => device.supports_extension("GL_EXT_blend_func_extended"),
@@ -4163,17 +4164,17 @@ impl Renderer {
             if clear_depth.is_some() {
                 self.device.enable_depth_write();
             }
 
             let clear_rect = match draw_target {
                 DrawTarget::NativeSurface { .. } => {
                     unreachable!("bug: native compositor surface in child target");
                 }
-                DrawTarget::Default { rect, total_size } if rect.origin == FramebufferIntPoint::zero() && rect.size == total_size => {
+                DrawTarget::Default { rect, total_size, .. } if rect.origin == FramebufferIntPoint::zero() && rect.size == total_size => {
                     // whole screen is covered, no need for scissor
                     None
                 }
                 DrawTarget::Default { rect, .. } => {
                     Some(rect)
                 }
                 DrawTarget::Texture { .. } if self.enable_clear_scissor => {
                     // TODO(gw): Applying a scissor rect and minimal clear here
@@ -4286,22 +4287,32 @@ impl Renderer {
                     }
                     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();
-                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(),
-                );
+                if !self.device.surface_is_y_flipped() {
+                    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,
@@ -4946,32 +4957,43 @@ impl Renderer {
 
             match pass.kind {
                 RenderPassKind::MainFramebuffer { ref main_target, .. } => {
                     if let Some(device_size) = device_size {
                         results.stats.color_target_count += 1;
 
                         let offset = frame.content_origin.to_f32();
                         let size = frame.device_rect.size.to_f32();
+                        let surface_is_y_flipped = self.device.surface_is_y_flipped();
+                        let (bottom, top) = if surface_is_y_flipped {
+                          (offset.y, offset.y + size.height)
+                        } else {
+                          (offset.y + size.height, offset.y)
+                        };
+
                         let projection = Transform3D::ortho(
                             offset.x,
                             offset.x + size.width,
-                            offset.y + size.height,
-                            offset.y,
+                            bottom,
+                            top,
                             ORTHO_NEAR_PLANE,
                             ORTHO_FAR_PLANE,
                         );
 
                         let fb_scale = Scale::<_, _, FramebufferPixel>::new(1i32);
                         let mut fb_rect = frame.device_rect * fb_scale;
-                        fb_rect.origin.y = device_size.height - fb_rect.origin.y - fb_rect.size.height;
+
+                        if !surface_is_y_flipped {
+                            fb_rect.origin.y = device_size.height - fb_rect.origin.y - fb_rect.size.height;
+                        }
 
                         let draw_target = DrawTarget::Default {
                             rect: fb_rect,
                             total_size: device_size * fb_scale,
+                            surface_is_y_flipped,
                         };
 
                         if self.enable_picture_caching {
                             // If we have a native OS compositor, then make use of that interface
                             // to specify how to composite each of the picture cache surfaces.
                             match self.native_compositor {
                                 Some(ref mut interface) => {
                                     frame.composite_state.composite_native(&mut **interface);
@@ -5400,17 +5422,17 @@ impl Renderer {
                 Some(RenderTargetInfo { has_depth: false }),
                 1,
             );
 
             self.zoom_debug_texture = Some(texture);
         }
 
         // Copy frame buffer into the zoom texture
-        let read_target = DrawTarget::new_default(device_size);
+        let read_target = DrawTarget::new_default(device_size, self.device.surface_is_y_flipped());
         self.device.blit_render_target(
             read_target.into(),
             read_target.to_framebuffer_rect(source_rect),
             DrawTarget::from_texture(
                 self.zoom_debug_texture.as_ref().unwrap(),
                 0,
                 false,
             ),
@@ -5536,22 +5558,32 @@ impl Renderer {
                     ColorU::new(0, 0, 0, 255),
                     Some(text_rect.to_f32())
                 );
 
                 // Blit the contents of the layer. We need to invert Y because
                 // we're blitting from a texture to the main framebuffer, which
                 // use different conventions.
                 let dest_rect = rect(x, y + tag_height, size, size);
-                device.blit_render_target_invert_y(
-                    ReadTarget::from_texture(texture, layer),
-                    src_rect,
-                    DrawTarget::new_default(device_size),
-                    FramebufferIntRect::from_untyped(&dest_rect),
-                );
+                if !device.surface_is_y_flipped() {
+                    device.blit_render_target_invert_y(
+                        ReadTarget::from_texture(texture, layer),
+                        src_rect,
+                        DrawTarget::new_default(device_size, device.surface_is_y_flipped()),
+                        FramebufferIntRect::from_untyped(&dest_rect),
+                    );
+                } else {
+                    device.blit_render_target(
+                        ReadTarget::from_texture(texture, layer),
+                        src_rect,
+                        DrawTarget::new_default(device_size, device.surface_is_y_flipped()),
+                        FramebufferIntRect::from_untyped(&dest_rect),
+                        TextureFilter::Linear,
+                    );
+                }
                 i += 1;
             }
         }
     }
 
     fn draw_epoch_debug(&mut self) {
         if !self.debug_flags.contains(DebugFlags::EPOCHS) {
             return;
@@ -5978,16 +6010,17 @@ pub struct RendererOptions {
     /// Start the debug server for this renderer.
     pub start_debug_server: bool,
     /// Output the source of the shader with the given name.
     pub dump_shader_source: Option<String>,
     /// An optional presentation config for compositor integration.
     pub present_config: Option<PresentConfig>,
     /// An optional client provided interface to a native / OS compositor.
     pub native_compositor: Option<Box<dyn Compositor>>,
+    pub surface_is_y_flipped: bool,
 }
 
 impl Default for RendererOptions {
     fn default() -> Self {
         RendererOptions {
             device_pixel_ratio: 1.0,
             resource_override_path: None,
             enable_aa: true,
@@ -6031,16 +6064,17 @@ impl Default for RendererOptions {
             // For backwards compatibility we set this to true by default, so
             // that if the debugger feature is enabled, the debug server will
             // be started automatically. Users can explicitly disable this as
             // needed.
             start_debug_server: true,
             dump_shader_source: None,
             present_config: None,
             native_compositor: None,
+            surface_is_y_flipped: false,
         }
     }
 }
 
 pub trait DebugServer {
     fn send(&mut self, _message: String);
 }
 
--- a/gfx/wr/webrender/src/screen_capture.rs
+++ b/gfx/wr/webrender/src/screen_capture.rs
@@ -287,17 +287,17 @@ impl AsyncScreenshotGrabber {
 
         let draw_target = DrawTarget::from_texture(&self.scaling_textures[level], 0 as _, false);
 
         let draw_target_rect = draw_target
             .to_framebuffer_rect(DeviceIntRect::new(DeviceIntPoint::new(0, 0), dest_size));
 
         let read_target_rect = FramebufferIntRect::from_untyped(&read_target_rect.to_untyped());
 
-        if level == 0 {
+        if level == 0 && !device.surface_is_y_flipped() {
             device.blit_render_target_invert_y(
                 read_target,
                 read_target_rect,
                 draw_target,
                 draw_target_rect,
             );
         } else {
             device.blit_render_target(