1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #![allow(clippy::missing_safety_doc)]
6 #![allow(clippy::not_unsafe_ptr_arg_deref)]
9 use std::cell::RefCell;
10 #[cfg(not(target_os = "macos"))]
11 use std::ffi::OsString;
12 use std::ffi::{CStr, CString};
14 use std::marker::PhantomData;
16 #[cfg(target_os = "android")]
17 use std::os::raw::c_int;
18 use std::os::raw::{c_char, c_float, c_void};
19 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
20 use std::os::unix::ffi::OsStringExt;
21 #[cfg(target_os = "windows")]
22 use std::os::windows::ffi::OsStringExt;
23 use std::path::PathBuf;
25 use std::sync::atomic::{AtomicUsize, Ordering};
27 use std::time::Duration;
28 use std::{env, mem, ptr, slice};
29 use thin_vec::ThinVec;
31 use euclid::SideOffsets2D;
32 use moz2d_renderer::Moz2dBlobImageHandler;
33 use nsstring::nsAString;
35 use program_cache::{remove_disk_cache, WrProgramCache};
37 use tracy_rs::register_thread_with_profiler;
38 use webrender::sw_compositor::SwCompositor;
40 api::units::*, api::*, render_api::*, set_profiler_hooks, AsyncPropertySampler, AsyncScreenshotHandle, Compositor,
41 CompositorCapabilities, CompositorConfig, CompositorSurfaceTransform, DebugFlags, Device, MappableCompositor,
42 MappedTileInfo, NativeSurfaceId, NativeSurfaceInfo, NativeTileId, PartialPresentCompositor, PipelineInfo,
43 ProfilerHooks, RecordedFrameHandle, Renderer, RendererOptions, RendererStats, SWGLCompositeSurfaceInfo,
44 SceneBuilderHooks, ShaderPrecacheFlags, Shaders, SharedShaders, TextureCacheConfig, UploadMethod, WindowVisibility,
47 use wr_malloc_size_of::MallocSizeOfOps;
50 #[cfg(target_os = "android")]
51 fn __android_log_write(prio: c_int, tag: *const c_char, text: *const c_char) -> c_int;
54 /// The unique id for WR resource identification.
55 static NEXT_NAMESPACE_ID: AtomicUsize = AtomicUsize::new(1);
57 /// Special value handled in this wrapper layer to signify a redundant clip chain.
58 pub const ROOT_CLIP_CHAIN: u64 = !0;
60 fn next_namespace_id() -> IdNamespace {
61 IdNamespace(NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed) as u32)
64 /// Whether a border should be antialiased.
66 #[derive(Eq, PartialEq, Copy, Clone)]
67 pub enum AntialiasBorder {
72 /// Used to indicate if an image is opaque, or has an alpha channel.
74 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
75 pub enum OpacityType {
80 /// cbindgen:field-names=[mHandle]
81 /// cbindgen:derive-lt=true
82 /// cbindgen:derive-lte=true
83 /// cbindgen:derive-neq=true
85 /// cbindgen:field-names=[mHandle]
86 /// cbindgen:derive-lt=true
87 /// cbindgen:derive-lte=true
88 /// cbindgen:derive-neq=true
89 pub type WrIdNamespace = IdNamespace;
91 /// cbindgen:field-names=[mNamespace, mHandle]
92 type WrDocumentId = DocumentId;
93 /// cbindgen:field-names=[mNamespace, mHandle]
94 type WrPipelineId = PipelineId;
95 /// cbindgen:field-names=[mNamespace, mHandle]
96 /// cbindgen:derive-neq=true
97 type WrImageKey = ImageKey;
98 /// cbindgen:field-names=[mNamespace, mHandle]
99 pub type WrFontKey = FontKey;
100 /// cbindgen:field-names=[mNamespace, mHandle]
101 pub type WrFontInstanceKey = FontInstanceKey;
102 /// cbindgen:field-names=[mNamespace, mHandle]
103 type WrYuvColorSpace = YuvColorSpace;
104 /// cbindgen:field-names=[mNamespace, mHandle]
105 type WrColorDepth = ColorDepth;
106 /// cbindgen:field-names=[mNamespace, mHandle]
107 type WrColorRange = ColorRange;
110 fn clip_chain_id_to_webrender(id: u64, pipeline_id: WrPipelineId) -> ClipId {
111 if id == ROOT_CLIP_CHAIN {
112 ClipId::root(pipeline_id)
114 ClipId::ClipChain(ClipChainId(id, pipeline_id))
119 pub struct WrSpaceAndClipChain {
124 impl WrSpaceAndClipChain {
125 fn to_webrender(&self, pipeline_id: WrPipelineId) -> SpaceAndClipInfo {
126 //Warning: special case here to support dummy clip chain
128 spatial_id: self.space.to_webrender(pipeline_id),
129 clip_id: clip_chain_id_to_webrender(self.clip_chain, pipeline_id),
135 pub enum WrStackingContextClip {
141 impl WrStackingContextClip {
142 fn to_webrender(&self, pipeline_id: WrPipelineId) -> Option<ClipId> {
144 WrStackingContextClip::None => None,
145 WrStackingContextClip::ClipChain(id) => Some(clip_chain_id_to_webrender(id, pipeline_id)),
146 WrStackingContextClip::ClipId(id) => Some(id.to_webrender(pipeline_id)),
151 unsafe fn make_slice<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
155 slice::from_raw_parts(ptr, len)
159 unsafe fn make_slice_mut<'a, T>(ptr: *mut T, len: usize) -> &'a mut [T] {
163 slice::from_raw_parts_mut(ptr, len)
167 pub struct DocumentHandle {
169 document_id: DocumentId,
170 // One of the two options below is Some and the other None at all times.
171 // It would be nice to model with an enum, however it is tricky to express
172 // moving a variant's content into another variant without moving the
174 hit_tester_request: Option<HitTesterRequest>,
175 hit_tester: Option<Arc<dyn ApiHitTester>>,
178 impl DocumentHandle {
181 hit_tester: Option<Arc<dyn ApiHitTester>>,
184 ) -> DocumentHandle {
185 let doc = api.add_document_with_id(size, id);
186 let hit_tester_request = if hit_tester.is_none() {
187 // Request the hit tester early to reduce the likelihood of blocking on the
188 // first hit testing query.
189 Some(api.request_hit_tester(doc))
202 fn ensure_hit_tester(&mut self) -> &Arc<dyn ApiHitTester> {
203 if let Some(ref ht) = self.hit_tester {
206 self.hit_tester = Some(self.hit_tester_request.take().unwrap().resolve());
207 self.hit_tester.as_ref().unwrap()
219 fn into_vec(self) -> Vec<u8> {
220 unsafe { Vec::from_raw_parts(self.data, self.length, self.capacity) }
223 // Equivalent to `into_vec` but clears self instead of consuming the value.
224 fn flush_into_vec(&mut self) -> Vec<u8> {
225 self.convert_into_vec::<u8>()
228 // Like flush_into_vec, but also does an unsafe conversion to the desired type.
229 fn convert_into_vec<T>(&mut self) -> Vec<T> {
233 self.length / mem::size_of::<T>(),
234 self.capacity / mem::size_of::<T>(),
237 self.data = ptr::null_mut();
243 fn from_vec(mut v: Vec<u8>) -> WrVecU8 {
245 data: v.as_mut_ptr(),
247 capacity: v.capacity(),
253 fn reserve(&mut self, len: usize) {
254 let mut vec = self.flush_into_vec();
256 *self = Self::from_vec(vec);
259 fn push_bytes(&mut self, bytes: &[u8]) {
260 let mut vec = self.flush_into_vec();
261 vec.extend_from_slice(bytes);
262 *self = Self::from_vec(vec);
267 pub extern "C" fn wr_vec_u8_push_bytes(v: &mut WrVecU8, bytes: ByteSlice) {
268 v.push_bytes(bytes.as_slice());
272 pub extern "C" fn wr_vec_u8_reserve(v: &mut WrVecU8, len: usize) {
277 pub extern "C" fn wr_vec_u8_free(v: WrVecU8) {
282 pub struct ByteSlice<'a> {
285 _phantom: PhantomData<&'a ()>,
288 impl<'a> ByteSlice<'a> {
289 pub fn new(slice: &'a [u8]) -> ByteSlice<'a> {
291 buffer: slice.as_ptr(),
293 _phantom: PhantomData,
297 pub fn as_slice(&self) -> &'a [u8] {
298 unsafe { make_slice(self.buffer, self.len) }
303 pub struct MutByteSlice<'a> {
306 _phantom: PhantomData<&'a ()>,
309 impl<'a> MutByteSlice<'a> {
310 pub fn new(slice: &'a mut [u8]) -> MutByteSlice<'a> {
311 let len = slice.len();
313 buffer: slice.as_mut_ptr(),
315 _phantom: PhantomData,
319 pub fn as_mut_slice(&mut self) -> &'a mut [u8] {
320 unsafe { make_slice_mut(self.buffer, self.len) }
325 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
326 pub struct WrImageDescriptor {
327 pub format: ImageFormat,
331 pub opacity: OpacityType,
332 // TODO(gw): Remove this flag (use prim flags instead).
333 pub prefer_compositor_surface: bool,
336 impl<'a> From<&'a WrImageDescriptor> for ImageDescriptor {
337 fn from(desc: &'a WrImageDescriptor) -> ImageDescriptor {
338 let mut flags = ImageDescriptorFlags::empty();
340 if desc.opacity == OpacityType::Opaque {
341 flags |= ImageDescriptorFlags::IS_OPAQUE;
345 size: DeviceIntSize::new(desc.width, desc.height),
346 stride: if desc.stride != 0 { Some(desc.stride) } else { None },
356 enum WrExternalImageType {
363 struct WrExternalImage {
364 image_type: WrExternalImageType,
366 // external texture handle
368 // external texture coordinate
374 // external image buffer
380 fn wr_renderer_lock_external_image(
381 renderer: *mut c_void,
382 external_image_id: ExternalImageId,
384 rendering: ImageRendering,
385 ) -> WrExternalImage;
386 fn wr_renderer_unlock_external_image(renderer: *mut c_void, external_image_id: ExternalImageId, channel_index: u8);
390 #[derive(Copy, Clone, Debug)]
391 pub struct WrExternalImageHandler {
392 external_image_obj: *mut c_void,
395 impl ExternalImageHandler for WrExternalImageHandler {
396 fn lock(&mut self, id: ExternalImageId, channel_index: u8, rendering: ImageRendering) -> ExternalImage {
397 let image = unsafe { wr_renderer_lock_external_image(self.external_image_obj, id, channel_index, rendering) };
399 uv: TexelRect::new(image.u0, image.v0, image.u1, image.v1),
400 source: match image.image_type {
401 WrExternalImageType::NativeTexture => ExternalImageSource::NativeTexture(image.handle),
402 WrExternalImageType::RawData => {
403 ExternalImageSource::RawData(unsafe { make_slice(image.buff, image.size) })
405 WrExternalImageType::Invalid => ExternalImageSource::Invalid,
410 fn unlock(&mut self, id: ExternalImageId, channel_index: u8) {
412 wr_renderer_unlock_external_image(self.external_image_obj, id, channel_index);
418 #[derive(Clone, Copy)]
419 // Used for ComponentTransfer only
420 pub struct WrFilterData {
421 funcR_type: ComponentTransferFuncType,
422 R_values: *mut c_float,
423 R_values_count: usize,
424 funcG_type: ComponentTransferFuncType,
425 G_values: *mut c_float,
426 G_values_count: usize,
427 funcB_type: ComponentTransferFuncType,
428 B_values: *mut c_float,
429 B_values_count: usize,
430 funcA_type: ComponentTransferFuncType,
431 A_values: *mut c_float,
432 A_values_count: usize,
437 pub enum WrAnimationType {
444 pub struct WrAnimationProperty {
445 effect_type: WrAnimationType,
447 key: SpatialTreeItemKey,
450 /// cbindgen:derive-eq=false
453 pub struct WrAnimationPropertyValue<T> {
458 pub type WrTransformProperty = WrAnimationPropertyValue<LayoutTransform>;
459 pub type WrOpacityProperty = WrAnimationPropertyValue<f32>;
460 pub type WrColorProperty = WrAnimationPropertyValue<ColorF>;
462 /// cbindgen:field-names=[mHandle]
463 /// cbindgen:derive-lt=true
464 /// cbindgen:derive-lte=true
466 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
467 pub struct WrWindowId(u64);
471 pub struct WrComputedTransformData {
472 pub scale_from: LayoutSize,
473 pub vertical_flip: bool,
474 pub rotation: WrRotation,
475 pub key: SpatialTreeItemKey,
479 pub struct WrTransformInfo {
480 pub transform: LayoutTransform,
481 pub key: SpatialTreeItemKey,
484 fn get_proc_address(glcontext_ptr: *mut c_void, name: &str) -> *const c_void {
486 fn get_proc_address_from_glcontext(glcontext_ptr: *mut c_void, procname: *const c_char) -> *const c_void;
489 let symbol_name = CString::new(name).unwrap();
490 let symbol = unsafe { get_proc_address_from_glcontext(glcontext_ptr, symbol_name.as_ptr()) };
495 pub enum TelemetryProbe {
502 fn is_in_compositor_thread() -> bool;
503 fn is_in_render_thread() -> bool;
504 fn is_in_main_thread() -> bool;
505 fn is_glcontext_gles(glcontext_ptr: *mut c_void) -> bool;
506 fn is_glcontext_angle(glcontext_ptr: *mut c_void) -> bool;
507 fn gfx_wr_resource_path_override() -> *const c_char;
508 fn gfx_wr_use_optimized_shaders() -> bool;
509 // TODO: make gfx_critical_error() work.
510 // We still have problem to pass the error message from render/render_backend
511 // thread to main thread now.
513 fn gfx_critical_error(msg: *const c_char);
514 fn gfx_critical_note(msg: *const c_char);
515 fn gfx_wr_set_crash_annotation(annotation: CrashAnnotation, value: *const c_char);
516 fn gfx_wr_clear_crash_annotation(annotation: CrashAnnotation);
520 window_id: WrWindowId,
523 unsafe impl Send for CppNotifier {}
526 fn wr_notifier_wake_up(window_id: WrWindowId, composite_needed: bool);
527 fn wr_notifier_new_frame_ready(window_id: WrWindowId);
528 fn wr_notifier_nop_frame_done(window_id: WrWindowId);
529 fn wr_notifier_external_event(window_id: WrWindowId, raw_event: usize);
530 fn wr_schedule_render(window_id: WrWindowId, reasons: RenderReasons);
531 // NOTE: This moves away from pipeline_info.
532 fn wr_finished_scene_build(window_id: WrWindowId, pipeline_info: &mut WrPipelineInfo);
534 fn wr_transaction_notification_notified(handler: usize, when: Checkpoint);
537 impl RenderNotifier for CppNotifier {
538 fn clone(&self) -> Box<dyn RenderNotifier> {
539 Box::new(CppNotifier {
540 window_id: self.window_id,
544 fn wake_up(&self, composite_needed: bool) {
546 wr_notifier_wake_up(self.window_id, composite_needed);
550 fn new_frame_ready(&self, _: DocumentId, _scrolled: bool, composite_needed: bool) {
552 if composite_needed {
553 wr_notifier_new_frame_ready(self.window_id);
555 wr_notifier_nop_frame_done(self.window_id);
560 fn external_event(&self, event: ExternalEvent) {
562 wr_notifier_external_event(self.window_id, event.unwrap());
567 struct MozCrashAnnotator;
569 unsafe impl Send for MozCrashAnnotator {}
571 impl CrashAnnotator for MozCrashAnnotator {
572 fn set(&self, annotation: CrashAnnotation, value: &std::ffi::CStr) {
574 gfx_wr_set_crash_annotation(annotation, value.as_ptr());
578 fn clear(&self, annotation: CrashAnnotation) {
580 gfx_wr_clear_crash_annotation(annotation);
584 fn box_clone(&self) -> Box<dyn CrashAnnotator> {
585 Box::new(MozCrashAnnotator)
590 pub extern "C" fn wr_renderer_set_clear_color(renderer: &mut Renderer, color: ColorF) {
591 renderer.set_clear_color(color);
595 pub extern "C" fn wr_renderer_set_external_image_handler(
596 renderer: &mut Renderer,
597 external_image_handler: &mut WrExternalImageHandler,
599 renderer.set_external_image_handler(Box::new(*external_image_handler));
603 pub extern "C" fn wr_renderer_update(renderer: &mut Renderer) {
608 pub extern "C" fn wr_renderer_render(
609 renderer: &mut Renderer,
613 out_stats: &mut RendererStats,
614 out_dirty_rects: &mut ThinVec<DeviceIntRect>,
616 match renderer.render(DeviceIntSize::new(width, height), buffer_age) {
618 *out_stats = results.stats;
619 out_dirty_rects.extend(results.dirty_rects);
624 warn!(" Failed to render: {:?}", e);
625 let msg = CString::new(format!("wr_renderer_render: {:?}", e)).unwrap();
627 gfx_critical_note(msg.as_ptr());
636 pub extern "C" fn wr_renderer_force_redraw(renderer: &mut Renderer) {
637 renderer.force_redraw();
641 pub extern "C" fn wr_renderer_record_frame(
642 renderer: &mut Renderer,
643 image_format: ImageFormat,
644 out_handle: &mut RecordedFrameHandle,
646 out_height: &mut i32,
648 if let Some((handle, size)) = renderer.record_frame(image_format) {
649 *out_handle = handle;
650 *out_width = size.width;
651 *out_height = size.height;
660 pub extern "C" fn wr_renderer_map_recorded_frame(
661 renderer: &mut Renderer,
662 handle: RecordedFrameHandle,
664 dst_buffer_len: usize,
667 renderer.map_recorded_frame(
669 unsafe { make_slice_mut(dst_buffer, dst_buffer_len) },
675 pub extern "C" fn wr_renderer_release_composition_recorder_structures(renderer: &mut Renderer) {
676 renderer.release_composition_recorder_structures();
680 pub extern "C" fn wr_renderer_get_screenshot_async(
681 renderer: &mut Renderer,
688 image_format: ImageFormat,
689 screenshot_width: *mut i32,
690 screenshot_height: *mut i32,
691 ) -> AsyncScreenshotHandle {
692 assert!(!screenshot_width.is_null());
693 assert!(!screenshot_height.is_null());
695 let (handle, size) = renderer.get_screenshot_async(
696 DeviceIntRect::from_origin_and_size(
697 DeviceIntPoint::new(window_x, window_y),
698 DeviceIntSize::new(window_width, window_height),
700 DeviceIntSize::new(buffer_width, buffer_height),
705 *screenshot_width = size.width;
706 *screenshot_height = size.height;
713 pub extern "C" fn wr_renderer_map_and_recycle_screenshot(
714 renderer: &mut Renderer,
715 handle: AsyncScreenshotHandle,
717 dst_buffer_len: usize,
720 renderer.map_and_recycle_screenshot(
722 unsafe { make_slice_mut(dst_buffer, dst_buffer_len) },
728 pub extern "C" fn wr_renderer_release_profiler_structures(renderer: &mut Renderer) {
729 renderer.release_profiler_structures();
732 // Call wr_renderer_render() before calling this function.
734 pub unsafe extern "C" fn wr_renderer_readback(
735 renderer: &mut Renderer,
742 assert!(is_in_render_thread());
744 let mut slice = make_slice_mut(dst_buffer, buffer_size);
745 renderer.read_pixels_into(FramebufferIntSize::new(width, height).into(), format, &mut slice);
749 pub unsafe extern "C" fn wr_renderer_set_profiler_ui(renderer: &mut Renderer, ui_str: *const u8, ui_str_len: usize) {
750 let slice = std::slice::from_raw_parts(ui_str, ui_str_len);
751 if let Ok(ui_str) = std::str::from_utf8(slice) {
752 renderer.set_profiler_ui(ui_str);
757 pub unsafe extern "C" fn wr_renderer_delete(renderer: *mut Renderer) {
758 let renderer = Box::from_raw(renderer);
760 // let renderer go out of scope and get dropped
764 pub unsafe extern "C" fn wr_renderer_accumulate_memory_report(
765 renderer: &mut Renderer,
766 report: &mut MemoryReport,
769 *report += renderer.report_memory(swgl);
772 // cbindgen doesn't support tuples, so we have a little struct instead, with
773 // an Into implementation to convert from the tuple to the struct.
775 pub struct WrPipelineEpoch {
776 pipeline_id: WrPipelineId,
777 document_id: WrDocumentId,
781 impl<'a> From<(&'a (WrPipelineId, WrDocumentId), &'a WrEpoch)> for WrPipelineEpoch {
782 fn from(tuple: (&(WrPipelineId, WrDocumentId), &WrEpoch)) -> WrPipelineEpoch {
784 pipeline_id: (tuple.0).0,
785 document_id: (tuple.0).1,
792 pub struct WrPipelineIdAndEpoch {
793 pipeline_id: WrPipelineId,
797 impl<'a> From<(&WrPipelineId, &WrEpoch)> for WrPipelineIdAndEpoch {
798 fn from(tuple: (&WrPipelineId, &WrEpoch)) -> WrPipelineIdAndEpoch {
799 WrPipelineIdAndEpoch {
800 pipeline_id: *tuple.0,
807 pub struct WrRemovedPipeline {
808 pipeline_id: WrPipelineId,
809 document_id: WrDocumentId,
812 impl<'a> From<&'a (WrPipelineId, WrDocumentId)> for WrRemovedPipeline {
813 fn from(tuple: &(WrPipelineId, WrDocumentId)) -> WrRemovedPipeline {
815 pipeline_id: tuple.0,
816 document_id: tuple.1,
822 pub struct WrPipelineInfo {
823 /// This contains an entry for each pipeline that was rendered, along with
824 /// the epoch at which it was rendered. Rendered pipelines include the root
825 /// pipeline and any other pipelines that were reachable via IFrame display
826 /// items from the root pipeline.
827 epochs: ThinVec<WrPipelineEpoch>,
828 /// This contains an entry for each pipeline that was removed during the
829 /// last transaction. These pipelines would have been explicitly removed by
830 /// calling remove_pipeline on the transaction object; the pipeline showing
831 /// up in this array means that the data structures have been torn down on
832 /// the webrender side, and so any remaining data structures on the caller
833 /// side can now be torn down also.
834 removed_pipelines: ThinVec<WrRemovedPipeline>,
837 impl WrPipelineInfo {
838 fn new(info: &PipelineInfo) -> Self {
840 epochs: info.epochs.iter().map(WrPipelineEpoch::from).collect(),
841 removed_pipelines: info.removed_pipelines.iter().map(WrRemovedPipeline::from).collect(),
847 pub unsafe extern "C" fn wr_renderer_flush_pipeline_info(renderer: &mut Renderer, out: &mut WrPipelineInfo) {
848 let info = renderer.flush_pipeline_info();
849 *out = WrPipelineInfo::new(&info);
853 pub fn gecko_profiler_thread_is_being_profiled() -> bool;
856 pub fn gecko_profiler_start_marker(name: &str) {
857 use gecko_profiler::{gecko_profiler_category, MarkerOptions, MarkerTiming, ProfilerTime, Tracing};
858 gecko_profiler::add_marker(
860 gecko_profiler_category!(Graphics),
862 timing: MarkerTiming::interval_start(ProfilerTime::now()),
865 Tracing("Webrender".to_string()),
868 pub fn gecko_profiler_end_marker(name: &str) {
869 use gecko_profiler::{gecko_profiler_category, MarkerOptions, MarkerTiming, ProfilerTime, Tracing};
870 gecko_profiler::add_marker(
872 gecko_profiler_category!(Graphics),
874 timing: MarkerTiming::interval_end(ProfilerTime::now()),
877 Tracing("Webrender".to_string()),
881 pub fn gecko_profiler_event_marker(name: &str) {
882 use gecko_profiler::{gecko_profiler_category, Tracing};
883 gecko_profiler::add_marker(
885 gecko_profiler_category!(Graphics),
887 Tracing("Webrender".to_string()),
891 pub fn gecko_profiler_add_text_marker(name: &str, text: &str, microseconds: f64) {
892 use gecko_profiler::{gecko_profiler_category, MarkerOptions, MarkerTiming, ProfilerTime};
893 if !gecko_profiler::can_accept_markers() {
897 let now = ProfilerTime::now();
898 let start = now.clone().subtract_microseconds(microseconds);
899 gecko_profiler::add_text_marker(
901 gecko_profiler_category!(Graphics),
903 timing: MarkerTiming::interval(start, now),
910 /// Simple implementation of the WR ProfilerHooks trait to allow profile
911 /// markers to be seen in the Gecko profiler.
912 struct GeckoProfilerHooks;
914 impl ProfilerHooks for GeckoProfilerHooks {
915 fn register_thread(&self, thread_name: &str) {
916 gecko_profiler::register_thread(thread_name);
919 fn unregister_thread(&self) {
920 gecko_profiler::unregister_thread();
923 fn begin_marker(&self, label: &str) {
924 gecko_profiler_start_marker(label);
927 fn end_marker(&self, label: &str) {
928 gecko_profiler_end_marker(label);
931 fn event_marker(&self, label: &str) {
932 gecko_profiler_event_marker(label);
935 fn add_text_marker(&self, label: &str, text: &str, duration: Duration) {
936 let micros = duration.as_micros() as f64;
937 gecko_profiler_add_text_marker(label, text, micros);
940 fn thread_is_being_profiled(&self) -> bool {
941 unsafe { gecko_profiler_thread_is_being_profiled() }
945 static PROFILER_HOOKS: GeckoProfilerHooks = GeckoProfilerHooks {};
947 #[allow(improper_ctypes)] // this is needed so that rustc doesn't complain about passing the &mut Transaction to an extern function
949 // These callbacks are invoked from the scene builder thread (aka the APZ
951 fn apz_register_updater(window_id: WrWindowId);
952 fn apz_pre_scene_swap(window_id: WrWindowId);
953 fn apz_post_scene_swap(window_id: WrWindowId, pipeline_info: &WrPipelineInfo);
954 fn apz_run_updater(window_id: WrWindowId);
955 fn apz_deregister_updater(window_id: WrWindowId);
957 // These callbacks are invoked from the render backend thread (aka the APZ
959 fn apz_register_sampler(window_id: WrWindowId);
960 fn apz_sample_transforms(window_id: WrWindowId, generated_frame_id: *const u64, transaction: &mut Transaction);
961 fn apz_deregister_sampler(window_id: WrWindowId);
963 fn omta_register_sampler(window_id: WrWindowId);
964 fn omta_sample(window_id: WrWindowId, transaction: &mut Transaction);
965 fn omta_deregister_sampler(window_id: WrWindowId);
968 struct APZCallbacks {
969 window_id: WrWindowId,
973 pub fn new(window_id: WrWindowId) -> Self {
974 APZCallbacks { window_id }
978 impl SceneBuilderHooks for APZCallbacks {
980 unsafe { apz_register_updater(self.window_id) }
983 fn pre_scene_build(&self) {
984 gecko_profiler_start_marker("SceneBuilding");
987 fn pre_scene_swap(&self) {
989 apz_pre_scene_swap(self.window_id);
993 fn post_scene_swap(&self, _document_ids: &Vec<DocumentId>, info: PipelineInfo) {
994 let mut info = WrPipelineInfo::new(&info);
996 apz_post_scene_swap(self.window_id, &info);
999 // After a scene swap we should schedule a render for the next vsync,
1000 // otherwise there's no guarantee that the new scene will get rendered
1002 unsafe { wr_finished_scene_build(self.window_id, &mut info) }
1003 gecko_profiler_end_marker("SceneBuilding");
1006 fn post_resource_update(&self, _document_ids: &Vec<DocumentId>) {
1007 unsafe { wr_schedule_render(self.window_id, RenderReasons::POST_RESOURCE_UPDATES_HOOK) }
1008 gecko_profiler_end_marker("SceneBuilding");
1011 fn post_empty_scene_build(&self) {
1012 gecko_profiler_end_marker("SceneBuilding");
1016 unsafe { apz_run_updater(self.window_id) }
1019 fn deregister(&self) {
1020 unsafe { apz_deregister_updater(self.window_id) }
1024 struct SamplerCallback {
1025 window_id: WrWindowId,
1028 impl SamplerCallback {
1029 pub fn new(window_id: WrWindowId) -> Self {
1030 SamplerCallback { window_id }
1034 impl AsyncPropertySampler for SamplerCallback {
1035 fn register(&self) {
1037 apz_register_sampler(self.window_id);
1038 omta_register_sampler(self.window_id);
1042 fn sample(&self, _document_id: DocumentId, generated_frame_id: Option<u64>) -> Vec<FrameMsg> {
1043 let generated_frame_id_value;
1044 let generated_frame_id: *const u64 = match generated_frame_id {
1046 generated_frame_id_value = id;
1047 &generated_frame_id_value
1049 None => ptr::null_mut(),
1051 let mut transaction = Transaction::new();
1052 // Reset the pending properties first because omta_sample and apz_sample_transforms
1053 // may be failed to reset them due to null samplers.
1054 transaction.reset_dynamic_properties();
1056 apz_sample_transforms(self.window_id, generated_frame_id, &mut transaction);
1057 omta_sample(self.window_id, &mut transaction);
1059 transaction.get_frame_ops()
1062 fn deregister(&self) {
1064 apz_deregister_sampler(self.window_id);
1065 omta_deregister_sampler(self.window_id);
1071 fn wr_register_thread_local_arena();
1074 pub struct WrThreadPool(Arc<rayon::ThreadPool>);
1077 pub extern "C" fn wr_thread_pool_new(low_priority: bool) -> *mut WrThreadPool {
1078 // Clamp the number of workers between 1 and 4/8. We get diminishing returns
1079 // with high worker counts and extra overhead because of rayon and font
1082 // We clamp to 4 high priority threads because contention and memory usage
1083 // make it not worth going higher
1084 let max = if low_priority { 8 } else { 4 };
1085 let num_threads = num_cpus::get().min(max);
1087 let priority_tag = if low_priority { "LP" } else { "" };
1089 let worker = rayon::ThreadPoolBuilder::new()
1090 .thread_name(move |idx| format!("WRWorker{}#{}", priority_tag, idx))
1091 .num_threads(num_threads)
1092 .start_handler(move |idx| {
1094 wr_register_thread_local_arena();
1096 let name = format!("WRWorker{}#{}", priority_tag, idx);
1097 register_thread_with_profiler(name.clone());
1098 gecko_profiler::register_thread(&name);
1100 .exit_handler(|_idx| {
1101 gecko_profiler::unregister_thread();
1105 let workers = Arc::new(worker.unwrap());
1107 Box::into_raw(Box::new(WrThreadPool(workers)))
1111 pub unsafe extern "C" fn wr_thread_pool_delete(thread_pool: *mut WrThreadPool) {
1112 Box::from_raw(thread_pool);
1116 pub unsafe extern "C" fn wr_program_cache_new(
1117 prof_path: &nsAString,
1118 thread_pool: *mut WrThreadPool,
1119 ) -> *mut WrProgramCache {
1120 let workers = &(*thread_pool).0;
1121 let program_cache = WrProgramCache::new(prof_path, workers);
1122 Box::into_raw(Box::new(program_cache))
1126 pub unsafe extern "C" fn wr_program_cache_delete(program_cache: *mut WrProgramCache) {
1127 Box::from_raw(program_cache);
1131 pub unsafe extern "C" fn wr_try_load_startup_shaders_from_disk(program_cache: *mut WrProgramCache) {
1132 (*program_cache).try_load_startup_shaders_from_disk();
1136 pub unsafe extern "C" fn remove_program_binary_disk_cache(prof_path: &nsAString) -> bool {
1137 match remove_disk_cache(prof_path) {
1140 error!("Failed to remove program binary disk cache");
1146 // This matches IsEnvSet in gfxEnv.h
1147 fn env_var_to_bool(key: &'static str) -> bool {
1148 env::var(key).ok().map_or(false, |v| !v.is_empty())
1151 // Call MakeCurrent before this.
1152 fn wr_device_new(gl_context: *mut c_void, pc: Option<&mut WrProgramCache>) -> Device {
1153 assert!(unsafe { is_in_render_thread() });
1156 if unsafe { is_glcontext_gles(gl_context) } {
1157 gl = unsafe { gl::GlesFns::load_with(|symbol| get_proc_address(gl_context, symbol)) };
1159 gl = unsafe { gl::GlFns::load_with(|symbol| get_proc_address(gl_context, symbol)) };
1162 let version = gl.get_string(gl::VERSION);
1164 info!("WebRender - OpenGL version new {}", version);
1166 let upload_method = if unsafe { is_glcontext_angle(gl_context) } {
1167 UploadMethod::Immediate
1169 UploadMethod::PixelBuffer(ONE_TIME_USAGE_HINT)
1172 let resource_override_path = unsafe {
1173 let override_charptr = gfx_wr_resource_path_override();
1174 if override_charptr.is_null() {
1177 match CStr::from_ptr(override_charptr).to_str() {
1178 Ok(override_str) => Some(PathBuf::from(override_str)),
1184 let use_optimized_shaders = unsafe { gfx_wr_use_optimized_shaders() };
1186 let cached_programs = pc.map(|cached_programs| Rc::clone(cached_programs.rc_get()));
1190 Some(Box::new(MozCrashAnnotator)),
1191 resource_override_path,
1192 use_optimized_shaders,
1205 fn wr_compositor_create_surface(
1206 compositor: *mut c_void,
1207 id: NativeSurfaceId,
1208 virtual_offset: DeviceIntPoint,
1209 tile_size: DeviceIntSize,
1212 fn wr_compositor_create_external_surface(compositor: *mut c_void, id: NativeSurfaceId, is_opaque: bool);
1213 fn wr_compositor_create_backdrop_surface(compositor: *mut c_void, id: NativeSurfaceId, color: ColorF);
1214 fn wr_compositor_destroy_surface(compositor: *mut c_void, id: NativeSurfaceId);
1215 fn wr_compositor_create_tile(compositor: *mut c_void, id: NativeSurfaceId, x: i32, y: i32);
1216 fn wr_compositor_destroy_tile(compositor: *mut c_void, id: NativeSurfaceId, x: i32, y: i32);
1217 fn wr_compositor_attach_external_image(
1218 compositor: *mut c_void,
1219 id: NativeSurfaceId,
1220 external_image: ExternalImageId,
1222 fn wr_compositor_bind(
1223 compositor: *mut c_void,
1225 offset: &mut DeviceIntPoint,
1227 dirty_rect: DeviceIntRect,
1228 valid_rect: DeviceIntRect,
1230 fn wr_compositor_unbind(compositor: *mut c_void);
1231 fn wr_compositor_begin_frame(compositor: *mut c_void);
1232 fn wr_compositor_add_surface(
1233 compositor: *mut c_void,
1234 id: NativeSurfaceId,
1235 transform: &CompositorSurfaceTransform,
1236 clip_rect: DeviceIntRect,
1237 image_rendering: ImageRendering,
1239 fn wr_compositor_start_compositing(
1240 compositor: *mut c_void,
1241 clear_color: ColorF,
1242 dirty_rects: *const DeviceIntRect,
1243 num_dirty_rects: usize,
1244 opaque_rects: *const DeviceIntRect,
1245 num_opaque_rects: usize,
1247 fn wr_compositor_end_frame(compositor: *mut c_void);
1248 fn wr_compositor_enable_native_compositor(compositor: *mut c_void, enable: bool);
1249 fn wr_compositor_deinit(compositor: *mut c_void);
1250 fn wr_compositor_get_capabilities(compositor: *mut c_void, caps: *mut CompositorCapabilities);
1251 fn wr_compositor_get_window_visibility(compositor: *mut c_void, caps: *mut WindowVisibility);
1252 fn wr_compositor_map_tile(
1253 compositor: *mut c_void,
1255 dirty_rect: DeviceIntRect,
1256 valid_rect: DeviceIntRect,
1257 data: &mut *mut c_void,
1260 fn wr_compositor_unmap_tile(compositor: *mut c_void);
1262 fn wr_partial_present_compositor_set_buffer_damage_region(
1263 compositor: *mut c_void,
1264 rects: *const DeviceIntRect,
1269 pub struct WrCompositor(*mut c_void);
1271 impl Compositor for WrCompositor {
1274 id: NativeSurfaceId,
1275 virtual_offset: DeviceIntPoint,
1276 tile_size: DeviceIntSize,
1280 wr_compositor_create_surface(self.0, id, virtual_offset, tile_size, is_opaque);
1284 fn create_external_surface(&mut self, id: NativeSurfaceId, is_opaque: bool) {
1286 wr_compositor_create_external_surface(self.0, id, is_opaque);
1290 fn create_backdrop_surface(&mut self, id: NativeSurfaceId, color: ColorF) {
1292 wr_compositor_create_backdrop_surface(self.0, id, color);
1296 fn destroy_surface(&mut self, id: NativeSurfaceId) {
1298 wr_compositor_destroy_surface(self.0, id);
1302 fn create_tile(&mut self, id: NativeTileId) {
1304 wr_compositor_create_tile(self.0, id.surface_id, id.x, id.y);
1308 fn destroy_tile(&mut self, id: NativeTileId) {
1310 wr_compositor_destroy_tile(self.0, id.surface_id, id.x, id.y);
1314 fn attach_external_image(&mut self, id: NativeSurfaceId, external_image: ExternalImageId) {
1316 wr_compositor_attach_external_image(self.0, id, external_image);
1320 fn bind(&mut self, id: NativeTileId, dirty_rect: DeviceIntRect, valid_rect: DeviceIntRect) -> NativeSurfaceInfo {
1321 let mut surface_info = NativeSurfaceInfo {
1322 origin: DeviceIntPoint::zero(),
1330 &mut surface_info.origin,
1331 &mut surface_info.fbo_id,
1340 fn unbind(&mut self) {
1342 wr_compositor_unbind(self.0);
1346 fn begin_frame(&mut self) {
1348 wr_compositor_begin_frame(self.0);
1354 id: NativeSurfaceId,
1355 transform: CompositorSurfaceTransform,
1356 clip_rect: DeviceIntRect,
1357 image_rendering: ImageRendering,
1360 wr_compositor_add_surface(self.0, id, &transform, clip_rect, image_rendering);
1364 fn start_compositing(
1366 clear_color: ColorF,
1367 dirty_rects: &[DeviceIntRect],
1368 opaque_rects: &[DeviceIntRect],
1371 wr_compositor_start_compositing(
1374 dirty_rects.as_ptr(),
1376 opaque_rects.as_ptr(),
1382 fn end_frame(&mut self) {
1384 wr_compositor_end_frame(self.0);
1388 fn enable_native_compositor(&mut self, enable: bool) {
1390 wr_compositor_enable_native_compositor(self.0, enable);
1394 fn deinit(&mut self) {
1396 wr_compositor_deinit(self.0);
1400 fn get_capabilities(&self) -> CompositorCapabilities {
1402 let mut caps: CompositorCapabilities = Default::default();
1403 wr_compositor_get_capabilities(self.0, &mut caps);
1408 fn get_window_visibility(&self) -> WindowVisibility {
1410 let mut visibility: WindowVisibility = Default::default();
1411 wr_compositor_get_window_visibility(self.0, &mut visibility);
1418 fn wr_swgl_lock_composite_surface(
1420 external_image_id: ExternalImageId,
1421 composite_info: *mut SWGLCompositeSurfaceInfo,
1423 fn wr_swgl_unlock_composite_surface(ctx: *mut c_void, external_image_id: ExternalImageId);
1426 impl MappableCompositor for WrCompositor {
1427 /// Map a tile's underlying buffer so it can be used as the backing for
1428 /// a SWGL framebuffer. This is intended to be a replacement for 'bind'
1429 /// in any compositors that intend to directly interoperate with SWGL
1430 /// while supporting some form of native layers.
1434 dirty_rect: DeviceIntRect,
1435 valid_rect: DeviceIntRect,
1436 ) -> Option<MappedTileInfo> {
1437 let mut tile_info = MappedTileInfo {
1438 data: ptr::null_mut(),
1443 wr_compositor_map_tile(
1448 &mut tile_info.data,
1449 &mut tile_info.stride,
1453 if !tile_info.data.is_null() && tile_info.stride != 0 {
1460 /// Unmap a tile that was was previously mapped via map_tile to signal
1461 /// that SWGL is done rendering to the buffer.
1462 fn unmap_tile(&mut self) {
1464 wr_compositor_unmap_tile(self.0);
1468 fn lock_composite_surface(
1471 external_image_id: ExternalImageId,
1472 composite_info: *mut SWGLCompositeSurfaceInfo,
1474 unsafe { wr_swgl_lock_composite_surface(ctx, external_image_id, composite_info) }
1476 fn unlock_composite_surface(&mut self, ctx: *mut c_void, external_image_id: ExternalImageId) {
1477 unsafe { wr_swgl_unlock_composite_surface(ctx, external_image_id) }
1481 pub struct WrPartialPresentCompositor(*mut c_void);
1483 impl PartialPresentCompositor for WrPartialPresentCompositor {
1484 fn set_buffer_damage_region(&mut self, rects: &[DeviceIntRect]) {
1486 wr_partial_present_compositor_set_buffer_damage_region(self.0, rects.as_ptr(), rects.len());
1491 /// A wrapper around a strong reference to a Shaders object.
1492 pub struct WrShaders(SharedShaders);
1494 // Call MakeCurrent before this.
1496 pub extern "C" fn wr_window_new(
1497 window_id: WrWindowId,
1500 is_main_window: bool,
1501 support_low_priority_transactions: bool,
1502 support_low_priority_threadpool: bool,
1503 allow_texture_swizzling: bool,
1504 allow_scissored_cache_clears: bool,
1505 swgl_context: *mut c_void,
1506 gl_context: *mut c_void,
1507 surface_origin_is_top_left: bool,
1508 program_cache: Option<&mut WrProgramCache>,
1509 shaders: Option<&mut WrShaders>,
1510 thread_pool: *mut WrThreadPool,
1511 thread_pool_low_priority: *mut WrThreadPool,
1512 size_of_op: VoidPtrToSizeFn,
1513 enclosing_size_of_op: VoidPtrToSizeFn,
1515 compositor: *mut c_void,
1516 use_native_compositor: bool,
1517 use_partial_present: bool,
1518 max_partial_present_rects: usize,
1519 draw_previous_partial_present_regions: bool,
1520 out_handle: &mut *mut DocumentHandle,
1521 out_renderer: &mut *mut Renderer,
1522 out_max_texture_size: *mut i32,
1523 out_err: &mut *mut c_char,
1524 enable_gpu_markers: bool,
1525 panic_on_gl_error: bool,
1526 picture_tile_width: i32,
1527 picture_tile_height: i32,
1528 reject_software_rasterizer: bool,
1529 low_quality_pinch_zoom: bool,
1531 assert!(unsafe { is_in_render_thread() });
1533 // Ensure the WR profiler callbacks are hooked up to the Gecko profiler.
1534 set_profiler_hooks(Some(&PROFILER_HOOKS));
1536 let software = !swgl_context.is_null();
1537 let (gl, sw_gl) = if software {
1538 let ctx = swgl::Context::from(swgl_context);
1540 (Rc::new(ctx) as Rc<dyn gl::Gl>, Some(ctx))
1543 if gl_context.is_null() {
1544 panic!("Native GL context required when not using SWGL!");
1545 } else if is_glcontext_gles(gl_context) {
1546 gl::GlesFns::load_with(|symbol| get_proc_address(gl_context, symbol))
1548 gl::GlFns::load_with(|symbol| get_proc_address(gl_context, symbol))
1554 let version = gl.get_string(gl::VERSION);
1556 info!("WebRender - OpenGL version new {}", version);
1558 let workers = unsafe { Arc::clone(&(*thread_pool).0) };
1559 let workers_low_priority = unsafe {
1560 if support_low_priority_threadpool {
1561 Arc::clone(&(*thread_pool_low_priority).0)
1563 Arc::clone(&(*thread_pool).0)
1567 let upload_method = if !gl_context.is_null() && unsafe { is_glcontext_angle(gl_context) } {
1568 UploadMethod::Immediate
1570 UploadMethod::PixelBuffer(ONE_TIME_USAGE_HINT)
1573 let precache_flags = if env_var_to_bool("MOZ_WR_PRECACHE_SHADERS") {
1574 ShaderPrecacheFlags::FULL_COMPILE
1576 ShaderPrecacheFlags::empty()
1579 let cached_programs = program_cache.map(|program_cache| Rc::clone(&program_cache.rc_get()));
1581 let color = if cfg!(target_os = "android") {
1582 // The color is for avoiding black flash before receiving display list.
1583 ColorF::new(1.0, 1.0, 1.0, 1.0)
1585 ColorF::new(0.0, 0.0, 0.0, 0.0)
1588 let compositor_config = if software {
1589 CompositorConfig::Native {
1590 compositor: Box::new(SwCompositor::new(
1592 Box::new(WrCompositor(compositor)),
1593 use_native_compositor,
1596 } else if use_native_compositor {
1597 CompositorConfig::Native {
1598 compositor: Box::new(WrCompositor(compositor)),
1601 CompositorConfig::Draw {
1602 max_partial_present_rects,
1603 draw_previous_partial_present_regions,
1604 partial_present: if use_partial_present {
1605 Some(Box::new(WrPartialPresentCompositor(compositor)))
1612 let picture_tile_size = if picture_tile_width > 0 && picture_tile_height > 0 {
1613 Some(DeviceIntSize::new(picture_tile_width, picture_tile_height))
1618 let texture_cache_config = if is_main_window {
1619 TextureCacheConfig::DEFAULT
1621 TextureCacheConfig {
1622 color8_linear_texture_size: 512,
1623 color8_nearest_texture_size: 512,
1624 color8_glyph_texture_size: 512,
1625 alpha8_texture_size: 512,
1626 alpha8_glyph_texture_size: 512,
1627 alpha16_texture_size: 512,
1631 let opts = RendererOptions {
1633 force_subpixel_aa: false,
1634 enable_subpixel_aa: cfg!(not(target_os = "android")),
1635 support_low_priority_transactions,
1636 allow_texture_swizzling,
1637 blob_image_handler: Some(Box::new(Moz2dBlobImageHandler::new(
1639 workers_low_priority,
1641 crash_annotator: Some(Box::new(MozCrashAnnotator)),
1642 workers: Some(workers),
1643 size_of_op: Some(size_of_op),
1644 enclosing_size_of_op: Some(enclosing_size_of_op),
1646 resource_override_path: unsafe {
1647 let override_charptr = gfx_wr_resource_path_override();
1648 if override_charptr.is_null() {
1651 match CStr::from_ptr(override_charptr).to_str() {
1652 Ok(override_str) => Some(PathBuf::from(override_str)),
1657 use_optimized_shaders: unsafe { gfx_wr_use_optimized_shaders() },
1658 renderer_id: Some(window_id.0),
1660 scene_builder_hooks: Some(Box::new(APZCallbacks::new(window_id))),
1661 sampler: Some(Box::new(SamplerCallback::new(window_id))),
1662 max_internal_texture_size: Some(8192), // We want to tile if larger than this
1665 namespace_alloc_by_client: true,
1666 // Font namespace must be allocated by the client
1667 shared_font_namespace: Some(next_namespace_id()),
1668 // SWGL doesn't support the GL_ALWAYS depth comparison function used by
1669 // `clear_caches_with_quads`, but scissored clears work well.
1670 clear_caches_with_quads: !software && !allow_scissored_cache_clears,
1671 // SWGL supports KHR_blend_equation_advanced safely, but we haven't yet
1672 // tested other HW platforms determine if it is safe to allow them.
1673 allow_advanced_blend_equation: software,
1674 surface_origin_is_top_left,
1679 texture_cache_config,
1680 reject_software_rasterizer,
1681 low_quality_pinch_zoom,
1682 ..Default::default()
1685 let window_size = DeviceIntSize::new(window_width, window_height);
1686 let notifier = Box::new(CppNotifier { window_id });
1687 let (renderer, sender) = match Renderer::new(gl, notifier, opts, shaders.map(|sh| &sh.0)) {
1688 Ok((renderer, sender)) => (renderer, sender),
1690 warn!(" Failed to create a Renderer: {:?}", e);
1691 let msg = CString::new(format!("wr_window_new: {:?}", e)).unwrap();
1693 gfx_critical_note(msg.as_ptr());
1695 *out_err = msg.into_raw();
1701 *out_max_texture_size = renderer.get_max_texture_size();
1703 *out_handle = Box::into_raw(Box::new(DocumentHandle::new(
1704 sender.create_api_by_client(next_namespace_id()),
1709 *out_renderer = Box::into_raw(Box::new(renderer));
1715 pub unsafe extern "C" fn wr_api_free_error_msg(msg: *mut c_char) {
1717 drop(CString::from_raw(msg));
1722 pub unsafe extern "C" fn wr_api_delete_document(dh: &mut DocumentHandle) {
1723 dh.api.delete_document(dh.document_id);
1727 pub extern "C" fn wr_api_clone(dh: &mut DocumentHandle, out_handle: &mut *mut DocumentHandle) {
1728 assert!(unsafe { is_in_compositor_thread() });
1730 let hit_tester = dh.ensure_hit_tester().clone();
1732 let handle = DocumentHandle {
1733 api: dh.api.create_sender().create_api_by_client(next_namespace_id()),
1734 document_id: dh.document_id,
1735 hit_tester: Some(hit_tester),
1736 hit_tester_request: None,
1738 *out_handle = Box::into_raw(Box::new(handle));
1742 pub unsafe extern "C" fn wr_api_delete(dh: *mut DocumentHandle) {
1743 let _ = Box::from_raw(dh);
1747 pub unsafe extern "C" fn wr_api_stop_render_backend(dh: &mut DocumentHandle) {
1748 dh.api.stop_render_backend();
1752 pub unsafe extern "C" fn wr_api_shut_down(dh: &mut DocumentHandle) {
1753 dh.api.shut_down(true);
1757 pub unsafe extern "C" fn wr_api_notify_memory_pressure(dh: &mut DocumentHandle) {
1758 dh.api.notify_memory_pressure();
1762 pub extern "C" fn wr_api_set_debug_flags(dh: &mut DocumentHandle, flags: DebugFlags) {
1763 dh.api.set_debug_flags(flags);
1767 pub extern "C" fn wr_api_set_bool(dh: &mut DocumentHandle, param_name: BoolParameter, val: bool) {
1768 dh.api.set_parameter(Parameter::Bool(param_name, val));
1772 pub extern "C" fn wr_api_set_int(dh: &mut DocumentHandle, param_name: IntParameter, val: i32) {
1773 dh.api.set_parameter(Parameter::Int(param_name, val));
1777 pub unsafe extern "C" fn wr_api_accumulate_memory_report(
1778 dh: &mut DocumentHandle,
1779 report: &mut MemoryReport,
1780 // we manually expand VoidPtrToSizeFn here because cbindgen otherwise fails to fold the Option<fn()>
1781 // https://github.com/eqrion/cbindgen/issues/552
1782 size_of_op: unsafe extern "C" fn(ptr: *const c_void) -> usize,
1783 enclosing_size_of_op: Option<unsafe extern "C" fn(ptr: *const c_void) -> usize>,
1785 let ops = MallocSizeOfOps::new(size_of_op, enclosing_size_of_op);
1786 *report += dh.api.report_memory(ops);
1790 pub unsafe extern "C" fn wr_api_clear_all_caches(dh: &mut DocumentHandle) {
1791 dh.api.send_debug_cmd(DebugCommand::ClearCaches(ClearCache::all()));
1795 pub unsafe extern "C" fn wr_api_enable_native_compositor(dh: &mut DocumentHandle, enable: bool) {
1796 dh.api.send_debug_cmd(DebugCommand::EnableNativeCompositor(enable));
1800 pub unsafe extern "C" fn wr_api_set_batching_lookback(dh: &mut DocumentHandle, count: u32) {
1801 dh.api.send_debug_cmd(DebugCommand::SetBatchingLookback(count));
1804 fn make_transaction(do_async: bool) -> Transaction {
1805 let mut transaction = Transaction::new();
1806 // Ensure that we either use async scene building or not based on the
1807 // gecko pref, regardless of what the default is. We can remove this once
1808 // the scene builder thread is enabled everywhere and working well.
1810 transaction.use_scene_builder_thread();
1812 transaction.skip_scene_builder();
1818 pub extern "C" fn wr_transaction_new(do_async: bool) -> *mut Transaction {
1819 Box::into_raw(Box::new(make_transaction(do_async)))
1823 pub extern "C" fn wr_transaction_delete(txn: *mut Transaction) {
1825 let _ = Box::from_raw(txn);
1830 pub extern "C" fn wr_transaction_set_low_priority(txn: &mut Transaction, low_priority: bool) {
1831 txn.set_low_priority(low_priority);
1835 pub extern "C" fn wr_transaction_is_empty(txn: &Transaction) -> bool {
1840 pub extern "C" fn wr_transaction_resource_updates_is_empty(txn: &Transaction) -> bool {
1841 txn.resource_updates.is_empty()
1845 pub extern "C" fn wr_transaction_is_rendered_frame_invalidated(txn: &Transaction) -> bool {
1846 txn.invalidate_rendered_frame
1850 pub extern "C" fn wr_transaction_notify(txn: &mut Transaction, when: Checkpoint, event: usize) {
1851 struct GeckoNotification(usize);
1852 impl NotificationHandler for GeckoNotification {
1853 fn notify(&self, when: Checkpoint) {
1855 wr_transaction_notification_notified(self.0, when);
1860 let handler = Box::new(GeckoNotification(event));
1861 txn.notify(NotificationRequest::new(when, handler));
1865 pub extern "C" fn wr_transaction_update_epoch(txn: &mut Transaction, pipeline_id: WrPipelineId, epoch: WrEpoch) {
1866 txn.update_epoch(pipeline_id, epoch);
1870 pub extern "C" fn wr_transaction_set_root_pipeline(txn: &mut Transaction, pipeline_id: WrPipelineId) {
1871 txn.set_root_pipeline(pipeline_id);
1875 pub extern "C" fn wr_transaction_remove_pipeline(txn: &mut Transaction, pipeline_id: WrPipelineId) {
1876 txn.remove_pipeline(pipeline_id);
1880 pub extern "C" fn wr_transaction_set_display_list(
1881 txn: &mut Transaction,
1884 viewport_size: LayoutSize,
1885 pipeline_id: WrPipelineId,
1886 dl_descriptor: BuiltDisplayListDescriptor,
1887 dl_items_data: &mut WrVecU8,
1888 dl_cache_data: &mut WrVecU8,
1889 dl_spatial_tree_data: &mut WrVecU8,
1891 let color = if background.a == 0.0 { None } else { Some(background) };
1893 let payload = DisplayListPayload {
1894 items_data: dl_items_data.flush_into_vec(),
1895 cache_data: dl_cache_data.flush_into_vec(),
1896 spatial_tree: dl_spatial_tree_data.flush_into_vec(),
1899 let dl = BuiltDisplayList::from_data(payload, dl_descriptor);
1901 txn.set_display_list(epoch, color, viewport_size, (pipeline_id, dl));
1905 pub extern "C" fn wr_transaction_set_document_view(txn: &mut Transaction, doc_rect: &DeviceIntRect) {
1906 txn.set_document_view(*doc_rect);
1910 pub extern "C" fn wr_transaction_generate_frame(txn: &mut Transaction, id: u64, reasons: RenderReasons) {
1911 txn.generate_frame(id, reasons);
1915 pub extern "C" fn wr_transaction_invalidate_rendered_frame(txn: &mut Transaction, reasons: RenderReasons) {
1916 txn.invalidate_rendered_frame(reasons);
1919 fn wr_animation_properties_into_vec<T>(
1920 animation_array: *const WrAnimationPropertyValue<T>,
1922 vec: &mut Vec<PropertyValue<T>>,
1926 if array_count > 0 {
1928 vec.capacity() - vec.len() >= array_count,
1929 "The Vec should have fufficient free capacity"
1931 let slice = unsafe { make_slice(animation_array, array_count) };
1933 for element in slice.iter() {
1934 let prop = PropertyValue {
1935 key: PropertyBindingKey::new(element.id),
1936 value: element.value,
1945 pub extern "C" fn wr_transaction_append_dynamic_properties(
1946 txn: &mut Transaction,
1947 opacity_array: *const WrOpacityProperty,
1948 opacity_count: usize,
1949 transform_array: *const WrTransformProperty,
1950 transform_count: usize,
1951 color_array: *const WrColorProperty,
1954 if opacity_count == 0 && transform_count == 0 && color_count == 0 {
1958 let mut properties = DynamicProperties {
1959 transforms: Vec::with_capacity(transform_count),
1960 floats: Vec::with_capacity(opacity_count),
1961 colors: Vec::with_capacity(color_count),
1964 wr_animation_properties_into_vec(transform_array, transform_count, &mut properties.transforms);
1966 wr_animation_properties_into_vec(opacity_array, opacity_count, &mut properties.floats);
1968 wr_animation_properties_into_vec(color_array, color_count, &mut properties.colors);
1970 txn.append_dynamic_properties(properties);
1974 pub extern "C" fn wr_transaction_append_transform_properties(
1975 txn: &mut Transaction,
1976 transform_array: *const WrTransformProperty,
1977 transform_count: usize,
1979 if transform_count == 0 {
1983 let mut transforms = Vec::with_capacity(transform_count);
1984 wr_animation_properties_into_vec(transform_array, transform_count, &mut transforms);
1986 txn.append_dynamic_transform_properties(transforms);
1990 pub extern "C" fn wr_transaction_scroll_layer(
1991 txn: &mut Transaction,
1992 pipeline_id: WrPipelineId,
1994 sampled_scroll_offsets: &ThinVec<SampledScrollOffset>,
1996 let scroll_id = ExternalScrollId(scroll_id, pipeline_id);
1997 txn.set_scroll_offsets(scroll_id, sampled_scroll_offsets.to_vec());
2001 pub extern "C" fn wr_transaction_set_is_transform_async_zooming(
2002 txn: &mut Transaction,
2006 txn.set_is_transform_async_zooming(is_zooming, PropertyBindingId::new(animation_id));
2010 pub extern "C" fn wr_transaction_set_quality_settings(txn: &mut Transaction, force_subpixel_aa_where_possible: bool) {
2011 txn.set_quality_settings(QualitySettings {
2012 force_subpixel_aa_where_possible,
2017 pub extern "C" fn wr_resource_updates_add_image(
2018 txn: &mut Transaction,
2019 image_key: WrImageKey,
2020 descriptor: &WrImageDescriptor,
2021 bytes: &mut WrVecU8,
2026 ImageData::new(bytes.flush_into_vec()),
2032 pub extern "C" fn wr_resource_updates_add_blob_image(
2033 txn: &mut Transaction,
2034 image_key: BlobImageKey,
2035 descriptor: &WrImageDescriptor,
2037 bytes: &mut WrVecU8,
2038 visible_rect: DeviceIntRect,
2043 Arc::new(bytes.flush_into_vec()),
2045 if descriptor.format == ImageFormat::BGRA8 {
2054 pub extern "C" fn wr_resource_updates_add_external_image(
2055 txn: &mut Transaction,
2056 image_key: WrImageKey,
2057 descriptor: &WrImageDescriptor,
2058 external_image_id: ExternalImageId,
2059 image_type: &ExternalImageType,
2065 ImageData::External(ExternalImageData {
2066 id: external_image_id,
2068 image_type: *image_type,
2075 pub extern "C" fn wr_resource_updates_update_image(
2076 txn: &mut Transaction,
2078 descriptor: &WrImageDescriptor,
2079 bytes: &mut WrVecU8,
2084 ImageData::new(bytes.flush_into_vec()),
2090 pub extern "C" fn wr_resource_updates_set_blob_image_visible_area(
2091 txn: &mut Transaction,
2093 area: &DeviceIntRect,
2095 txn.set_blob_image_visible_area(key, *area);
2099 pub extern "C" fn wr_resource_updates_update_external_image(
2100 txn: &mut Transaction,
2102 descriptor: &WrImageDescriptor,
2103 external_image_id: ExternalImageId,
2104 image_type: &ExternalImageType,
2110 ImageData::External(ExternalImageData {
2111 id: external_image_id,
2113 image_type: *image_type,
2120 pub extern "C" fn wr_resource_updates_update_external_image_with_dirty_rect(
2121 txn: &mut Transaction,
2123 descriptor: &WrImageDescriptor,
2124 external_image_id: ExternalImageId,
2125 image_type: &ExternalImageType,
2127 dirty_rect: DeviceIntRect,
2132 ImageData::External(ExternalImageData {
2133 id: external_image_id,
2135 image_type: *image_type,
2137 &DirtyRect::Partial(dirty_rect),
2142 pub extern "C" fn wr_resource_updates_update_blob_image(
2143 txn: &mut Transaction,
2144 image_key: BlobImageKey,
2145 descriptor: &WrImageDescriptor,
2146 bytes: &mut WrVecU8,
2147 visible_rect: DeviceIntRect,
2148 dirty_rect: LayoutIntRect,
2150 txn.update_blob_image(
2153 Arc::new(bytes.flush_into_vec()),
2155 &DirtyRect::Partial(dirty_rect),
2160 pub extern "C" fn wr_resource_updates_delete_image(txn: &mut Transaction, key: WrImageKey) {
2161 txn.delete_image(key);
2165 pub extern "C" fn wr_resource_updates_delete_blob_image(txn: &mut Transaction, key: BlobImageKey) {
2166 txn.delete_blob_image(key);
2170 pub extern "C" fn wr_api_send_transaction(dh: &mut DocumentHandle, transaction: &mut Transaction, is_async: bool) {
2171 if transaction.is_empty() {
2174 let new_txn = make_transaction(is_async);
2175 let txn = mem::replace(transaction, new_txn);
2176 dh.api.send_transaction(dh.document_id, txn);
2180 pub unsafe extern "C" fn wr_transaction_clear_display_list(
2181 txn: &mut Transaction,
2183 pipeline_id: WrPipelineId,
2185 let mut frame_builder = WebRenderFrameBuilder::new(pipeline_id);
2186 frame_builder.dl_builder.begin();
2188 txn.set_display_list(epoch, None, LayoutSize::new(0.0, 0.0), frame_builder.dl_builder.end());
2192 pub extern "C" fn wr_api_send_external_event(dh: &mut DocumentHandle, evt: usize) {
2193 assert!(unsafe { !is_in_render_thread() });
2195 dh.api.send_external_event(ExternalEvent::from_raw(evt));
2199 pub extern "C" fn wr_resource_updates_add_raw_font(
2200 txn: &mut Transaction,
2202 bytes: &mut WrVecU8,
2205 txn.add_raw_font(key, bytes.flush_into_vec(), index);
2208 fn generate_capture_path(path: *const c_char) -> Option<PathBuf> {
2209 use std::fs::{create_dir_all, File};
2212 let cstr = unsafe { CStr::from_ptr(path) };
2213 let local_dir = PathBuf::from(&*cstr.to_string_lossy());
2215 // On Android we need to write into a particular folder on external
2216 // storage so that (a) it can be written without requiring permissions
2217 // and (b) it can be pulled off via `adb pull`. This env var is set
2218 // in GeckoLoader.java.
2219 // When running in Firefox CI, the MOZ_UPLOAD_DIR variable is set to a path
2220 // that taskcluster will export artifacts from, so let's put it there.
2221 let mut path = if let Ok(storage_path) = env::var("PUBLIC_STORAGE") {
2222 PathBuf::from(storage_path).join(local_dir)
2223 } else if let Ok(storage_path) = env::var("MOZ_UPLOAD_DIR") {
2224 PathBuf::from(storage_path).join(local_dir)
2225 } else if let Some(storage_path) = dirs::home_dir() {
2226 storage_path.join(local_dir)
2231 // Increment the extension until we find a fresh path
2232 while path.is_dir() {
2233 let count: u32 = path
2235 .and_then(|x| x.to_str())
2236 .and_then(|x| x.parse().ok())
2238 path.set_extension((count + 1).to_string());
2241 // Use warn! so that it gets emitted to logcat on android as well
2242 let border = "--------------------------\n";
2243 warn!("{} Capturing WR state to: {:?}\n{}", &border, &path, &border);
2245 let _ = create_dir_all(&path);
2246 match File::create(path.join("wr.txt")) {
2248 // The Gecko HG revision is available at compile time
2249 if let Some(moz_revision) = option_env!("GECKO_HEAD_REV") {
2250 writeln!(file, "mozilla-central {}", moz_revision).unwrap();
2255 warn!("Unable to create path '{:?}' for capture: {:?}", path, e);
2262 pub extern "C" fn wr_api_capture(dh: &mut DocumentHandle, path: *const c_char, bits_raw: u32) {
2263 if let Some(path) = generate_capture_path(path) {
2264 let bits = CaptureBits::from_bits(bits_raw as _).unwrap();
2265 dh.api.save_capture(path, bits);
2270 pub extern "C" fn wr_api_start_capture_sequence(dh: &mut DocumentHandle, path: *const c_char, bits_raw: u32) {
2271 if let Some(path) = generate_capture_path(path) {
2272 let bits = CaptureBits::from_bits(bits_raw as _).unwrap();
2273 dh.api.start_capture_sequence(path, bits);
2278 pub extern "C" fn wr_api_stop_capture_sequence(dh: &mut DocumentHandle) {
2279 let border = "--------------------------\n";
2280 warn!("{} Stop capturing WR state\n{}", &border, &border);
2281 dh.api.stop_capture_sequence();
2284 #[cfg(target_os = "windows")]
2285 fn read_font_descriptor(bytes: &mut WrVecU8, index: u32) -> NativeFontHandle {
2286 let wchars = bytes.convert_into_vec::<u16>();
2288 path: PathBuf::from(OsString::from_wide(&wchars)),
2293 #[cfg(target_os = "macos")]
2294 fn read_font_descriptor(bytes: &mut WrVecU8, _index: u32) -> NativeFontHandle {
2295 let chars = bytes.flush_into_vec();
2297 name: String::from_utf8(chars).unwrap()
2301 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
2302 fn read_font_descriptor(bytes: &mut WrVecU8, index: u32) -> NativeFontHandle {
2303 let chars = bytes.flush_into_vec();
2305 path: PathBuf::from(OsString::from_vec(chars)),
2311 pub extern "C" fn wr_resource_updates_add_font_descriptor(
2312 txn: &mut Transaction,
2314 bytes: &mut WrVecU8,
2317 let native_font_handle = read_font_descriptor(bytes, index);
2318 txn.add_native_font(key, native_font_handle);
2322 pub extern "C" fn wr_resource_updates_delete_font(txn: &mut Transaction, key: WrFontKey) {
2323 txn.delete_font(key);
2327 pub extern "C" fn wr_resource_updates_add_font_instance(
2328 txn: &mut Transaction,
2329 key: WrFontInstanceKey,
2330 font_key: WrFontKey,
2332 options: *const FontInstanceOptions,
2333 platform_options: *const FontInstancePlatformOptions,
2334 variations: &mut WrVecU8,
2336 txn.add_font_instance(
2340 unsafe { options.as_ref().cloned() },
2341 unsafe { platform_options.as_ref().cloned() },
2342 variations.convert_into_vec::<FontVariation>(),
2347 pub extern "C" fn wr_resource_updates_delete_font_instance(txn: &mut Transaction, key: WrFontInstanceKey) {
2348 txn.delete_font_instance(key);
2352 pub extern "C" fn wr_resource_updates_clear(txn: &mut Transaction) {
2353 txn.resource_updates.clear();
2357 pub unsafe extern "C" fn wr_api_get_namespace(dh: &mut DocumentHandle) -> WrIdNamespace {
2358 dh.api.get_namespace_id()
2362 pub unsafe extern "C" fn wr_api_wake_scene_builder(dh: &mut DocumentHandle) {
2363 dh.api.wake_scene_builder();
2367 pub unsafe extern "C" fn wr_api_flush_scene_builder(dh: &mut DocumentHandle) {
2368 dh.api.flush_scene_builder();
2371 // RenderThread WIP notes:
2372 // In order to separate the compositor thread (or ipc receiver) and the render
2373 // thread, some of the logic below needs to be rewritten. In particular
2374 // the WrWindowState and Notifier implementations aren't designed to work with
2375 // a separate render thread.
2376 // As part of that I am moving the bindings closer to WebRender's API boundary,
2377 // and moving more of the logic in C++ land.
2378 // This work is tracked by bug 1328602.
2380 // See RenderThread.h for some notes about how the pieces fit together.
2382 pub struct WebRenderFrameBuilder {
2383 pub root_pipeline_id: WrPipelineId,
2384 pub dl_builder: DisplayListBuilder,
2387 impl WebRenderFrameBuilder {
2388 pub fn new(root_pipeline_id: WrPipelineId) -> WebRenderFrameBuilder {
2389 WebRenderFrameBuilder {
2391 dl_builder: DisplayListBuilder::new(root_pipeline_id),
2396 pub struct WrState {
2397 pipeline_id: WrPipelineId,
2398 frame_builder: WebRenderFrameBuilder,
2402 pub extern "C" fn wr_state_new(pipeline_id: WrPipelineId) -> *mut WrState {
2403 assert!(unsafe { !is_in_render_thread() });
2405 let state = Box::new(WrState {
2407 frame_builder: WebRenderFrameBuilder::new(pipeline_id),
2410 Box::into_raw(state)
2414 pub extern "C" fn wr_state_delete(state: *mut WrState) {
2415 assert!(unsafe { !is_in_render_thread() });
2418 Box::from_raw(state);
2423 pub extern "C" fn wr_dp_save(state: &mut WrState) {
2424 state.frame_builder.dl_builder.save();
2428 pub extern "C" fn wr_dp_restore(state: &mut WrState) {
2429 state.frame_builder.dl_builder.restore();
2433 pub extern "C" fn wr_dp_clear_save(state: &mut WrState) {
2434 state.frame_builder.dl_builder.clear_save();
2438 #[derive(PartialEq, Eq, Debug)]
2439 pub enum WrReferenceFrameKind {
2445 #[derive(PartialEq, Eq, Debug)]
2446 pub enum WrRotation {
2453 /// IMPORTANT: If you add fields to this struct, you need to also add initializers
2454 /// for those fields in WebRenderAPI.h.
2456 pub struct WrStackingContextParams {
2457 pub clip: WrStackingContextClip,
2458 pub animation: *const WrAnimationProperty,
2459 pub opacity: *const f32,
2460 pub computed_transform: *const WrComputedTransformData,
2461 pub transform_style: TransformStyle,
2462 pub reference_frame_kind: WrReferenceFrameKind,
2463 pub is_2d_scale_translation: bool,
2464 pub should_snap: bool,
2465 pub paired_with_perspective: bool,
2466 pub scrolling_relative_to: *const u64,
2467 pub prim_flags: PrimitiveFlags,
2468 pub mix_blend_mode: MixBlendMode,
2469 pub flags: StackingContextFlags,
2473 pub extern "C" fn wr_dp_push_stacking_context(
2474 state: &mut WrState,
2476 spatial_id: WrSpatialId,
2477 params: &WrStackingContextParams,
2478 transform: *const WrTransformInfo,
2479 filters: *const FilterOp,
2480 filter_count: usize,
2481 filter_datas: *const WrFilterData,
2482 filter_datas_count: usize,
2483 glyph_raster_space: RasterSpace,
2485 debug_assert!(unsafe { !is_in_render_thread() });
2487 let c_filters = unsafe { make_slice(filters, filter_count) };
2488 let mut filters: Vec<FilterOp> = c_filters.iter().copied().collect();
2490 let c_filter_datas = unsafe { make_slice(filter_datas, filter_datas_count) };
2491 let r_filter_datas: Vec<FilterData> = c_filter_datas
2493 .map(|c_filter_data| FilterData {
2494 func_r_type: c_filter_data.funcR_type,
2495 r_values: unsafe { make_slice(c_filter_data.R_values, c_filter_data.R_values_count).to_vec() },
2496 func_g_type: c_filter_data.funcG_type,
2497 g_values: unsafe { make_slice(c_filter_data.G_values, c_filter_data.G_values_count).to_vec() },
2498 func_b_type: c_filter_data.funcB_type,
2499 b_values: unsafe { make_slice(c_filter_data.B_values, c_filter_data.B_values_count).to_vec() },
2500 func_a_type: c_filter_data.funcA_type,
2501 a_values: unsafe { make_slice(c_filter_data.A_values, c_filter_data.A_values_count).to_vec() },
2505 let transform_ref = unsafe { transform.as_ref() };
2506 let mut transform_binding = transform_ref.map(|info| (PropertyBinding::Value(info.transform), info.key));
2508 let computed_ref = unsafe { params.computed_transform.as_ref() };
2509 let opacity_ref = unsafe { params.opacity.as_ref() };
2510 let mut has_opacity_animation = false;
2511 let anim = unsafe { params.animation.as_ref() };
2512 if let Some(anim) = anim {
2513 debug_assert!(anim.id > 0);
2514 match anim.effect_type {
2515 WrAnimationType::Opacity => {
2516 filters.push(FilterOp::Opacity(
2517 PropertyBinding::Binding(
2518 PropertyBindingKey::new(anim.id),
2519 // We have to set the static opacity value as
2520 // the value for the case where the animation is
2521 // in not in-effect (e.g. in the delay phase
2522 // with no corresponding fill mode).
2523 opacity_ref.cloned().unwrap_or(1.0),
2527 has_opacity_animation = true;
2529 WrAnimationType::Transform => {
2530 transform_binding = Some((
2531 PropertyBinding::Binding(
2532 PropertyBindingKey::new(anim.id),
2533 // Same as above opacity case.
2535 .map(|info| info.transform)
2536 .unwrap_or_else(LayoutTransform::identity),
2541 _ => unreachable!("{:?} should not create a stacking context", anim.effect_type),
2545 if let Some(opacity) = opacity_ref {
2546 if !has_opacity_animation && *opacity < 1.0 {
2547 filters.push(FilterOp::Opacity(PropertyBinding::Value(*opacity), *opacity));
2551 let mut wr_spatial_id = spatial_id.to_webrender(state.pipeline_id);
2552 let wr_clip_id = params.clip.to_webrender(state.pipeline_id);
2554 let mut origin = bounds.min;
2556 // Note: 0 has special meaning in WR land, standing for ROOT_REFERENCE_FRAME.
2557 // However, it is never returned by `push_reference_frame`, and we need to return
2558 // an option here across FFI, so we take that 0 value for the None semantics.
2559 // This is resolved into proper `Maybe<WrSpatialId>` inside `WebRenderAPI::PushStackingContext`.
2560 let mut result = WrSpatialId { id: 0 };
2561 if let Some(transform_binding) = transform_binding {
2562 let scrolling_relative_to = match unsafe { params.scrolling_relative_to.as_ref() } {
2563 Some(scroll_id) => {
2564 debug_assert_eq!(params.reference_frame_kind, WrReferenceFrameKind::Perspective);
2565 Some(ExternalScrollId(*scroll_id, state.pipeline_id))
2570 let reference_frame_kind = match params.reference_frame_kind {
2571 WrReferenceFrameKind::Transform => ReferenceFrameKind::Transform {
2572 is_2d_scale_translation: params.is_2d_scale_translation,
2573 should_snap: params.should_snap,
2574 paired_with_perspective: params.paired_with_perspective,
2576 WrReferenceFrameKind::Perspective => ReferenceFrameKind::Perspective { scrolling_relative_to },
2578 wr_spatial_id = state.frame_builder.dl_builder.push_reference_frame(
2581 params.transform_style,
2582 transform_binding.0,
2583 reference_frame_kind,
2584 transform_binding.1,
2587 origin = LayoutPoint::zero();
2588 result.id = wr_spatial_id.0;
2589 assert_ne!(wr_spatial_id.0, 0);
2590 } else if let Some(data) = computed_ref {
2591 let rotation = match data.rotation {
2592 WrRotation::Degree0 => Rotation::Degree0,
2593 WrRotation::Degree90 => Rotation::Degree90,
2594 WrRotation::Degree180 => Rotation::Degree180,
2595 WrRotation::Degree270 => Rotation::Degree270,
2597 wr_spatial_id = state.frame_builder.dl_builder.push_computed_frame(
2600 Some(data.scale_from),
2606 origin = LayoutPoint::zero();
2607 result.id = wr_spatial_id.0;
2608 assert_ne!(wr_spatial_id.0, 0);
2611 state.frame_builder.dl_builder.push_stacking_context(
2616 params.transform_style,
2617 params.mix_blend_mode,
2629 pub extern "C" fn wr_dp_pop_stacking_context(state: &mut WrState, is_reference_frame: bool) {
2630 debug_assert!(unsafe { !is_in_render_thread() });
2631 state.frame_builder.dl_builder.pop_stacking_context();
2632 if is_reference_frame {
2633 state.frame_builder.dl_builder.pop_reference_frame();
2638 pub extern "C" fn wr_dp_define_clipchain(
2639 state: &mut WrState,
2640 parent_clipchain_id: *const u64,
2641 clips: *const WrClipId,
2644 debug_assert!(unsafe { is_in_main_thread() });
2645 let parent = unsafe { parent_clipchain_id.as_ref() }.map(|id| ClipChainId(*id, state.pipeline_id));
2647 let pipeline_id = state.pipeline_id;
2648 let clips = unsafe { make_slice(clips, clips_count) }
2650 .map(|clip_id| clip_id.to_webrender(pipeline_id));
2652 let clipchain_id = state.frame_builder.dl_builder.define_clip_chain(parent, clips);
2653 assert!(clipchain_id.1 == state.pipeline_id);
2658 pub extern "C" fn wr_dp_define_image_mask_clip_with_parent_clip_chain(
2659 state: &mut WrState,
2660 parent: &WrSpaceAndClipChain,
2662 points: *const LayoutPoint,
2664 fill_rule: FillRule,
2666 debug_assert!(unsafe { is_in_main_thread() });
2668 let c_points = unsafe { make_slice(points, point_count) };
2669 let points: Vec<LayoutPoint> = c_points.iter().copied().collect();
2671 let clip_id = state.frame_builder.dl_builder.define_clip_image_mask(
2672 &parent.to_webrender(state.pipeline_id),
2677 WrClipId::from_webrender(clip_id)
2681 pub extern "C" fn wr_dp_define_rounded_rect_clip(
2682 state: &mut WrState,
2684 complex: ComplexClipRegion,
2686 debug_assert!(unsafe { is_in_main_thread() });
2688 let space_and_clip = SpaceAndClipInfo {
2689 spatial_id: space.to_webrender(state.pipeline_id),
2690 clip_id: ClipId::root(state.pipeline_id),
2696 .define_clip_rounded_rect(&space_and_clip, complex);
2697 WrClipId::from_webrender(clip_id)
2701 pub extern "C" fn wr_dp_define_rounded_rect_clip_with_parent_clip_chain(
2702 state: &mut WrState,
2703 parent: &WrSpaceAndClipChain,
2704 complex: ComplexClipRegion,
2706 debug_assert!(unsafe { is_in_main_thread() });
2711 .define_clip_rounded_rect(&parent.to_webrender(state.pipeline_id), complex);
2712 WrClipId::from_webrender(clip_id)
2716 pub extern "C" fn wr_dp_define_rect_clip(state: &mut WrState, space: WrSpatialId, clip_rect: LayoutRect) -> WrClipId {
2717 debug_assert!(unsafe { is_in_main_thread() });
2719 let space_and_clip = SpaceAndClipInfo {
2720 spatial_id: space.to_webrender(state.pipeline_id),
2721 clip_id: ClipId::root(state.pipeline_id),
2727 .define_clip_rect(&space_and_clip, clip_rect);
2728 WrClipId::from_webrender(clip_id)
2732 pub extern "C" fn wr_dp_define_rect_clip_with_parent_clip_chain(
2733 state: &mut WrState,
2734 parent: &WrSpaceAndClipChain,
2735 clip_rect: LayoutRect,
2737 debug_assert!(unsafe { is_in_main_thread() });
2742 .define_clip_rect(&parent.to_webrender(state.pipeline_id), clip_rect);
2743 WrClipId::from_webrender(clip_id)
2747 pub extern "C" fn wr_dp_define_sticky_frame(
2748 state: &mut WrState,
2749 parent_spatial_id: WrSpatialId,
2750 content_rect: LayoutRect,
2751 top_margin: *const f32,
2752 right_margin: *const f32,
2753 bottom_margin: *const f32,
2754 left_margin: *const f32,
2755 vertical_bounds: StickyOffsetBounds,
2756 horizontal_bounds: StickyOffsetBounds,
2757 applied_offset: LayoutVector2D,
2758 key: SpatialTreeItemKey,
2760 assert!(unsafe { is_in_main_thread() });
2761 let spatial_id = state.frame_builder.dl_builder.define_sticky_frame(
2762 parent_spatial_id.to_webrender(state.pipeline_id),
2765 unsafe { top_margin.as_ref() }.cloned(),
2766 unsafe { right_margin.as_ref() }.cloned(),
2767 unsafe { bottom_margin.as_ref() }.cloned(),
2768 unsafe { left_margin.as_ref() }.cloned(),
2776 WrSpatialId { id: spatial_id.0 }
2780 pub extern "C" fn wr_dp_define_scroll_layer(
2781 state: &mut WrState,
2782 external_scroll_id: u64,
2783 parent: &WrSpatialId,
2784 content_rect: LayoutRect,
2785 clip_rect: LayoutRect,
2786 scroll_offset: LayoutVector2D,
2787 scroll_offset_generation: APZScrollGeneration,
2788 has_scroll_linked_effect: HasScrollLinkedEffect,
2789 key: SpatialTreeItemKey,
2791 assert!(unsafe { is_in_main_thread() });
2793 let space_and_clip = state.frame_builder.dl_builder.define_scroll_frame(
2794 parent.to_webrender(state.pipeline_id),
2795 ExternalScrollId(external_scroll_id, state.pipeline_id),
2799 scroll_offset_generation,
2800 has_scroll_linked_effect,
2804 WrSpatialId::from_webrender(space_and_clip)
2808 pub extern "C" fn wr_dp_push_iframe(
2809 state: &mut WrState,
2812 _is_backface_visible: bool,
2813 parent: &WrSpaceAndClipChain,
2814 pipeline_id: WrPipelineId,
2815 ignore_missing_pipeline: bool,
2817 debug_assert!(unsafe { is_in_main_thread() });
2819 state.frame_builder.dl_builder.push_iframe(
2822 &parent.to_webrender(state.pipeline_id),
2824 ignore_missing_pipeline,
2828 // A helper fn to construct a PrimitiveFlags
2829 fn prim_flags(is_backface_visible: bool, prefer_compositor_surface: bool) -> PrimitiveFlags {
2830 let mut flags = PrimitiveFlags::empty();
2832 if is_backface_visible {
2833 flags |= PrimitiveFlags::IS_BACKFACE_VISIBLE;
2836 if prefer_compositor_surface {
2837 flags |= PrimitiveFlags::PREFER_COMPOSITOR_SURFACE;
2844 is_backface_visible: bool,
2845 prefer_compositor_surface: bool,
2846 supports_external_compositing: bool,
2847 ) -> PrimitiveFlags {
2848 let mut flags = PrimitiveFlags::empty();
2850 if supports_external_compositing {
2851 flags |= PrimitiveFlags::SUPPORTS_EXTERNAL_COMPOSITOR_SURFACE;
2854 flags | prim_flags(is_backface_visible, prefer_compositor_surface)
2857 fn common_item_properties_for_rect(
2858 state: &mut WrState,
2859 clip_rect: LayoutRect,
2860 is_backface_visible: bool,
2861 parent: &WrSpaceAndClipChain,
2862 ) -> CommonItemProperties {
2863 let space_and_clip = parent.to_webrender(state.pipeline_id);
2865 CommonItemProperties {
2866 // NB: the damp-e10s talos-test will frequently crash on startup if we
2867 // early-return here for empty rects. I couldn't figure out why, but
2868 // it's pretty harmless to feed these through, so, uh, we do?
2870 clip_id: space_and_clip.clip_id,
2871 spatial_id: space_and_clip.spatial_id,
2872 flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
2877 pub extern "C" fn wr_dp_push_rect(
2878 state: &mut WrState,
2881 is_backface_visible: bool,
2882 force_antialiasing: bool,
2883 is_checkerboard: bool,
2884 parent: &WrSpaceAndClipChain,
2887 debug_assert!(unsafe { !is_in_render_thread() });
2889 let mut prim_info = common_item_properties_for_rect(state, clip, is_backface_visible, parent);
2890 if force_antialiasing {
2891 prim_info.flags |= PrimitiveFlags::ANTIALISED;
2893 if is_checkerboard {
2894 prim_info.flags |= PrimitiveFlags::CHECKERBOARD_BACKGROUND;
2897 state.frame_builder.dl_builder.push_rect(&prim_info, rect, color);
2901 pub extern "C" fn wr_dp_push_rect_with_animation(
2902 state: &mut WrState,
2905 is_backface_visible: bool,
2906 parent: &WrSpaceAndClipChain,
2908 animation: *const WrAnimationProperty,
2910 debug_assert!(unsafe { !is_in_render_thread() });
2912 let prim_info = common_item_properties_for_rect(state, clip, is_backface_visible, parent);
2914 let anim = unsafe { animation.as_ref() };
2915 if let Some(anim) = anim {
2916 debug_assert!(anim.id > 0);
2917 match anim.effect_type {
2918 WrAnimationType::BackgroundColor => state.frame_builder.dl_builder.push_rect_with_animation(
2921 PropertyBinding::Binding(PropertyBindingKey::new(anim.id), color),
2923 _ => unreachable!("Didn't expect {:?} animation", anim.effect_type),
2929 pub extern "C" fn wr_dp_push_backdrop_filter(
2930 state: &mut WrState,
2933 is_backface_visible: bool,
2934 parent: &WrSpaceAndClipChain,
2935 filters: *const FilterOp,
2936 filter_count: usize,
2937 filter_datas: *const WrFilterData,
2938 filter_datas_count: usize,
2940 debug_assert!(unsafe { !is_in_render_thread() });
2942 let c_filters = unsafe { make_slice(filters, filter_count) };
2943 let filters: Vec<FilterOp> = c_filters.iter().copied().collect();
2945 let c_filter_datas = unsafe { make_slice(filter_datas, filter_datas_count) };
2946 let filter_datas: Vec<FilterData> = c_filter_datas
2948 .map(|c_filter_data| FilterData {
2949 func_r_type: c_filter_data.funcR_type,
2950 r_values: unsafe { make_slice(c_filter_data.R_values, c_filter_data.R_values_count).to_vec() },
2951 func_g_type: c_filter_data.funcG_type,
2952 g_values: unsafe { make_slice(c_filter_data.G_values, c_filter_data.G_values_count).to_vec() },
2953 func_b_type: c_filter_data.funcB_type,
2954 b_values: unsafe { make_slice(c_filter_data.B_values, c_filter_data.B_values_count).to_vec() },
2955 func_a_type: c_filter_data.funcA_type,
2956 a_values: unsafe { make_slice(c_filter_data.A_values, c_filter_data.A_values_count).to_vec() },
2960 let space_and_clip = parent.to_webrender(state.pipeline_id);
2962 let clip_rect = clip.intersection(&rect);
2963 if clip_rect.is_none() {
2967 let prim_info = CommonItemProperties {
2968 clip_rect: clip_rect.unwrap(),
2969 clip_id: space_and_clip.clip_id,
2970 spatial_id: space_and_clip.spatial_id,
2971 flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
2977 .push_backdrop_filter(&prim_info, &filters, &filter_datas, &[]);
2981 pub extern "C" fn wr_dp_push_clear_rect(
2982 state: &mut WrState,
2984 clip_rect: LayoutRect,
2985 parent: &WrSpaceAndClipChain,
2987 debug_assert!(unsafe { !is_in_render_thread() });
2989 let space_and_clip = parent.to_webrender(state.pipeline_id);
2991 let prim_info = CommonItemProperties {
2993 clip_id: space_and_clip.clip_id,
2994 spatial_id: space_and_clip.spatial_id,
2995 flags: prim_flags(true, /* prefer_compositor_surface */ false),
2998 state.frame_builder.dl_builder.push_clear_rect(&prim_info, rect);
3002 pub extern "C" fn wr_dp_push_hit_test(
3003 state: &mut WrState,
3006 is_backface_visible: bool,
3007 parent: &WrSpaceAndClipChain,
3011 debug_assert!(unsafe { !is_in_render_thread() });
3013 let space_and_clip = parent.to_webrender(state.pipeline_id);
3015 let clip_rect = clip.intersection(&rect);
3016 if clip_rect.is_none() {
3019 let tag = (scroll_id, hit_info);
3021 let prim_info = CommonItemProperties {
3022 clip_rect: clip_rect.unwrap(),
3023 clip_id: space_and_clip.clip_id,
3024 spatial_id: space_and_clip.spatial_id,
3025 flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
3028 state.frame_builder.dl_builder.push_hit_test(&prim_info, tag);
3032 pub extern "C" fn wr_dp_push_image(
3033 state: &mut WrState,
3036 is_backface_visible: bool,
3037 force_antialiasing: bool,
3038 parent: &WrSpaceAndClipChain,
3039 image_rendering: ImageRendering,
3041 premultiplied_alpha: bool,
3043 prefer_compositor_surface: bool,
3044 supports_external_compositing: bool,
3046 debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
3048 let space_and_clip = parent.to_webrender(state.pipeline_id);
3050 let mut flags = prim_flags2(
3051 is_backface_visible,
3052 prefer_compositor_surface,
3053 supports_external_compositing,
3056 if force_antialiasing {
3057 flags |= PrimitiveFlags::ANTIALISED;
3060 let prim_info = CommonItemProperties {
3062 clip_id: space_and_clip.clip_id,
3063 spatial_id: space_and_clip.spatial_id,
3067 let alpha_type = if premultiplied_alpha {
3068 AlphaType::PremultipliedAlpha
3076 .push_image(&prim_info, bounds, image_rendering, alpha_type, key, color);
3080 pub extern "C" fn wr_dp_push_repeating_image(
3081 state: &mut WrState,
3084 is_backface_visible: bool,
3085 parent: &WrSpaceAndClipChain,
3086 stretch_size: LayoutSize,
3087 tile_spacing: LayoutSize,
3088 image_rendering: ImageRendering,
3090 premultiplied_alpha: bool,
3093 debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
3095 let space_and_clip = parent.to_webrender(state.pipeline_id);
3097 let prim_info = CommonItemProperties {
3099 clip_id: space_and_clip.clip_id,
3100 spatial_id: space_and_clip.spatial_id,
3101 flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
3104 let alpha_type = if premultiplied_alpha {
3105 AlphaType::PremultipliedAlpha
3110 state.frame_builder.dl_builder.push_repeating_image(
3122 /// Push a 3 planar yuv image.
3124 pub extern "C" fn wr_dp_push_yuv_planar_image(
3125 state: &mut WrState,
3128 is_backface_visible: bool,
3129 parent: &WrSpaceAndClipChain,
3130 image_key_0: WrImageKey,
3131 image_key_1: WrImageKey,
3132 image_key_2: WrImageKey,
3133 color_depth: WrColorDepth,
3134 color_space: WrYuvColorSpace,
3135 color_range: WrColorRange,
3136 image_rendering: ImageRendering,
3137 prefer_compositor_surface: bool,
3138 supports_external_compositing: bool,
3140 debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
3142 let space_and_clip = parent.to_webrender(state.pipeline_id);
3144 let prim_info = CommonItemProperties {
3146 clip_id: space_and_clip.clip_id,
3147 spatial_id: space_and_clip.spatial_id,
3149 is_backface_visible,
3150 prefer_compositor_surface,
3151 supports_external_compositing,
3155 state.frame_builder.dl_builder.push_yuv_image(
3158 YuvData::PlanarYCbCr(image_key_0, image_key_1, image_key_2),
3166 /// Push a 2 planar NV12 image.
3168 pub extern "C" fn wr_dp_push_yuv_NV12_image(
3169 state: &mut WrState,
3172 is_backface_visible: bool,
3173 parent: &WrSpaceAndClipChain,
3174 image_key_0: WrImageKey,
3175 image_key_1: WrImageKey,
3176 color_depth: WrColorDepth,
3177 color_space: WrYuvColorSpace,
3178 color_range: WrColorRange,
3179 image_rendering: ImageRendering,
3180 prefer_compositor_surface: bool,
3181 supports_external_compositing: bool,
3183 debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
3185 let space_and_clip = parent.to_webrender(state.pipeline_id);
3187 let prim_info = CommonItemProperties {
3189 clip_id: space_and_clip.clip_id,
3190 spatial_id: space_and_clip.spatial_id,
3192 is_backface_visible,
3193 prefer_compositor_surface,
3194 supports_external_compositing,
3198 state.frame_builder.dl_builder.push_yuv_image(
3201 YuvData::NV12(image_key_0, image_key_1),
3209 /// Push a 2 planar P010 image.
3211 pub extern "C" fn wr_dp_push_yuv_P010_image(
3212 state: &mut WrState,
3215 is_backface_visible: bool,
3216 parent: &WrSpaceAndClipChain,
3217 image_key_0: WrImageKey,
3218 image_key_1: WrImageKey,
3219 color_depth: WrColorDepth,
3220 color_space: WrYuvColorSpace,
3221 color_range: WrColorRange,
3222 image_rendering: ImageRendering,
3223 prefer_compositor_surface: bool,
3224 supports_external_compositing: bool,
3226 debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
3228 let space_and_clip = parent.to_webrender(state.pipeline_id);
3230 let prim_info = CommonItemProperties {
3232 clip_id: space_and_clip.clip_id,
3233 spatial_id: space_and_clip.spatial_id,
3235 is_backface_visible,
3236 prefer_compositor_surface,
3237 supports_external_compositing,
3241 state.frame_builder.dl_builder.push_yuv_image(
3244 YuvData::P010(image_key_0, image_key_1),
3252 /// Push a yuv interleaved image.
3254 pub extern "C" fn wr_dp_push_yuv_interleaved_image(
3255 state: &mut WrState,
3258 is_backface_visible: bool,
3259 parent: &WrSpaceAndClipChain,
3260 image_key_0: WrImageKey,
3261 color_depth: WrColorDepth,
3262 color_space: WrYuvColorSpace,
3263 color_range: WrColorRange,
3264 image_rendering: ImageRendering,
3265 prefer_compositor_surface: bool,
3266 supports_external_compositing: bool,
3268 debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
3270 let space_and_clip = parent.to_webrender(state.pipeline_id);
3272 let prim_info = CommonItemProperties {
3274 clip_id: space_and_clip.clip_id,
3275 spatial_id: space_and_clip.spatial_id,
3277 is_backface_visible,
3278 prefer_compositor_surface,
3279 supports_external_compositing,
3283 state.frame_builder.dl_builder.push_yuv_image(
3286 YuvData::InterleavedYCbCr(image_key_0),
3295 pub extern "C" fn wr_dp_push_text(
3296 state: &mut WrState,
3299 is_backface_visible: bool,
3300 parent: &WrSpaceAndClipChain,
3302 font_key: WrFontInstanceKey,
3303 glyphs: *const GlyphInstance,
3305 glyph_options: *const GlyphOptions,
3307 debug_assert!(unsafe { is_in_main_thread() });
3309 let glyph_slice = unsafe { make_slice(glyphs, glyph_count as usize) };
3311 let space_and_clip = parent.to_webrender(state.pipeline_id);
3313 let prim_info = CommonItemProperties {
3315 spatial_id: space_and_clip.spatial_id,
3316 clip_id: space_and_clip.clip_id,
3317 flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
3323 .push_text(&prim_info, bounds, &glyph_slice, font_key, color, unsafe {
3324 glyph_options.as_ref().cloned()
3329 pub extern "C" fn wr_dp_push_shadow(
3330 state: &mut WrState,
3331 _bounds: LayoutRect,
3333 _is_backface_visible: bool,
3334 parent: &WrSpaceAndClipChain,
3336 should_inflate: bool,
3338 debug_assert!(unsafe { is_in_main_thread() });
3343 .push_shadow(&parent.to_webrender(state.pipeline_id), shadow, should_inflate);
3347 pub extern "C" fn wr_dp_pop_all_shadows(state: &mut WrState) {
3348 debug_assert!(unsafe { is_in_main_thread() });
3350 state.frame_builder.dl_builder.pop_all_shadows();
3354 pub extern "C" fn wr_dp_push_line(
3355 state: &mut WrState,
3357 is_backface_visible: bool,
3358 parent: &WrSpaceAndClipChain,
3359 bounds: &LayoutRect,
3360 wavy_line_thickness: f32,
3361 orientation: LineOrientation,
3365 debug_assert!(unsafe { is_in_main_thread() });
3367 let space_and_clip = parent.to_webrender(state.pipeline_id);
3369 let prim_info = CommonItemProperties {
3371 clip_id: space_and_clip.clip_id,
3372 spatial_id: space_and_clip.spatial_id,
3373 flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
3379 .push_line(&prim_info, bounds, wavy_line_thickness, orientation, color, style);
3383 pub extern "C" fn wr_dp_push_border(
3384 state: &mut WrState,
3387 is_backface_visible: bool,
3388 parent: &WrSpaceAndClipChain,
3389 do_aa: AntialiasBorder,
3390 widths: LayoutSideOffsets,
3395 radius: BorderRadius,
3397 debug_assert!(unsafe { is_in_main_thread() });
3399 let border_details = BorderDetails::Normal(NormalBorder {
3405 do_aa: do_aa == AntialiasBorder::Yes,
3408 let space_and_clip = parent.to_webrender(state.pipeline_id);
3410 let prim_info = CommonItemProperties {
3412 clip_id: space_and_clip.clip_id,
3413 spatial_id: space_and_clip.spatial_id,
3414 flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
3420 .push_border(&prim_info, rect, widths, border_details);
3424 pub struct WrBorderImage {
3425 widths: LayoutSideOffsets,
3427 image_rendering: ImageRendering,
3431 slice: DeviceIntSideOffsets,
3432 outset: LayoutSideOffsets,
3433 repeat_horizontal: RepeatMode,
3434 repeat_vertical: RepeatMode,
3438 pub extern "C" fn wr_dp_push_border_image(
3439 state: &mut WrState,
3442 is_backface_visible: bool,
3443 parent: &WrSpaceAndClipChain,
3444 params: &WrBorderImage,
3446 debug_assert!(unsafe { is_in_main_thread() });
3447 let border_details = BorderDetails::NinePatch(NinePatchBorder {
3448 source: NinePatchBorderSource::Image(params.image, params.image_rendering),
3449 width: params.width,
3450 height: params.height,
3451 slice: params.slice,
3453 outset: params.outset,
3454 repeat_horizontal: params.repeat_horizontal,
3455 repeat_vertical: params.repeat_vertical,
3457 let space_and_clip = parent.to_webrender(state.pipeline_id);
3459 let prim_info = CommonItemProperties {
3461 clip_id: space_and_clip.clip_id,
3462 spatial_id: space_and_clip.spatial_id,
3463 flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
3469 .push_border(&prim_info, rect, params.widths, border_details);
3473 pub extern "C" fn wr_dp_push_border_gradient(
3474 state: &mut WrState,
3477 is_backface_visible: bool,
3478 parent: &WrSpaceAndClipChain,
3479 widths: LayoutSideOffsets,
3483 slice: DeviceIntSideOffsets,
3484 start_point: LayoutPoint,
3485 end_point: LayoutPoint,
3486 stops: *const GradientStop,
3488 extend_mode: ExtendMode,
3489 outset: LayoutSideOffsets,
3491 debug_assert!(unsafe { is_in_main_thread() });
3493 let stops_slice = unsafe { make_slice(stops, stops_count) };
3494 let stops_vector = stops_slice.to_owned();
3496 let gradient = state
3499 .create_gradient(start_point, end_point, stops_vector, extend_mode);
3501 let border_details = BorderDetails::NinePatch(NinePatchBorder {
3502 source: NinePatchBorderSource::Gradient(gradient),
3508 repeat_horizontal: RepeatMode::Stretch,
3509 repeat_vertical: RepeatMode::Stretch,
3512 let space_and_clip = parent.to_webrender(state.pipeline_id);
3514 let prim_info = CommonItemProperties {
3516 clip_id: space_and_clip.clip_id,
3517 spatial_id: space_and_clip.spatial_id,
3518 flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
3524 .push_border(&prim_info, rect, widths, border_details);
3528 pub extern "C" fn wr_dp_push_border_radial_gradient(
3529 state: &mut WrState,
3532 is_backface_visible: bool,
3533 parent: &WrSpaceAndClipChain,
3534 widths: LayoutSideOffsets,
3536 center: LayoutPoint,
3538 stops: *const GradientStop,
3540 extend_mode: ExtendMode,
3541 outset: LayoutSideOffsets,
3543 debug_assert!(unsafe { is_in_main_thread() });
3545 let stops_slice = unsafe { make_slice(stops, stops_count) };
3546 let stops_vector = stops_slice.to_owned();
3548 let slice = SideOffsets2D::new(
3550 widths.right as i32,
3551 widths.bottom as i32,
3555 let gradient = state
3558 .create_radial_gradient(center, radius, stops_vector, extend_mode);
3560 let border_details = BorderDetails::NinePatch(NinePatchBorder {
3561 source: NinePatchBorderSource::RadialGradient(gradient),
3562 width: rect.width() as i32,
3563 height: rect.height() as i32,
3567 repeat_horizontal: RepeatMode::Stretch,
3568 repeat_vertical: RepeatMode::Stretch,
3571 let space_and_clip = parent.to_webrender(state.pipeline_id);
3573 let prim_info = CommonItemProperties {
3575 clip_id: space_and_clip.clip_id,
3576 spatial_id: space_and_clip.spatial_id,
3577 flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
3583 .push_border(&prim_info, rect, widths, border_details);
3587 pub extern "C" fn wr_dp_push_border_conic_gradient(
3588 state: &mut WrState,
3591 is_backface_visible: bool,
3592 parent: &WrSpaceAndClipChain,
3593 widths: LayoutSideOffsets,
3595 center: LayoutPoint,
3597 stops: *const GradientStop,
3599 extend_mode: ExtendMode,
3600 outset: LayoutSideOffsets,
3602 debug_assert!(unsafe { is_in_main_thread() });
3604 let stops_slice = unsafe { make_slice(stops, stops_count) };
3605 let stops_vector = stops_slice.to_owned();
3607 let slice = SideOffsets2D::new(
3609 widths.right as i32,
3610 widths.bottom as i32,
3614 let gradient = state
3617 .create_conic_gradient(center, angle, stops_vector, extend_mode);
3619 let border_details = BorderDetails::NinePatch(NinePatchBorder {
3620 source: NinePatchBorderSource::ConicGradient(gradient),
3621 width: rect.width() as i32,
3622 height: rect.height() as i32,
3626 repeat_horizontal: RepeatMode::Stretch,
3627 repeat_vertical: RepeatMode::Stretch,
3630 let space_and_clip = parent.to_webrender(state.pipeline_id);
3632 let prim_info = CommonItemProperties {
3634 clip_id: space_and_clip.clip_id,
3635 spatial_id: space_and_clip.spatial_id,
3636 flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
3642 .push_border(&prim_info, rect, widths, border_details);
3646 pub extern "C" fn wr_dp_push_linear_gradient(
3647 state: &mut WrState,
3650 is_backface_visible: bool,
3651 parent: &WrSpaceAndClipChain,
3652 start_point: LayoutPoint,
3653 end_point: LayoutPoint,
3654 stops: *const GradientStop,
3656 extend_mode: ExtendMode,
3657 tile_size: LayoutSize,
3658 tile_spacing: LayoutSize,
3660 debug_assert!(unsafe { is_in_main_thread() });
3662 let stops_slice = unsafe { make_slice(stops, stops_count) };
3663 let stops_vector = stops_slice.to_owned();
3665 let gradient = state
3668 .create_gradient(start_point, end_point, stops_vector, extend_mode);
3670 let space_and_clip = parent.to_webrender(state.pipeline_id);
3672 let prim_info = CommonItemProperties {
3674 clip_id: space_and_clip.clip_id,
3675 spatial_id: space_and_clip.spatial_id,
3676 flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
3682 .push_gradient(&prim_info, rect, gradient, tile_size, tile_spacing);
3686 pub extern "C" fn wr_dp_push_radial_gradient(
3687 state: &mut WrState,
3690 is_backface_visible: bool,
3691 parent: &WrSpaceAndClipChain,
3692 center: LayoutPoint,
3694 stops: *const GradientStop,
3696 extend_mode: ExtendMode,
3697 tile_size: LayoutSize,
3698 tile_spacing: LayoutSize,
3700 debug_assert!(unsafe { is_in_main_thread() });
3702 let stops_slice = unsafe { make_slice(stops, stops_count) };
3703 let stops_vector = stops_slice.to_owned();
3705 let gradient = state
3708 .create_radial_gradient(center, radius, stops_vector, extend_mode);
3710 let space_and_clip = parent.to_webrender(state.pipeline_id);
3712 let prim_info = CommonItemProperties {
3714 clip_id: space_and_clip.clip_id,
3715 spatial_id: space_and_clip.spatial_id,
3716 flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
3722 .push_radial_gradient(&prim_info, rect, gradient, tile_size, tile_spacing);
3726 pub extern "C" fn wr_dp_push_conic_gradient(
3727 state: &mut WrState,
3730 is_backface_visible: bool,
3731 parent: &WrSpaceAndClipChain,
3732 center: LayoutPoint,
3734 stops: *const GradientStop,
3736 extend_mode: ExtendMode,
3737 tile_size: LayoutSize,
3738 tile_spacing: LayoutSize,
3740 debug_assert!(unsafe { is_in_main_thread() });
3742 let stops_slice = unsafe { make_slice(stops, stops_count) };
3743 let stops_vector = stops_slice.to_owned();
3745 let gradient = state
3748 .create_conic_gradient(center, angle, stops_vector, extend_mode);
3750 let space_and_clip = parent.to_webrender(state.pipeline_id);
3752 let prim_info = CommonItemProperties {
3754 clip_id: space_and_clip.clip_id,
3755 spatial_id: space_and_clip.spatial_id,
3756 flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
3762 .push_conic_gradient(&prim_info, rect, gradient, tile_size, tile_spacing);
3766 pub extern "C" fn wr_dp_push_box_shadow(
3767 state: &mut WrState,
3770 is_backface_visible: bool,
3771 parent: &WrSpaceAndClipChain,
3772 box_bounds: LayoutRect,
3773 offset: LayoutVector2D,
3777 border_radius: BorderRadius,
3778 clip_mode: BoxShadowClipMode,
3780 debug_assert!(unsafe { is_in_main_thread() });
3782 let space_and_clip = parent.to_webrender(state.pipeline_id);
3784 let prim_info = CommonItemProperties {
3786 clip_id: space_and_clip.clip_id,
3787 spatial_id: space_and_clip.spatial_id,
3788 flags: prim_flags(is_backface_visible, /* prefer_compositor_surface */ false),
3791 state.frame_builder.dl_builder.push_box_shadow(
3804 pub extern "C" fn wr_dp_start_item_group(state: &mut WrState) {
3805 state.frame_builder.dl_builder.start_item_group();
3809 pub extern "C" fn wr_dp_cancel_item_group(state: &mut WrState, discard: bool) {
3810 state.frame_builder.dl_builder.cancel_item_group(discard);
3814 pub extern "C" fn wr_dp_finish_item_group(state: &mut WrState, key: ItemKey) -> bool {
3815 state.frame_builder.dl_builder.finish_item_group(key)
3819 pub extern "C" fn wr_dp_push_reuse_items(state: &mut WrState, key: ItemKey) {
3820 state.frame_builder.dl_builder.push_reuse_items(key);
3824 pub extern "C" fn wr_dp_set_cache_size(state: &mut WrState, cache_size: usize) {
3825 state.frame_builder.dl_builder.set_cache_size(cache_size);
3829 pub extern "C" fn wr_dump_display_list(
3830 state: &mut WrState,
3832 start: *const usize,
3835 let start = unsafe { start.as_ref().cloned() };
3836 let end = unsafe { end.as_ref().cloned() };
3837 let range = Range { start, end };
3838 let mut sink = Cursor::new(Vec::new());
3842 .emit_display_list(indent, range, &mut sink);
3844 // For Android, dump to logcat instead of stderr. This is the same as
3845 // what printf_stderr does on the C++ side.
3847 #[cfg(target_os = "android")]
3849 let gecko = CString::new("Gecko").unwrap();
3850 let sink = CString::new(sink.into_inner()).unwrap();
3851 __android_log_write(4 /* info */, gecko.as_ptr(), sink.as_ptr());
3854 #[cfg(not(target_os = "android"))]
3855 eprint!("{}", String::from_utf8(sink.into_inner()).unwrap());
3861 pub extern "C" fn wr_dump_serialized_display_list(state: &mut WrState) {
3862 state.frame_builder.dl_builder.dump_serialized_display_list();
3866 pub unsafe extern "C" fn wr_api_begin_builder(state: &mut WrState) {
3867 state.frame_builder.dl_builder.begin();
3871 pub unsafe extern "C" fn wr_api_end_builder(
3872 state: &mut WrState,
3873 dl_descriptor: &mut BuiltDisplayListDescriptor,
3874 dl_items_data: &mut WrVecU8,
3875 dl_cache_data: &mut WrVecU8,
3876 dl_spatial_tree: &mut WrVecU8,
3878 let (_, dl) = state.frame_builder.dl_builder.end();
3879 let (payload, descriptor) = dl.into_data();
3880 *dl_items_data = WrVecU8::from_vec(payload.items_data);
3881 *dl_cache_data = WrVecU8::from_vec(payload.cache_data);
3882 *dl_spatial_tree = WrVecU8::from_vec(payload.spatial_tree);
3883 *dl_descriptor = descriptor;
3887 pub struct HitResult {
3888 pipeline_id: WrPipelineId,
3894 pub extern "C" fn wr_api_hit_test(dh: &mut DocumentHandle, point: WorldPoint, out_results: &mut ThinVec<HitResult>) {
3895 let result = dh.ensure_hit_tester().hit_test(point);
3896 for item in &result.items {
3897 out_results.push(HitResult {
3898 pipeline_id: item.pipeline,
3899 scroll_id: item.tag.0,
3900 hit_info: item.tag.1,
3905 pub type VecU8 = Vec<u8>;
3906 pub type ArcVecU8 = Arc<VecU8>;
3909 pub extern "C" fn wr_add_ref_arc(arc: &ArcVecU8) -> *const VecU8 {
3910 Arc::into_raw(arc.clone())
3914 pub unsafe extern "C" fn wr_dec_ref_arc(arc: *const VecU8) {
3919 // Update for the new blob image interface changes.
3922 // TODO: figure out the API for tiled blob images.
3923 pub fn wr_moz2d_render_cb(
3925 format: ImageFormat,
3926 render_rect: &LayoutIntRect,
3927 visible_rect: &DeviceIntRect,
3929 tile_offset: &TileOffset,
3930 dirty_rect: Option<&LayoutIntRect>,
3931 output: MutByteSlice,
3936 pub extern "C" fn wr_root_scroll_node_id() -> WrSpatialId {
3937 // The PipelineId doesn't matter here, since we just want the numeric part of the id
3938 // produced for any given root reference frame.
3940 id: SpatialId::root_scroll_node(PipelineId(0, 0)).0,
3945 pub extern "C" fn wr_root_clip_id() -> WrClipId {
3946 // The PipelineId doesn't matter here, since we just want the numeric part of the id
3947 // produced for any given root reference frame.
3948 WrClipId::from_webrender(ClipId::root(PipelineId(0, 0)))
3952 #[derive(Clone, Copy)]
3953 pub struct WrClipId {
3958 fn to_webrender(&self, pipeline_id: WrPipelineId) -> ClipId {
3959 ClipId::Clip(self.id, pipeline_id)
3962 fn from_webrender(clip_id: ClipId) -> Self {
3964 ClipId::Clip(id, _) => WrClipId { id },
3965 ClipId::ClipChain(_) => panic!("Unexpected clip chain"),
3971 #[derive(Clone, Copy)]
3972 pub struct WrSpatialId {
3977 fn to_webrender(&self, pipeline_id: WrPipelineId) -> SpatialId {
3978 SpatialId::new(self.id, pipeline_id)
3981 fn from_webrender(id: SpatialId) -> Self {
3982 WrSpatialId { id: id.0 }
3987 pub unsafe extern "C" fn wr_device_delete(device: *mut Device) {
3988 Box::from_raw(device);
3991 // Call MakeCurrent before this.
3993 pub extern "C" fn wr_shaders_new(
3994 gl_context: *mut c_void,
3995 program_cache: Option<&mut WrProgramCache>,
3996 precache_shaders: bool,
3997 ) -> *mut WrShaders {
3998 let mut device = wr_device_new(gl_context, program_cache);
4000 let precache_flags = if precache_shaders || env_var_to_bool("MOZ_WR_PRECACHE_SHADERS") {
4001 ShaderPrecacheFlags::FULL_COMPILE
4003 ShaderPrecacheFlags::ASYNC_COMPILE
4006 let opts = RendererOptions {
4008 ..Default::default()
4011 let gl_type = device.gl().get_type();
4012 device.begin_frame();
4014 let shaders = Rc::new(RefCell::new(match Shaders::new(&mut device, gl_type, &opts) {
4015 Ok(shaders) => shaders,
4017 warn!(" Failed to create a Shaders: {:?}", e);
4018 let msg = CString::new(format!("wr_shaders_new: {:?}", e)).unwrap();
4020 gfx_critical_note(msg.as_ptr());
4022 return ptr::null_mut();
4026 let shaders = WrShaders(shaders);
4029 Box::into_raw(Box::new(shaders))
4033 pub unsafe extern "C" fn wr_shaders_delete(shaders: *mut WrShaders, gl_context: *mut c_void) {
4034 let mut device = wr_device_new(gl_context, None);
4035 let shaders = Box::from_raw(shaders);
4036 if let Ok(shaders) = Rc::try_unwrap(shaders.0) {
4037 shaders.into_inner().deinit(&mut device);
4039 // let shaders go out of scope and get dropped
4043 pub unsafe extern "C" fn wr_program_cache_report_memory(
4044 cache: *const WrProgramCache,
4045 size_of_op: VoidPtrToSizeFn,
4047 (*cache).program_cache.report_memory(size_of_op)