mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Auto merge of #20658 - kvark:deglify, r=emilio
Make gleam optional for compositor, switch various names from glutin to winit <!-- Please describe your changes on the following line: --> The PR goes an extra step towards discriminating GL to a level of an optional dependency. This would ease up the transition to gfx-hal when the time calls for it, and upstreams some of the patches we've been using locally, cleans up some of the old cruft along the way. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach build-geckolib` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #20612 <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ it's just refactoring <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/20658) <!-- Reviewable:end -->
This commit is contained in:
commit
89bf7c2a92
10 changed files with 288 additions and 252 deletions
|
@ -10,10 +10,13 @@ build = "build.rs"
|
|||
name = "compositing"
|
||||
path = "lib.rs"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
euclid = "0.17"
|
||||
gfx_traits = {path = "../gfx_traits"}
|
||||
gleam = "0.4.34"
|
||||
gleam = { version = "0.4.34", optional = true }
|
||||
image = "0.18"
|
||||
ipc-channel = "0.10"
|
||||
libc = "0.2"
|
||||
|
|
|
@ -8,12 +8,16 @@ use compositor_thread::{CompositorProxy, CompositorReceiver};
|
|||
use compositor_thread::{InitialCompositorState, Msg};
|
||||
use euclid::{TypedPoint2D, TypedVector2D, TypedScale};
|
||||
use gfx_traits::Epoch;
|
||||
use gleam::gl;
|
||||
use image::{DynamicImage, ImageFormat, RgbImage};
|
||||
use ipc_channel::ipc::{self, IpcSharedMemory};
|
||||
#[cfg(feature = "gleam")]
|
||||
use gl;
|
||||
#[cfg(feature = "gleam")]
|
||||
use image::{DynamicImage, ImageFormat};
|
||||
use ipc_channel::ipc;
|
||||
use libc::c_void;
|
||||
use msg::constellation_msg::{PipelineId, PipelineIndex, PipelineNamespaceId};
|
||||
use net_traits::image::base::{Image, PixelFormat};
|
||||
use net_traits::image::base::Image;
|
||||
#[cfg(feature = "gleam")]
|
||||
use net_traits::image::base::PixelFormat;
|
||||
use nonzero::NonZeroU32;
|
||||
use profile_traits::time::{self, ProfilerCategory, profile};
|
||||
use script_traits::{AnimationState, AnimationTickType, ConstellationMsg, LayoutControlMsg};
|
||||
|
@ -21,7 +25,7 @@ use script_traits::{MouseButton, MouseEventType, ScrollState, TouchEventType, To
|
|||
use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType};
|
||||
use script_traits::CompositorEvent::{MouseMoveEvent, MouseButtonEvent, TouchEvent};
|
||||
use servo_config::opts;
|
||||
use servo_geometry::{DeviceIndependentPixel, DeviceUintLength};
|
||||
use servo_geometry::DeviceIndependentPixel;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::fs::{File, create_dir_all};
|
||||
|
@ -39,6 +43,7 @@ use webrender_api::{self, DeviceIntPoint, DevicePoint, HitTestFlags, HitTestResu
|
|||
use webrender_api::{LayoutVector2D, ScrollEventPhase, ScrollLocation};
|
||||
use windowing::{self, EmbedderCoordinates, MouseWindowEvent, WebRenderDebugOption, WindowMethods};
|
||||
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum UnableToComposite {
|
||||
WindowUnprepared,
|
||||
|
@ -176,9 +181,6 @@ pub struct IOCompositor<Window: WindowMethods> {
|
|||
/// The webrender interface, if enabled.
|
||||
webrender_api: webrender_api::RenderApi,
|
||||
|
||||
/// GL functions interface (may be GL or GLES)
|
||||
gl: Rc<gl::Gl>,
|
||||
|
||||
/// Map of the pending paint metrics per layout thread.
|
||||
/// The layout thread for each specific pipeline expects the compositor to
|
||||
/// paint frames with specific given IDs (epoch). Once the compositor paints
|
||||
|
@ -254,58 +256,6 @@ enum CompositeTarget {
|
|||
PngFile
|
||||
}
|
||||
|
||||
struct RenderTargetInfo {
|
||||
framebuffer_ids: Vec<gl::GLuint>,
|
||||
renderbuffer_ids: Vec<gl::GLuint>,
|
||||
texture_ids: Vec<gl::GLuint>,
|
||||
}
|
||||
|
||||
impl RenderTargetInfo {
|
||||
fn empty() -> RenderTargetInfo {
|
||||
RenderTargetInfo {
|
||||
framebuffer_ids: Vec::new(),
|
||||
renderbuffer_ids: Vec::new(),
|
||||
texture_ids: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn initialize_png(gl: &gl::Gl, width: DeviceUintLength, height: DeviceUintLength) -> RenderTargetInfo {
|
||||
let framebuffer_ids = gl.gen_framebuffers(1);
|
||||
gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_ids[0]);
|
||||
|
||||
let texture_ids = gl.gen_textures(1);
|
||||
gl.bind_texture(gl::TEXTURE_2D, texture_ids[0]);
|
||||
|
||||
gl.tex_image_2d(gl::TEXTURE_2D, 0, gl::RGB as gl::GLint, width.get() as gl::GLsizei,
|
||||
height.get() as gl::GLsizei, 0, gl::RGB, gl::UNSIGNED_BYTE, None);
|
||||
gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as gl::GLint);
|
||||
gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as gl::GLint);
|
||||
|
||||
gl.framebuffer_texture_2d(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D,
|
||||
texture_ids[0], 0);
|
||||
|
||||
gl.bind_texture(gl::TEXTURE_2D, 0);
|
||||
|
||||
let renderbuffer_ids = gl.gen_renderbuffers(1);
|
||||
let depth_rb = renderbuffer_ids[0];
|
||||
gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
|
||||
gl.renderbuffer_storage(gl::RENDERBUFFER,
|
||||
gl::DEPTH_COMPONENT24,
|
||||
width.get() as gl::GLsizei,
|
||||
height.get() as gl::GLsizei);
|
||||
gl.framebuffer_renderbuffer(gl::FRAMEBUFFER,
|
||||
gl::DEPTH_ATTACHMENT,
|
||||
gl::RENDERBUFFER,
|
||||
depth_rb);
|
||||
|
||||
RenderTargetInfo {
|
||||
framebuffer_ids: framebuffer_ids,
|
||||
renderbuffer_ids: renderbuffer_ids,
|
||||
texture_ids: texture_ids,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RenderNotifier {
|
||||
compositor_proxy: CompositorProxy,
|
||||
|
@ -343,17 +293,15 @@ impl webrender_api::RenderNotifier for RenderNotifier {
|
|||
}
|
||||
|
||||
impl<Window: WindowMethods> IOCompositor<Window> {
|
||||
fn new(window: Rc<Window>, state: InitialCompositorState)
|
||||
-> IOCompositor<Window> {
|
||||
fn new(window: Rc<Window>, state: InitialCompositorState) -> Self {
|
||||
let composite_target = match opts::get().output_file {
|
||||
Some(_) => CompositeTarget::PngFile,
|
||||
None => CompositeTarget::Window
|
||||
};
|
||||
|
||||
IOCompositor {
|
||||
gl: window.gl(),
|
||||
embedder_coordinates: window.get_coordinates(),
|
||||
window: window,
|
||||
window,
|
||||
port: state.receiver,
|
||||
root_pipeline: None,
|
||||
pipeline_details: HashMap::new(),
|
||||
|
@ -362,7 +310,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
touch_handler: TouchHandler::new(),
|
||||
pending_scroll_zoom_events: Vec::new(),
|
||||
waiting_for_results_of_scroll: false,
|
||||
composite_target: composite_target,
|
||||
composite_target,
|
||||
shutdown_state: ShutdownState::NotShuttingDown,
|
||||
page_zoom: TypedScale::new(1.0),
|
||||
viewport_zoom: PinchZoomFactor::new(1.0),
|
||||
|
@ -384,7 +332,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn create(window: Rc<Window>, state: InitialCompositorState) -> IOCompositor<Window> {
|
||||
pub fn create(window: Rc<Window>, state: InitialCompositorState) -> Self {
|
||||
let mut compositor = IOCompositor::new(window, state);
|
||||
|
||||
// Set the size of the root layer.
|
||||
|
@ -567,9 +515,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
|
||||
/// Sets or unsets the animations-running flag for the given pipeline, and schedules a
|
||||
/// recomposite if necessary.
|
||||
fn change_running_animations_state(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
animation_state: AnimationState) {
|
||||
fn change_running_animations_state(
|
||||
&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
animation_state: AnimationState,
|
||||
) {
|
||||
match animation_state {
|
||||
AnimationState::AnimationsPresent => {
|
||||
let visible = self.pipeline_details(pipeline_id).visible;
|
||||
|
@ -1285,9 +1235,18 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
}
|
||||
}
|
||||
|
||||
let render_target_info = match target {
|
||||
CompositeTarget::Window => RenderTargetInfo::empty(),
|
||||
_ => initialize_png(&*self.gl, width, height)
|
||||
let rt_info = match target {
|
||||
#[cfg(feature = "gleam")]
|
||||
CompositeTarget::Window => {
|
||||
gl::RenderTargetInfo::default()
|
||||
}
|
||||
#[cfg(feature = "gleam")]
|
||||
CompositeTarget::WindowAndPng |
|
||||
CompositeTarget::PngFile => {
|
||||
gl::initialize_png(&*self.window.gl(), width, height)
|
||||
}
|
||||
#[cfg(not(feature = "gleam"))]
|
||||
_ => ()
|
||||
};
|
||||
|
||||
profile(ProfilerCategory::Compositing, None, self.time_profiler_chan.clone(), || {
|
||||
|
@ -1332,24 +1291,25 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
|
||||
let rv = match target {
|
||||
CompositeTarget::Window => None,
|
||||
#[cfg(feature = "gleam")]
|
||||
CompositeTarget::WindowAndPng => {
|
||||
let img = self.draw_img(render_target_info,
|
||||
width,
|
||||
height);
|
||||
let img = gl::draw_img(&*self.window.gl(), rt_info, width, height);
|
||||
Some(Image {
|
||||
width: img.width(),
|
||||
height: img.height(),
|
||||
format: PixelFormat::RGB8,
|
||||
bytes: IpcSharedMemory::from_bytes(&*img),
|
||||
bytes: ipc::IpcSharedMemory::from_bytes(&*img),
|
||||
id: None,
|
||||
})
|
||||
}
|
||||
#[cfg(feature = "gleam")]
|
||||
CompositeTarget::PngFile => {
|
||||
let gl = &*self.window.gl();
|
||||
profile(ProfilerCategory::ImageSaving, None, self.time_profiler_chan.clone(), || {
|
||||
match opts::get().output_file.as_ref() {
|
||||
Some(path) => match File::create(path) {
|
||||
Ok(mut file) => {
|
||||
let img = self.draw_img(render_target_info, width, height);
|
||||
let img = gl::draw_img(gl, rt_info, width, height);
|
||||
let dynamic_image = DynamicImage::ImageRgb8(img);
|
||||
if let Err(e) = dynamic_image.save(&mut file, ImageFormat::PNG) {
|
||||
error!("Failed to save {} ({}).", path, e);
|
||||
|
@ -1362,6 +1322,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
});
|
||||
None
|
||||
}
|
||||
#[cfg(not(feature = "gleam"))]
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Perform the page flip. This will likely block for a while.
|
||||
|
@ -1378,44 +1340,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
Ok(rv)
|
||||
}
|
||||
|
||||
fn draw_img(&self,
|
||||
render_target_info: RenderTargetInfo,
|
||||
width: DeviceUintLength,
|
||||
height: DeviceUintLength)
|
||||
-> RgbImage {
|
||||
let width = width.get() as usize;
|
||||
let height = height.get() as usize;
|
||||
// For some reason, OSMesa fails to render on the 3rd
|
||||
// attempt in headless mode, under some conditions.
|
||||
// I think this can only be some kind of synchronization
|
||||
// bug in OSMesa, but explicitly un-binding any vertex
|
||||
// array here seems to work around that bug.
|
||||
// See https://github.com/servo/servo/issues/18606.
|
||||
self.gl.bind_vertex_array(0);
|
||||
|
||||
let mut pixels = self.gl.read_pixels(0, 0,
|
||||
width as gl::GLsizei,
|
||||
height as gl::GLsizei,
|
||||
gl::RGB, gl::UNSIGNED_BYTE);
|
||||
|
||||
self.gl.bind_framebuffer(gl::FRAMEBUFFER, 0);
|
||||
|
||||
self.gl.delete_buffers(&render_target_info.texture_ids);
|
||||
self.gl.delete_renderbuffers(&render_target_info.renderbuffer_ids);
|
||||
self.gl.delete_framebuffers(&render_target_info.framebuffer_ids);
|
||||
|
||||
// flip image vertically (texture is upside down)
|
||||
let orig_pixels = pixels.clone();
|
||||
let stride = width * 3;
|
||||
for y in 0..height {
|
||||
let dst_start = y * stride;
|
||||
let src_start = (height - y - 1) * stride;
|
||||
let src_slice = &orig_pixels[src_start .. src_start + stride];
|
||||
(&mut pixels[dst_start .. dst_start + stride]).clone_from_slice(&src_slice[..stride]);
|
||||
}
|
||||
RgbImage::from_raw(width as u32, height as u32, pixels).expect("Flipping image failed!")
|
||||
}
|
||||
|
||||
fn composite_if_necessary(&mut self, reason: CompositingReason) {
|
||||
if self.composition_request == CompositionRequest::NoCompositingNecessary {
|
||||
if opts::get().is_running_problem_test {
|
||||
|
|
97
components/compositing/gl.rs
Normal file
97
components/compositing/gl.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
/* 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 gleam::gl;
|
||||
use image::RgbImage;
|
||||
use servo_geometry::DeviceUintLength;
|
||||
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RenderTargetInfo {
|
||||
framebuffer_ids: Vec<gl::GLuint>,
|
||||
renderbuffer_ids: Vec<gl::GLuint>,
|
||||
texture_ids: Vec<gl::GLuint>,
|
||||
}
|
||||
|
||||
pub fn initialize_png(
|
||||
gl: &gl::Gl, width: DeviceUintLength, height: DeviceUintLength
|
||||
) -> RenderTargetInfo {
|
||||
let framebuffer_ids = gl.gen_framebuffers(1);
|
||||
gl.bind_framebuffer(gl::FRAMEBUFFER, framebuffer_ids[0]);
|
||||
|
||||
let texture_ids = gl.gen_textures(1);
|
||||
gl.bind_texture(gl::TEXTURE_2D, texture_ids[0]);
|
||||
|
||||
gl.tex_image_2d(gl::TEXTURE_2D, 0, gl::RGB as gl::GLint, width.get() as gl::GLsizei,
|
||||
height.get() as gl::GLsizei, 0, gl::RGB, gl::UNSIGNED_BYTE, None);
|
||||
gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as gl::GLint);
|
||||
gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as gl::GLint);
|
||||
|
||||
gl.framebuffer_texture_2d(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D,
|
||||
texture_ids[0], 0);
|
||||
|
||||
gl.bind_texture(gl::TEXTURE_2D, 0);
|
||||
|
||||
let renderbuffer_ids = gl.gen_renderbuffers(1);
|
||||
let depth_rb = renderbuffer_ids[0];
|
||||
gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb);
|
||||
gl.renderbuffer_storage(gl::RENDERBUFFER,
|
||||
gl::DEPTH_COMPONENT24,
|
||||
width.get() as gl::GLsizei,
|
||||
height.get() as gl::GLsizei);
|
||||
gl.framebuffer_renderbuffer(gl::FRAMEBUFFER,
|
||||
gl::DEPTH_ATTACHMENT,
|
||||
gl::RENDERBUFFER,
|
||||
depth_rb);
|
||||
|
||||
RenderTargetInfo {
|
||||
framebuffer_ids,
|
||||
renderbuffer_ids,
|
||||
texture_ids,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_img(
|
||||
gl: &gl::Gl,
|
||||
render_target_info: RenderTargetInfo,
|
||||
width: DeviceUintLength,
|
||||
height: DeviceUintLength,
|
||||
) -> RgbImage {
|
||||
let width = width.get() as usize;
|
||||
let height = height.get() as usize;
|
||||
// For some reason, OSMesa fails to render on the 3rd
|
||||
// attempt in headless mode, under some conditions.
|
||||
// I think this can only be some kind of synchronization
|
||||
// bug in OSMesa, but explicitly un-binding any vertex
|
||||
// array here seems to work around that bug.
|
||||
// See https://github.com/servo/servo/issues/18606.
|
||||
gl.bind_vertex_array(0);
|
||||
|
||||
let mut pixels = gl.read_pixels(
|
||||
0, 0,
|
||||
width as gl::GLsizei,
|
||||
height as gl::GLsizei,
|
||||
gl::RGB, gl::UNSIGNED_BYTE,
|
||||
);
|
||||
|
||||
gl.bind_framebuffer(gl::FRAMEBUFFER, 0);
|
||||
|
||||
gl.delete_buffers(&render_target_info.texture_ids);
|
||||
gl.delete_renderbuffers(&render_target_info.renderbuffer_ids);
|
||||
gl.delete_framebuffers(&render_target_info.framebuffer_ids);
|
||||
|
||||
// flip image vertically (texture is upside down)
|
||||
let orig_pixels = pixels.clone();
|
||||
let stride = width * 3;
|
||||
for y in 0..height {
|
||||
let dst_start = y * stride;
|
||||
let src_start = (height - y - 1) * stride;
|
||||
let src_slice = &orig_pixels[src_start .. src_start + stride];
|
||||
(&mut pixels[dst_start .. dst_start + stride]).clone_from_slice(&src_slice[..stride]);
|
||||
}
|
||||
|
||||
RgbImage::from_raw(width as u32, height as u32, pixels)
|
||||
.expect("Flipping image failed!")
|
||||
}
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
extern crate euclid;
|
||||
extern crate gfx_traits;
|
||||
#[cfg(feature = "gleam")]
|
||||
extern crate gleam;
|
||||
#[cfg(feature = "gleam")]
|
||||
extern crate image;
|
||||
extern crate ipc_channel;
|
||||
extern crate libc;
|
||||
|
@ -38,6 +40,8 @@ use style_traits::CSSPixel;
|
|||
|
||||
mod compositor;
|
||||
pub mod compositor_thread;
|
||||
#[cfg(feature = "gleam")]
|
||||
mod gl;
|
||||
mod touch;
|
||||
pub mod windowing;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
use compositor_thread::EventLoopWaker;
|
||||
use euclid::TypedScale;
|
||||
#[cfg(feature = "gleam")]
|
||||
use gleam::gl;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use msg::constellation_msg::{Key, KeyModifiers, KeyState, TopLevelBrowsingContextId, TraversalDirection};
|
||||
|
@ -13,6 +14,7 @@ use script_traits::{MouseButton, TouchEventType, TouchId};
|
|||
use servo_geometry::{DeviceIndependentPixel, DeviceUintLength};
|
||||
use servo_url::ServoUrl;
|
||||
use std::fmt::{Debug, Error, Formatter};
|
||||
#[cfg(feature = "gleam")]
|
||||
use std::rc::Rc;
|
||||
use style_traits::DevicePixel;
|
||||
use webrender_api::{DeviceIntPoint, DevicePoint, DeviceUintSize, DeviceUintRect, ScrollLocation};
|
||||
|
@ -126,6 +128,7 @@ pub trait WindowMethods {
|
|||
/// proceed and false if it should not.
|
||||
fn prepare_for_composite(&self, width: DeviceUintLength, height: DeviceUintLength) -> bool;
|
||||
/// Return the GL function pointer trait.
|
||||
#[cfg(feature = "gleam")]
|
||||
fn gl(&self) -> Rc<gl::Gl>;
|
||||
/// Returns a thread-safe object to wake up the window's event loop.
|
||||
fn create_event_loop_waker(&self) -> Box<EventLoopWaker>;
|
||||
|
|
|
@ -32,7 +32,7 @@ bluetooth_traits = {path = "../bluetooth_traits"}
|
|||
bluetooth = {path = "../bluetooth"}
|
||||
canvas = {path = "../canvas"}
|
||||
canvas_traits = {path = "../canvas_traits"}
|
||||
compositing = {path = "../compositing"}
|
||||
compositing = {path = "../compositing", features = ["gleam"]}
|
||||
constellation = {path = "../constellation"}
|
||||
debugger = {path = "../debugger"}
|
||||
devtools = {path = "../devtools"}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue