webrender/renderer/
init.rs

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/. */
4
5use api::{BlobImageHandler, ColorF, CrashAnnotator, DocumentId, IdNamespace};
6use api::{VoidPtrToSizeFn, FontRenderMode, ImageFormat};
7use api::{RenderNotifier, ImageBufferKind};
8use api::units::*;
9use api::channel::unbounded_channel;
10pub use api::DebugFlags;
11
12use crate::bump_allocator::ChunkPool;
13use crate::render_api::{RenderApiSender, FrameMsg};
14use crate::composite::{CompositorKind, CompositorConfig};
15use crate::device::{
16    UploadMethod, UploadPBOPool, VertexUsageHint, Device, ProgramCache, TextureFilter
17};
18use crate::frame_builder::FrameBuilderConfig;
19use crate::glyph_cache::GlyphCache;
20use glyph_rasterizer::{GlyphRasterThread, GlyphRasterizer, SharedFontResources};
21use crate::gpu_types::PrimitiveInstanceData;
22use crate::internal_types::{FastHashMap, FastHashSet, FrameId};
23use crate::picture;
24use crate::profiler::{self, Profiler, TransactionProfile};
25use crate::device::query::{GpuProfiler, GpuDebugMethod};
26use crate::render_backend::RenderBackend;
27use crate::resource_cache::ResourceCache;
28use crate::scene_builder_thread::{SceneBuilderThread, SceneBuilderThreadChannels, LowPrioritySceneBuilderThread};
29use crate::texture_cache::{TextureCache, TextureCacheConfig};
30use crate::picture_textures::PictureTextures;
31use crate::renderer::{
32    debug, gpu_cache, vertex, gl,
33    Renderer, DebugOverlayState, BufferDamageTracker, PipelineInfo, TextureResolver,
34    RendererError, ShaderPrecacheFlags, VERTEX_DATA_TEXTURE_COUNT,
35    upload::UploadTexturePool,
36    shade::{Shaders, SharedShaders},
37};
38#[cfg(feature = "debugger")]
39use crate::debugger::Debugger;
40
41use std::{
42    mem,
43    thread,
44    cell::RefCell,
45    collections::VecDeque,
46    rc::Rc,
47    sync::{Arc, atomic::{AtomicBool, Ordering}},
48    num::NonZeroUsize,
49    path::PathBuf,
50};
51
52use tracy_rs::register_thread_with_profiler;
53use rayon::{ThreadPool, ThreadPoolBuilder};
54use malloc_size_of::MallocSizeOfOps;
55
56/// Use this hint for all vertex data re-initialization. This allows
57/// the driver to better re-use RBOs internally.
58pub const ONE_TIME_USAGE_HINT: VertexUsageHint = VertexUsageHint::Stream;
59
60/// Is only false if no WR instances have ever been created.
61static HAS_BEEN_INITIALIZED: AtomicBool = AtomicBool::new(false);
62
63/// Returns true if a WR instance has ever been initialized in this process.
64pub fn wr_has_been_initialized() -> bool {
65    HAS_BEEN_INITIALIZED.load(Ordering::SeqCst)
66}
67
68/// Allows callers to hook in at certain points of the async scene build. These
69/// functions are all called from the scene builder thread.
70pub trait SceneBuilderHooks {
71    /// This is called exactly once, when the scene builder thread is started
72    /// and before it processes anything.
73    fn register(&self);
74    /// This is called before each scene build starts.
75    fn pre_scene_build(&self);
76    /// This is called before each scene swap occurs.
77    fn pre_scene_swap(&self);
78    /// This is called after each scene swap occurs. The PipelineInfo contains
79    /// the updated epochs and pipelines removed in the new scene compared to
80    /// the old scene.
81    fn post_scene_swap(&self, document_id: &Vec<DocumentId>, info: PipelineInfo, schedule_frame: bool);
82    /// This is called after a resource update operation on the scene builder
83    /// thread, in the case where resource updates were applied without a scene
84    /// build.
85    fn post_resource_update(&self, document_ids: &Vec<DocumentId>);
86    /// This is called after a scene build completes without any changes being
87    /// made. We guarantee that each pre_scene_build call will be matched with
88    /// exactly one of post_scene_swap, post_resource_update or
89    /// post_empty_scene_build.
90    fn post_empty_scene_build(&self);
91    /// This is a generic callback which provides an opportunity to run code
92    /// on the scene builder thread. This is called as part of the main message
93    /// loop of the scene builder thread, but outside of any specific message
94    /// handler.
95    fn poke(&self);
96    /// This is called exactly once, when the scene builder thread is about to
97    /// terminate.
98    fn deregister(&self);
99}
100
101/// Allows callers to hook into the main render_backend loop and provide
102/// additional frame ops for generate_frame transactions. These functions
103/// are all called from the render backend thread.
104pub trait AsyncPropertySampler {
105    /// This is called exactly once, when the render backend thread is started
106    /// and before it processes anything.
107    fn register(&self);
108    /// This is called for each transaction with the generate_frame flag set
109    /// (i.e. that will trigger a render). The list of frame messages returned
110    /// are processed as though they were part of the original transaction.
111    fn sample(&self, document_id: DocumentId, generated_frame_id: Option<u64>) -> Vec<FrameMsg>;
112    /// This is called exactly once, when the render backend thread is about to
113    /// terminate.
114    fn deregister(&self);
115}
116
117pub trait RenderBackendHooks {
118    fn init_thread(&self);
119}
120
121pub struct WebRenderOptions {
122    pub resource_override_path: Option<PathBuf>,
123    /// Whether to use shaders that have been optimized at build time.
124    pub use_optimized_shaders: bool,
125    pub enable_aa: bool,
126    pub enable_dithering: bool,
127    pub max_recorded_profiles: usize,
128    pub precache_flags: ShaderPrecacheFlags,
129    /// Enable sub-pixel anti-aliasing if a fast implementation is available.
130    pub enable_subpixel_aa: bool,
131    pub clear_color: ColorF,
132    pub enable_clear_scissor: Option<bool>,
133    pub max_internal_texture_size: Option<i32>,
134    pub image_tiling_threshold: i32,
135    pub upload_method: UploadMethod,
136    /// The default size in bytes for PBOs used to upload texture data.
137    pub upload_pbo_default_size: usize,
138    pub batched_upload_threshold: i32,
139    pub workers: Option<Arc<ThreadPool>>,
140    /// A pool of large memory chunks used by the per-frame allocators.
141    /// Providing the pool here makes it possible to share a single pool for
142    /// all WebRender instances.
143    pub chunk_pool: Option<Arc<ChunkPool>>,
144    pub dedicated_glyph_raster_thread: Option<GlyphRasterThread>,
145    pub enable_multithreading: bool,
146    pub blob_image_handler: Option<Box<dyn BlobImageHandler>>,
147    pub crash_annotator: Option<Box<dyn CrashAnnotator>>,
148    pub size_of_op: Option<VoidPtrToSizeFn>,
149    pub enclosing_size_of_op: Option<VoidPtrToSizeFn>,
150    pub cached_programs: Option<Rc<ProgramCache>>,
151    pub debug_flags: DebugFlags,
152    pub renderer_id: Option<u64>,
153    pub scene_builder_hooks: Option<Box<dyn SceneBuilderHooks + Send>>,
154    pub render_backend_hooks: Option<Box<dyn RenderBackendHooks + Send>>,
155    pub sampler: Option<Box<dyn AsyncPropertySampler + Send>>,
156    pub support_low_priority_transactions: bool,
157    pub namespace_alloc_by_client: bool,
158    /// If namespaces are allocated by the client, then the namespace for fonts
159    /// must also be allocated by the client to avoid namespace collisions with
160    /// the backend.
161    pub shared_font_namespace: Option<IdNamespace>,
162    pub testing: bool,
163    /// Set to true if this GPU supports hardware fast clears as a performance
164    /// optimization. Likely requires benchmarking on various GPUs to see if
165    /// it is a performance win. The default is false, which tends to be best
166    /// performance on lower end / integrated GPUs.
167    pub gpu_supports_fast_clears: bool,
168    pub allow_dual_source_blending: bool,
169    pub allow_advanced_blend_equation: bool,
170    /// If true, allow textures to be initialized with glTexStorage.
171    /// This affects VRAM consumption and data upload paths.
172    pub allow_texture_storage_support: bool,
173    /// If true, we allow the data uploaded in a different format from the
174    /// one expected by the driver, pretending the format is matching, and
175    /// swizzling the components on all the shader sampling.
176    pub allow_texture_swizzling: bool,
177    /// Use `ps_clear` shader with batched quad rendering to clear the rects
178    /// in texture cache and picture cache tasks.
179    /// This helps to work around some Intel drivers
180    /// that incorrectly synchronize clears to following draws.
181    pub clear_caches_with_quads: bool,
182    /// Output the source of the shader with the given name.
183    pub dump_shader_source: Option<String>,
184    pub surface_origin_is_top_left: bool,
185    /// The configuration options defining how WR composites the final scene.
186    pub compositor_config: CompositorConfig,
187    pub enable_gpu_markers: bool,
188    /// If true, panic whenever a GL error occurs. This has a significant
189    /// performance impact, so only use when debugging specific problems!
190    pub panic_on_gl_error: bool,
191    pub picture_tile_size: Option<DeviceIntSize>,
192    pub texture_cache_config: TextureCacheConfig,
193    /// If true, we'll use instanced vertex attributes. Each instace is a quad.
194    /// If false, we'll duplicate the instance attributes per vertex and issue
195    /// regular indexed draws instead.
196    pub enable_instancing: bool,
197    /// If true, we'll reject contexts backed by a software rasterizer, except
198    /// Software WebRender.
199    pub reject_software_rasterizer: bool,
200    /// If enabled, pinch-zoom will apply the zoom factor during compositing
201    /// of picture cache tiles. This is higher performance (tiles are not
202    /// re-rasterized during zoom) but lower quality result. For most display
203    /// items, if the zoom factor is relatively small, bilinear filtering should
204    /// make the result look quite close to the high-quality zoom, except for glyphs.
205    pub low_quality_pinch_zoom: bool,
206    pub max_shared_surface_size: i32,
207    /// If true, open a debug socket to listen for remote debugger.
208    /// Relies on `debugger` cargo feature being enabled.
209    pub enable_debugger: bool,
210
211    /// Use a more precise method for sampling gradients.
212    pub precise_linear_gradients: bool,
213    pub precise_radial_gradients: bool,
214    pub precise_conic_gradients: bool,
215}
216
217impl WebRenderOptions {
218    /// Number of batches to look back in history for adding the current
219    /// transparent instance into.
220    const BATCH_LOOKBACK_COUNT: usize = 10;
221
222    /// Since we are re-initializing the instance buffers on every draw call,
223    /// the driver has to internally manage PBOs in flight.
224    /// It's typically done by bucketing up to a specific limit, and then
225    /// just individually managing the largest buffers.
226    /// Having a limit here allows the drivers to more easily manage
227    /// the PBOs for us.
228    const MAX_INSTANCE_BUFFER_SIZE: usize = 0x20000; // actual threshold in macOS GL drivers
229}
230
231impl Default for WebRenderOptions {
232    fn default() -> Self {
233        WebRenderOptions {
234            resource_override_path: None,
235            use_optimized_shaders: false,
236            enable_aa: true,
237            enable_dithering: false,
238            debug_flags: DebugFlags::empty(),
239            max_recorded_profiles: 0,
240            precache_flags: ShaderPrecacheFlags::empty(),
241            enable_subpixel_aa: false,
242            clear_color: ColorF::new(1.0, 1.0, 1.0, 1.0),
243            enable_clear_scissor: None,
244            max_internal_texture_size: None,
245            image_tiling_threshold: 4096,
246            // This is best as `Immediate` on Angle, or `Pixelbuffer(Dynamic)` on GL,
247            // but we are unable to make this decision here, so picking the reasonable medium.
248            upload_method: UploadMethod::PixelBuffer(ONE_TIME_USAGE_HINT),
249            upload_pbo_default_size: 512 * 512 * 4,
250            batched_upload_threshold: 512 * 512,
251            workers: None,
252            chunk_pool: None,
253            dedicated_glyph_raster_thread: None,
254            enable_multithreading: true,
255            blob_image_handler: None,
256            crash_annotator: None,
257            size_of_op: None,
258            enclosing_size_of_op: None,
259            renderer_id: None,
260            cached_programs: None,
261            scene_builder_hooks: None,
262            render_backend_hooks: None,
263            sampler: None,
264            support_low_priority_transactions: false,
265            namespace_alloc_by_client: false,
266            shared_font_namespace: None,
267            testing: false,
268            gpu_supports_fast_clears: false,
269            allow_dual_source_blending: true,
270            allow_advanced_blend_equation: false,
271            allow_texture_storage_support: true,
272            allow_texture_swizzling: true,
273            clear_caches_with_quads: true,
274            dump_shader_source: None,
275            surface_origin_is_top_left: false,
276            compositor_config: CompositorConfig::default(),
277            enable_gpu_markers: true,
278            panic_on_gl_error: false,
279            picture_tile_size: None,
280            texture_cache_config: TextureCacheConfig::DEFAULT,
281            // Disabling instancing means more vertex data to upload and potentially
282            // process by the vertex shaders.
283            enable_instancing: true,
284            reject_software_rasterizer: false,
285            low_quality_pinch_zoom: false,
286            max_shared_surface_size: 2048,
287            enable_debugger: true,
288            precise_linear_gradients: false,
289            precise_radial_gradients: false,
290            precise_conic_gradients: false,
291        }
292    }
293}
294
295/// Initializes WebRender and creates a `Renderer` and `RenderApiSender`.
296///
297/// # Examples
298/// Initializes a `Renderer` with some reasonable values. For more information see
299/// [`WebRenderOptions`][WebRenderOptions].
300///
301/// ```rust,ignore
302/// # use webrender::renderer::Renderer;
303/// # use std::path::PathBuf;
304/// let opts = webrender::WebRenderOptions {
305///    device_pixel_ratio: 1.0,
306///    resource_override_path: None,
307///    enable_aa: false,
308/// };
309/// let (renderer, sender) = Renderer::new(opts);
310/// ```
311/// [WebRenderOptions]: struct.WebRenderOptions.html
312pub fn create_webrender_instance(
313    gl: Rc<dyn gl::Gl>,
314    notifier: Box<dyn RenderNotifier>,
315    mut options: WebRenderOptions,
316    shaders: Option<&SharedShaders>,
317) -> Result<(Renderer, RenderApiSender), RendererError> {
318    if !wr_has_been_initialized() {
319        // If the profiler feature is enabled, try to load the profiler shared library
320        // if the path was provided.
321        #[cfg(feature = "profiler")]
322        unsafe {
323            if let Ok(ref tracy_path) = std::env::var("WR_TRACY_PATH") {
324                let ok = tracy_rs::load(tracy_path);
325                info!("Load tracy from {} -> {}", tracy_path, ok);
326            }
327        }
328
329        register_thread_with_profiler("Compositor".to_owned());
330    }
331
332    HAS_BEEN_INITIALIZED.store(true, Ordering::SeqCst);
333
334    // For now, we assume that native OS compositors are top-left origin. If that doesn't
335    // turn out to be the case, we can add a query method on `LayerCompositor`.
336    match options.compositor_config {
337        CompositorConfig::Draw { .. } | CompositorConfig::Native { .. } => {}
338        CompositorConfig::Layer { .. } => {
339            options.surface_origin_is_top_left = true;
340        }
341    }
342
343    let (api_tx, api_rx) = unbounded_channel();
344    let (result_tx, result_rx) = unbounded_channel();
345    let gl_type = gl.get_type();
346
347    let mut device = Device::new(
348        gl,
349        options.crash_annotator.clone(),
350        options.resource_override_path.clone(),
351        options.use_optimized_shaders,
352        options.upload_method.clone(),
353        options.batched_upload_threshold,
354        options.cached_programs.take(),
355        options.allow_texture_storage_support,
356        options.allow_texture_swizzling,
357        options.dump_shader_source.take(),
358        options.surface_origin_is_top_left,
359        options.panic_on_gl_error,
360    );
361
362    let color_cache_formats = device.preferred_color_formats();
363    let swizzle_settings = device.swizzle_settings();
364    let use_dual_source_blending =
365        device.get_capabilities().supports_dual_source_blending &&
366        options.allow_dual_source_blending;
367    let ext_blend_equation_advanced =
368        options.allow_advanced_blend_equation &&
369        device.get_capabilities().supports_advanced_blend_equation;
370    let ext_blend_equation_advanced_coherent =
371        device.supports_extension("GL_KHR_blend_equation_advanced_coherent");
372
373    let enable_clear_scissor = options
374        .enable_clear_scissor
375        .unwrap_or(device.get_capabilities().prefers_clear_scissor);
376
377    // 2048 is the minimum that the texture cache can work with.
378    const MIN_TEXTURE_SIZE: i32 = 2048;
379    let mut max_internal_texture_size = device.max_texture_size();
380    if max_internal_texture_size < MIN_TEXTURE_SIZE {
381        // Broken GL contexts can return a max texture size of zero (See #1260).
382        // Better to gracefully fail now than panic as soon as a texture is allocated.
383        error!(
384            "Device reporting insufficient max texture size ({})",
385            max_internal_texture_size
386        );
387        return Err(RendererError::MaxTextureSize);
388    }
389    if let Some(internal_limit) = options.max_internal_texture_size {
390        assert!(internal_limit >= MIN_TEXTURE_SIZE);
391        max_internal_texture_size = max_internal_texture_size.min(internal_limit);
392    }
393
394    if options.reject_software_rasterizer {
395        let renderer_name_lc = device.get_capabilities().renderer_name.to_lowercase();
396        if renderer_name_lc.contains("llvmpipe") || renderer_name_lc.contains("softpipe") || renderer_name_lc.contains("software rasterizer") {
397        return Err(RendererError::SoftwareRasterizer);
398        }
399    }
400
401    let image_tiling_threshold = options.image_tiling_threshold
402        .min(max_internal_texture_size);
403
404    device.begin_frame();
405
406    let shaders = match shaders {
407        Some(shaders) => Rc::clone(shaders),
408        None => {
409            let mut shaders = Shaders::new(&mut device, gl_type, &options)?;
410            if options.precache_flags.intersects(ShaderPrecacheFlags::ASYNC_COMPILE | ShaderPrecacheFlags::FULL_COMPILE) {
411                let mut pending_shaders = shaders.precache_all(options.precache_flags);
412                while shaders.resume_precache(&mut device, &mut pending_shaders)? {}
413            }
414            Rc::new(RefCell::new(shaders))
415        }
416    };
417
418    let dither_matrix_texture = if options.enable_dithering {
419        let dither_matrix: [u8; 64] = [
420            0,
421            48,
422            12,
423            60,
424            3,
425            51,
426            15,
427            63,
428            32,
429            16,
430            44,
431            28,
432            35,
433            19,
434            47,
435            31,
436            8,
437            56,
438            4,
439            52,
440            11,
441            59,
442            7,
443            55,
444            40,
445            24,
446            36,
447            20,
448            43,
449            27,
450            39,
451            23,
452            2,
453            50,
454            14,
455            62,
456            1,
457            49,
458            13,
459            61,
460            34,
461            18,
462            46,
463            30,
464            33,
465            17,
466            45,
467            29,
468            10,
469            58,
470            6,
471            54,
472            9,
473            57,
474            5,
475            53,
476            42,
477            26,
478            38,
479            22,
480            41,
481            25,
482            37,
483            21,
484        ];
485
486        let texture = device.create_texture(
487            ImageBufferKind::Texture2D,
488            ImageFormat::R8,
489            8,
490            8,
491            TextureFilter::Nearest,
492            None,
493        );
494        device.upload_texture_immediate(&texture, &dither_matrix);
495
496        Some(texture)
497    } else {
498        None
499    };
500
501    let max_primitive_instance_count =
502        WebRenderOptions::MAX_INSTANCE_BUFFER_SIZE / mem::size_of::<PrimitiveInstanceData>();
503    let vaos = vertex::RendererVAOs::new(
504        &mut device,
505        if options.enable_instancing { None } else { NonZeroUsize::new(max_primitive_instance_count) },
506    );
507
508    let texture_upload_pbo_pool = UploadPBOPool::new(&mut device, options.upload_pbo_default_size);
509    let staging_texture_pool = UploadTexturePool::new();
510    let texture_resolver = TextureResolver::new(&mut device);
511
512    let mut vertex_data_textures = Vec::new();
513    for _ in 0 .. VERTEX_DATA_TEXTURE_COUNT {
514        vertex_data_textures.push(vertex::VertexDataTextures::new());
515    }
516
517    // On some (mostly older, integrated) GPUs, the normal GPU texture cache update path
518    // doesn't work well when running on ANGLE, causing CPU stalls inside D3D and/or the
519    // GPU driver. See https://bugzilla.mozilla.org/show_bug.cgi?id=1576637 for much
520    // more detail. To reduce the number of code paths we have active that require testing,
521    // we will enable the GPU cache scatter update path on all devices running with ANGLE.
522    // We want a better solution long-term, but for now this is a significant performance
523    // improvement on HD4600 era GPUs, and shouldn't hurt performance in a noticeable
524    // way on other systems running under ANGLE.
525    let is_software = device.get_capabilities().renderer_name.starts_with("Software");
526
527    // On other GL platforms, like macOS or Android, creating many PBOs is very inefficient.
528    // This is what happens in GPU cache updates in PBO path. Instead, we switch everything
529    // except software GL to use the GPU scattered updates.
530    let supports_scatter = device.get_capabilities().supports_color_buffer_float;
531    let gpu_cache_texture = gpu_cache::GpuCacheTexture::new(
532        &mut device,
533        supports_scatter && !is_software,
534    )?;
535
536    device.end_frame();
537
538    let backend_notifier = notifier.clone();
539
540    let clear_alpha_targets_with_quads = !device.get_capabilities().supports_alpha_target_clears;
541
542    let prefer_subpixel_aa = options.enable_subpixel_aa && use_dual_source_blending;
543    let default_font_render_mode = match (options.enable_aa, prefer_subpixel_aa) {
544        (true, true) => FontRenderMode::Subpixel,
545        (true, false) => FontRenderMode::Alpha,
546        (false, _) => FontRenderMode::Mono,
547    };
548
549    let compositor_kind = match options.compositor_config {
550        CompositorConfig::Draw { max_partial_present_rects, draw_previous_partial_present_regions, .. } => {
551            CompositorKind::Draw { max_partial_present_rects, draw_previous_partial_present_regions }
552        }
553        CompositorConfig::Native { ref compositor } => {
554            let capabilities = compositor.get_capabilities(&mut device);
555
556            CompositorKind::Native {
557                capabilities,
558            }
559        }
560        CompositorConfig::Layer { .. } => {
561            CompositorKind::Layer {
562            }
563        }
564    };
565
566    let config = FrameBuilderConfig {
567        default_font_render_mode,
568        dual_source_blending_is_supported: use_dual_source_blending,
569        testing: options.testing,
570        gpu_supports_fast_clears: options.gpu_supports_fast_clears,
571        gpu_supports_advanced_blend: ext_blend_equation_advanced,
572        advanced_blend_is_coherent: ext_blend_equation_advanced_coherent,
573        gpu_supports_render_target_partial_update: device.get_capabilities().supports_render_target_partial_update,
574        external_images_require_copy: !device.get_capabilities().supports_image_external_essl3,
575        batch_lookback_count: WebRenderOptions::BATCH_LOOKBACK_COUNT,
576        background_color: Some(options.clear_color),
577        compositor_kind,
578        tile_size_override: None,
579        max_surface_override: None,
580        max_depth_ids: device.max_depth_ids(),
581        max_target_size: max_internal_texture_size,
582        force_invalidation: false,
583        is_software,
584        low_quality_pinch_zoom: options.low_quality_pinch_zoom,
585        max_shared_surface_size: options.max_shared_surface_size,
586        enable_dithering: options.enable_dithering,
587        precise_linear_gradients: options.precise_linear_gradients,
588        precise_radial_gradients: options.precise_radial_gradients,
589        precise_conic_gradients: options.precise_conic_gradients,
590    };
591    info!("WR {:?}", config);
592
593    let debug_flags = options.debug_flags;
594    let size_of_op = options.size_of_op;
595    let enclosing_size_of_op = options.enclosing_size_of_op;
596    let make_size_of_ops =
597        move || size_of_op.map(|o| MallocSizeOfOps::new(o, enclosing_size_of_op));
598    let workers = options
599        .workers
600        .take()
601        .unwrap_or_else(|| {
602            let worker = ThreadPoolBuilder::new()
603                .thread_name(|idx|{ format!("WRWorker#{}", idx) })
604                .start_handler(move |idx| {
605                    register_thread_with_profiler(format!("WRWorker#{}", idx));
606                    profiler::register_thread(&format!("WRWorker#{}", idx));
607                })
608                .exit_handler(move |_idx| {
609                    profiler::unregister_thread();
610                })
611                .build();
612            Arc::new(worker.unwrap())
613        });
614    let sampler = options.sampler;
615    let namespace_alloc_by_client = options.namespace_alloc_by_client;
616
617    // Ensure shared font keys exist within their own unique namespace so
618    // that they don't accidentally collide across Renderer instances.
619    let font_namespace = if namespace_alloc_by_client {
620        options.shared_font_namespace.expect("Shared font namespace must be allocated by client")
621    } else {
622        RenderBackend::next_namespace_id()
623    };
624    let fonts = SharedFontResources::new(font_namespace);
625
626    let blob_image_handler = options.blob_image_handler.take();
627    let scene_builder_hooks = options.scene_builder_hooks;
628    let rb_thread_name = format!("WRRenderBackend#{}", options.renderer_id.unwrap_or(0));
629    let scene_thread_name = format!("WRSceneBuilder#{}", options.renderer_id.unwrap_or(0));
630    let lp_scene_thread_name = format!("WRSceneBuilderLP#{}", options.renderer_id.unwrap_or(0));
631
632    let glyph_rasterizer = GlyphRasterizer::new(
633        workers,
634        options.dedicated_glyph_raster_thread,
635        device.get_capabilities().supports_r8_texture_upload,
636    );
637
638    let (scene_builder_channels, scene_tx) =
639        SceneBuilderThreadChannels::new(api_tx.clone());
640
641    let sb_fonts = fonts.clone();
642
643    thread::Builder::new().name(scene_thread_name.clone()).spawn(move || {
644        register_thread_with_profiler(scene_thread_name.clone());
645        profiler::register_thread(&scene_thread_name);
646
647        let mut scene_builder = SceneBuilderThread::new(
648            config,
649            sb_fonts,
650            make_size_of_ops(),
651            scene_builder_hooks,
652            scene_builder_channels,
653        );
654        scene_builder.run();
655
656        profiler::unregister_thread();
657    })?;
658
659    let low_priority_scene_tx = if options.support_low_priority_transactions {
660        let (low_priority_scene_tx, low_priority_scene_rx) = unbounded_channel();
661        let lp_builder = LowPrioritySceneBuilderThread {
662            rx: low_priority_scene_rx,
663            tx: scene_tx.clone(),
664            tile_pool: api::BlobTilePool::new(),
665        };
666
667        thread::Builder::new().name(lp_scene_thread_name.clone()).spawn(move || {
668            register_thread_with_profiler(lp_scene_thread_name.clone());
669            profiler::register_thread(&lp_scene_thread_name);
670
671            let mut scene_builder = lp_builder;
672            scene_builder.run();
673
674            profiler::unregister_thread();
675        })?;
676
677        low_priority_scene_tx
678    } else {
679        scene_tx.clone()
680    };
681
682    let rb_blob_handler = blob_image_handler
683        .as_ref()
684        .map(|handler| handler.create_similar());
685
686    let texture_cache_config = options.texture_cache_config.clone();
687    let mut picture_tile_size = options.picture_tile_size.unwrap_or(picture::TILE_SIZE_DEFAULT);
688    // Clamp the picture tile size to reasonable values.
689    picture_tile_size.width = picture_tile_size.width.max(128).min(4096);
690    picture_tile_size.height = picture_tile_size.height.max(128).min(4096);
691
692    let picture_texture_filter = if options.low_quality_pinch_zoom {
693        TextureFilter::Linear
694    } else {
695        TextureFilter::Nearest
696    };
697
698    let render_backend_hooks = options.render_backend_hooks.take();
699
700    let chunk_pool = options.chunk_pool.take().unwrap_or_else(|| {
701        Arc::new(ChunkPool::new())
702    });
703
704    let rb_scene_tx = scene_tx.clone();
705    let rb_fonts = fonts.clone();
706    let enable_multithreading = options.enable_multithreading;
707    thread::Builder::new().name(rb_thread_name.clone()).spawn(move || {
708        if let Some(hooks) = render_backend_hooks {
709            hooks.init_thread();
710        }
711        register_thread_with_profiler(rb_thread_name.clone());
712        profiler::register_thread(&rb_thread_name);
713
714        let texture_cache = TextureCache::new(
715            max_internal_texture_size,
716            image_tiling_threshold,
717            color_cache_formats,
718            swizzle_settings,
719            &texture_cache_config,
720        );
721
722        let picture_textures = PictureTextures::new(
723            picture_tile_size,
724            picture_texture_filter,
725        );
726
727        let glyph_cache = GlyphCache::new();
728
729        let mut resource_cache = ResourceCache::new(
730            texture_cache,
731            picture_textures,
732            glyph_rasterizer,
733            glyph_cache,
734            rb_fonts,
735            rb_blob_handler,
736        );
737
738        resource_cache.enable_multithreading(enable_multithreading);
739
740        let mut backend = RenderBackend::new(
741            api_rx,
742            result_tx,
743            rb_scene_tx,
744            resource_cache,
745            chunk_pool,
746            backend_notifier,
747            config,
748            sampler,
749            make_size_of_ops(),
750            debug_flags,
751            namespace_alloc_by_client,
752        );
753        backend.run();
754        profiler::unregister_thread();
755    })?;
756
757    let debug_method = if !options.enable_gpu_markers {
758        // The GPU markers are disabled.
759        GpuDebugMethod::None
760    } else if device.get_capabilities().supports_khr_debug {
761        GpuDebugMethod::KHR
762    } else if device.supports_extension("GL_EXT_debug_marker") {
763        GpuDebugMethod::MarkerEXT
764    } else {
765        warn!("asking to enable_gpu_markers but no supporting extension was found");
766        GpuDebugMethod::None
767    };
768
769    info!("using {:?}", debug_method);
770
771    let gpu_profiler = GpuProfiler::new(Rc::clone(device.rc_gl()), debug_method);
772    #[cfg(feature = "capture")]
773    let read_fbo = device.create_fbo();
774
775    let mut renderer = Renderer {
776        result_rx,
777        api_tx: api_tx.clone(),
778        device,
779        active_documents: FastHashMap::default(),
780        pending_texture_updates: Vec::new(),
781        pending_texture_cache_updates: false,
782        pending_native_surface_updates: Vec::new(),
783        pending_gpu_cache_updates: Vec::new(),
784        pending_gpu_cache_clear: false,
785        pending_shader_updates: Vec::new(),
786        shaders,
787        debug: debug::LazyInitializedDebugRenderer::new(),
788        debug_flags: DebugFlags::empty(),
789        profile: TransactionProfile::new(),
790        frame_counter: 0,
791        resource_upload_time: 0.0,
792        gpu_cache_upload_time: 0.0,
793        profiler: Profiler::new(),
794        max_recorded_profiles: options.max_recorded_profiles,
795        clear_color: options.clear_color,
796        enable_clear_scissor,
797        enable_advanced_blend_barriers: !ext_blend_equation_advanced_coherent,
798        clear_caches_with_quads: options.clear_caches_with_quads,
799        clear_alpha_targets_with_quads,
800        last_time: 0,
801        gpu_profiler,
802        vaos,
803        vertex_data_textures,
804        current_vertex_data_textures: 0,
805        pipeline_info: PipelineInfo::default(),
806        dither_matrix_texture,
807        external_image_handler: None,
808        size_of_ops: make_size_of_ops(),
809        cpu_profiles: VecDeque::new(),
810        gpu_profiles: VecDeque::new(),
811        gpu_cache_texture,
812        gpu_cache_debug_chunks: Vec::new(),
813        gpu_cache_frame_id: FrameId::INVALID,
814        gpu_cache_overflow: false,
815        texture_upload_pbo_pool,
816        staging_texture_pool,
817        texture_resolver,
818        renderer_errors: Vec::new(),
819        async_frame_recorder: None,
820        async_screenshots: None,
821        #[cfg(feature = "capture")]
822        read_fbo,
823        #[cfg(feature = "replay")]
824        owned_external_images: FastHashMap::default(),
825        notifications: Vec::new(),
826        device_size: None,
827        zoom_debug_texture: None,
828        cursor_position: DeviceIntPoint::zero(),
829        shared_texture_cache_cleared: false,
830        documents_seen: FastHashSet::default(),
831        force_redraw: true,
832        compositor_config: options.compositor_config,
833        current_compositor_kind: compositor_kind,
834        allocated_native_surfaces: FastHashSet::default(),
835        debug_overlay_state: DebugOverlayState::new(),
836        buffer_damage_tracker: BufferDamageTracker::default(),
837        max_primitive_instance_count,
838        enable_instancing: options.enable_instancing,
839        consecutive_oom_frames: 0,
840        target_frame_publish_id: None,
841        pending_result_msg: None,
842        layer_compositor_frame_state_in_prev_frame: None,
843        #[cfg(feature = "debugger")]
844        debugger: Debugger::new(),
845    };
846
847    // We initially set the flags to default and then now call set_debug_flags
848    // to ensure any potential transition when enabling a flag is run.
849    renderer.set_debug_flags(debug_flags);
850    renderer.profiler.set_ui("Default");
851
852    let sender = RenderApiSender::new(
853        api_tx,
854        scene_tx,
855        low_priority_scene_tx,
856        blob_image_handler,
857        fonts,
858    );
859
860    #[cfg(feature = "debugger")]
861    if options.enable_debugger {
862        let api = if namespace_alloc_by_client {
863            sender.create_api_by_client(IdNamespace::DEBUGGER)
864        } else {
865            sender.create_api()
866        };
867        crate::debugger::start(api);
868    }
869
870    Ok((renderer, sender))
871}