Upgrade WebRender to e491e1ae637b2eed1e7195855d88357e5eb3ddf9 (#30323)

* Upgrade vendored version of WebRender

* Patch WebRender: upgrade version of gleam

* Restore hit testing implementation

* Fix WebRender warnings

* Adapt Servo to new WebRender

* Update results

* Add a workaround for #30313

This slightly expands text boundaries in order to take into account the
fact that layout isn't measuring glyph boundaries.
This commit is contained in:
Martin Robinson 2023-09-10 14:38:56 +02:00 committed by GitHub
parent c079acb3c3
commit a9d37cb85a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
563 changed files with 48524 additions and 51657 deletions

65
Cargo.lock generated
View file

@ -332,15 +332,6 @@ dependencies = [
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
dependencies = [
"byteorder",
]
[[package]]
name = "base64"
version = "0.13.0"
@ -970,6 +961,7 @@ dependencies = [
"servo_url",
"style_traits",
"webgpu",
"webrender",
"webrender_api",
"webrender_traits",
"webxr-api",
@ -1186,22 +1178,12 @@ dependencies = [
[[package]]
name = "cstr"
version = "0.1.7"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f7a08ed4ecd7e077d4cee63937473e6f7cf57b702a9114ef41751b2cbc0f60"
checksum = "8aa998c33a6d3271e3678950a22134cd7dd27cef86dee1b611b5b14207d1d90b"
dependencies = [
"cstr-macros",
"procedural-masquerade",
]
[[package]]
name = "cstr-macros"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd670e5ff58768ef624207fb95709ce63b8d05573fb9a05165f0eef471ea6a3a"
dependencies = [
"procedural-masquerade",
"syn 1.0.103",
"proc-macro2",
"quote",
]
[[package]]
@ -1651,6 +1633,17 @@ dependencies = [
"str-buf",
]
[[package]]
name = "etagere"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcf22f748754352918e082e0039335ee92454a5d62bcaf69b5e8daf5907d9644"
dependencies = [
"euclid",
"serde",
"svg_fmt",
]
[[package]]
name = "euclid"
version = "0.22.7"
@ -2119,9 +2112,9 @@ dependencies = [
[[package]]
name = "gleam"
version = "0.12.2"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6da1b0a7e3f0833efe2c59f11c0e196d6208735018693c340eded4bd7aacb49"
checksum = "0173481f2bb6e809bf4985de2e86c83876d84d2805830e3301cd37355e897f0f"
dependencies = [
"gl_generator 0.14.0",
]
@ -4480,12 +4473,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "procedural-masquerade"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f1383dff4092fe903ac180e391a8d4121cc48f08ccf850614b0290c6673b69d"
[[package]]
name = "profile"
version = "0.0.1"
@ -4732,11 +4719,11 @@ checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422"
[[package]]
name = "ron"
version = "0.5.1"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ece421e0c4129b90e4a35b6f625e472e96c552136f5093a2f4fa2bbb75a62d5"
checksum = "86018df177b1beef6c7c8ef949969c4f7cb9a9344181b92486b23c79995bdaa4"
dependencies = [
"base64 0.10.1",
"base64 0.13.0",
"bitflags 1.3.2",
"serde",
]
@ -6822,6 +6809,7 @@ dependencies = [
"serde",
"servo_config",
"smallvec",
"webrender",
"webrender_api",
"webrender_traits",
"wgpu-core",
@ -6851,17 +6839,17 @@ dependencies = [
name = "webrender"
version = "0.61.0"
dependencies = [
"backtrace",
"bincode",
"bitflags 1.3.2",
"build-parallel",
"byteorder",
"cfg-if 0.1.10",
"core-foundation 0.9.3",
"core-graphics",
"core-text",
"cstr",
"derive_more",
"dwrote",
"etagere",
"euclid",
"freetype",
"fxhash",
@ -6872,11 +6860,11 @@ dependencies = [
"log",
"malloc_size_of_derive",
"num-traits",
"objc",
"plane-split",
"rayon",
"ron 0.5.1",
"ron 0.6.6",
"serde",
"sig",
"smallvec",
"svg_fmt",
"time 0.1.45",
@ -6895,6 +6883,7 @@ dependencies = [
"byteorder",
"core-foundation 0.9.3",
"core-graphics",
"crossbeam-channel",
"derive_more",
"euclid",
"malloc_size_of_derive",

View file

@ -32,7 +32,7 @@ env_logger = "0.8"
fnv = "1.0"
fxhash = "0.2"
getopts = "0.2.11"
gleam = "0.12"
gleam = "0.15"
headers = "0.3"
html5ever = "0.26"
http = "0.2"

View file

@ -13,7 +13,8 @@ use std::default::Default;
use std::sync::{Arc, Mutex};
use surfman::chains::{SwapChainAPI, SwapChains, SwapChainsAPI};
use surfman::{Device, SurfaceInfo, SurfaceTexture};
use webrender_api::{DocumentId, RenderApiSender};
use webrender::RenderApiSender;
use webrender_api::DocumentId;
use webrender_surfman::WebrenderSurfman;
use webrender_traits::{
WebrenderExternalImageApi, WebrenderExternalImageRegistry, WebrenderImageSource,

View file

@ -63,10 +63,11 @@ use surfman::{
self, Adapter, Connection, Context, ContextAttributeFlags, ContextAttributes, Device,
GLVersion, SurfaceAccess, SurfaceInfo, SurfaceType,
};
use webrender::{RenderApi, RenderApiSender, Transaction};
use webrender_api::{
units::DeviceIntSize, DirtyRect, DocumentId, ExternalImageData, ExternalImageId,
ExternalImageType, ImageData, ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey,
RenderApi, RenderApiSender, TextureTarget, Transaction,
ExternalImageType, ImageBufferKind, ImageData, ImageDescriptor, ImageDescriptorFlags,
ImageFormat, ImageKey,
};
use webrender_traits::{WebrenderExternalImageRegistry, WebrenderImageHandlerType};
use webxr::SurfmanGL as WebXRSurfman;
@ -608,7 +609,7 @@ impl WebGLThread {
let descriptor_attributes = self.device.context_descriptor_attributes(&descriptor);
let gl_version = descriptor_attributes.version;
let has_alpha = requested_flags.contains(ContextAttributeFlags::ALPHA);
let texture_target = current_wr_texture_target(&self.device);
let image_buffer_kind = current_wr_image_buffer_kind(&self.device);
self.device.make_context_current(&ctx).unwrap();
let framebuffer = self
@ -667,7 +668,7 @@ impl WebGLThread {
size.to_i32(),
has_alpha,
id,
texture_target,
image_buffer_kind,
);
self.cached_context_info
@ -723,7 +724,7 @@ impl WebGLThread {
.state
.requested_flags
.contains(ContextAttributeFlags::ALPHA);
let texture_target = current_wr_texture_target(&self.device);
let image_buffer_kind = current_wr_image_buffer_kind(&self.device);
Self::update_wr_external_image(
&mut self.webrender_api,
self.webrender_doc,
@ -731,7 +732,7 @@ impl WebGLThread {
has_alpha,
context_id,
info.image_key,
texture_target,
image_buffer_kind,
);
debug_assert_eq!(data.gl.get_error(), gl::NO_ERROR);
@ -927,10 +928,10 @@ impl WebGLThread {
size: Size2D<i32>,
alpha: bool,
context_id: WebGLContextId,
target: TextureTarget,
image_buffer_kind: ImageBufferKind,
) -> ImageKey {
let descriptor = Self::image_descriptor(size, alpha);
let data = Self::external_image_data(context_id, target);
let data = Self::external_image_data(context_id, image_buffer_kind);
let image_key = webrender_api.generate_image_key();
let mut txn = Transaction::new();
@ -948,10 +949,10 @@ impl WebGLThread {
alpha: bool,
context_id: WebGLContextId,
image_key: ImageKey,
target: TextureTarget,
image_buffer_kind: ImageBufferKind,
) {
let descriptor = Self::image_descriptor(size, alpha);
let data = Self::external_image_data(context_id, target);
let data = Self::external_image_data(context_id, image_buffer_kind);
let mut txn = Transaction::new();
txn.update_image(image_key, descriptor, data, &DirtyRect::All);
@ -972,11 +973,14 @@ impl WebGLThread {
}
/// Helper function to create a `ImageData::External` instance.
fn external_image_data(context_id: WebGLContextId, target: TextureTarget) -> ImageData {
fn external_image_data(
context_id: WebGLContextId,
image_buffer_kind: ImageBufferKind,
) -> ImageData {
let data = ExternalImageData {
id: ExternalImageId(context_id.0 as u64),
channel_index: 0,
image_type: ExternalImageType::TextureHandle(target),
image_type: ExternalImageType::TextureHandle(image_buffer_kind),
};
ImageData::External(data)
}
@ -1006,10 +1010,10 @@ struct WebGLContextInfo {
}
// TODO(pcwalton): Add `GL_TEXTURE_EXTERNAL_OES`?
fn current_wr_texture_target(device: &Device) -> TextureTarget {
fn current_wr_image_buffer_kind(device: &Device) -> ImageBufferKind {
match device.surface_gl_texture_target() {
gl::TEXTURE_RECTANGLE => TextureTarget::Rect,
_ => TextureTarget::Default,
gl::TEXTURE_RECTANGLE => ImageBufferKind::TextureRect,
_ => ImageBufferKind::Texture2D,
}
}

View file

@ -48,13 +48,14 @@ use std::num::NonZeroU32;
use std::rc::Rc;
use style_traits::{CSSPixel, DevicePixel, PinchZoomFactor};
use time::{now, precise_time_ns, precise_time_s};
use webrender;
use webrender::{CaptureBits, RenderApi, Transaction};
use webrender_api::units::{
DeviceIntPoint, DeviceIntSize, DevicePoint, LayoutPoint, LayoutVector2D, WorldPoint,
};
use webrender_api::{
self, BuiltDisplayList, CaptureBits, DirtyRect, DocumentId, Epoch as WebRenderEpoch,
ExternalScrollId, HitTestFlags, PipelineId as WebRenderPipelineId, RenderApi, ScrollClamping,
ScrollLocation, Transaction, ZoomFactor,
self, BuiltDisplayList, DirtyRect, DocumentId, Epoch as WebRenderEpoch, ExternalScrollId,
HitTestFlags, PipelineId as WebRenderPipelineId, ScrollClamping, ScrollLocation, ZoomFactor,
};
use webrender_surfman::WebrenderSurfman;
@ -649,58 +650,55 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
WebRenderEpoch(0),
None,
Default::default(),
(pipeline, Default::default(), Default::default()),
(pipeline, Default::default()),
false,
);
self.webrender_api
.send_transaction(self.webrender_document, txn);
},
WebrenderMsg::Layout(script_traits::WebrenderMsg::SendScrollNode(
point,
scroll_id,
clamping,
)) => {
WebrenderMsg::Layout(script_traits::WebrenderMsg::SendScrollNode(point, scroll_id)) => {
self.waiting_for_results_of_scroll = true;
let mut txn = Transaction::new();
txn.scroll_node_with_id(point, scroll_id, clamping);
txn.generate_frame();
txn.scroll_node_with_id(point, scroll_id, ScrollClamping::ToContentBounds);
txn.generate_frame(0);
self.webrender_api
.send_transaction(self.webrender_document, txn);
},
WebrenderMsg::Layout(script_traits::WebrenderMsg::SendDisplayList {
display_list_info,
content_size,
display_list_descriptor,
display_list_receiver,
}) => match display_list_receiver.recv() {
Ok(data) => {
self.waiting_on_pending_frame = true;
}) => {
let display_list_data = match display_list_receiver.recv() {
Ok(display_list_data) => display_list_data,
_ => return warn!("Could not recieve WebRender display list."),
};
let pipeline_id = display_list_info.pipeline_id;
let details = self.pipeline_details(PipelineId::from_webrender(pipeline_id));
details.hit_test_items = display_list_info.hit_test_info;
details.install_new_scroll_tree(display_list_info.scroll_tree);
self.waiting_on_pending_frame = true;
let mut txn = Transaction::new();
txn.set_display_list(
display_list_info.epoch,
None,
display_list_info.viewport_size,
(
pipeline_id,
content_size,
BuiltDisplayList::from_data(data, display_list_descriptor),
),
true,
);
txn.generate_frame();
self.webrender_api
.send_transaction(self.webrender_document, txn);
},
Err(e) => warn!("error receiving display list data: {e:?}"),
let pipeline_id = display_list_info.pipeline_id;
let details = self.pipeline_details(PipelineId::from_webrender(pipeline_id));
details.hit_test_items = display_list_info.hit_test_info;
details.install_new_scroll_tree(display_list_info.scroll_tree);
let mut txn = Transaction::new();
txn.set_display_list(
display_list_info.epoch,
None,
display_list_info.viewport_size,
(
pipeline_id,
BuiltDisplayList::from_data(display_list_data, display_list_descriptor),
),
true,
);
txn.generate_frame(0);
self.webrender_api
.send_transaction(self.webrender_document, txn);
},
WebrenderMsg::Layout(script_traits::WebrenderMsg::HitTest(
@ -876,7 +874,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
let pipeline_id = frame_tree.pipeline.id.to_webrender();
let mut txn = Transaction::new();
txn.set_root_pipeline(pipeline_id);
txn.generate_frame();
txn.generate_frame(0);
self.webrender_api
.send_transaction(self.webrender_document, txn);
@ -925,14 +923,16 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
self.pipeline_details.remove(&pipeline_id);
}
fn send_window_size(&self, size_type: WindowSizeType) {
fn send_window_size(&mut self, size_type: WindowSizeType) {
let dppx = self.page_zoom * self.embedder_coordinates.hidpi_factor;
self.webrender_api.set_document_view(
self.webrender_document,
let mut transaction = Transaction::new();
transaction.set_document_view(
self.embedder_coordinates.get_viewport(),
self.embedder_coordinates.hidpi_factor.get(),
);
self.webrender_api
.send_transaction(self.webrender_document, transaction);
let initial_viewport = self.embedder_coordinates.viewport.size.to_f32() / dppx;
@ -1195,17 +1195,17 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
pub fn on_scroll_event(
&mut self,
delta: ScrollLocation,
scroll_location: ScrollLocation,
cursor: DeviceIntPoint,
phase: TouchEventType,
) {
match phase {
TouchEventType::Move => self.on_scroll_window_event(delta, cursor),
TouchEventType::Move => self.on_scroll_window_event(scroll_location, cursor),
TouchEventType::Up | TouchEventType::Cancel => {
self.on_scroll_window_event(delta, cursor);
self.on_scroll_window_event(scroll_location, cursor);
},
TouchEventType::Down => {
self.on_scroll_window_event(delta, cursor);
self.on_scroll_window_event(scroll_location, cursor);
},
}
}
@ -1213,7 +1213,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
fn on_scroll_window_event(&mut self, scroll_location: ScrollLocation, cursor: DeviceIntPoint) {
self.pending_scroll_zoom_events.push(ScrollZoomEvent {
magnification: 1.0,
scroll_location: scroll_location,
scroll_location,
cursor: cursor,
event_count: 1,
});
@ -1307,7 +1307,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
self.set_pinch_zoom_level(old_zoom * combined_event.magnification);
txn.set_pinch_zoom(ZoomFactor::new(self.pinch_zoom_level()));
}
txn.generate_frame();
txn.generate_frame(0);
self.webrender_api
.send_transaction(self.webrender_document, txn);
self.waiting_for_results_of_scroll = true
@ -1395,7 +1395,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
fn update_page_zoom_for_webrender(&mut self) {
let page_zoom = ZoomFactor::new(self.page_zoom.get());
let mut txn = Transaction::new();
let mut txn = webrender::Transaction::new();
txn.set_page_zoom(page_zoom);
self.webrender_api
.send_transaction(self.webrender_document, txn);
@ -1609,7 +1609,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
// Paint the scene.
// TODO(gw): Take notice of any errors the renderer returns!
self.clear_background();
self.webrender.render(size).ok();
self.webrender.render(size, 0 /* buffer_age */).ok();
},
);
@ -1904,7 +1904,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
self.webrender.set_debug_flags(flags);
let mut txn = Transaction::new();
txn.generate_frame();
txn.generate_frame(0);
self.webrender_api
.send_transaction(self.webrender_document, txn);
}

View file

@ -12,8 +12,8 @@ use crossbeam_channel::Sender;
use profile_traits::mem;
use profile_traits::time;
use std::rc::Rc;
use webrender::RenderApi;
use webrender_api::DocumentId;
use webrender_api::RenderApi;
use webrender_surfman::WebrenderSurfman;
mod compositor;

View file

@ -42,6 +42,7 @@ servo_remutex = { path = "../remutex" }
servo_url = { path = "../url" }
style_traits = { path = "../style_traits" }
webgpu = { path = "../webgpu" }
webrender = { workspace = true }
webrender_api = { workspace = true }
webrender_traits = { path = "../webrender_traits" }
webxr-api = { git = "https://github.com/servo/webxr", features = ["ipc"] }

View file

@ -179,7 +179,8 @@ use std::sync::{Arc, Mutex};
use std::thread;
use style_traits::CSSPixel;
use webgpu::{self, WebGPU, WebGPURequest};
use webrender_api::{DocumentId, RenderApi, RenderApiSender};
use webrender::{RenderApi, RenderApiSender};
use webrender_api::DocumentId;
use webrender_traits::WebrenderExternalImageRegistry;
type PendingApprovalNavigations = HashMap<PipelineId, (LoadData, HistoryEntryReplacement)>;

View file

@ -71,10 +71,11 @@ use style::values::specified::ui::CursorKind;
use style::values::RGBA;
use style_traits::{CSSPixel, ToCss};
use webrender_api::units::{LayoutRect, LayoutTransform, LayoutVector2D};
use webrender_api::{self, BorderDetails, BorderRadius, BorderSide, BoxShadowClipMode, ColorF};
use webrender_api::{ColorU, ExternalScrollId, FilterOp, GlyphInstance, ImageRendering, LineStyle};
use webrender_api::{NinePatchBorder, NinePatchBorderSource, NormalBorder, PropertyBinding};
use webrender_api::{ScrollSensitivity, StickyOffsetBounds};
use webrender_api::{
self, BorderDetails, BorderRadius, BorderSide, BoxShadowClipMode, ColorF, ColorU,
ExternalScrollId, FilterOp, GlyphInstance, ImageRendering, LineStyle, NinePatchBorder,
NinePatchBorderSource, NormalBorder, PropertyBinding, ScrollSensitivity, StickyOffsetBounds,
};
static THREAD_TINT_COLORS: [ColorF; 8] = [
ColorF {
@ -2117,7 +2118,7 @@ impl Fragment {
}
// Text
let mut glyphs = convert_text_run_to_glyphs(
let (largest_advance, mut glyphs) = convert_text_run_to_glyphs(
text_fragment.run.clone(),
text_fragment.range,
baseline_origin,
@ -2131,6 +2132,22 @@ impl Fragment {
};
state.indexable_text.insert(self.node, indexable_text);
// FIXME(mrobinson, #30313): This is a serious hack to enable a WebRender upgrade.
// Servo is not calculating glyph boundaries and is instead relying on the
// measured size of the content box here -- which is based on the positioning
// of the text. The issue is that glyphs can extend beyond the boundaries
// established by their brush origin and advance. Servo should be measuring
// the ink boundary rectangle based on the brush origin and the glyph extents
// instead.
//
// We don't yet have that information here, so in the meantime simply expand
// the boundary rectangle of the text by the largest character advance of the
// painted text run in all directions. This is used as a heuristic for a
// reasonable amount of "fudge" space to include the entire text run.
let inflated_bounds = stacking_relative_content_box
.inflate(largest_advance, largest_advance)
.to_layout();
// Process glyphs in chunks to avoid overflowing WebRender's internal limits (#17230).
while !glyphs.is_empty() {
let mut rest_of_glyphs = vec![];
@ -2142,7 +2159,7 @@ impl Fragment {
state.add_display_item(DisplayItem::Text(CommonDisplayItem::with_data(
base.clone(),
webrender_api::TextDisplayItem {
bounds: stacking_relative_content_box.to_layout(),
bounds: inflated_bounds,
common: items::empty_common_item_properties(),
font_key: text_fragment.run.font_key,
color: text_color.to_layout(),
@ -3013,7 +3030,8 @@ fn convert_text_run_to_glyphs(
text_run: Arc<TextRun>,
range: Range<ByteIndex>,
mut origin: Point2D<Au>,
) -> Vec<GlyphInstance> {
) -> (Au, Vec<GlyphInstance>) {
let mut largest_advance = Au(0);
let mut glyphs = vec![];
for slice in text_run.natural_word_slices_in_visual_order(&range) {
@ -3023,6 +3041,8 @@ fn convert_text_run_to_glyphs(
} else {
glyph.advance()
};
largest_advance = largest_advance.max(glyph.advance());
if !slice.glyphs.is_whitespace() {
let glyph_offset = glyph.offset().unwrap_or(Point2D::zero());
let point = origin + glyph_offset.to_vector();
@ -3035,7 +3055,7 @@ fn convert_text_run_to_glyphs(
origin.x += glyph_advance;
}
}
return glyphs;
(largest_advance, glyphs)
}
pub struct IndexableTextItem {

View file

@ -39,7 +39,7 @@ impl ToLayout for Filter {
type Type = wr::FilterOp;
fn to_layout(&self) -> Self::Type {
match *self {
Filter::Blur(radius) => wr::FilterOp::Blur(radius.px()),
Filter::Blur(radius) => wr::FilterOp::Blur(radius.px(), radius.px()),
Filter::Brightness(amount) => wr::FilterOp::Brightness(amount.0),
Filter::Contrast(amount) => wr::FilterOp::Contrast(amount.0),
Filter::Grayscale(amount) => wr::FilterOp::Grayscale(amount.0),

View file

@ -29,11 +29,10 @@ use style::computed_values::_servo_top_layer::T as InTopLayer;
use webrender_api as wr;
use webrender_api::units::{LayoutPixel, LayoutRect, LayoutTransform};
use webrender_api::{
BorderRadius, ClipId, ClipMode, CommonItemProperties, ComplexClipRegion, ExternalScrollId,
FilterOp, GlyphInstance, GradientStop, ImageKey, MixBlendMode, PrimitiveFlags,
ScrollSensitivity, Shadow, SpatialId, StickyOffsetBounds, TransformStyle,
BorderRadius, ClipChainId, ClipId, ClipMode, CommonItemProperties, ComplexClipRegion,
ExternalScrollId, FilterOp, GlyphInstance, GradientStop, ImageKey, MixBlendMode,
PrimitiveFlags, ScrollSensitivity, Shadow, SpatialId, StickyOffsetBounds, TransformStyle,
};
use wr::ClipChainId;
pub use style::dom::OpaqueNode;
@ -497,7 +496,6 @@ pub fn empty_common_item_properties() -> CommonItemProperties {
clip_rect: LayoutRect::max_rect(),
clip_id: ClipId::root(wr::PipelineId::dummy()),
spatial_id: SpatialId::root_scroll_node(wr::PipelineId::dummy()),
hit_info: None,
flags: PrimitiveFlags::empty(),
}
}

View file

@ -46,6 +46,7 @@ impl<'a> ClipScrollState<'a> {
let root_clip_chain =
builder.define_clip_chain(None, [ClipId::root(state.compositor_info.pipeline_id)]);
state.add_clip_node_mapping(0, root_clip_chain);
state.add_clip_node_mapping(1, root_clip_chain);
@ -111,11 +112,7 @@ impl DisplayList {
epoch: Epoch,
) -> (DisplayListBuilder, CompositorDisplayListInfo, IsContentful) {
let webrender_pipeline = pipeline_id.to_webrender();
let mut builder = DisplayListBuilder::with_capacity(
webrender_pipeline,
self.bounds().size,
1024 * 1024, // 1 MB of space
);
let mut builder = DisplayListBuilder::new(webrender_pipeline);
let content_size = self.bounds().size;
let mut state = ClipScrollState::new(
@ -153,17 +150,16 @@ impl DisplayItem {
let internal_clip_id = clip_and_scroll_indices
.clipping
.unwrap_or(clip_and_scroll_indices.scrolling);
let current_clip_id = state.webrender_clip_id_for_index(internal_clip_id.to_index());
let current_clip_chain_id = state.webrender_clip_id_for_index(internal_clip_id.to_index());
let hit_test_bounds = self.bounds().intersection(&self.base().clip_rect);
let build_common_item_properties = |base: &BaseDisplayItem| {
CommonItemProperties {
clip_rect: base.clip_rect,
spatial_id: current_scroll_node_id.spatial_id,
clip_id: ClipId::ClipChain(current_clip_id),
clip_id: ClipId::ClipChain(current_clip_chain_id),
// TODO(gw): Make use of the WR backface visibility functionality.
flags: PrimitiveFlags::default(),
hit_info: None,
}
};
@ -184,10 +180,15 @@ impl DisplayItem {
current_scroll_node_id,
);
let mut common = build_common_item_properties(base);
common.hit_info = Some((hit_test_index as u64, 0u16));
common.clip_rect = bounds;
builder.push_hit_test(&common);
builder.push_hit_test(
&CommonItemProperties {
clip_rect: bounds,
spatial_id: current_scroll_node_id.spatial_id,
clip_id: ClipId::ClipChain(current_clip_chain_id),
flags: PrimitiveFlags::default(),
},
(hit_test_index as u64, 0u16),
);
};
match *self {
@ -299,7 +300,13 @@ impl DisplayItem {
scrolling_relative_to: None,
},
),
(Some(t), None) => (t, ReferenceFrameKind::Transform),
(Some(t), None) => (
t,
ReferenceFrameKind::Transform {
is_2d_scale_translation: false,
should_snap: false,
},
),
(Some(t), Some(p)) => (
p.then(&t),
ReferenceFrameKind::Perspective {
@ -318,7 +325,7 @@ impl DisplayItem {
);
let index = frame_index.to_index();
state.add_clip_node_mapping(index, current_clip_id);
state.add_clip_node_mapping(index, current_clip_chain_id);
state.register_spatial_node(
index,
new_spatial_id,
@ -405,7 +412,7 @@ impl DisplayItem {
let spatial_id = builder
.define_scroll_frame(
&parent_space_and_clip_info,
Some(external_id),
external_id,
node.content_rect,
item_rect,
scroll_sensitivity,

View file

@ -64,8 +64,8 @@ pub(super) fn painting_area<'a>(
};
// The 'backgound-clip' property maps directly to `clip_rect` in `CommonItemProperties`:
let mut common = builder.common_properties(*painting_area, &fb.fragment.style);
if let Some(clip_id) = clip {
common.clip_id = clip_id
if let Some(clip_chain_id) = clip {
common.clip_id = wr::ClipId::ClipChain(clip_chain_id)
}
(painting_area, common)
}

View file

@ -19,7 +19,7 @@ impl ToWebRender for ComputedFilter {
type Type = FilterOp;
fn to_webrender(&self) -> Self::Type {
match *self {
ComputedFilter::Blur(radius) => FilterOp::Blur(radius.px()),
ComputedFilter::Blur(radius) => FilterOp::Blur(radius.px(), radius.px()),
ComputedFilter::Brightness(amount) => FilterOp::Brightness(amount.0),
ComputedFilter::Contrast(amount) => FilterOp::Contrast(amount.0),
ComputedFilter::Grayscale(amount) => FilterOp::Grayscale(amount.0),

View file

@ -26,7 +26,7 @@ use style::values::computed::{BorderStyle, Color, Length, LengthPercentage, Outl
use style::values::specified::text::TextDecorationLine;
use style::values::specified::ui::CursorKind;
use style_traits::CSSPixel;
use webrender_api::{self as wr, units};
use webrender_api::{self as wr, units, ClipChainId, ClipId, CommonItemProperties};
mod background;
mod conversions;
@ -71,7 +71,7 @@ impl DisplayList {
epoch: wr::Epoch,
) -> Self {
Self {
wr: wr::DisplayListBuilder::new(pipeline_id, content_size),
wr: wr::DisplayListBuilder::new(pipeline_id),
compositor_info: CompositorDisplayListInfo::new(
viewport_size,
content_size,
@ -93,7 +93,7 @@ pub(crate) struct DisplayListBuilder<'a> {
/// only passing the builder instead passing the containing
/// [stacking_context::StackingContextFragment] as an argument to display
/// list building functions.
current_clip_id: wr::ClipId,
current_clip_chain_id: ClipChainId,
/// The [OpaqueNode] handle to the node used to paint the page background
/// if the background was a canvas.
@ -126,8 +126,8 @@ impl DisplayList {
root_stacking_context: &StackingContext,
) -> (FnvHashMap<BrowsingContextId, Size2D<f32, CSSPixel>>, bool) {
let mut builder = DisplayListBuilder {
current_scroll_node_id: self.compositor_info.root_scroll_node_id,
current_clip_id: wr::ClipId::root(self.wr.pipeline_id),
current_scroll_node_id: self.compositor_info.root_reference_frame_id,
current_clip_chain_id: ClipChainId(0, self.compositor_info.pipeline_id),
element_for_canvas_background: fragment_tree.canvas_background.from_element,
is_contentful: false,
context,
@ -155,8 +155,7 @@ impl<'a> DisplayListBuilder<'a> {
wr::CommonItemProperties {
clip_rect,
spatial_id: self.current_scroll_node_id.spatial_id,
clip_id: self.current_clip_id,
hit_info: None,
clip_id: ClipId::ClipChain(self.current_clip_chain_id),
flags: style.get_webrender_primitive_flags(),
}
}
@ -281,12 +280,21 @@ impl Fragment {
return;
}
let common = builder.common_properties(rect.to_webrender(), &fragment.parent_style);
let hit_info = builder.hit_info(&fragment.parent_style, fragment.base.tag, Cursor::Text);
let mut hit_test_common = common.clone();
hit_test_common.hit_info = hit_info;
builder.wr().push_hit_test(&hit_test_common);
if let Some(hit_info) =
builder.hit_info(&fragment.parent_style, fragment.base.tag, Cursor::Text)
{
let clip_chain_id = builder.current_clip_chain_id;
let spatial_id = builder.current_scroll_node_id.spatial_id;
builder.wr().push_hit_test(
&CommonItemProperties {
clip_rect: rect.to_webrender(),
clip_id: ClipId::ClipChain(clip_chain_id),
spatial_id,
flags: fragment.parent_style.get_webrender_primitive_flags(),
},
hit_info,
);
}
let color = fragment.parent_style.clone_color();
let font_metrics = &fragment.font_metrics;
@ -318,6 +326,7 @@ impl Fragment {
}
// Text.
let common = builder.common_properties(rect.to_webrender(), &fragment.parent_style);
builder.wr().push_text(
&common,
rect.to_webrender(),
@ -376,9 +385,9 @@ struct BuilderForBoxFragment<'a> {
padding_rect: OnceCell<units::LayoutRect>,
content_rect: OnceCell<units::LayoutRect>,
border_radius: wr::BorderRadius,
border_edge_clip_id: OnceCell<Option<wr::ClipId>>,
padding_edge_clip_id: OnceCell<Option<wr::ClipId>>,
content_edge_clip_id: OnceCell<Option<wr::ClipId>>,
border_edge_clip_chain_id: OnceCell<Option<ClipChainId>>,
padding_edge_clip_chain_id: OnceCell<Option<ClipChainId>>,
content_edge_clip_chain_id: OnceCell<Option<ClipChainId>>,
}
impl<'a> BuilderForBoxFragment<'a> {
@ -433,9 +442,9 @@ impl<'a> BuilderForBoxFragment<'a> {
border_radius,
padding_rect: OnceCell::new(),
content_rect: OnceCell::new(),
border_edge_clip_id: OnceCell::new(),
padding_edge_clip_id: OnceCell::new(),
content_edge_clip_id: OnceCell::new(),
border_edge_clip_chain_id: OnceCell::new(),
padding_edge_clip_chain_id: OnceCell::new(),
content_edge_clip_chain_id: OnceCell::new(),
}
}
@ -459,14 +468,14 @@ impl<'a> BuilderForBoxFragment<'a> {
})
}
fn border_edge_clip(&self, builder: &mut DisplayListBuilder) -> Option<wr::ClipId> {
fn border_edge_clip(&self, builder: &mut DisplayListBuilder) -> Option<ClipChainId> {
*self
.border_edge_clip_id
.border_edge_clip_chain_id
.get_or_init(|| clip_for_radii(self.border_radius, self.border_rect, builder))
}
fn padding_edge_clip(&self, builder: &mut DisplayListBuilder) -> Option<wr::ClipId> {
*self.padding_edge_clip_id.get_or_init(|| {
fn padding_edge_clip(&self, builder: &mut DisplayListBuilder) -> Option<ClipChainId> {
*self.padding_edge_clip_chain_id.get_or_init(|| {
clip_for_radii(
inner_radii(
self.border_radius,
@ -481,8 +490,8 @@ impl<'a> BuilderForBoxFragment<'a> {
})
}
fn content_edge_clip(&self, builder: &mut DisplayListBuilder) -> Option<wr::ClipId> {
*self.content_edge_clip_id.get_or_init(|| {
fn content_edge_clip(&self, builder: &mut DisplayListBuilder) -> Option<ClipChainId> {
*self.content_edge_clip_chain_id.get_or_init(|| {
clip_for_radii(
inner_radii(
self.border_radius,
@ -512,14 +521,16 @@ impl<'a> BuilderForBoxFragment<'a> {
self.fragment.base.tag,
Cursor::Default,
);
if hit_info.is_some() {
let mut common = builder.common_properties(self.border_rect, &self.fragment.style);
common.hit_info = hit_info;
if let Some(clip_id) = self.border_edge_clip(builder) {
common.clip_id = clip_id
}
builder.wr().push_hit_test(&common)
let hit_info = match hit_info {
Some(hit_info) => hit_info,
None => return,
};
let mut common = builder.common_properties(self.border_rect, &self.fragment.style);
if let Some(clip_chain_id) = self.border_edge_clip(builder) {
common.clip_id = ClipId::ClipChain(clip_chain_id);
}
builder.wr().push_hit_test(&common, hit_info);
}
fn build_background(&mut self, builder: &mut DisplayListBuilder) {
@ -868,21 +879,27 @@ fn clip_for_radii(
radii: wr::BorderRadius,
rect: units::LayoutRect,
builder: &mut DisplayListBuilder,
) -> Option<wr::ClipId> {
) -> Option<ClipChainId> {
if radii.is_zero() {
None
} else {
let clip_chain_id = builder.current_clip_chain_id.clone();
let parent_space_and_clip = wr::SpaceAndClipInfo {
spatial_id: builder.current_scroll_node_id.spatial_id,
clip_id: builder.current_clip_id,
clip_id: ClipId::ClipChain(clip_chain_id),
};
Some(builder.wr().define_clip_rounded_rect(
let new_clip_id = builder.wr().define_clip_rounded_rect(
&parent_space_and_clip,
wr::ComplexClipRegion {
rect,
radii,
mode: wr::ClipMode::Clip,
},
))
);
Some(
builder
.wr()
.define_clip_chain(Some(clip_chain_id), [new_clip_id]),
)
}
}

View file

@ -28,6 +28,7 @@ use style::values::generics::transform;
use style::values::specified::box_::DisplayOutside;
use webrender_api as wr;
use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D};
use webrender_api::ScrollSensitivity;
#[derive(Clone)]
pub(crate) struct ContainingBlock {
@ -150,17 +151,16 @@ impl DisplayList {
external_id: wr::ExternalScrollId,
content_rect: LayoutRect,
clip_rect: LayoutRect,
scroll_sensitivity: wr::ScrollSensitivity,
external_scroll_offset: LayoutVector2D,
scroll_sensitivity: ScrollSensitivity,
) -> (ScrollTreeNodeId, wr::ClipChainId) {
let parent_space_and_clip_info = wr::SpaceAndClipInfo {
spatial_id: parent_scroll_node_id.spatial_id,
clip_id: wr::ClipId::root(self.wr.pipeline_id),
};
let new_clip_id = self
.wr
.define_clip_rect(&parent_space_and_clip_info, clip_rect);
let new_clip_chain_id = self
.wr
.define_clip_chain(Some(*parent_clip_chain_id), [new_clip_id]);
@ -169,11 +169,11 @@ impl DisplayList {
.wr
.define_scroll_frame(
&parent_space_and_clip_info,
Some(external_id),
external_id,
content_rect,
clip_rect,
scroll_sensitivity,
external_scroll_offset,
LayoutVector2D::zero(), /* external_scroll_offset */
)
.spatial_id;
@ -202,7 +202,7 @@ pub(crate) struct StackingContextFragment {
impl StackingContextFragment {
fn build_display_list(&self, builder: &mut DisplayListBuilder) {
builder.current_scroll_node_id = self.scroll_node_id;
builder.current_clip_id = wr::ClipId::ClipChain(self.clip_chain_id);
builder.current_clip_chain_id = self.clip_chain_id;
self.fragment
.borrow()
.build_display_list(builder, &self.containing_block, self.section);
@ -964,9 +964,9 @@ impl BoxFragment {
let sensitivity =
if ComputedOverflow::Hidden == overflow_x && ComputedOverflow::Hidden == overflow_y {
wr::ScrollSensitivity::Script
ScrollSensitivity::Script
} else {
wr::ScrollSensitivity::ScriptAndInputEvents
ScrollSensitivity::ScriptAndInputEvents
};
let padding_rect = self
@ -984,7 +984,6 @@ impl BoxFragment {
.to_webrender(),
padding_rect,
sensitivity,
LayoutVector2D::zero(),
),
)
}
@ -1013,7 +1012,13 @@ impl BoxFragment {
scrolling_relative_to: None,
},
),
(Some(transform), None) => (transform, wr::ReferenceFrameKind::Transform),
(Some(transform), None) => (
transform,
wr::ReferenceFrameKind::Transform {
is_2d_scale_translation: false,
should_snap: false,
},
),
(Some(transform), Some(perspective)) => (
perspective.then(&transform),
wr::ReferenceFrameKind::Perspective {

View file

@ -109,7 +109,7 @@ use style::traversal_flags::TraversalFlags;
use style_traits::CSSPixel;
use style_traits::DevicePixel;
use style_traits::SpeculativePainter;
use webrender_api::{units, ColorF, HitTestFlags, ScrollClamping};
use webrender_api::{units, ColorF, HitTestFlags};
/// Information needed by the layout thread.
pub struct LayoutThread {
@ -1073,7 +1073,7 @@ impl LayoutThread {
.maybe_observe_paint_time(self, epoch, is_contentful.0);
self.webrender_api
.send_display_list(compositor_info, builder.finalize());
.send_display_list(compositor_info, builder.finalize().1);
},
);
}
@ -1508,11 +1508,8 @@ impl LayoutThread {
.insert(state.scroll_id, state.scroll_offset);
let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y);
self.webrender_api.send_scroll_node(
units::LayoutPoint::from_untyped(point),
state.scroll_id,
ScrollClamping::ToContentBounds,
);
self.webrender_api
.send_scroll_node(units::LayoutPoint::from_untyped(point), state.scroll_id);
}
fn set_scroll_states<'a, 'b>(

View file

@ -95,7 +95,7 @@ use style::traversal_flags::TraversalFlags;
use style_traits::CSSPixel;
use style_traits::DevicePixel;
use style_traits::SpeculativePainter;
use webrender_api::{units, HitTestFlags, ScrollClamping};
use webrender_api::{units, HitTestFlags};
/// Information needed by the layout thread.
pub struct LayoutThread {
@ -1173,11 +1173,8 @@ impl LayoutThread {
.insert(state.scroll_id, state.scroll_offset);
let point = Point2D::new(-state.scroll_offset.x, -state.scroll_offset.y);
self.webrender_api.send_scroll_node(
units::LayoutPoint::from_untyped(point),
state.scroll_id,
ScrollClamping::ToContentBounds,
);
self.webrender_api
.send_scroll_node(units::LayoutPoint::from_untyped(point), state.scroll_id);
}
fn set_scroll_states<'a, 'b>(
@ -1284,7 +1281,7 @@ impl LayoutThread {
.maybe_observe_paint_time(self, epoch, is_contentful);
self.webrender_api
.send_display_list(display_list.compositor_info, display_list.wr.finalize());
.send_display_list(display_list.compositor_info, display_list.wr.finalize().1);
self.update_iframe_sizes(iframe_sizes);

View file

@ -62,7 +62,7 @@ use webrender_api::{
BorderRadius, BorderStyle, BoxShadowClipMode, ColorF, ComplexClipRegion, ExtendMode,
ExternalScrollId, FilterOp, FontInstanceKey, GlyphInstance, GradientStop, ImageKey,
ImageRendering, LineStyle, MixBlendMode, NinePatchBorder, NormalBorder, RepeatMode,
ScrollSensitivity, StickyOffsetBounds, TransformStyle,
StickyOffsetBounds, TransformStyle,
};
/// A C function that takes a pointer to a heap allocation and returns its size.
@ -844,8 +844,6 @@ malloc_size_of_is_0!(NormalBorder);
#[cfg(feature = "webrender_api")]
malloc_size_of_is_0!(RepeatMode);
#[cfg(feature = "webrender_api")]
malloc_size_of_is_0!(ScrollSensitivity);
#[cfg(feature = "webrender_api")]
malloc_size_of_is_0!(StickyOffsetBounds);
#[cfg(feature = "webrender_api")]
malloc_size_of_is_0!(TransformStyle);

View file

@ -96,7 +96,7 @@ use std::rc::Rc;
use std::sync::{Arc, Mutex};
use time::{self, Duration, Timespec};
use webrender_api::ImageKey;
use webrender_api::{ExternalImageData, ExternalImageId, ExternalImageType, TextureTarget};
use webrender_api::{ExternalImageData, ExternalImageId, ExternalImageType, ImageBufferKind};
use webrender_api::{ImageData, ImageDescriptor, ImageDescriptorFlags, ImageFormat};
#[derive(PartialEq)]
@ -225,9 +225,9 @@ impl VideoFrameRenderer for MediaFrameRenderer {
let image_data = if frame.is_gl_texture() && self.player_id.is_some() {
let texture_target = if frame.is_external_oes() {
TextureTarget::External
ImageBufferKind::TextureExternal
} else {
TextureTarget::Default
ImageBufferKind::Texture2D
};
ImageData::External(ExternalImageData {
@ -254,9 +254,9 @@ impl VideoFrameRenderer for MediaFrameRenderer {
let image_data = if frame.is_gl_texture() && self.player_id.is_some() {
let texture_target = if frame.is_external_oes() {
TextureTarget::External
ImageBufferKind::TextureExternal
} else {
TextureTarget::Default
ImageBufferKind::Texture2D
};
ImageData::External(ExternalImageData {

View file

@ -220,6 +220,9 @@ pub struct CompositorDisplayListInfo {
/// The size of the viewport that this display list renders into.
pub viewport_size: LayoutSize,
/// The size of this display list's content.
pub content_size: LayoutSize,
/// The epoch of the display list.
pub epoch: Epoch,
@ -270,6 +273,7 @@ impl CompositorDisplayListInfo {
CompositorDisplayListInfo {
pipeline_id,
viewport_size,
content_size,
epoch,
hit_test_info: Default::default(),
scroll_tree,

View file

@ -69,13 +69,10 @@ use std::sync::Arc;
use style_traits::CSSPixel;
use style_traits::SpeculativePainter;
use webgpu::identity::WebGPUMsg;
use webrender_api::units::{
DeviceIntSize, DevicePixel, LayoutPixel, LayoutPoint, LayoutSize, WorldPoint,
};
use webrender_api::units::{DeviceIntSize, DevicePixel, LayoutPixel, LayoutPoint, WorldPoint};
use webrender_api::{
BuiltDisplayList, BuiltDisplayListDescriptor, DocumentId, ExternalImageData, ExternalScrollId,
HitTestFlags, ImageData, ImageDescriptor, ImageKey, PipelineId as WebRenderPipelineId,
ScrollClamping,
};
/// The address of a node. Layout sends these back. They must be validated via
@ -1125,13 +1122,11 @@ pub enum WebrenderMsg {
/// Inform WebRender of the existence of this pipeline.
SendInitialTransaction(WebRenderPipelineId),
/// Perform a scroll operation.
SendScrollNode(LayoutPoint, ExternalScrollId, ScrollClamping),
SendScrollNode(LayoutPoint, ExternalScrollId),
/// Inform WebRender of a new display list for the given pipeline.
SendDisplayList {
/// The [CompositorDisplayListInfo] that describes the display list being sent.
display_list_info: CompositorDisplayListInfo,
/// The content size of this display list as calculated by WebRender.
content_size: LayoutSize,
/// A descriptor of this display list used to construct this display list from raw data.
display_list_descriptor: BuiltDisplayListDescriptor,
/// An [ipc::IpcBytesReceiver] used to send the raw data of the display list.
@ -1170,16 +1165,8 @@ impl WebrenderIpcSender {
}
/// Perform a scroll operation.
pub fn send_scroll_node(
&self,
point: LayoutPoint,
scroll_id: ExternalScrollId,
clamping: ScrollClamping,
) {
if let Err(e) = self
.0
.send(WebrenderMsg::SendScrollNode(point, scroll_id, clamping))
{
pub fn send_scroll_node(&self, point: LayoutPoint, scroll_id: ExternalScrollId) {
if let Err(e) = self.0.send(WebrenderMsg::SendScrollNode(point, scroll_id)) {
warn!("Error sending scroll node: {}", e);
}
}
@ -1188,13 +1175,12 @@ impl WebrenderIpcSender {
pub fn send_display_list(
&self,
display_list_info: CompositorDisplayListInfo,
(_, content_size, list): (WebRenderPipelineId, LayoutSize, BuiltDisplayList),
list: BuiltDisplayList,
) {
let (display_list_data, display_list_descriptor) = list.into_data();
let (display_list_sender, display_list_receiver) = ipc::bytes_channel().unwrap();
if let Err(e) = self.0.send(WebrenderMsg::SendDisplayList {
display_list_info,
content_size,
display_list_descriptor,
display_list_receiver,
}) {

View file

@ -29,11 +29,14 @@ pub use devtools_traits;
pub use embedder_traits;
pub use euclid;
pub use gfx;
pub use gleam::gl;
pub use ipc_channel;
pub use keyboard_types;
pub use layout_thread_2013;
pub use layout_thread_2020;
pub use media;
pub use msg;
pub use msg::constellation_msg::TopLevelBrowsingContextId as BrowserId;
pub use net;
pub use net_traits;
pub use profile;
@ -41,25 +44,18 @@ pub use profile_traits;
pub use script;
pub use script_layout_interface;
pub use script_traits;
pub use servo_config as config;
pub use servo_config;
pub use servo_geometry;
pub use servo_url as url;
pub use servo_url;
pub use style;
pub use style_traits;
pub use webgpu;
pub use webrender_api;
use webrender_api::{DocumentId, FontInstanceKey, FontKey, ImageKey, RenderApiSender};
pub use webrender_surfman;
pub use webrender_traits;
#[cfg(feature = "webdriver")]
fn webdriver(port: u16, constellation: Sender<ConstellationMsg>) {
webdriver_server::start_server(port, constellation);
}
#[cfg(not(feature = "webdriver"))]
fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) {}
use bluetooth::BluetoothThreadFactory;
use bluetooth_traits::BluetoothRequest;
use canvas::canvas_paint_thread::{self, CanvasPaintThread};
@ -84,7 +80,7 @@ use constellation::{FromCompositorLogger, FromScriptLogger};
use crossbeam_channel::{unbounded, Sender};
use embedder_traits::{EmbedderMsg, EmbedderProxy, EmbedderReceiver, EventLoopWaker};
use env_logger::Builder as EnvLoggerBuilder;
use euclid::{Scale, Size2D};
use euclid::Scale;
#[cfg(all(
not(target_os = "windows"),
not(target_os = "ios"),
@ -119,16 +115,19 @@ use std::rc::Rc;
use std::sync::Arc;
use std::sync::Mutex;
use surfman::GLApi;
use webrender::ShaderPrecacheFlags;
use webrender::{RenderApiSender, ShaderPrecacheFlags};
use webrender_api::{DocumentId, FontInstanceKey, FontKey, ImageKey};
use webrender_traits::WebrenderExternalImageHandlers;
use webrender_traits::WebrenderExternalImageRegistry;
use webrender_traits::WebrenderImageHandlerType;
pub use gleam::gl;
pub use keyboard_types;
pub use msg::constellation_msg::TopLevelBrowsingContextId as BrowserId;
pub use servo_config as config;
pub use servo_url as url;
#[cfg(feature = "webdriver")]
fn webdriver(port: u16, constellation: Sender<ConstellationMsg>) {
webdriver_server::start_server(port, constellation);
}
#[cfg(not(feature = "webdriver"))]
fn webdriver(_port: u16, _constellation: Sender<ConstellationMsg>) {}
#[cfg(feature = "media-gstreamer")]
mod media_platform {
@ -217,9 +216,11 @@ impl webrender_api::RenderNotifier for RenderNotifier {
Box::new(RenderNotifier::new(self.compositor_proxy.clone()))
}
fn wake_up(&self) {
self.compositor_proxy
.recomposite(CompositingReason::NewWebRenderFrame);
fn wake_up(&self, composite_needed: bool) {
if composite_needed {
self.compositor_proxy
.recomposite(CompositingReason::NewWebRenderFrame);
}
}
fn new_frame_ready(
@ -233,7 +234,7 @@ impl webrender_api::RenderNotifier for RenderNotifier {
self.compositor_proxy
.send(CompositorMsg::NewScrollFrameReady(composite_needed));
} else {
self.wake_up();
self.wake_up(true);
}
}
}
@ -333,7 +334,7 @@ where
None
};
let coordinates = window.get_coordinates();
let coordinates: compositing::windowing::EmbedderCoordinates = window.get_coordinates();
let device_pixel_ratio = coordinates.hidpi_factor.get();
let viewport_size = coordinates.viewport.size.to_f32() / device_pixel_ratio;
@ -345,10 +346,6 @@ where
);
let render_notifier = Box::new(RenderNotifier::new(compositor_proxy.clone()));
// Cast from `DeviceIndependentPixel` to `DevicePixel`
let window_size = Size2D::from_untyped(viewport_size.to_i32().to_untyped());
webrender::Renderer::new(
webrender_gl.clone(),
render_notifier,
@ -369,20 +366,12 @@ where
..Default::default()
},
None,
window_size,
)
.expect("Unable to initialize webrender!")
};
let webrender_api = webrender_api_sender.create_api();
let wr_document_layer = 0; //TODO
let webrender_document =
webrender_api.add_document(coordinates.framebuffer, wr_document_layer);
webrender_api.set_document_view(
webrender_document,
coordinates.get_viewport(),
coordinates.hidpi_factor.get(),
);
let webrender_document = webrender_api.add_document(coordinates.get_viewport().size);
// Important that this call is done in a single-threaded fashion, we
// can't defer it after `create_constellation` has started.
@ -575,8 +564,9 @@ where
self.compositor.on_wheel_event(delta, location);
},
EmbedderEvent::Scroll(delta, cursor, phase) => {
self.compositor.on_scroll_event(delta, cursor, phase);
EmbedderEvent::Scroll(scroll_location, cursor, phase) => {
self.compositor
.on_scroll_event(scroll_location, cursor, phase);
},
EmbedderEvent::Zoom(magnification) => {

View file

@ -20,6 +20,7 @@ msg = { path = "../msg" }
serde = { workspace = true, features = ["serde_derive"] }
servo_config = { path = "../config" }
smallvec = { workspace = true, features = ["serde"] }
webrender = { workspace = true }
webrender_api = { workspace = true }
webrender_traits = { path = "../webrender_traits" }
wgpu-core = { version = "0.17", features = ["replay", "trace", "serial-pass", "wgsl"] }

View file

@ -26,10 +26,8 @@ use std::rc::Rc;
use std::slice;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use webrender_api::{
DirtyRect, DocumentId, ExternalImageId, ImageData, ImageDescriptor, ImageKey, RenderApi,
RenderApiSender, Transaction,
};
use webrender::{RenderApi, RenderApiSender, Transaction};
use webrender_api::{DirtyRect, DocumentId, ExternalImageId, ImageData, ImageDescriptor, ImageKey};
use webrender_traits::{
WebrenderExternalImageApi, WebrenderExternalImageRegistry, WebrenderImageHandlerType,
WebrenderImageSource,

View file

@ -51,7 +51,7 @@ egui_glow = { version = "0.22.0", features = ["winit"] }
egui-winit = { version = "0.22.0", default-features = false, features = ["clipboard", "wayland"] }
euclid = { workspace = true }
getopts = { workspace = true }
gleam = "0.12"
gleam = { workspace = true }
glow = "0.12.2"
keyboard-types = { workspace = true }
lazy_static = { workspace = true }

View file

@ -543,7 +543,9 @@ impl WindowMethods for Window {
let inner_size = Size2D::new(width as f32, height as f32) * dpr;
let viewport_size = inner_size - Size2D::new(0f32, toolbar_height);
let viewport_origin = DeviceIntPoint::zero(); // bottom left
let viewport = DeviceIntRect::new(viewport_origin, viewport_size.to_i32());
let viewport = DeviceIntRect::new(
viewport_origin, viewport_size.to_i32()
);
let framebuffer = DeviceIntSize::from_untyped(viewport.size.to_untyped());
EmbedderCoordinates {

View file

@ -1,2 +0,0 @@
[background-attachment-fixed-inline-scrolled.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[background-size-041.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[background-size-042.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[position-sticky-escape-scroller-002.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[position-sticky-escape-scroller-004.html]
expected: FAIL

View file

@ -1,4 +0,0 @@
[elementFromPoint-mixed-font-sizes.html]
[document.elementFromPoint finds container SPAN in the empty region above a child SPAN with a smaller font size]
expected: FAIL

View file

@ -1,2 +0,0 @@
[background-attachment-fixed-inline-scrolled.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[background-size-041.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[background-size-042.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[flexbox-mbp-horiz-003-reverse.xhtml]
expected: FAIL

View file

@ -0,0 +1,2 @@
[flexbox-mbp-horiz-003.xhtml]
expected: FAIL

View file

@ -0,0 +1,2 @@
[flexbox-mbp-horiz-003v.xhtml]
expected: FAIL

View file

@ -0,0 +1,2 @@
[position-sticky-escape-scroller-002.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[perspective-split-by-zero-w.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[filtered-html-is-not-container.html]
expected: FAIL

View file

@ -1,2 +1,2 @@
[border-radius-image.html]
fuzzy: /_mozilla/css/border-radius-image-ref.html:63;696
expected: FAIL

View file

@ -1,2 +0,0 @@
[pixel_snapping_position_a.html]
expected: FAIL

View file

@ -1,3 +0,0 @@
[opacity-background-1.html]
expected:
if os == "linux": FAIL

View file

@ -0,0 +1,2 @@
[scroll_root.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[pixel_snapping_position_a.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[opacity-background-1.html]
expected: FAIL

View file

@ -0,0 +1,3 @@
[scrollBy.html]
[Ensure that the window.scrollBy function affects scroll position as expected]
expected: FAIL

View file

@ -0,0 +1,2 @@
[scroll_root.html]
expected: FAIL

1752
third_party/webrender/Cargo.lock generated vendored

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,5 @@
[workspace]
members = [
"direct-composition",
"examples",
"webrender",
"webrender_api",
@ -16,6 +15,13 @@ panic = "abort"
[profile.dev]
panic = "abort"
# optimizing glsl more makes a big difference in swgl build times
[profile.dev.package.glsl]
opt-level = 2
[profile.release.package.glsl]
opt-level = 2
# Running wrench on android built with master cargo-apk results in a crash
# due to a mismatched version of android_glue (a dependency of winit).
# Override it to use a suitable version of android_glue.
@ -23,3 +29,5 @@ panic = "abort"
# This can be removed once a new version of android_glue is published to crates.io.
[patch.crates-io]
android_glue = { git = "https://github.com/rust-windowing/android-rs-glue.git", rev = "e3ac6edea5814e1faca0c31ea8fac6877cb929ea" }
# this is a version that fixes some incompatibilites with newer rust/aarch64
winit = { version = "0.19", git = "https://github.com/jrmuizel/winit", branch="wr" }

View file

@ -14,15 +14,19 @@ test "$(whoami)" == 'root'
# Install stuff we need
apt-get -y update
apt-get install -y \
bison \
bzip2 \
cmake \
curl \
flex \
gcc \
git \
g++ \
libfontconfig1-dev \
libgl1-mesa-dev \
libx11-dev \
llvm-dev \
ninja-build \
openjdk-8-jdk \
pkg-config \
python \
@ -31,7 +35,10 @@ apt-get install -y \
python-setuptools \
python-voluptuous \
python-yaml \
software-properties-common
python3-pip \
python3-mako \
software-properties-common \
clang
# Other stuff we need
pip install servo-tidy==0.3.0

View file

@ -0,0 +1,18 @@
[binaries]
llvm-config = '/builds/worker/fetches/clang/bin/llvm-config'
[properties]
# When linking `libOSMesa.dylib` Meson uses options provided by `llvm-config`.
# The binary for `llvm-config` in Firefox CI comes from a native Linux clang,
# which gives the link options for the Linux libLLVM-11.so in the Linux clang.
# However, we want to link against a native macOS clang's libLLVM.dylib, which
# we have available in a separate directory.
# Meson will still have -lLLVM-11 on the command line, but the linker will
# only warn that it has the wrong format (because it's not for macOS).
cpp_link_args = ['-L/builds/worker/fetches/clang-mac/clang/lib', '-lLLVM']
[host_machine]
system = 'darwin'
cpu_family = 'x86_64'
cpu = 'i686'
endian = 'little'

View file

@ -0,0 +1,22 @@
#!/usr/bin/env bash
# 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/. */
# This file downloads and installs meson which is required for building
# osmesa-src, a dependency of wrench.
set -o errexit
set -o nounset
set -o pipefail
set -o xtrace
MESON_VER=0.55.1
MESON_BASE_URL="https://github.com/mesonbuild/meson/releases/download"
curl -L ${MESON_BASE_URL}/${MESON_VER}/meson-${MESON_VER}.tar.gz -o meson.tar.gz
tar -xf meson.tar.gz
mv meson-${MESON_VER} meson
cd meson
ln -s meson.py meson

View file

@ -32,5 +32,5 @@ cargo build ${CARGOFLAGS}
popd
cargo test ${CARGOFLAGS} \
--all --exclude compositor-windows --exclude compositor \
--exclude glsl-to-cxx --exclude swgl
--all --exclude compositor --exclude compositor-wayland \
--exclude compositor-windows --exclude glsl-to-cxx --exclude swgl

View file

@ -16,7 +16,13 @@ set -o xtrace
CARGOFLAGS=${CARGOFLAGS:-""} # default to empty if not set
pushd wrench
# Test that all shaders compile successfully.
python script/headless.py --precache test_init
python script/headless.py --precache --use-unoptimized-shaders test_init
python script/headless.py reftest
python script/headless.py rawtest
cargo build ${CARGOFLAGS} --release
python script/headless.py test_invalidation
CXX=clang++ cargo run ${CARGOFLAGS} --release --features=software -- \
--software --headless reftest
popd

View file

@ -38,5 +38,5 @@ cargo check ${CARGOFLAGS}
popd
cargo test ${CARGOFLAGS} ${CARGOTESTFLAGS} \
--all --exclude compositor-windows --exclude compositor \
--exclude glsl-to-cxx --exclude swgl
--all --exclude compositor --exclude compositor-wayland \
--exclude compositor-windows --exclude glsl-to-cxx --exclude swgl

View file

@ -19,7 +19,13 @@ CARGOFLAGS=${CARGOFLAGS:-""} # default to empty if not set
WRENCH_BINARY=${WRENCH_BINARY:-""}
pushd wrench
# Test that all shaders compile successfully.
python script/headless.py --precache test_init
python script/headless.py --precache --use-unoptimized-shaders test_init
python script/headless.py reftest
python script/headless.py test_invalidation
if [[ -z "${WRENCH_BINARY}" ]]; then
cargo build ${CARGOFLAGS} --release
WRENCH_BINARY="../target/release/wrench"

View file

@ -21,6 +21,13 @@ popd
pushd wrench
cargo test --verbose
if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%
:: Test that all shaders compile successfully. --precache compiles all shaders
:: during initialization, therefore if init is successful then the shaders compile.
cargo run --release -- --angle --precache test_init
if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%
cargo run --release -- --angle --precache --use-unoptimized-shaders test_init
if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%
cargo run --release -- --angle reftest
if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%
popd
@ -29,8 +36,3 @@ pushd examples
cargo check --verbose
if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%
popd
pushd direct-composition
cargo check --verbose
if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL%
popd

View file

@ -1,6 +0,0 @@
{
"presets": [
["env", { "modules": false }],
"stage-3"
]
}

View file

@ -1,11 +0,0 @@
.DS_Store
node_modules/
npm-debug.log
yarn-error.log
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln

View file

@ -1,23 +0,0 @@
# WebRender Debugger
A web based debugger for WebRender.
## Using the debugger
Build your application with the debugger feature enabled, for example in wrench:
```
cargo build --features=debugger
```
Now, open your browser and open the debugger/index.html file. Click Connect and
the debugger will attempt to connect to WR via websocket.
## Using the debugger with Gecko
In the Gecko source tree, open ```gfx/webrender_bindings/Cargo.toml``` in a text editor.
Add ```features = ['debugger']``` to the end of the file (in the ```dependencies.webrender``` section).
Vendor the rust dependencies locally for the debugger (we don't want these committed to the repo):
```./mach vendor rust```
Now, build and run as usual, and the debugger will be available.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,11 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>WebRender Debugger</title>
</head>
<body>
<div id="app"></div>
<script src="dist/build.js"></script>
</body>
</html>

File diff suppressed because it is too large Load diff

View file

@ -1,36 +0,0 @@
{
"name": "debugger",
"description": "WebRender Debugger",
"version": "1.0.0",
"author": "Glenn Watson <github@intuitionlibrary.com>",
"license": "MIT",
"private": true,
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
},
"dependencies": {
"buefy": "^0.6.7",
"vue": "^2.5.11",
"vue-material-design-icons": "^0.8.2",
"vuex": "^3.0.1"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
],
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.0",
"babel-preset-stage-3": "^6.24.1",
"cross-env": "^5.0.5",
"css-loader": "^0.28.7",
"file-loader": "^1.1.4",
"vue-loader": "^13.0.5",
"vue-template-compiler": "^2.4.4",
"webpack": "^3.6.0",
"webpack-dev-server": "^2.9.1"
}
}

View file

@ -1,55 +0,0 @@
<template>
<div>
<app-navbar></app-navbar>
<div class="section">
<div class="container">
<div class="columns">
<div class="column is-3">
<app-navmenu></app-navmenu>
</div>
<div class="column">
<app-options v-if="page == 'options'"></app-options>
<app-passview v-if="page == 'passes'"></app-passview>
<app-rendertaskview v-if="page == 'render_tasks'"></app-rendertaskview>
<app-documentview v-if="page == 'documents'"></app-documentview>
<app-clipscrolltreeview v-if="page == 'clip_scroll_tree'"></app-clipscrolltreeview>
<app-screenshotview v-if="page == 'screenshot'"></app-screenshotview>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import NavBar from './components/NavBar.vue'
import NavMenu from './components/NavMenu.vue'
import OptionsPage from './components/OptionsPage.vue'
import PassViewPage from './components/PassViewPage.vue'
import RenderTaskViewPage from './components/RenderTaskViewPage.vue'
import DocumentViewPage from './components/DocumentViewPage.vue'
import ClipScrollTreeViewPage from './components/ClipScrollTreeViewPage.vue'
import ScreenshotPage from './components/ScreenshotPage.vue'
export default {
name: 'app',
components: {
'app-navbar': NavBar,
'app-navmenu': NavMenu,
'app-options': OptionsPage,
'app-passview': PassViewPage,
'app-rendertaskview': RenderTaskViewPage,
'app-documentview': DocumentViewPage,
'app-clipscrolltreeview': ClipScrollTreeViewPage,
'app-screenshotview': ScreenshotPage,
},
computed: {
page() {
return this.$store.state.page;
}
},
}
</script>
<style>
</style>

View file

@ -1,37 +0,0 @@
<template>
<div class="box">
<h1 class="title">Clip-Scroll Tree <a :disabled="disabled" v-on:click="fetch" class="button is-info">Refresh</a></h1>
<hr/>
<div>
<ul>
<app-treeview :model=clip_scroll_tree></app-treeview>
</ul>
</div>
</div>
</template>
<script>
import TreeView from './TreeView.vue'
export default {
components: {
'app-treeview': TreeView,
},
methods: {
fetch: function() {
this.$store.dispatch('sendMessage', "fetch_clip_scroll_tree");
}
},
computed: {
disabled() {
return !this.$store.state.connected
},
clip_scroll_tree() {
return this.$store.state.clip_scroll_tree
}
},
}
</script>
<style>
</style>

View file

@ -1,37 +0,0 @@
<template>
<div class="box">
<h1 class="title">Documents <a :disabled="disabled" v-on:click="fetch" class="button is-info">Refresh</a></h1>
<hr/>
<div>
<ul>
<app-treeview :model=documents></app-treeview>
</ul>
</div>
</div>
</template>
<script>
import TreeView from './TreeView.vue'
export default {
components: {
'app-treeview': TreeView,
},
methods: {
fetch: function() {
this.$store.dispatch('sendMessage', "fetch_documents");
}
},
computed: {
disabled() {
return !this.$store.state.connected
},
documents() {
return this.$store.state.documents
}
},
}
</script>
<style>
</style>

View file

@ -1,41 +0,0 @@
<template>
<nav class="navbar has-shadow">
<div class="navbar-brand">
<a class="navbar-item" href="#">WebRender Debugger</a>
</div>
<div class="navbar-menu">
<div class="navbar-start"></div>
<div class="navbar-end">
<div class="navbar-item">
<p class="control">
<button v-if="isConnected" @click="disconnect" class="button is-danger">Disconnect</button>
<button v-else @click="connect" class="button is-success">Connect</button>
</p>
</div>
</div>
</div>
</nav>
</template>
<script>
export default {
computed: {
isConnected() {
return this.$store.state.connected;
},
},
methods: {
connect() {
this.$store.dispatch('connect');
},
disconnect() {
this.$store.dispatch('disconnect');
},
}
}
</script>
<style>
</style>

View file

@ -1,33 +0,0 @@
<template>
<aside class="menu">
<p class="menu-label">
Pages
</p>
<ul class="menu-list">
<li><a @click="setPage('options')" :class="{ 'is-active': page == 'options' }">Debug Options</a></li>
<li><a @click="setPage('passes')" :class="{ 'is-active': page == 'passes' }">Passes</a></li>
<li><a @click="setPage('render_tasks')" :class="{ 'is-active': page == 'render_tasks' }">Render Tasks</a></li>
<li><a @click="setPage('documents')" :class="{ 'is-active': page == 'documents' }">Documents</a></li>
<li><a @click="setPage('clip_scroll_tree')" v-bind:class="{ 'is-active': page == 'clip_scroll_tree' }">Clip-Scroll Tree</a></li>
<li><a @click="setPage('screenshot')" v-bind:class="{ 'is-active': page == 'screenshot' }">Screenshot</a></li>
</ul>
</aside>
</template>
<script>
export default {
methods: {
setPage(name) {
this.$store.commit('setPage', name);
},
},
computed: {
page() {
return this.$store.state.page;
}
},
}
</script>
<style>
</style>

View file

@ -1,162 +0,0 @@
<template>
<div class="box">
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setProfiler($event.target.checked)">
Profiler
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setTextureCacheDebugger($event.target.checked)">
Texture cache debugger
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setRenderTargetDebugger($event.target.checked)">
Render target debugger
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setAlphaRectsDebugger($event.target.checked)">
Alpha primitive rects debugger
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setGpuTimeQueries($event.target.checked)">
Enable GPU time queries
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setGpuSampleQueries($event.target.checked)">
Enable GPU sample queries
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setOpaquePass(!$event.target.checked)">
Disable opaque pass
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setAlphaPass(!$event.target.checked)">
Disable alpha pass
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setClipMasks(!$event.target.checked)">
Disable clip masks
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setTextPrims(!$event.target.checked)">
Disable text primitives
</label>
</div>
<div class="field">
<label class="checkbox">
<input type="checkbox" :disabled="disabled" v-on:click="setGradientPrims(!$event.target.checked)">
Disable gradient primitives
</label>
</div>
</div>
</template>
<script>
export default {
computed: {
disabled() {
return !this.$store.state.connected
}
},
methods: {
setProfiler(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_profiler");
} else {
this.$store.dispatch('sendMessage', "disable_profiler");
}
},
setTextureCacheDebugger(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_texture_cache_debug");
} else {
this.$store.dispatch('sendMessage', "disable_texture_cache_debug");
}
},
setRenderTargetDebugger(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_render_target_debug");
} else {
this.$store.dispatch('sendMessage', "disable_render_target_debug");
}
},
setAlphaRectsDebugger(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_alpha_rects_debug");
} else {
this.$store.dispatch('sendMessage', "disable_alpha_rects_debug");
}
},
setGpuTimeQueries(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_gpu_time_queries");
} else {
this.$store.dispatch('sendMessage', "disable_gpu_time_queries");
}
},
setGpuSampleQueries(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_gpu_sample_queries");
} else {
this.$store.dispatch('sendMessage', "disable_gpu_sample_queries");
}
},
setOpaquePass(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_opaque_pass");
} else {
this.$store.dispatch('sendMessage', "disable_opaque_pass");
}
},
setAlphaPass(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_alpha_pass");
} else {
this.$store.dispatch('sendMessage', "disable_alpha_pass");
}
},
setClipMasks(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_clip_masks");
} else {
this.$store.dispatch('sendMessage', "disable_clip_masks");
}
},
setTextPrims(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_text_prims");
} else {
this.$store.dispatch('sendMessage', "disable_text_prims");
}
},
setGradientPrims(enabled) {
if (enabled) {
this.$store.dispatch('sendMessage', "enable_gradient_prims");
} else {
this.$store.dispatch('sendMessage', "disable_gradient_prims");
}
}
},
}
</script>
<style>
</style>

View file

@ -1,37 +0,0 @@
<template>
<div class="box">
<h1 class="title">Passes <a :disabled="disabled" v-on:click="fetch" class="button is-info">Refresh</a></h1>
<hr/>
<div v-for="(pass, pass_index) in passes">
<p class="has-text-black-bis">Pass {{pass_index}}</p>
<div v-for="(target, target_index) in pass.targets">
<p style="text-indent: 2em;" class="has-text-grey-dark">Target {{target_index}} ({{target.kind}})</p>
<div v-for="(batch, batch_index) in target.batches">
<p style="text-indent: 4em;" class="has-text-grey">Batch {{batch_index}} ({{batch.description}}, {{batch.kind}}, {{batch.count}} instances)</p>
</div>
</div>
<hr/>
</div>
</div>
</template>
<script>
export default {
methods: {
fetch: function() {
this.$store.dispatch('sendMessage', "fetch_passes");
}
},
computed: {
disabled() {
return !this.$store.state.connected
},
passes() {
return this.$store.state.passes
}
},
}
</script>
<style>
</style>

View file

@ -1,37 +0,0 @@
<template>
<div class="box">
<h1 class="title">Render Tasks <a :disabled="disabled" v-on:click="fetch" class="button is-info">Refresh</a></h1>
<hr/>
<div>
<ul>
<app-treeview :model=render_tasks></app-treeview>
</ul>
</div>
</div>
</template>
<script>
import TreeView from './TreeView.vue'
export default {
components: {
'app-treeview': TreeView,
},
methods: {
fetch: function() {
this.$store.dispatch('sendMessage', "fetch_render_tasks");
}
},
computed: {
disabled() {
return !this.$store.state.connected
},
render_tasks() {
return this.$store.state.render_tasks
}
},
}
</script>
<style>
</style>

View file

@ -1,32 +0,0 @@
<template>
<div class="box">
<h1 class="title">Screenshot <a :disabled="disabled" v-on:click="fetch" class="button is-info">Refresh</a></h1>
<hr/>
<div>
<ul>
<img v-if="screenshot.length > 0" style="transform: scaleY(-1); width: 1024px; height:768px" :src="'data:image/png;base64,' + screenshot" />
</ul>
</div>
</div>
</template>
<script>
export default {
computed: {
disabled() {
return !this.$store.state.connected
},
screenshot() {
return this.$store.state.screenshot
},
},
methods: {
fetch: function() {
this.$store.dispatch('sendMessage', "fetch_screenshot");
}
},
}
</script>
<style>
</style>

View file

@ -1,40 +0,0 @@
<template>
<li>
<div v-on:click="toggle">
<span v-if="isFolder">[{{open ? '-' : '+'}}]</span>
{{model.description}}
</div>
<ul style="padding-left: 1em; line-height: 1.5em;" v-show="open" v-if="isFolder">
<treeview v-for="model in model.children" :model="model"></treeview>
</ul>
</li>
</template>
<script>
export default {
name: 'treeview',
props: [
'model',
],
data: function () {
return {
open: false
}
},
computed: {
isFolder: function () {
return this.model.children && this.model.children.length
}
},
methods: {
toggle: function () {
if (this.isFolder) {
this.open = !this.open
}
},
},
}
</script>
<style>
</style>

View file

@ -1,14 +0,0 @@
import Vue from 'vue';
import Buefy from 'buefy';
import 'buefy/dist/buefy.css';
import "vue-material-design-icons/styles.css";
import App from './App.vue';
import store from './store';
Vue.use(Buefy);
new Vue({
el: '#app',
store,
render: h => h(App)
})

View file

@ -1,105 +0,0 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
class Connection {
constructor() {
this.ws = null;
}
connect(context) {
var ws = new WebSocket("ws://127.0.0.1:3583");
ws.onopen = function() {
context.commit('setConnected', true);
}
ws.onmessage = function(evt) {
var json = JSON.parse(evt.data);
if (json['kind'] == "passes") {
context.commit('setPasses', json['passes']);
} else if (json['kind'] == "render_tasks") {
context.commit('setRenderTasks', json['root']);
} else if (json['kind'] == "documents") {
context.commit('setDocuments', json['root']);
} else if (json['kind'] == "clip_scroll_tree") {
context.commit('setClipScrollTree', json['root']);
} else if (json['kind'] == "screenshot") {
context.commit('setScreenshot', json['data']);
} else {
console.warn("unknown message kind: " + json['kind']);
}
}
ws.onclose = function() {
context.commit('setConnected', false);
}
this.ws = ws;
}
send(msg) {
if (this.ws !== null) {
this.ws.send(msg);
}
}
disconnect() {
if (this.ws !== null) {
this.ws.close();
this.ws = null;
}
}
}
var connection = new Connection();
const store = new Vuex.Store({
strict: true,
state: {
connected: false,
page: 'options',
passes: [],
render_tasks: [],
documents: [],
clip_scroll_tree: [],
screenshot: [],
},
mutations: {
setConnected(state, connected) {
state.connected = connected;
},
setPage(state, name) {
state.page = name;
},
setPasses(state, passes) {
state.passes = passes;
},
setRenderTasks(state, render_tasks) {
state.render_tasks = render_tasks;
},
setDocuments(state, documents) {
state.documents = documents;
},
setClipScrollTree(state, clip_scroll_tree) {
state.clip_scroll_tree = clip_scroll_tree;
},
setScreenshot(state, screenshot) {
state.screenshot = screenshot;
},
},
actions: {
connect(context) {
connection.connect(context);
},
disconnect(context) {
connection.disconnect();
},
sendMessage(context, msg) {
connection.send(msg);
}
}
});
export default store;

View file

@ -1,81 +0,0 @@
var path = require('path')
var webpack = require('webpack')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
],
}, {
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
}
// other vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
},
alias : {
"icons": path.resolve(__dirname, "node_modules/vue-material-design-icons")
},
extensions: ['*', '.js', '.vue', '.json']
},
devServer: {
historyApiFallback: true,
noInfo: true,
overlay: true
},
performance: {
hints: false
},
devtool: '#eval-source-map'
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}

View file

@ -1,14 +0,0 @@
[package]
name = "direct-composition"
version = "0.1.0"
authors = ["Simon Sapin <simon.sapin@exyr.org>"]
license = "MPL-2.0"
edition = "2018"
[target.'cfg(windows)'.dependencies]
euclid = "0.22"
gleam = "0.12"
mozangle = {version = "0.3.1", features = ["egl"]}
webrender = {path = "../webrender"}
winapi = {version = "0.3", features = ["winerror", "d3d11", "dcomp"]}
winit = "0.19"

View file

@ -1,112 +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/. */
use std::ops;
use std::ptr;
use winapi::Interface;
use winapi::ctypes::c_void;
use winapi::shared::guiddef::GUID;
use winapi::shared::winerror::HRESULT;
use winapi::shared::winerror::SUCCEEDED;
use winapi::um::unknwnbase::IUnknown;
pub fn as_ptr<T>(x: &T) -> *mut T {
x as *const T as _
}
pub trait CheckHResult {
fn check_hresult(self);
}
impl CheckHResult for HRESULT {
fn check_hresult(self) {
if !SUCCEEDED(self) {
panic_com(self)
}
}
}
fn panic_com(hresult: HRESULT) -> ! {
panic!("COM error 0x{:08X}", hresult as u32)
}
/// Forked from <https://github.com/retep998/wio-rs/blob/44093f7db8/src/com.rs>
#[derive(PartialEq, Debug)]
pub struct ComPtr<T>(*mut T) where T: Interface;
impl<T> ComPtr<T> where T: Interface {
/// Creates a `ComPtr` to wrap a raw pointer.
/// It takes ownership over the pointer which means it does __not__ call `AddRef`.
/// `T` __must__ be a COM interface that inherits from `IUnknown`.
pub unsafe fn from_raw(ptr: *mut T) -> ComPtr<T> {
assert!(!ptr.is_null());
ComPtr(ptr)
}
/// For use with APIs that take an interface UUID and
/// "return" a new COM object through a `*mut *mut c_void` out-parameter.
pub unsafe fn new_with_uuid<F>(f: F) -> Self
where F: FnOnce(&GUID, *mut *mut c_void) -> HRESULT
{
Self::new_with(|ptr| f(&T::uuidof(), ptr as _))
}
/// For use with APIs that "return" a new COM object through a `*mut *mut T` out-parameter.
pub unsafe fn new_with<F>(f: F) -> Self
where F: FnOnce(*mut *mut T) -> HRESULT
{
let mut ptr = ptr::null_mut();
let hresult = f(&mut ptr);
if SUCCEEDED(hresult) {
ComPtr::from_raw(ptr)
} else {
if !ptr.is_null() {
let ptr = ptr as *mut IUnknown;
(*ptr).Release();
}
panic_com(hresult)
}
}
pub fn as_raw(&self) -> *mut T {
self.0
}
fn as_unknown(&self) -> &IUnknown {
unsafe {
&*(self.0 as *mut IUnknown)
}
}
/// Performs QueryInterface fun.
pub fn cast<U>(&self) -> ComPtr<U> where U: Interface {
unsafe {
ComPtr::<U>::new_with_uuid(|uuid, ptr| self.as_unknown().QueryInterface(uuid, ptr))
}
}
}
impl<T> ops::Deref for ComPtr<T> where T: Interface {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.0 }
}
}
impl<T> Clone for ComPtr<T> where T: Interface {
fn clone(&self) -> Self {
unsafe {
self.as_unknown().AddRef();
ComPtr(self.0)
}
}
}
impl<T> Drop for ComPtr<T> where T: Interface {
fn drop(&mut self) {
unsafe {
self.as_unknown().Release();
}
}
}

View file

@ -1,174 +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/. */
use mozangle::egl::ffi::*;
use std::os::raw::c_void;
use std::ptr;
use std::rc::Rc;
use winapi::um::d3d11::ID3D11Device;
use winapi::um::d3d11::ID3D11Texture2D;
pub use mozangle::egl::get_proc_address;
pub struct SharedEglThings {
device: EGLDeviceEXT,
display: types::EGLDisplay,
config: types::EGLConfig,
context: types::EGLContext,
}
fn cast_attributes(slice: &[types::EGLenum]) -> &EGLint {
unsafe {
&*(slice.as_ptr() as *const EGLint)
}
}
macro_rules! attributes {
($( $key: expr => $value: expr, )*) => {
cast_attributes(&[
$( $key, $value, )*
NONE,
])
}
}
impl SharedEglThings {
pub unsafe fn new(d3d_device: *mut ID3D11Device) -> Rc<Self> {
let device = eglCreateDeviceANGLE(
D3D11_DEVICE_ANGLE,
d3d_device as *mut c_void,
ptr::null(),
).check();
let display = GetPlatformDisplayEXT(
PLATFORM_DEVICE_EXT,
device,
attributes! [
EXPERIMENTAL_PRESENT_PATH_ANGLE => EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE,
],
).check();
Initialize(display, ptr::null_mut(), ptr::null_mut()).check();
// Adapted from
// https://searchfox.org/mozilla-central/rev/056a4057/gfx/gl/GLContextProviderEGL.cpp#635
let mut configs = [ptr::null(); 64];
let mut num_configs = 0;
ChooseConfig(
display,
attributes! [
SURFACE_TYPE => WINDOW_BIT,
RENDERABLE_TYPE => OPENGL_ES2_BIT,
RED_SIZE => 8,
GREEN_SIZE => 8,
BLUE_SIZE => 8,
ALPHA_SIZE => 8,
],
configs.as_mut_ptr(),
configs.len() as i32,
&mut num_configs,
).check();
let config = pick_config(&configs[..num_configs as usize]);
let context = CreateContext(
display, config, NO_CONTEXT,
attributes![
CONTEXT_CLIENT_VERSION => 3,
]
).check();
MakeCurrent(display, NO_SURFACE, NO_SURFACE, context).check();
Rc::new(SharedEglThings { device, display, config, context })
}
}
fn pick_config(configs: &[types::EGLConfig]) -> types::EGLConfig {
// FIXME: better criteria to make this choice?
// Firefox uses GetConfigAttrib to find a config that has the requested r/g/b/a sizes
// https://searchfox.org/mozilla-central/rev/056a4057/gfx/gl/GLContextProviderEGL.cpp#662-685
configs[0]
}
impl Drop for SharedEglThings {
fn drop(&mut self) {
unsafe {
// FIXME does EGLDisplay or EGLConfig need clean up? How?
DestroyContext(self.display, self.context).check();
eglReleaseDeviceANGLE(self.device).check();
}
}
}
pub struct PerVisualEglThings {
shared: Rc<SharedEglThings>,
surface: types::EGLSurface,
}
impl PerVisualEglThings {
pub unsafe fn new(shared: Rc<SharedEglThings>, buffer: *const ID3D11Texture2D,
width: u32, height: u32)
-> Self {
let surface = CreatePbufferFromClientBuffer(
shared.display,
D3D_TEXTURE_ANGLE,
buffer as types::EGLClientBuffer,
shared.config,
attributes! [
WIDTH => width,
HEIGHT => height,
FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE => TRUE,
],
).check();
PerVisualEglThings { shared, surface }
}
pub fn make_current(&self) {
unsafe {
MakeCurrent(self.shared.display, self.surface, self.surface, self.shared.context).check();
}
}
}
impl Drop for PerVisualEglThings {
fn drop(&mut self) {
unsafe {
DestroySurface(self.shared.display, self.surface).check();
}
}
}
fn check_error() {
unsafe {
let error = GetError() as types::EGLenum;
assert_eq!(error, SUCCESS, "0x{:x} != 0x{:x}", error, SUCCESS);
}
}
trait Check {
fn check(self) -> Self;
}
impl Check for *const c_void {
fn check(self) -> Self {
check_error();
assert!(!self.is_null());
self
}
}
impl Check for *mut c_void {
fn check(self) -> Self {
check_error();
assert!(!self.is_null());
self
}
}
impl Check for types::EGLBoolean {
fn check(self) -> Self {
check_error();
assert_eq!(self, TRUE);
self
}
}

View file

@ -1,179 +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/. */
#![cfg(windows)]
use gleam;
use mozangle;
use winapi;
use com::{ComPtr, CheckHResult, as_ptr};
use std::ptr;
use std::rc::Rc;
use winapi::shared::dxgi1_2::DXGI_SWAP_CHAIN_DESC1;
use winapi::shared::dxgi1_2::IDXGIFactory2;
use winapi::shared::minwindef::{TRUE, FALSE};
use winapi::shared::windef::HWND;
use winapi::um::d3d11::ID3D11Device;
use winapi::um::dcomp::IDCompositionDevice;
use winapi::um::dcomp::IDCompositionTarget;
use winapi::um::dcomp::IDCompositionVisual;
mod com;
mod egl;
pub struct DirectComposition {
d3d_device: ComPtr<ID3D11Device>,
dxgi_factory: ComPtr<IDXGIFactory2>,
egl: Rc<egl::SharedEglThings>,
pub gleam: Rc<dyn gleam::gl::Gl>,
composition_device: ComPtr<IDCompositionDevice>,
root_visual: ComPtr<IDCompositionVisual>,
#[allow(unused)] // Needs to be kept alive
composition_target: ComPtr<IDCompositionTarget>,
}
impl DirectComposition {
/// Initialize DirectComposition in the given window
///
/// # Safety
///
/// `hwnd` must be a valid handle to a window.
pub unsafe fn new(hwnd: HWND) -> Self {
let d3d_device = ComPtr::new_with(|ptr_ptr| winapi::um::d3d11::D3D11CreateDevice(
ptr::null_mut(),
winapi::um::d3dcommon::D3D_DRIVER_TYPE_HARDWARE,
ptr::null_mut(),
winapi::um::d3d11::D3D11_CREATE_DEVICE_BGRA_SUPPORT |
if cfg!(debug_assertions) {
winapi::um::d3d11::D3D11_CREATE_DEVICE_DEBUG
} else {
0
},
ptr::null_mut(),
0,
winapi::um::d3d11::D3D11_SDK_VERSION,
ptr_ptr,
&mut 0,
ptr::null_mut(),
));
let egl = egl::SharedEglThings::new(d3d_device.as_raw());
let gleam = gleam::gl::GlesFns::load_with(egl::get_proc_address);
let dxgi_device = d3d_device.cast::<winapi::shared::dxgi::IDXGIDevice>();
// https://msdn.microsoft.com/en-us/library/windows/desktop/hh404556(v=vs.85).aspx#code-snippet-1
// “Because you can create a Direct3D device without creating a swap chain,
// you might need to retrieve the factory that is used to create the device
// in order to create a swap chain.”
let adapter = ComPtr::new_with(|ptr_ptr| dxgi_device.GetAdapter(ptr_ptr));
let dxgi_factory = ComPtr::<IDXGIFactory2>::new_with_uuid(|uuid, ptr_ptr| {
adapter.GetParent(uuid, ptr_ptr)
});
// Create the DirectComposition device object.
let composition_device = ComPtr::<IDCompositionDevice>::new_with_uuid(|uuid, ptr_ptr| {
winapi::um::dcomp::DCompositionCreateDevice(&*dxgi_device, uuid, ptr_ptr)
});
// Create the composition target object based on the
// specified application window.
let composition_target = ComPtr::new_with(|ptr_ptr| {
composition_device.CreateTargetForHwnd(hwnd, TRUE, ptr_ptr)
});
let root_visual = ComPtr::new_with(|ptr_ptr| composition_device.CreateVisual(ptr_ptr));
composition_target.SetRoot(&*root_visual).check_hresult();
DirectComposition {
d3d_device, dxgi_factory,
egl, gleam,
composition_device, composition_target, root_visual,
}
}
/// Execute changes to the DirectComposition scene.
pub fn commit(&self) {
unsafe {
self.composition_device.Commit().check_hresult()
}
}
pub fn create_angle_visual(&self, width: u32, height: u32) -> AngleVisual {
unsafe {
let desc = DXGI_SWAP_CHAIN_DESC1 {
Width: width,
Height: height,
Format: winapi::shared::dxgiformat::DXGI_FORMAT_B8G8R8A8_UNORM,
Stereo: FALSE,
SampleDesc: winapi::shared::dxgitype::DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
BufferUsage: winapi::shared::dxgitype::DXGI_USAGE_RENDER_TARGET_OUTPUT,
BufferCount: 2,
Scaling: winapi::shared::dxgi1_2::DXGI_SCALING_STRETCH,
SwapEffect: winapi::shared::dxgi::DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
AlphaMode: winapi::shared::dxgi1_2::DXGI_ALPHA_MODE_PREMULTIPLIED,
Flags: 0,
};
let swap_chain = ComPtr::<winapi::shared::dxgi1_2::IDXGISwapChain1>::new_with(|ptr_ptr| {
self.dxgi_factory.CreateSwapChainForComposition(
as_ptr(&self.d3d_device),
&desc,
ptr::null_mut(),
ptr_ptr,
)
});
let back_buffer = ComPtr::<winapi::um::d3d11::ID3D11Texture2D>::new_with_uuid(|uuid, ptr_ptr| {
swap_chain.GetBuffer(0, uuid, ptr_ptr)
});
let egl = egl::PerVisualEglThings::new(self.egl.clone(), &*back_buffer, width, height);
let gleam = self.gleam.clone();
let visual = ComPtr::new_with(|ptr_ptr| self.composition_device.CreateVisual(ptr_ptr));
visual.SetContent(&*****swap_chain).check_hresult();
self.root_visual.AddVisual(&*visual, FALSE, ptr::null_mut()).check_hresult();
AngleVisual { visual, swap_chain, egl, gleam }
}
}
}
/// A DirectComposition "visual" configured for rendering with Direct3D.
pub struct AngleVisual {
visual: ComPtr<IDCompositionVisual>,
swap_chain: ComPtr<winapi::shared::dxgi1_2::IDXGISwapChain1>,
egl: egl::PerVisualEglThings,
pub gleam: Rc<dyn gleam::gl::Gl>,
}
impl AngleVisual {
pub fn set_offset_x(&self, offset_x: f32) {
unsafe {
self.visual.SetOffsetX_1(offset_x).check_hresult()
}
}
pub fn set_offset_y(&self, offset_y: f32) {
unsafe {
self.visual.SetOffsetY_1(offset_y).check_hresult()
}
}
pub fn make_current(&self) {
self.egl.make_current()
}
pub fn present(&self) {
self.gleam.finish();
unsafe {
self.swap_chain.Present(0, 0).check_hresult()
}
}
}

View file

@ -1,212 +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/. */
use direct_composition;
use euclid;
use gleam;
use webrender;
use winit;
use euclid::size2;
use direct_composition::DirectComposition;
use std::sync::mpsc;
use webrender::api;
use winit::os::windows::{WindowExt, WindowBuilderExt};
use winit::dpi::LogicalSize;
fn main() {
let mut events_loop = winit::EventsLoop::new();
let (tx, rx) = mpsc::channel();
let notifier = Box::new(Notifier { events_proxy: events_loop.create_proxy(), tx });
let window = winit::WindowBuilder::new()
.with_title("WebRender + ANGLE + DirectComposition")
.with_dimensions(LogicalSize::new(1024., 768.))
.with_decorations(true)
.with_transparency(true)
.with_no_redirection_bitmap(true)
.build(&events_loop)
.unwrap();
let composition = direct_composition_from_window(&window);
let factor = window.get_hidpi_factor() as f32;
let mut clicks: usize = 0;
let mut offset_y = 100.;
let mut rects = [
Rectangle::new(&composition, &notifier, factor, size2(300, 200), 0., 0.2, 0.4, 1.),
Rectangle::new(&composition, &notifier, factor, size2(400, 300), 0., 0.5, 0., 0.5),
];
rects[0].render(factor, &rx);
rects[1].render(factor, &rx);
rects[0].visual.set_offset_x(100.);
rects[0].visual.set_offset_y(50.);
rects[1].visual.set_offset_x(200.);
rects[1].visual.set_offset_y(offset_y);
composition.commit();
events_loop.run_forever(|event| {
if let winit::Event::WindowEvent { event, .. } = event {
match event {
winit::WindowEvent::CloseRequested => {
return winit::ControlFlow::Break
}
winit::WindowEvent::MouseWheel { delta, .. } => {
let dy = match delta {
winit::MouseScrollDelta::LineDelta(_, dy) => dy,
winit::MouseScrollDelta::PixelDelta(pos) => pos.y as f32,
};
offset_y = (offset_y - 10. * dy).max(0.).min(468.);
rects[1].visual.set_offset_y(offset_y);
composition.commit();
}
winit::WindowEvent::MouseInput {
button: winit::MouseButton::Left,
state: winit::ElementState::Pressed,
..
} => {
clicks += 1;
let rect = &mut rects[clicks % 2];
rect.color.g += 0.1;
rect.color.g %= 1.;
rect.render(factor, &rx)
}
_ => {}
}
}
winit::ControlFlow::Continue
});
}
fn direct_composition_from_window(window: &winit::Window) -> DirectComposition {
unsafe {
DirectComposition::new(window.get_hwnd() as _)
}
}
struct Rectangle {
visual: direct_composition::AngleVisual,
renderer: Option<webrender::Renderer>,
api: api::RenderApi,
document_id: api::DocumentId,
size: api::units::DeviceIntSize,
color: api::ColorF,
}
impl Rectangle {
fn new(composition: &DirectComposition, notifier: &Box<Notifier>,
device_pixel_ratio: f32, size: api::units::DeviceIntSize, r: f32, g: f32, b: f32, a: f32)
-> Self {
let visual = composition.create_angle_visual(size.width as u32, size.height as u32);
visual.make_current();
let (renderer, sender) = webrender::Renderer::new(
composition.gleam.clone(),
notifier.clone(),
webrender::RendererOptions {
clear_color: Some(api::ColorF::new(0., 0., 0., 0.)),
device_pixel_ratio,
..webrender::RendererOptions::default()
},
None,
size,
).unwrap();
let api = sender.create_api();
Rectangle {
visual,
renderer: Some(renderer),
document_id: api.add_document(size, 0),
api,
size,
color: api::ColorF { r, g, b, a },
}
}
fn render(&mut self, device_pixel_ratio: f32, rx: &mpsc::Receiver<()>) {
self.visual.make_current();
let pipeline_id = api::PipelineId(0, 0);
let layout_size = self.size.to_f32() / euclid::Scale::new(device_pixel_ratio);
let mut builder = api::DisplayListBuilder::new(pipeline_id, layout_size);
let rect = euclid::Rect::new(euclid::Point2D::zero(), layout_size);
let region = api::ComplexClipRegion::new(
rect,
api::BorderRadius::uniform(20.),
api::ClipMode::Clip
);
let clip_id = builder.define_clip_rounded_rect(
&api::SpaceAndClipInfo::root_scroll(pipeline_id),
region,
);
builder.push_rect(
&api::CommonItemProperties::new(
rect,
api::SpaceAndClipInfo {
spatial_id: api::SpatialId::root_scroll_node(pipeline_id),
clip_id,
},
),
rect,
self.color,
);
let mut transaction = api::Transaction::new();
transaction.set_display_list(
api::Epoch(0),
None,
layout_size,
builder.finalize(),
true,
);
transaction.set_root_pipeline(pipeline_id);
transaction.generate_frame();
self.api.send_transaction(self.document_id, transaction);
rx.recv().unwrap();
let renderer = self.renderer.as_mut().unwrap();
renderer.update();
renderer.render(self.size).unwrap();
let _ = renderer.flush_pipeline_info();
self.visual.present();
}
}
impl Drop for Rectangle {
fn drop(&mut self) {
self.renderer.take().unwrap().deinit()
}
}
#[derive(Clone)]
struct Notifier {
events_proxy: winit::EventsLoopProxy,
tx: mpsc::Sender<()>,
}
impl api::RenderNotifier for Notifier {
fn clone(&self) -> Box<dyn api::RenderNotifier> {
Box::new(Clone::clone(self))
}
fn wake_up(&self) {
self.tx.send(()).unwrap();
let _ = self.events_proxy.wakeup();
}
fn new_frame_ready(&self,
_: api::DocumentId,
_: bool,
_: bool,
_: Option<u64>) {
self.wake_up();
}
}

View file

@ -0,0 +1,11 @@
[package]
name = "compositor-wayland"
version = "0.1.0"
authors = ["Glenn Watson <gw@intuitionlibrary.com>",
"Robert Mader <robert.mader@posteo.de>"]
edition = "2018"
license = "MPL-2.0"
[build-dependencies]
cc = "1.0"
pkg-config = "^0.3.17"

View file

@ -0,0 +1,63 @@
/* 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 std::process::Command;
use std::env;
use std::fs;
extern crate pkg_config;
fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
fs::create_dir_all(&format!("{}/include", out_dir)).unwrap();
Command::new("wayland-scanner")
.args(&["client-header", "/usr/share/wayland-protocols/stable/viewporter/viewporter.xml"])
.arg(&format!("{}/include/viewporter-client-protocol.h", out_dir))
.status().unwrap();
Command::new("wayland-scanner")
.args(&["public-code", "/usr/share/wayland-protocols/stable/viewporter/viewporter.xml"])
.arg(&format!("{}/viewporter-protocol.c", out_dir))
.status().unwrap();
Command::new("wayland-scanner")
.args(&["client-header", "/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml"])
.arg(&format!("{}/include/xdg-shell-client-protocol.h", out_dir))
.status().unwrap();
Command::new("wayland-scanner")
.args(&["public-code", "/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml"])
.arg(&format!("{}/xdg-shell-protocol.c", out_dir))
.status().unwrap();
cc::Build::new()
.include(&format!("{}/include", out_dir))
.file("src/lib.cpp")
.file(&format!("{}/viewporter-protocol.c", out_dir))
.file(&format!("{}/xdg-shell-protocol.c", out_dir))
.compile("wayland");
println!("cargo:rustc-link-lib=dylib=stdc++");
pkg_config::Config::new()
.atleast_version("1")
.probe("egl")
.unwrap();
pkg_config::Config::new()
.atleast_version("1")
.probe("gl")
.unwrap();
pkg_config::Config::new()
.atleast_version("1")
.probe("wayland-client")
.unwrap();
pkg_config::Config::new()
.atleast_version("1")
.probe("wayland-egl")
.unwrap();
println!("cargo:rerun-if-changed=src/lib.rs");
println!("cargo:rerun-if-changed=src/lib.cpp");
}

View file

@ -0,0 +1,772 @@
/* 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/. */
#define UNICODE
#include <algorithm>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <map>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <unordered_map>
#include <vector>
#include <wayland-client.h>
#include <wayland-egl.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GL/gl.h>
#include <GLES2/gl2.h>
#include "viewporter-client-protocol.h"
#include "xdg-shell-client-protocol.h"
#define UNUSED(x) (void)(x)
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define NUM_QUERIES 2
#define VIRTUAL_OFFSET 512 * 1024
enum SyncMode {
None_ = 0,
Swap = 1,
Commit = 2,
Flush = 3,
Query = 4,
};
// The OS compositor representation of a picture cache tile.
struct Tile {
uint64_t surface_id;
int x;
int y;
struct wl_surface* surface;
struct wl_subsurface* subsurface;
struct wp_viewport* viewport;
struct wl_egl_window* egl_window;
EGLSurface egl_surface;
bool is_visible;
std::vector<EGLint> damage_rects;
};
struct TileKey {
int x;
int y;
TileKey(int ax, int ay) : x(ax), y(ay) {}
};
bool operator==(const TileKey& k0, const TileKey& k1) {
return k0.x == k1.x && k0.y == k1.y;
}
struct TileKeyHasher {
size_t operator()(const TileKey& key) const { return key.x ^ key.y; }
};
struct Surface {
uint64_t id;
int tile_width;
int tile_height;
bool is_opaque;
std::unordered_map<TileKey, Tile*, TileKeyHasher> tiles;
};
struct WLDisplay {
struct wl_display* display;
struct wl_registry* registry;
struct wl_compositor* compositor;
struct wl_subcompositor* subcompositor;
struct xdg_wm_base* wm_base;
struct wl_seat* seat;
struct wl_pointer* pointer;
struct wl_touch* touch;
struct wl_keyboard* keyboard;
struct wl_shm* shm;
struct wl_cursor_theme* cursor_theme;
struct wl_cursor* default_cursor;
struct wl_surface* cursor_surface;
struct wp_viewporter* viewporter;
PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage;
};
struct WLGeometry {
int width, height;
};
struct WLWindow {
WLGeometry geometry;
bool enable_compositor;
SyncMode sync_mode;
bool closed;
WLDisplay* display;
struct wl_surface* surface;
struct xdg_surface* xdg_surface;
struct xdg_toplevel* xdg_toplevel;
struct wl_callback* callback;
struct wp_viewport* viewport;
bool wait_for_configure;
struct wl_egl_window* egl_window;
EGLSurface egl_surface;
EGLDeviceEXT eglDevice;
EGLDisplay eglDisplay;
EGLContext eglContext;
EGLConfig config;
// Maintain list of layer state between frames to avoid visual tree rebuild.
std::vector<uint64_t> currentLayers;
std::vector<uint64_t> prevLayers;
// Maps WR surface IDs to each OS surface
std::unordered_map<uint64_t, Surface> surfaces;
std::vector<Tile*> destroyedTiles;
std::vector<Tile*> hiddenTiles;
};
extern "C" {
static void init_wl_registry(WLWindow* window);
static void init_xdg_window(WLWindow* window);
WLWindow* com_wl_create_window(int width, int height, bool enable_compositor,
SyncMode sync_mode) {
WLDisplay* display = new WLDisplay;
WLWindow* window = new WLWindow;
window->display = display;
window->geometry.width = width;
window->geometry.height = height;
window->enable_compositor = enable_compositor;
window->sync_mode = sync_mode;
window->closed = false;
display->display = wl_display_connect(NULL);
assert(display->display);
init_wl_registry(window);
if (enable_compositor && !display->viewporter) {
fprintf(stderr, "Native compositor mode requires wp_viewporter support\n");
window->closed = true;
}
window->eglDisplay =
eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, display->display, NULL);
eglInitialize(window->eglDisplay, nullptr, nullptr);
eglBindAPI(EGL_OPENGL_API);
EGLint num_configs = 0;
EGLint cfg_attribs[] = {EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE,
EGL_OPENGL_BIT,
EGL_RED_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_ALPHA_SIZE,
8,
EGL_DEPTH_SIZE,
24,
EGL_NONE};
EGLConfig configs[32];
eglChooseConfig(window->eglDisplay, cfg_attribs, configs,
sizeof(configs) / sizeof(EGLConfig), &num_configs);
assert(num_configs > 0);
window->config = configs[0];
EGLint ctx_attribs[] = {EGL_CONTEXT_OPENGL_PROFILE_MASK,
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
EGL_CONTEXT_MAJOR_VERSION,
3,
EGL_CONTEXT_MINOR_VERSION,
2,
EGL_NONE};
// Create an EGL context that can be used for drawing
window->eglContext = eglCreateContext(window->eglDisplay, window->config,
EGL_NO_CONTEXT, ctx_attribs);
window->surface = wl_compositor_create_surface(display->compositor);
init_xdg_window(window);
struct wl_region* region =
wl_compositor_create_region(window->display->compositor);
wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_set_opaque_region(window->surface, region);
wl_region_destroy(region);
if (enable_compositor) {
xdg_toplevel_set_title(window->xdg_toplevel,
"example-compositor (Wayland)");
} else {
xdg_toplevel_set_title(window->xdg_toplevel, "example-compositor (Simple)");
}
window->wait_for_configure = true;
wl_surface_commit(window->surface);
EGLBoolean ok = eglMakeCurrent(window->eglDisplay, EGL_NO_SURFACE,
EGL_NO_SURFACE, window->eglContext);
assert(ok);
display->swap_buffers_with_damage =
(PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC)eglGetProcAddress(
"eglSwapBuffersWithDamageKHR");
return window;
}
bool com_wl_tick(WLWindow* window) {
if (window->wait_for_configure) {
int ret = 0;
while (window->wait_for_configure && !window->closed && ret != -1) {
wl_display_dispatch(window->display->display);
}
} else {
wl_display_dispatch_pending(window->display->display);
}
return !window->closed;
}
static void unmap_hidden_tiles(WLWindow* window) {
for (Tile* tile : window->hiddenTiles) {
if (tile->subsurface) {
wl_subsurface_destroy(tile->subsurface);
tile->subsurface = nullptr;
}
}
window->hiddenTiles.clear();
}
static void clean_up_tiles(WLWindow* window) {
for (Tile* tile : window->destroyedTiles) {
eglDestroySurface(window->eglDisplay, tile->egl_surface);
wl_egl_window_destroy(tile->egl_window);
wp_viewport_destroy(tile->viewport);
wl_surface_destroy(tile->surface);
delete tile;
}
window->destroyedTiles.clear();
}
static void handle_callback(void* data, struct wl_callback* callback,
uint32_t time) {
WLWindow* window = (WLWindow*)data;
UNUSED(time);
assert(window->callback == callback);
wl_callback_destroy(callback);
window->callback = nullptr;
}
static const struct wl_callback_listener frame_listener = {handle_callback};
void com_wl_swap_buffers(WLWindow* window) {
if (window->enable_compositor) {
for (auto surface_it = window->surfaces.begin();
surface_it != window->surfaces.end(); ++surface_it) {
Surface* surface = &surface_it->second;
for (auto tile_it = surface->tiles.begin();
tile_it != surface->tiles.end(); ++tile_it) {
Tile* tile = tile_it->second;
if (!tile->damage_rects.empty() && tile->is_visible) {
eglMakeCurrent(window->eglDisplay, tile->egl_surface,
tile->egl_surface, window->eglContext);
eglSwapInterval(window->eglDisplay, 0);
/* if (window->display->swap_buffers_with_damage) {
window->display->swap_buffers_with_damage(
window->eglDisplay, tile->egl_surface,
tile->damage_rects.data(), tile->damage_rects.size() / 4);
} else */
eglSwapBuffers(window->eglDisplay, tile->egl_surface);
tile->damage_rects.clear();
eglMakeCurrent(window->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
window->eglContext);
} else {
wl_surface_commit(tile->surface);
}
}
}
wl_surface_commit(window->surface);
unmap_hidden_tiles(window);
clean_up_tiles(window);
int ret = 0;
switch (window->sync_mode) {
case SyncMode::None_:
wl_display_roundtrip(window->display->display);
break;
case SyncMode::Swap:
window->callback = wl_surface_frame(window->surface);
wl_callback_add_listener(window->callback, &frame_listener, window);
wl_surface_commit(window->surface);
while (window->callback && !window->closed && ret != -1) {
ret = wl_display_dispatch(window->display->display);
}
break;
default:
assert(false);
break;
}
} else {
// If not using native mode, then do a normal EGL swap buffers.
switch (window->sync_mode) {
case SyncMode::None_:
eglSwapInterval(window->eglDisplay, 0);
break;
case SyncMode::Swap:
eglSwapInterval(window->eglDisplay, 1);
break;
default:
assert(false);
break;
}
eglSwapBuffers(window->eglDisplay, window->egl_surface);
}
}
// Create a new native surface
void com_wl_create_surface(WLWindow* window, uint64_t surface_id,
int tile_width, int tile_height, bool is_opaque) {
assert(window->surfaces.count(surface_id) == 0);
Surface surface;
surface.id = surface_id;
surface.tile_width = tile_width;
surface.tile_height = tile_height;
surface.is_opaque = is_opaque;
window->surfaces.emplace(surface_id, surface);
}
void com_wl_create_tile(WLWindow* window, uint64_t surface_id, int x, int y) {
WLDisplay* display = window->display;
assert(window->surfaces.count(surface_id) == 1);
Surface* surface = &window->surfaces.at(surface_id);
TileKey key(x, y);
assert(surface->tiles.count(key) == 0);
Tile* tile = new Tile;
tile->surface_id = surface_id;
tile->x = x;
tile->y = y;
tile->is_visible = false;
tile->surface = wl_compositor_create_surface(display->compositor);
tile->viewport =
wp_viewporter_get_viewport(display->viewporter, tile->surface);
if (surface->is_opaque) {
struct wl_region* region =
wl_compositor_create_region(window->display->compositor);
wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_set_opaque_region(tile->surface, region);
wl_region_destroy(region);
}
tile->egl_window = wl_egl_window_create(tile->surface, surface->tile_width,
surface->tile_height);
tile->egl_surface = eglCreateWindowSurface(window->eglDisplay, window->config,
tile->egl_window, NULL);
assert(tile->egl_surface != EGL_NO_SURFACE);
surface->tiles.emplace(key, tile);
}
static void show_tile(WLWindow* window, Tile* tile) {
if (tile->is_visible) {
assert(tile->subsurface);
return;
}
tile->subsurface = wl_subcompositor_get_subsurface(
window->display->subcompositor, tile->surface, window->surface);
/* This is not comprehensive yet, see hide_tile() */
Surface* surface = &window->surfaces.at(tile->surface_id);
for (auto tile_it = surface->tiles.begin(); tile_it != surface->tiles.end();
++tile_it) {
Tile* other_tile = tile_it->second;
if (other_tile->is_visible) {
wl_subsurface_place_above(tile->subsurface, other_tile->surface);
}
}
tile->is_visible = true;
}
static void hide_tile(WLWindow* window, Tile* tile) {
if (!tile->is_visible) {
return;
}
/*
* This is a workaround for missing API on the egl-wayland platform. We
* likely want to replace it a solution that detaches the buffer from
* the surface, which would require us to manage buffers manually.
*/
wl_subsurface_set_position(tile->subsurface, window->geometry.width / 2,
window->geometry.height / 2);
wp_viewport_set_source(tile->viewport, wl_fixed_from_int(0),
wl_fixed_from_int(0), wl_fixed_from_int(1),
wl_fixed_from_int(1));
wl_subsurface_place_below(tile->subsurface, window->surface);
tile->is_visible = false;
window->hiddenTiles.push_back(tile);
}
void com_wl_destroy_tile(WLWindow* window, uint64_t surface_id, int x, int y) {
assert(window->surfaces.count(surface_id) == 1);
Surface* surface = &window->surfaces.at(surface_id);
TileKey key(x, y);
assert(surface->tiles.count(key) == 1);
Tile* tile = surface->tiles[key];
hide_tile(window, tile);
wl_surface_commit(tile->surface);
window->destroyedTiles.push_back(tile);
surface->tiles.erase(key);
}
void com_wl_destroy_surface(WLWindow* window, uint64_t surface_id) {
assert(window->surfaces.count(surface_id) == 1);
Surface* surface = &window->surfaces.at(surface_id);
for (auto tile_it = surface->tiles.begin(); tile_it != surface->tiles.end();
tile_it = surface->tiles.begin()) {
Tile* tile = tile_it->second;
com_wl_destroy_tile(window, surface_id, tile->x, tile->y);
}
window->surfaces.erase(surface_id);
}
void com_wl_destroy_window(WLWindow* window) {
for (auto surface_it = window->surfaces.begin();
surface_it != window->surfaces.end(); ++surface_it) {
Surface& surface = surface_it->second;
com_wl_destroy_surface(window, surface.id);
}
if (window->egl_surface != EGL_NO_SURFACE) {
eglDestroySurface(window->eglDisplay, window->egl_surface);
}
eglDestroyContext(window->eglDisplay, window->eglContext);
eglTerminate(window->eglDisplay);
delete window;
}
// Bind a native surface to allow issuing GL commands to it
GLuint com_wl_bind_surface(WLWindow* window, uint64_t surface_id, int tile_x,
int tile_y, int* x_offset, int* y_offset,
int dirty_x0, int dirty_y0, int dirty_width,
int dirty_height) {
*x_offset = 0;
*y_offset = 0;
assert(window->surfaces.count(surface_id) == 1);
Surface* surface = &window->surfaces[surface_id];
TileKey key(tile_x, tile_y);
assert(surface->tiles.count(key) == 1);
Tile* tile = surface->tiles[key];
tile->damage_rects.push_back(dirty_x0);
tile->damage_rects.push_back(dirty_y0);
tile->damage_rects.push_back(dirty_width);
tile->damage_rects.push_back(dirty_height);
EGLBoolean ok = eglMakeCurrent(window->eglDisplay, tile->egl_surface,
tile->egl_surface, window->eglContext);
assert(ok);
return 0;
}
// Unbind a currently bound native surface
void com_wl_unbind_surface(WLWindow* window) {
eglMakeCurrent(window->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
window->eglContext);
}
void com_wl_begin_transaction(WLWindow*) {}
// Add a native surface to the visual tree. Called per-frame to build the
// composition.
void com_wl_add_surface(WLWindow* window, uint64_t surface_id, int offset_x,
int offset_y, int clip_x, int clip_y, int clip_w,
int clip_h) {
Surface* surface = &window->surfaces[surface_id];
window->currentLayers.push_back(surface_id);
for (auto tile_it = surface->tiles.begin(); tile_it != surface->tiles.end();
++tile_it) {
Tile* tile = tile_it->second;
int pos_x = MAX((tile->x * surface->tile_width) + offset_x, clip_x);
int pos_y = MAX((tile->y * surface->tile_height) + offset_y, clip_y);
float view_x = MAX((clip_x - offset_x) - tile->x * surface->tile_width, 0);
float view_y = MAX((clip_y - offset_y) - tile->y * surface->tile_height, 0);
float view_w = MIN(surface->tile_width - view_x, (clip_x + clip_w) - pos_x);
float view_h =
MIN(surface->tile_height - view_y, (clip_y + clip_h) - pos_y);
view_w = MIN(window->geometry.width - pos_x, view_w);
view_h = MIN(window->geometry.height - pos_y, view_h);
if (view_w > 0 && view_h > 0) {
show_tile(window, tile);
wl_surface_set_buffer_transform(tile->surface,
WL_OUTPUT_TRANSFORM_FLIPPED_180);
wl_subsurface_set_position(tile->subsurface, pos_x, pos_y);
wp_viewport_set_source(tile->viewport, wl_fixed_from_double(view_x),
wl_fixed_from_double(view_y),
wl_fixed_from_double(view_w),
wl_fixed_from_double(view_h));
} else {
hide_tile(window, tile);
}
}
}
void com_wl_end_transaction(WLWindow* window) {
bool same = window->prevLayers == window->currentLayers;
if (!same) {
struct wl_surface* prev_surface = window->surface;
for (auto it = window->currentLayers.begin();
it != window->currentLayers.end(); ++it) {
Surface* surface = &window->surfaces[*it];
struct wl_surface* next_surface = nullptr;
for (auto tile_it = surface->tiles.begin();
tile_it != surface->tiles.end(); ++tile_it) {
Tile* tile = tile_it->second;
if (tile->is_visible) {
wl_subsurface_place_above(tile->subsurface, prev_surface);
if (!next_surface) {
next_surface = tile->surface;
}
}
}
prev_surface = next_surface;
}
}
window->prevLayers.swap(window->currentLayers);
window->currentLayers.clear();
}
void glInvalidateFramebuffer(GLenum target, GLsizei numAttachments,
const GLenum* attachments) {
UNUSED(target);
UNUSED(numAttachments);
UNUSED(attachments);
}
// Get a pointer to an EGL symbol
void* com_wl_get_proc_address(const char* name) {
/* Disable glInvalidateFramebuffer for now as it triggers errors.
* This is likely due to the egl-wayland platform, which we may want to
* replace with a custom implementation in order to have more control
* over the low-lever bits.
*/
if (strcmp(name, "glInvalidateFramebuffer") == 0) {
return (void*)glInvalidateFramebuffer;
}
return (void*)eglGetProcAddress(name);
}
void com_wl_deinit(WLWindow* window) { UNUSED(window); }
static void handle_xdg_surface_configure(void* data,
struct xdg_surface* surface,
uint32_t serial) {
WLWindow* window = (WLWindow*)data;
xdg_surface_ack_configure(surface, serial);
if (window->wait_for_configure) {
if (window->enable_compositor) {
int width = window->geometry.width;
int height = window->geometry.height;
window->egl_window = wl_egl_window_create(window->surface, 1, 1);
window->egl_surface = eglCreateWindowSurface(
window->eglDisplay, window->config, window->egl_window, NULL);
assert(window->egl_surface != EGL_NO_SURFACE);
EGLBoolean ok = eglMakeCurrent(window->eglDisplay, window->egl_surface,
window->egl_surface, window->eglContext);
assert(ok);
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
window->viewport = wp_viewporter_get_viewport(window->display->viewporter,
window->surface);
wp_viewport_set_destination(window->viewport, width, height);
eglSwapBuffers(window->eglDisplay, window->egl_surface);
} else {
window->egl_window = wl_egl_window_create(
window->surface, window->geometry.width, window->geometry.height);
window->egl_surface = eglCreateWindowSurface(
window->eglDisplay, window->config, window->egl_window, NULL);
assert(window->egl_surface != EGL_NO_SURFACE);
EGLBoolean ok = eglMakeCurrent(window->eglDisplay, window->egl_surface,
window->egl_surface, window->eglContext);
assert(ok);
}
}
window->wait_for_configure = false;
}
static const struct xdg_surface_listener xdg_surface_listener = {
handle_xdg_surface_configure};
static void handle_xdg_toplevel_configure(void* data,
struct xdg_toplevel* toplevel,
int32_t width, int32_t height,
struct wl_array* states) {
WLWindow* window = (WLWindow*)data;
UNUSED(toplevel);
UNUSED(states);
if (width > 0 && height > 0) {
window->geometry.width = width;
window->geometry.height = height;
if (!window->wait_for_configure) {
if (window->enable_compositor) {
wp_viewport_set_destination(window->viewport, window->geometry.width,
window->geometry.height);
} else {
wl_egl_window_resize(window->egl_window, window->geometry.width,
window->geometry.height, 0, 0);
}
}
}
}
static void handle_xdg_toplevel_close(void* data,
struct xdg_toplevel* toplevel) {
UNUSED(toplevel);
WLWindow* window = (WLWindow*)data;
window->closed = true;
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
handle_xdg_toplevel_configure,
handle_xdg_toplevel_close,
};
static void xdg_wm_base_ping(void* data, struct xdg_wm_base* shell,
uint32_t serial) {
UNUSED(data);
xdg_wm_base_pong(shell, serial);
}
static const struct xdg_wm_base_listener wm_base_listener = {
xdg_wm_base_ping,
};
static void registry_handle_global(void* data, struct wl_registry* registry,
uint32_t name, const char* interface,
uint32_t version) {
WLDisplay* d = (WLDisplay*)data;
if (strcmp(interface, "wl_compositor") == 0) {
d->compositor = (struct wl_compositor*)wl_registry_bind(
registry, name, &wl_compositor_interface, MIN(version, 4));
} else if (strcmp(interface, "wp_viewporter") == 0) {
d->viewporter = (struct wp_viewporter*)wl_registry_bind(
registry, name, &wp_viewporter_interface, 1);
} else if (strcmp(interface, "xdg_wm_base") == 0) {
d->wm_base = (struct xdg_wm_base*)wl_registry_bind(
registry, name, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(d->wm_base, &wm_base_listener, NULL);
} else if (strcmp(interface, "wl_subcompositor") == 0) {
d->subcompositor = (struct wl_subcompositor*)wl_registry_bind(
registry, name, &wl_subcompositor_interface, 1);
}
}
static void registry_handle_global_remove(void* data,
struct wl_registry* registry,
uint32_t name) {
UNUSED(data);
UNUSED(registry);
UNUSED(name);
}
static const struct wl_registry_listener registry_listener = {
registry_handle_global, registry_handle_global_remove};
static void init_wl_registry(WLWindow* window) {
WLDisplay* display = window->display;
display->registry = wl_display_get_registry(display->display);
wl_registry_add_listener(display->registry, &registry_listener, display);
wl_display_roundtrip(display->display);
assert(display->compositor);
assert(display->wm_base);
assert(display->subcompositor);
}
static void init_xdg_window(WLWindow* window) {
window->xdg_surface =
xdg_wm_base_get_xdg_surface(window->display->wm_base, window->surface);
assert(window->xdg_surface);
xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window);
window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface);
xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener,
window);
assert(window->xdg_toplevel);
}
}

View file

@ -0,0 +1,269 @@
/* 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 std::os::raw::{c_void, c_char};
/*
This is a very simple (and unsafe!) rust wrapper for the Wayland / EGL
implementation in lib.cpp.
It just proxies the calls from the Compositor impl to the C99 code. This is very
hacky and not suitable for production!
*/
// Opaque wrapper for the Window type in lib.cpp
#[repr(C)]
pub struct Window {
_unused: [u8; 0]
}
// C99 functions that do the compositor work
extern {
fn com_wl_create_window(
width: i32,
height: i32,
enable_compositor: bool,
sync_mode: i32,
) -> *mut Window;
fn com_wl_destroy_window(window: *mut Window);
fn com_wl_tick(window: *mut Window) -> bool;
fn com_wl_get_proc_address(name: *const c_char) -> *const c_void;
fn com_wl_swap_buffers(window: *mut Window);
fn com_wl_create_surface(
window: *mut Window,
id: u64,
tile_width: i32,
tile_height: i32,
is_opaque: bool,
);
fn com_wl_create_tile(
window: *mut Window,
id: u64,
x: i32,
y: i32,
);
fn com_wl_destroy_tile(
window: *mut Window,
id: u64,
x: i32,
y: i32,
);
fn com_wl_destroy_surface(
window: *mut Window,
id: u64,
);
fn com_wl_bind_surface(
window: *mut Window,
surface_id: u64,
tile_x: i32,
tile_y: i32,
x_offset: &mut i32,
y_offset: &mut i32,
dirty_x0: i32,
dirty_y0: i32,
dirty_width: i32,
dirty_height: i32,
) -> u32;
fn com_wl_unbind_surface(window: *mut Window);
fn com_wl_begin_transaction(window: *mut Window);
fn com_wl_add_surface(
window: *mut Window,
id: u64,
x: i32,
y: i32,
clip_x: i32,
clip_y: i32,
clip_w: i32,
clip_h: i32,
);
fn com_wl_end_transaction(window: *mut Window);
fn com_wl_deinit(window: *mut Window);
}
pub fn create_window(
width: i32,
height: i32,
enable_compositor: bool,
sync_mode: i32,
) -> *mut Window {
unsafe {
com_wl_create_window(width, height, enable_compositor, sync_mode)
}
}
pub fn destroy_window(window: *mut Window) {
unsafe {
com_wl_destroy_window(window);
}
}
pub fn tick(window: *mut Window) -> bool {
unsafe {
com_wl_tick(window)
}
}
pub fn get_proc_address(name: *const c_char) -> *const c_void {
unsafe {
com_wl_get_proc_address(name)
}
}
pub fn create_surface(
window: *mut Window,
id: u64,
tile_width: i32,
tile_height: i32,
is_opaque: bool,
) {
unsafe {
com_wl_create_surface(
window,
id,
tile_width,
tile_height,
is_opaque,
)
}
}
pub fn create_tile(
window: *mut Window,
id: u64,
x: i32,
y: i32,
) {
unsafe {
com_wl_create_tile(
window,
id,
x,
y,
)
}
}
pub fn destroy_tile(
window: *mut Window,
id: u64,
x: i32,
y: i32,
) {
unsafe {
com_wl_destroy_tile(
window,
id,
x,
y,
)
}
}
pub fn destroy_surface(
window: *mut Window,
id: u64,
) {
unsafe {
com_wl_destroy_surface(
window,
id,
)
}
}
pub fn bind_surface(
window: *mut Window,
surface_id: u64,
tile_x: i32,
tile_y: i32,
dirty_x0: i32,
dirty_y0: i32,
dirty_width: i32,
dirty_height: i32,
) -> (u32, i32, i32) {
unsafe {
let mut x_offset = 0;
let mut y_offset = 0;
let fbo_id = com_wl_bind_surface(
window,
surface_id,
tile_x,
tile_y,
&mut x_offset,
&mut y_offset,
dirty_x0,
dirty_y0,
dirty_width,
dirty_height,
);
(fbo_id, x_offset, y_offset)
}
}
pub fn add_surface(
window: *mut Window,
id: u64,
x: i32,
y: i32,
clip_x: i32,
clip_y: i32,
clip_w: i32,
clip_h: i32,
) {
unsafe {
com_wl_add_surface(
window,
id,
x,
y,
clip_x,
clip_y,
clip_w,
clip_h,
)
}
}
pub fn begin_transaction(window: *mut Window) {
unsafe {
com_wl_begin_transaction(window)
}
}
pub fn unbind_surface(window: *mut Window) {
unsafe {
com_wl_unbind_surface(window)
}
}
pub fn end_transaction(window: *mut Window) {
unsafe {
com_wl_end_transaction(window)
}
}
pub fn swap_buffers(window: *mut Window) {
unsafe {
com_wl_swap_buffers(window);
}
}
pub fn deinit(window: *mut Window) {
unsafe {
com_wl_deinit(window);
}
}

File diff suppressed because it is too large Load diff

View file

@ -88,6 +88,8 @@ extern {
);
fn com_dc_end_transaction(window: *mut Window);
fn deinit(window: *mut Window);
}
pub fn create_window(
@ -259,3 +261,7 @@ pub fn swap_buffers(window: *mut Window) {
com_dc_swap_buffers(window);
}
}
pub fn deinit(window: *mut Window) {
todo!()
}

View file

@ -7,7 +7,10 @@ license = "MPL-2.0"
[dependencies]
webrender = { path = "../../webrender" }
gleam = "0.12.0"
gleam = "0.15"
[target.'cfg(windows)'.dependencies]
compositor-windows = { path = "../compositor-windows" }
[target.'cfg(target_os = "linux")'.dependencies]
compositor-wayland = { path = "../compositor-wayland" }

View file

@ -16,10 +16,12 @@ use euclid::Angle;
use gleam::gl;
use std::ffi::CString;
use std::sync::mpsc;
use webrender::api::*;
use webrender::{CompositorSurfaceTransform, Transaction, api::*, euclid::point2};
use webrender::api::units::*;
#[cfg(target_os = "windows")]
use compositor_windows as compositor;
#[cfg(target_os = "linux")]
use compositor_wayland as compositor;
use std::{env, f32, process};
// A very hacky integration with DirectComposite. It proxies calls from the compositor
@ -42,6 +44,7 @@ impl webrender::Compositor for DirectCompositeInterface {
fn create_surface(
&mut self,
id: webrender::NativeSurfaceId,
_virtual_offset: DeviceIntPoint,
tile_size: DeviceIntSize,
is_opaque: bool,
) {
@ -89,6 +92,7 @@ impl webrender::Compositor for DirectCompositeInterface {
&mut self,
id: webrender::NativeTileId,
dirty_rect: DeviceIntRect,
_valid_rect: DeviceIntRect,
) -> webrender::NativeSurfaceInfo {
let (fbo_id, x, y) = compositor::bind_surface(
self.window,
@ -118,14 +122,15 @@ impl webrender::Compositor for DirectCompositeInterface {
fn add_surface(
&mut self,
id: webrender::NativeSurfaceId,
position: DeviceIntPoint,
transform: CompositorSurfaceTransform,
clip_rect: DeviceIntRect,
_image_rendering: ImageRendering,
) {
compositor::add_surface(
self.window,
id.0,
position.x,
position.y,
transform.transform_point2d(point2(0., 0.)).unwrap().x as i32,
transform.transform_point2d(point2(0., 0.)).unwrap().y as i32,
clip_rect.origin.x,
clip_rect.origin.y,
clip_rect.size.width,
@ -136,6 +141,42 @@ impl webrender::Compositor for DirectCompositeInterface {
fn end_frame(&mut self) {
compositor::end_transaction(self.window);
}
fn create_external_surface(&mut self, _: webrender::NativeSurfaceId, _: bool) { todo!() }
fn attach_external_image(
&mut self,
_id: webrender::NativeSurfaceId,
_external_image: ExternalImageId
) {
todo!()
}
fn enable_native_compositor(&mut self, _enable: bool) {
todo!()
}
fn deinit(&mut self) {
compositor::deinit(self.window);
}
fn get_capabilities(&self) -> webrender::CompositorCapabilities {
webrender::CompositorCapabilities {
virtual_surface_size: 1024 * 1024,
..Default::default()
}
}
fn invalidate_tile(
&mut self,
_id: webrender::NativeTileId,
_valid_rect: DeviceIntRect,
) {}
fn start_compositing(
&mut self,
_dirty_rects: &[DeviceIntRect],
_opaque_rects: &[DeviceIntRect],
) {}
}
// Simplisitic implementation of the WR notifier interface to know when a frame
@ -159,7 +200,7 @@ impl RenderNotifier for Notifier {
})
}
fn wake_up(&self) {
fn wake_up(&self, _composite_needed: bool) {
}
fn new_frame_ready(&self,
@ -181,7 +222,7 @@ fn push_rotated_rect(
time: f32,
) {
let color = color.scale_rgb(time);
let rotation = LayoutTransform::create_rotation(
let rotation = LayoutTransform::rotation(
0.0,
0.0,
1.0,
@ -194,13 +235,16 @@ fn push_rotated_rect(
);
let transform = rotation
.pre_translate(-transform_origin)
.post_translate(transform_origin);
.then_translate(transform_origin);
let spatial_id = builder.push_reference_frame(
LayoutPoint::zero(),
spatial_id,
TransformStyle::Flat,
PropertyBinding::Value(transform),
ReferenceFrameKind::Transform,
ReferenceFrameKind::Transform {
is_2d_scale_translation: false,
should_snap: false,
},
);
builder.push_rect(
&CommonItemProperties::new(
@ -235,7 +279,7 @@ fn build_display_list(
let scroll_space_info = builder.define_scroll_frame(
&fixed_space_info,
Some(scroll_id),
scroll_id,
LayoutRect::new(LayoutPoint::zero(), layout_size),
LayoutRect::new(LayoutPoint::zero(), layout_size),
ScrollSensitivity::Script,
@ -289,6 +333,32 @@ fn build_display_list(
0.1,
time,
);
push_rotated_rect(
builder,
LayoutRect::new(
LayoutPoint::new(100.0, 600.0),
LayoutSize::new(size_factor * 400.0, size_factor * 400.0),
),
ColorF::new(1.0, 1.0, 0.0, 1.0),
scroll_space_info.spatial_id,
root_pipeline_id,
time,
time,
);
push_rotated_rect(
builder,
LayoutRect::new(
LayoutPoint::new(700.0, 600.0),
LayoutSize::new(size_factor * 400.0, size_factor * 400.0),
),
ColorF::new(0.0, 1.0, 1.0, 1.0),
scroll_space_info.spatial_id,
root_pipeline_id,
time,
time,
);
}
#[derive(Debug, Copy, Clone)]
@ -358,13 +428,15 @@ fn main() {
} else {
webrender::CompositorConfig::Draw {
max_partial_present_rects: 0,
draw_previous_partial_present_regions: false,
partial_present: None,
}
};
let opts = webrender::RendererOptions {
clear_color: Some(ColorF::new(1.0, 1.0, 1.0, 1.0)),
debug_flags,
enable_picture_caching: true,
compositor_config,
surface_origin_is_top_left: false,
..webrender::RendererOptions::default()
};
let (tx, rx) = mpsc::channel();
@ -383,10 +455,9 @@ fn main() {
notifier,
opts,
None,
device_size,
).unwrap();
let api = sender.create_api();
let document_id = api.add_document(device_size, 0);
let mut api = sender.create_api();
let document_id = api.add_document(device_size);
let device_pixel_ratio = 1.0;
let mut current_epoch = Epoch(0);
let root_pipeline_id = PipelineId(0, 0);
@ -399,7 +470,7 @@ fn main() {
txn.set_root_pipeline(root_pipeline_id);
if let Invalidations::Scrolling = inv_mode {
let mut root_builder = DisplayListBuilder::new(root_pipeline_id, layout_size);
let mut root_builder = DisplayListBuilder::new(root_pipeline_id);
build_display_list(
&mut root_builder,
@ -419,7 +490,7 @@ fn main() {
);
}
txn.generate_frame();
txn.generate_frame(0);
api.send_transaction(document_id, txn);
// Tick the compositor (in this sample, we don't block on UI events)
@ -429,7 +500,7 @@ fn main() {
// Update and render. This will invoke the native compositor interface implemented above
// as required.
renderer.update();
renderer.render(device_size).unwrap();
renderer.render(device_size, 0).unwrap();
let _ = renderer.flush_pipeline_info();
// Construct a simple display list that can be drawn and composited by DC.
@ -437,7 +508,7 @@ fn main() {
match inv_mode {
Invalidations::Small | Invalidations::Large => {
let mut root_builder = DisplayListBuilder::new(root_pipeline_id, layout_size);
let mut root_builder = DisplayListBuilder::new(root_pipeline_id);
build_display_list(
&mut root_builder,
@ -466,7 +537,7 @@ fn main() {
}
}
txn.generate_frame();
txn.generate_frame(0);
api.send_transaction(document_id, txn);
current_epoch.0 += 1;
time += 0.001;

View file

@ -26,10 +26,6 @@ path = "blob.rs"
name = "document"
path = "document.rs"
[[bin]]
name = "frame_output"
path = "frame_output.rs"
[[bin]]
name = "iframe"
path = "iframe.rs"
@ -55,13 +51,13 @@ name = "yuv"
path = "yuv.rs"
[features]
debug = ["webrender/capture", "webrender/debugger", "webrender/profiler"]
debug = ["webrender/capture", "webrender/profiler"]
[dependencies]
app_units = "0.7"
env_logger = "0.5"
euclid = "0.22"
gleam = "0.12"
gleam = "0.15"
glutin = "0.21"
rayon = "1"
webrender = { path = "../webrender" }

View file

@ -2,11 +2,11 @@
* 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 euclid;
use gleam;
use glutin;
use webrender;
use winit;
extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
@ -14,6 +14,7 @@ mod boilerplate;
use crate::boilerplate::{Example, HandyDandyRectBuilder};
use std::cmp;
use webrender::api::*;
use webrender::render_api::*;
use webrender::api::units::DeviceIntSize;

View file

@ -10,11 +10,11 @@
//! rounded cornered rectangle, which is done automatically during the
//! scene building for render optimization.
use euclid;
use gleam;
use glutin;
use webrender;
use winit;
extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
@ -22,6 +22,7 @@ mod boilerplate;
use crate::boilerplate::{Example, HandyDandyRectBuilder};
use euclid::Angle;
use webrender::api::*;
use webrender::render_api::*;
use webrender::api::units::*;
@ -62,7 +63,10 @@ impl App {
SpatialId::root_scroll_node(pipeline_id),
TransformStyle::Flat,
PropertyBinding::Binding(property_key, LayoutTransform::identity()),
ReferenceFrameKind::Transform,
ReferenceFrameKind::Transform {
is_2d_scale_translation: false,
should_snap: false,
},
);
builder.push_simple_stacking_context_with_filters(
@ -194,7 +198,7 @@ impl Example for App {
colors: vec![],
},
);
txn.generate_frame();
txn.generate_frame(0);
api.send_transaction(document_id, txn);
}
_ => (),

View file

@ -2,11 +2,11 @@
* 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 euclid;
use gleam;
use glutin;
use webrender;
use winit;
extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
@ -17,6 +17,7 @@ use winit::TouchPhase;
use std::collections::HashMap;
use webrender::ShaderPrecacheFlags;
use webrender::api::*;
use webrender::render_api::*;
use webrender::api::units::*;
@ -190,7 +191,7 @@ impl Example for App {
pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let content_bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
let content_bounds = LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(800.0, 600.0));
let root_space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
let spatial_id = root_space_and_clip.spatial_id;
@ -220,6 +221,8 @@ impl Example for App {
let mask_clip_id = builder.define_clip_image_mask(
&root_space_and_clip,
mask,
&vec![],
FillRule::Nonzero,
);
let clip_id = builder.define_clip_rounded_rect(
&SpaceAndClipInfo {
@ -312,7 +315,7 @@ impl Example for App {
}
if !txn.is_empty() {
txn.generate_frame();
txn.generate_frame(0);
api.send_transaction(document_id, txn);
}

View file

@ -2,11 +2,11 @@
* 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;
use glutin;
use rayon;
use webrender;
use winit;
extern crate gleam;
extern crate glutin;
extern crate rayon;
extern crate webrender;
extern crate winit;
#[path = "common/boilerplate.rs"]
mod boilerplate;
@ -16,9 +16,10 @@ use rayon::{ThreadPool, ThreadPoolBuilder};
use rayon::prelude::*;
use std::collections::HashMap;
use std::sync::Arc;
use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, PrimitiveFlags, RenderApi, Transaction};
use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, PrimitiveFlags};
use webrender::api::{ColorF, CommonItemProperties, SpaceAndClipInfo, ImageDescriptorFlags};
use webrender::api::units::*;
use webrender::render_api::*;
use webrender::euclid::size2;
// This example shows how to implement a very basic BlobImageHandler that can only render

View file

@ -10,6 +10,7 @@ use webrender;
use winit;
use webrender::{DebugFlags, ShaderPrecacheFlags};
use webrender::api::*;
use webrender::render_api::*;
use webrender::api::units::*;
struct Notifier {
@ -29,7 +30,7 @@ impl RenderNotifier for Notifier {
})
}
fn wake_up(&self) {
fn wake_up(&self, _composite_needed: bool) {
#[cfg(not(target_os = "android"))]
let _ = self.events_proxy.wakeup();
}
@ -37,9 +38,9 @@ impl RenderNotifier for Notifier {
fn new_frame_ready(&self,
_: DocumentId,
_scrolled: bool,
_composite_needed: bool,
composite_needed: bool,
_render_time: Option<u64>) {
self.wake_up();
self.wake_up(composite_needed);
}
}
@ -88,12 +89,11 @@ pub trait Example {
) -> 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) {
}
@ -183,16 +183,11 @@ 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 document_id = api.add_document(device_size);
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);
@ -201,7 +196,7 @@ pub fn main_wrapper<E: Example>(
let epoch = Epoch(0);
let pipeline_id = PipelineId(0, 0);
let layout_size = device_size.to_f32() / euclid::Scale::new(device_pixel_ratio);
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
let mut builder = DisplayListBuilder::new(pipeline_id);
let mut txn = Transaction::new();
example.render(
@ -220,7 +215,7 @@ pub fn main_wrapper<E: Example>(
true,
);
txn.set_root_pipeline(pipeline_id);
txn.generate_frame();
txn.generate_frame(0);
api.send_transaction(document_id, txn);
println!("Entering event loop");
@ -259,14 +254,10 @@ pub fn main_wrapper<E: Example>(
winit::VirtualKeyCode::P => debug_flags.toggle(DebugFlags::PROFILER_DBG),
winit::VirtualKeyCode::O => debug_flags.toggle(DebugFlags::RENDER_TARGET_DBG),
winit::VirtualKeyCode::I => debug_flags.toggle(DebugFlags::TEXTURE_CACHE_DBG),
winit::VirtualKeyCode::S => debug_flags.toggle(DebugFlags::COMPACT_PROFILER),
winit::VirtualKeyCode::T => debug_flags.toggle(DebugFlags::PICTURE_CACHING_DBG),
winit::VirtualKeyCode::Q => debug_flags.toggle(
DebugFlags::GPU_TIME_QUERIES | DebugFlags::GPU_SAMPLE_QUERIES
),
winit::VirtualKeyCode::F => debug_flags.toggle(
DebugFlags::NEW_FRAME_INDICATOR | DebugFlags::NEW_SCENE_INDICATOR
),
winit::VirtualKeyCode::G => debug_flags.toggle(DebugFlags::GPU_CACHE_DBG),
winit::VirtualKeyCode::Key1 => txn.set_document_view(
device_size.into(),
@ -304,7 +295,7 @@ pub fn main_wrapper<E: Example>(
}
if custom_event {
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
let mut builder = DisplayListBuilder::new(pipeline_id);
example.render(
&mut api,
@ -321,12 +312,12 @@ pub fn main_wrapper<E: Example>(
builder.finalize(),
true,
);
txn.generate_frame();
txn.generate_frame(0);
}
api.send_transaction(document_id, txn);
renderer.update();
renderer.render(device_size).unwrap();
renderer.render(device_size, 0).unwrap();
let _ = renderer.flush_pipeline_info();
example.draw_custom(&*gl);
windowed_context.swap_buffers().ok();

Some files were not shown because too many files have changed in this diff Show more