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