summaryrefslogtreecommitdiffstats
path: root/gfx/wr/webrender/src/renderer/init.rs
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/wr/webrender/src/renderer/init.rs')
-rw-r--r--gfx/wr/webrender/src/renderer/init.rs783
1 files changed, 783 insertions, 0 deletions
diff --git a/gfx/wr/webrender/src/renderer/init.rs b/gfx/wr/webrender/src/renderer/init.rs
new file mode 100644
index 0000000000..38274b20b9
--- /dev/null
+++ b/gfx/wr/webrender/src/renderer/init.rs
@@ -0,0 +1,783 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use api::{BlobImageHandler, ColorF, IdNamespace, DocumentId, CrashAnnotator};
+use api::{VoidPtrToSizeFn, FontRenderMode, ImageFormat};
+use api::{RenderNotifier, ImageBufferKind};
+use api::units::*;
+use api::channel::unbounded_channel;
+pub use api::DebugFlags;
+
+use crate::render_api::{RenderApiSender, FrameMsg};
+use crate::composite::{CompositorKind, CompositorConfig};
+use crate::device::{
+ UploadMethod, UploadPBOPool, VertexUsageHint, Device, ProgramCache, TextureFilter
+};
+use crate::frame_builder::FrameBuilderConfig;
+use crate::glyph_cache::GlyphCache;
+use glyph_rasterizer::{GlyphRasterizer, SharedFontResources};
+use crate::gpu_types::PrimitiveInstanceData;
+use crate::internal_types::{FastHashMap, FastHashSet, FrameId};
+use crate::picture;
+use crate::profiler::{self, Profiler, TransactionProfile};
+use crate::device::query::{GpuProfiler, GpuDebugMethod};
+use crate::render_backend::RenderBackend;
+use crate::resource_cache::ResourceCache;
+use crate::scene_builder_thread::{SceneBuilderThread, SceneBuilderThreadChannels, LowPrioritySceneBuilderThread};
+use crate::texture_cache::{TextureCache, TextureCacheConfig};
+use crate::picture_textures::PictureTextures;
+use crate::renderer::{
+ debug, gpu_cache, vertex, gl,
+ Renderer, DebugOverlayState, BufferDamageTracker, PipelineInfo, TextureResolver,
+ RendererError, ShaderPrecacheFlags, VERTEX_DATA_TEXTURE_COUNT,
+ upload::UploadTexturePool,
+ shade::{Shaders, SharedShaders},
+};
+
+use std::{
+ mem,
+ thread,
+ cell::RefCell,
+ collections::VecDeque,
+ rc::Rc,
+ sync::{Arc, atomic::{AtomicBool, Ordering}},
+ num::NonZeroUsize,
+ path::PathBuf,
+};
+
+use tracy_rs::register_thread_with_profiler;
+use rayon::{ThreadPool, ThreadPoolBuilder};
+use malloc_size_of::MallocSizeOfOps;
+
+/// Use this hint for all vertex data re-initialization. This allows
+/// the driver to better re-use RBOs internally.
+pub const ONE_TIME_USAGE_HINT: VertexUsageHint = VertexUsageHint::Stream;
+
+/// Is only false if no WR instances have ever been created.
+static HAS_BEEN_INITIALIZED: AtomicBool = AtomicBool::new(false);
+
+/// Returns true if a WR instance has ever been initialized in this process.
+pub fn wr_has_been_initialized() -> bool {
+ HAS_BEEN_INITIALIZED.load(Ordering::SeqCst)
+}
+
+/// Allows callers to hook in at certain points of the async scene build. These
+/// functions are all called from the scene builder thread.
+pub trait SceneBuilderHooks {
+ /// This is called exactly once, when the scene builder thread is started
+ /// and before it processes anything.
+ fn register(&self);
+ /// This is called before each scene build starts.
+ fn pre_scene_build(&self);
+ /// This is called before each scene swap occurs.
+ fn pre_scene_swap(&self);
+ /// This is called after each scene swap occurs. The PipelineInfo contains
+ /// the updated epochs and pipelines removed in the new scene compared to
+ /// the old scene.
+ fn post_scene_swap(&self, document_id: &Vec<DocumentId>, info: PipelineInfo);
+ /// This is called after a resource update operation on the scene builder
+ /// thread, in the case where resource updates were applied without a scene
+ /// build.
+ fn post_resource_update(&self, document_ids: &Vec<DocumentId>);
+ /// This is called after a scene build completes without any changes being
+ /// made. We guarantee that each pre_scene_build call will be matched with
+ /// exactly one of post_scene_swap, post_resource_update or
+ /// post_empty_scene_build.
+ fn post_empty_scene_build(&self);
+ /// This is a generic callback which provides an opportunity to run code
+ /// on the scene builder thread. This is called as part of the main message
+ /// loop of the scene builder thread, but outside of any specific message
+ /// handler.
+ fn poke(&self);
+ /// This is called exactly once, when the scene builder thread is about to
+ /// terminate.
+ fn deregister(&self);
+}
+
+/// Allows callers to hook into the main render_backend loop and provide
+/// additional frame ops for generate_frame transactions. These functions
+/// are all called from the render backend thread.
+pub trait AsyncPropertySampler {
+ /// This is called exactly once, when the render backend thread is started
+ /// and before it processes anything.
+ fn register(&self);
+ /// This is called for each transaction with the generate_frame flag set
+ /// (i.e. that will trigger a render). The list of frame messages returned
+ /// are processed as though they were part of the original transaction.
+ fn sample(&self, document_id: DocumentId, generated_frame_id: Option<u64>) -> Vec<FrameMsg>;
+ /// This is called exactly once, when the render backend thread is about to
+ /// terminate.
+ fn deregister(&self);
+}
+
+pub struct WebRenderOptions {
+ pub resource_override_path: Option<PathBuf>,
+ /// Whether to use shaders that have been optimized at build time.
+ pub use_optimized_shaders: bool,
+ pub enable_aa: bool,
+ pub enable_dithering: bool,
+ pub max_recorded_profiles: usize,
+ pub precache_flags: ShaderPrecacheFlags,
+ /// Enable sub-pixel anti-aliasing if a fast implementation is available.
+ pub enable_subpixel_aa: bool,
+ pub clear_color: ColorF,
+ pub enable_clear_scissor: Option<bool>,
+ pub max_internal_texture_size: Option<i32>,
+ pub image_tiling_threshold: i32,
+ pub upload_method: UploadMethod,
+ /// The default size in bytes for PBOs used to upload texture data.
+ pub upload_pbo_default_size: usize,
+ pub batched_upload_threshold: i32,
+ pub workers: Option<Arc<ThreadPool>>,
+ pub enable_multithreading: bool,
+ pub blob_image_handler: Option<Box<dyn BlobImageHandler>>,
+ pub crash_annotator: Option<Box<dyn CrashAnnotator>>,
+ pub size_of_op: Option<VoidPtrToSizeFn>,
+ pub enclosing_size_of_op: Option<VoidPtrToSizeFn>,
+ pub cached_programs: Option<Rc<ProgramCache>>,
+ pub debug_flags: DebugFlags,
+ pub renderer_id: Option<u64>,
+ pub scene_builder_hooks: Option<Box<dyn SceneBuilderHooks + Send>>,
+ pub sampler: Option<Box<dyn AsyncPropertySampler + Send>>,
+ pub support_low_priority_transactions: bool,
+ pub namespace_alloc_by_client: bool,
+ /// If namespaces are allocated by the client, then the namespace for fonts
+ /// must also be allocated by the client to avoid namespace collisions with
+ /// the backend.
+ pub shared_font_namespace: Option<IdNamespace>,
+ pub testing: bool,
+ /// Set to true if this GPU supports hardware fast clears as a performance
+ /// optimization. Likely requires benchmarking on various GPUs to see if
+ /// it is a performance win. The default is false, which tends to be best
+ /// performance on lower end / integrated GPUs.
+ pub gpu_supports_fast_clears: bool,
+ pub allow_dual_source_blending: bool,
+ pub allow_advanced_blend_equation: bool,
+ /// If true, allow textures to be initialized with glTexStorage.
+ /// This affects VRAM consumption and data upload paths.
+ pub allow_texture_storage_support: bool,
+ /// If true, we allow the data uploaded in a different format from the
+ /// one expected by the driver, pretending the format is matching, and
+ /// swizzling the components on all the shader sampling.
+ pub allow_texture_swizzling: bool,
+ /// Use `ps_clear` shader with batched quad rendering to clear the rects
+ /// in texture cache and picture cache tasks.
+ /// This helps to work around some Intel drivers
+ /// that incorrectly synchronize clears to following draws.
+ pub clear_caches_with_quads: bool,
+ /// Output the source of the shader with the given name.
+ pub dump_shader_source: Option<String>,
+ pub surface_origin_is_top_left: bool,
+ /// The configuration options defining how WR composites the final scene.
+ pub compositor_config: CompositorConfig,
+ pub enable_gpu_markers: bool,
+ /// If true, panic whenever a GL error occurs. This has a significant
+ /// performance impact, so only use when debugging specific problems!
+ pub panic_on_gl_error: bool,
+ pub picture_tile_size: Option<DeviceIntSize>,
+ pub texture_cache_config: TextureCacheConfig,
+ /// If true, we'll use instanced vertex attributes. Each instace is a quad.
+ /// If false, we'll duplicate the instance attributes per vertex and issue
+ /// regular indexed draws instead.
+ pub enable_instancing: bool,
+ /// If true, we'll reject contexts backed by a software rasterizer, except
+ /// Software WebRender.
+ pub reject_software_rasterizer: bool,
+ /// If enabled, pinch-zoom will apply the zoom factor during compositing
+ /// of picture cache tiles. This is higher performance (tiles are not
+ /// re-rasterized during zoom) but lower quality result. For most display
+ /// items, if the zoom factor is relatively small, bilinear filtering should
+ /// make the result look quite close to the high-quality zoom, except for glyphs.
+ pub low_quality_pinch_zoom: bool,
+}
+
+impl WebRenderOptions {
+ /// Number of batches to look back in history for adding the current
+ /// transparent instance into.
+ const BATCH_LOOKBACK_COUNT: usize = 10;
+
+ /// Since we are re-initializing the instance buffers on every draw call,
+ /// the driver has to internally manage PBOs in flight.
+ /// It's typically done by bucketing up to a specific limit, and then
+ /// just individually managing the largest buffers.
+ /// Having a limit here allows the drivers to more easily manage
+ /// the PBOs for us.
+ const MAX_INSTANCE_BUFFER_SIZE: usize = 0x20000; // actual threshold in macOS GL drivers
+}
+
+impl Default for WebRenderOptions {
+ fn default() -> Self {
+ WebRenderOptions {
+ resource_override_path: None,
+ use_optimized_shaders: false,
+ enable_aa: true,
+ enable_dithering: false,
+ debug_flags: DebugFlags::empty(),
+ max_recorded_profiles: 0,
+ precache_flags: ShaderPrecacheFlags::empty(),
+ enable_subpixel_aa: false,
+ clear_color: ColorF::new(1.0, 1.0, 1.0, 1.0),
+ enable_clear_scissor: None,
+ max_internal_texture_size: None,
+ image_tiling_threshold: 4096,
+ // This is best as `Immediate` on Angle, or `Pixelbuffer(Dynamic)` on GL,
+ // but we are unable to make this decision here, so picking the reasonable medium.
+ upload_method: UploadMethod::PixelBuffer(ONE_TIME_USAGE_HINT),
+ upload_pbo_default_size: 512 * 512 * 4,
+ batched_upload_threshold: 512 * 512,
+ workers: None,
+ enable_multithreading: true,
+ blob_image_handler: None,
+ crash_annotator: None,
+ size_of_op: None,
+ enclosing_size_of_op: None,
+ renderer_id: None,
+ cached_programs: None,
+ scene_builder_hooks: None,
+ sampler: None,
+ support_low_priority_transactions: false,
+ namespace_alloc_by_client: false,
+ shared_font_namespace: None,
+ testing: false,
+ gpu_supports_fast_clears: false,
+ allow_dual_source_blending: true,
+ allow_advanced_blend_equation: false,
+ allow_texture_storage_support: true,
+ allow_texture_swizzling: true,
+ clear_caches_with_quads: true,
+ dump_shader_source: None,
+ surface_origin_is_top_left: false,
+ compositor_config: CompositorConfig::default(),
+ enable_gpu_markers: true,
+ panic_on_gl_error: false,
+ picture_tile_size: None,
+ texture_cache_config: TextureCacheConfig::DEFAULT,
+ // Disabling instancing means more vertex data to upload and potentially
+ // process by the vertex shaders.
+ enable_instancing: true,
+ reject_software_rasterizer: false,
+ low_quality_pinch_zoom: false,
+ }
+ }
+}
+
+/// Initializes WebRender and creates a `Renderer` and `RenderApiSender`.
+///
+/// # Examples
+/// Initializes a `Renderer` with some reasonable values. For more information see
+/// [`WebRenderOptions`][WebRenderOptions].
+///
+/// ```rust,ignore
+/// # use webrender::renderer::Renderer;
+/// # use std::path::PathBuf;
+/// let opts = webrender::WebRenderOptions {
+/// device_pixel_ratio: 1.0,
+/// resource_override_path: None,
+/// enable_aa: false,
+/// };
+/// let (renderer, sender) = Renderer::new(opts);
+/// ```
+/// [WebRenderOptions]: struct.WebRenderOptions.html
+pub fn create_webrender_instance(
+ gl: Rc<dyn gl::Gl>,
+ notifier: Box<dyn RenderNotifier>,
+ mut options: WebRenderOptions,
+ shaders: Option<&SharedShaders>,
+) -> Result<(Renderer, RenderApiSender), RendererError> {
+ if !wr_has_been_initialized() {
+ // If the profiler feature is enabled, try to load the profiler shared library
+ // if the path was provided.
+ #[cfg(feature = "profiler")]
+ unsafe {
+ if let Ok(ref tracy_path) = std::env::var("WR_TRACY_PATH") {
+ let ok = tracy_rs::load(tracy_path);
+ info!("Load tracy from {} -> {}", tracy_path, ok);
+ }
+ }
+
+ register_thread_with_profiler("Compositor".to_owned());
+ }
+
+ HAS_BEEN_INITIALIZED.store(true, Ordering::SeqCst);
+
+ let (api_tx, api_rx) = unbounded_channel();
+ let (result_tx, result_rx) = unbounded_channel();
+ let gl_type = gl.get_type();
+
+ let mut device = Device::new(
+ gl,
+ options.crash_annotator.clone(),
+ options.resource_override_path.clone(),
+ options.use_optimized_shaders,
+ options.upload_method.clone(),
+ options.batched_upload_threshold,
+ options.cached_programs.take(),
+ options.allow_texture_storage_support,
+ options.allow_texture_swizzling,
+ options.dump_shader_source.take(),
+ options.surface_origin_is_top_left,
+ options.panic_on_gl_error,
+ );
+
+ let color_cache_formats = device.preferred_color_formats();
+ let swizzle_settings = device.swizzle_settings();
+ let use_dual_source_blending =
+ device.get_capabilities().supports_dual_source_blending &&
+ options.allow_dual_source_blending;
+ let ext_blend_equation_advanced =
+ options.allow_advanced_blend_equation &&
+ device.get_capabilities().supports_advanced_blend_equation;
+ let ext_blend_equation_advanced_coherent =
+ device.supports_extension("GL_KHR_blend_equation_advanced_coherent");
+
+ let enable_clear_scissor = options
+ .enable_clear_scissor
+ .unwrap_or(device.get_capabilities().prefers_clear_scissor);
+
+ // 2048 is the minimum that the texture cache can work with.
+ const MIN_TEXTURE_SIZE: i32 = 2048;
+ let mut max_internal_texture_size = device.max_texture_size();
+ if max_internal_texture_size < MIN_TEXTURE_SIZE {
+ // Broken GL contexts can return a max texture size of zero (See #1260).
+ // Better to gracefully fail now than panic as soon as a texture is allocated.
+ error!(
+ "Device reporting insufficient max texture size ({})",
+ max_internal_texture_size
+ );
+ return Err(RendererError::MaxTextureSize);
+ }
+ if let Some(internal_limit) = options.max_internal_texture_size {
+ assert!(internal_limit >= MIN_TEXTURE_SIZE);
+ max_internal_texture_size = max_internal_texture_size.min(internal_limit);
+ }
+
+ if options.reject_software_rasterizer {
+ let renderer_name_lc = device.get_capabilities().renderer_name.to_lowercase();
+ if renderer_name_lc.contains("llvmpipe") || renderer_name_lc.contains("softpipe") || renderer_name_lc.contains("software rasterizer") {
+ return Err(RendererError::SoftwareRasterizer);
+ }
+ }
+
+ let image_tiling_threshold = options.image_tiling_threshold
+ .min(max_internal_texture_size);
+
+ device.begin_frame();
+
+ let shaders = match shaders {
+ Some(shaders) => Rc::clone(shaders),
+ None => Rc::new(RefCell::new(Shaders::new(&mut device, gl_type, &options)?)),
+ };
+
+ let dither_matrix_texture = if options.enable_dithering {
+ let dither_matrix: [u8; 64] = [
+ 0,
+ 48,
+ 12,
+ 60,
+ 3,
+ 51,
+ 15,
+ 63,
+ 32,
+ 16,
+ 44,
+ 28,
+ 35,
+ 19,
+ 47,
+ 31,
+ 8,
+ 56,
+ 4,
+ 52,
+ 11,
+ 59,
+ 7,
+ 55,
+ 40,
+ 24,
+ 36,
+ 20,
+ 43,
+ 27,
+ 39,
+ 23,
+ 2,
+ 50,
+ 14,
+ 62,
+ 1,
+ 49,
+ 13,
+ 61,
+ 34,
+ 18,
+ 46,
+ 30,
+ 33,
+ 17,
+ 45,
+ 29,
+ 10,
+ 58,
+ 6,
+ 54,
+ 9,
+ 57,
+ 5,
+ 53,
+ 42,
+ 26,
+ 38,
+ 22,
+ 41,
+ 25,
+ 37,
+ 21,
+ ];
+
+ let texture = device.create_texture(
+ ImageBufferKind::Texture2D,
+ ImageFormat::R8,
+ 8,
+ 8,
+ TextureFilter::Nearest,
+ None,
+ );
+ device.upload_texture_immediate(&texture, &dither_matrix);
+
+ Some(texture)
+ } else {
+ None
+ };
+
+ let max_primitive_instance_count =
+ WebRenderOptions::MAX_INSTANCE_BUFFER_SIZE / mem::size_of::<PrimitiveInstanceData>();
+ let vaos = vertex::RendererVAOs::new(
+ &mut device,
+ if options.enable_instancing { None } else { NonZeroUsize::new(max_primitive_instance_count) },
+ );
+
+ let texture_upload_pbo_pool = UploadPBOPool::new(&mut device, options.upload_pbo_default_size);
+ let staging_texture_pool = UploadTexturePool::new();
+ let texture_resolver = TextureResolver::new(&mut device);
+
+ let mut vertex_data_textures = Vec::new();
+ for _ in 0 .. VERTEX_DATA_TEXTURE_COUNT {
+ vertex_data_textures.push(vertex::VertexDataTextures::new());
+ }
+
+ // On some (mostly older, integrated) GPUs, the normal GPU texture cache update path
+ // doesn't work well when running on ANGLE, causing CPU stalls inside D3D and/or the
+ // GPU driver. See https://bugzilla.mozilla.org/show_bug.cgi?id=1576637 for much
+ // more detail. To reduce the number of code paths we have active that require testing,
+ // we will enable the GPU cache scatter update path on all devices running with ANGLE.
+ // We want a better solution long-term, but for now this is a significant performance
+ // improvement on HD4600 era GPUs, and shouldn't hurt performance in a noticeable
+ // way on other systems running under ANGLE.
+ let is_software = device.get_capabilities().renderer_name.starts_with("Software");
+
+ // On other GL platforms, like macOS or Android, creating many PBOs is very inefficient.
+ // This is what happens in GPU cache updates in PBO path. Instead, we switch everything
+ // except software GL to use the GPU scattered updates.
+ let supports_scatter = device.get_capabilities().supports_color_buffer_float;
+ let gpu_cache_texture = gpu_cache::GpuCacheTexture::new(
+ &mut device,
+ supports_scatter && !is_software,
+ )?;
+
+ device.end_frame();
+
+ let backend_notifier = notifier.clone();
+
+ let clear_alpha_targets_with_quads = !device.get_capabilities().supports_alpha_target_clears;
+
+ let prefer_subpixel_aa = options.enable_subpixel_aa && use_dual_source_blending;
+ let default_font_render_mode = match (options.enable_aa, prefer_subpixel_aa) {
+ (true, true) => FontRenderMode::Subpixel,
+ (true, false) => FontRenderMode::Alpha,
+ (false, _) => FontRenderMode::Mono,
+ };
+
+ let compositor_kind = match options.compositor_config {
+ CompositorConfig::Draw { max_partial_present_rects, draw_previous_partial_present_regions, .. } => {
+ CompositorKind::Draw { max_partial_present_rects, draw_previous_partial_present_regions }
+ }
+ CompositorConfig::Native { ref compositor } => {
+ let capabilities = compositor.get_capabilities();
+
+ CompositorKind::Native {
+ capabilities,
+ }
+ }
+ };
+
+ let config = FrameBuilderConfig {
+ default_font_render_mode,
+ dual_source_blending_is_supported: use_dual_source_blending,
+ testing: options.testing,
+ gpu_supports_fast_clears: options.gpu_supports_fast_clears,
+ gpu_supports_advanced_blend: ext_blend_equation_advanced,
+ advanced_blend_is_coherent: ext_blend_equation_advanced_coherent,
+ gpu_supports_render_target_partial_update: device.get_capabilities().supports_render_target_partial_update,
+ external_images_require_copy: !device.get_capabilities().supports_image_external_essl3,
+ batch_lookback_count: WebRenderOptions::BATCH_LOOKBACK_COUNT,
+ background_color: Some(options.clear_color),
+ compositor_kind,
+ tile_size_override: None,
+ max_surface_override: None,
+ max_depth_ids: device.max_depth_ids(),
+ max_target_size: max_internal_texture_size,
+ force_invalidation: false,
+ is_software,
+ low_quality_pinch_zoom: options.low_quality_pinch_zoom,
+ };
+ info!("WR {:?}", config);
+
+ let debug_flags = options.debug_flags;
+ let size_of_op = options.size_of_op;
+ let enclosing_size_of_op = options.enclosing_size_of_op;
+ let make_size_of_ops =
+ move || size_of_op.map(|o| MallocSizeOfOps::new(o, enclosing_size_of_op));
+ let workers = options
+ .workers
+ .take()
+ .unwrap_or_else(|| {
+ let worker = ThreadPoolBuilder::new()
+ .thread_name(|idx|{ format!("WRWorker#{}", idx) })
+ .start_handler(move |idx| {
+ register_thread_with_profiler(format!("WRWorker#{}", idx));
+ profiler::register_thread(&format!("WRWorker#{}", idx));
+ })
+ .exit_handler(move |_idx| {
+ profiler::unregister_thread();
+ })
+ .build();
+ Arc::new(worker.unwrap())
+ });
+ let sampler = options.sampler;
+ let namespace_alloc_by_client = options.namespace_alloc_by_client;
+
+ // Ensure shared font keys exist within their own unique namespace so
+ // that they don't accidentally collide across Renderer instances.
+ let font_namespace = if namespace_alloc_by_client {
+ options.shared_font_namespace.expect("Shared font namespace must be allocated by client")
+ } else {
+ RenderBackend::next_namespace_id()
+ };
+ let fonts = SharedFontResources::new(font_namespace);
+
+ let blob_image_handler = options.blob_image_handler.take();
+ let scene_builder_hooks = options.scene_builder_hooks;
+ let rb_thread_name = format!("WRRenderBackend#{}", options.renderer_id.unwrap_or(0));
+ let scene_thread_name = format!("WRSceneBuilder#{}", options.renderer_id.unwrap_or(0));
+ let lp_scene_thread_name = format!("WRSceneBuilderLP#{}", options.renderer_id.unwrap_or(0));
+ let glyph_rasterizer = GlyphRasterizer::new(workers, device.get_capabilities().supports_r8_texture_upload);
+
+ let (scene_builder_channels, scene_tx) =
+ SceneBuilderThreadChannels::new(api_tx.clone());
+
+ let sb_fonts = fonts.clone();
+
+ thread::Builder::new().name(scene_thread_name.clone()).spawn(move || {
+ register_thread_with_profiler(scene_thread_name.clone());
+ profiler::register_thread(&scene_thread_name);
+
+ let mut scene_builder = SceneBuilderThread::new(
+ config,
+ sb_fonts,
+ make_size_of_ops(),
+ scene_builder_hooks,
+ scene_builder_channels,
+ );
+ scene_builder.run();
+
+ profiler::unregister_thread();
+ })?;
+
+ let low_priority_scene_tx = if options.support_low_priority_transactions {
+ let (low_priority_scene_tx, low_priority_scene_rx) = unbounded_channel();
+ let lp_builder = LowPrioritySceneBuilderThread {
+ rx: low_priority_scene_rx,
+ tx: scene_tx.clone(),
+ };
+
+ thread::Builder::new().name(lp_scene_thread_name.clone()).spawn(move || {
+ register_thread_with_profiler(lp_scene_thread_name.clone());
+ profiler::register_thread(&lp_scene_thread_name);
+
+ let mut scene_builder = lp_builder;
+ scene_builder.run();
+
+ profiler::unregister_thread();
+ })?;
+
+ low_priority_scene_tx
+ } else {
+ scene_tx.clone()
+ };
+
+ let rb_blob_handler = blob_image_handler
+ .as_ref()
+ .map(|handler| handler.create_similar());
+
+ let texture_cache_config = options.texture_cache_config.clone();
+ let mut picture_tile_size = options.picture_tile_size.unwrap_or(picture::TILE_SIZE_DEFAULT);
+ // Clamp the picture tile size to reasonable values.
+ picture_tile_size.width = picture_tile_size.width.max(128).min(4096);
+ picture_tile_size.height = picture_tile_size.height.max(128).min(4096);
+
+ let picture_texture_filter = if options.low_quality_pinch_zoom {
+ TextureFilter::Linear
+ } else {
+ TextureFilter::Nearest
+ };
+
+ let rb_scene_tx = scene_tx.clone();
+ let rb_fonts = fonts.clone();
+ let enable_multithreading = options.enable_multithreading;
+ thread::Builder::new().name(rb_thread_name.clone()).spawn(move || {
+ register_thread_with_profiler(rb_thread_name.clone());
+ profiler::register_thread(&rb_thread_name);
+
+ let texture_cache = TextureCache::new(
+ max_internal_texture_size,
+ image_tiling_threshold,
+ color_cache_formats,
+ swizzle_settings,
+ &texture_cache_config,
+ );
+
+ let picture_textures = PictureTextures::new(
+ picture_tile_size,
+ picture_texture_filter,
+ );
+
+ let glyph_cache = GlyphCache::new();
+
+ let mut resource_cache = ResourceCache::new(
+ texture_cache,
+ picture_textures,
+ glyph_rasterizer,
+ glyph_cache,
+ rb_fonts,
+ rb_blob_handler,
+ );
+
+ resource_cache.enable_multithreading(enable_multithreading);
+
+ let mut backend = RenderBackend::new(
+ api_rx,
+ result_tx,
+ rb_scene_tx,
+ resource_cache,
+ backend_notifier,
+ config,
+ sampler,
+ make_size_of_ops(),
+ debug_flags,
+ namespace_alloc_by_client,
+ );
+ backend.run();
+ profiler::unregister_thread();
+ })?;
+
+ let debug_method = if !options.enable_gpu_markers {
+ // The GPU markers are disabled.
+ GpuDebugMethod::None
+ } else if device.supports_extension("GL_KHR_debug") {
+ GpuDebugMethod::KHR
+ } else if device.supports_extension("GL_EXT_debug_marker") {
+ GpuDebugMethod::MarkerEXT
+ } else {
+ warn!("asking to enable_gpu_markers but no supporting extension was found");
+ GpuDebugMethod::None
+ };
+
+ info!("using {:?}", debug_method);
+
+ let gpu_profiler = GpuProfiler::new(Rc::clone(device.rc_gl()), debug_method);
+ #[cfg(feature = "capture")]
+ let read_fbo = device.create_fbo();
+
+ let mut renderer = Renderer {
+ result_rx,
+ api_tx: api_tx.clone(),
+ device,
+ active_documents: FastHashMap::default(),
+ pending_texture_updates: Vec::new(),
+ pending_texture_cache_updates: false,
+ pending_native_surface_updates: Vec::new(),
+ pending_gpu_cache_updates: Vec::new(),
+ pending_gpu_cache_clear: false,
+ pending_shader_updates: Vec::new(),
+ shaders,
+ debug: debug::LazyInitializedDebugRenderer::new(),
+ debug_flags: DebugFlags::empty(),
+ profile: TransactionProfile::new(),
+ frame_counter: 0,
+ resource_upload_time: 0.0,
+ gpu_cache_upload_time: 0.0,
+ profiler: Profiler::new(),
+ max_recorded_profiles: options.max_recorded_profiles,
+ clear_color: options.clear_color,
+ enable_clear_scissor,
+ enable_advanced_blend_barriers: !ext_blend_equation_advanced_coherent,
+ clear_caches_with_quads: options.clear_caches_with_quads,
+ clear_alpha_targets_with_quads,
+ last_time: 0,
+ gpu_profiler,
+ vaos,
+ vertex_data_textures,
+ current_vertex_data_textures: 0,
+ pipeline_info: PipelineInfo::default(),
+ dither_matrix_texture,
+ external_image_handler: None,
+ size_of_ops: make_size_of_ops(),
+ cpu_profiles: VecDeque::new(),
+ gpu_profiles: VecDeque::new(),
+ gpu_cache_texture,
+ gpu_cache_debug_chunks: Vec::new(),
+ gpu_cache_frame_id: FrameId::INVALID,
+ gpu_cache_overflow: false,
+ texture_upload_pbo_pool,
+ staging_texture_pool,
+ texture_resolver,
+ renderer_errors: Vec::new(),
+ async_frame_recorder: None,
+ async_screenshots: None,
+ #[cfg(feature = "capture")]
+ read_fbo,
+ #[cfg(feature = "replay")]
+ owned_external_images: FastHashMap::default(),
+ notifications: Vec::new(),
+ device_size: None,
+ zoom_debug_texture: None,
+ cursor_position: DeviceIntPoint::zero(),
+ shared_texture_cache_cleared: false,
+ documents_seen: FastHashSet::default(),
+ force_redraw: true,
+ compositor_config: options.compositor_config,
+ current_compositor_kind: compositor_kind,
+ allocated_native_surfaces: FastHashSet::default(),
+ debug_overlay_state: DebugOverlayState::new(),
+ buffer_damage_tracker: BufferDamageTracker::default(),
+ max_primitive_instance_count,
+ enable_instancing: options.enable_instancing,
+ consecutive_oom_frames: 0,
+ };
+
+ // We initially set the flags to default and then now call set_debug_flags
+ // to ensure any potential transition when enabling a flag is run.
+ renderer.set_debug_flags(debug_flags);
+
+ let sender = RenderApiSender::new(
+ api_tx,
+ scene_tx,
+ low_priority_scene_tx,
+ blob_image_handler,
+ fonts,
+ );
+ Ok((renderer, sender))
+}