/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{BlobImageHandler, ColorF, CrashAnnotator, DocumentId, IdNamespace}; use api::{VoidPtrToSizeFn, FontRenderMode, ImageFormat}; use api::{RenderNotifier, ImageBufferKind}; use api::units::*; use api::channel::unbounded_channel; pubuse api::DebugFlags;
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. pubconst 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. pubfn 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. pubtrait 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, schedule_frame: bool); /// 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. pubtrait 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);
}
pubstruct 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>>, /// A pool of large memory chunks used by the per-frame allocators. /// Providing the pool here makes it possible to share a single pool for /// all WebRender instances. pub chunk_pool: Option<Arc<ChunkPool>>, pub dedicated_glyph_raster_thread: Option<GlyphRasterThread>, 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 render_backend_hooks: Option<Box<dyn RenderBackendHooks + 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, pub max_shared_surface_size: i32, /// If true, open a debug socket to listen for remote debugger. /// Relies on `debugger` cargo feature being enabled. pub enable_debugger: 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,
chunk_pool: None,
dedicated_glyph_raster_thread: 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,
render_backend_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,
max_shared_surface_size: 2048,
enable_debugger: true,
}
}
}
/// 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 pubfn 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 { iflet Ok(ref tracy_path) = std::env::var("WR_TRACY_PATH") { let ok = tracy_rs::load(tracy_path);
info!("Load tracy from {} -> {}", tracy_path, ok);
}
}
// For now, we assume that native OS compositors are top-left origin. If that doesn't // turn out to be the case, we can add a query method on `LayerCompositor`. match options.compositor_config {
CompositorConfig::Draw { .. } | CompositorConfig::Native { .. } => {}
CompositorConfig::Layer { .. } => {
options.surface_origin_is_top_left = true;
}
}
let (api_tx, api_rx) = unbounded_channel(); let (result_tx, result_rx) = unbounded_channel(); let gl_type = gl.get_type();
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; letmut 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);
} iflet 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 => { letmut shaders = Shaders::new(&mut device, gl_type, &options)?; if options.precache_flags.intersects(ShaderPrecacheFlags::ASYNC_COMPILE | ShaderPrecacheFlags::FULL_COMPILE) { letmut pending_shaders = shaders.precache_all(options.precache_flags); while shaders.resume_precache(&mut device, &mut pending_shaders)? {}
}
Rc::new(RefCell::new(shaders))
}
};
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 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,
options.dedicated_glyph_raster_thread,
device.get_capabilities().supports_r8_texture_upload,
);
let (scene_builder_channels, scene_tx) =
SceneBuilderThreadChannels::new(api_tx.clone());
let debug_method = if !options.enable_gpu_markers { // The GPU markers are disabled.
GpuDebugMethod::None
} elseif device.get_capabilities().supports_khr_debug {
GpuDebugMethod::KHR
} elseif 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();
// 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);
renderer.profiler.set_ui("Default");
let sender = RenderApiSender::new(
api_tx,
scene_tx,
low_priority_scene_tx,
blob_image_handler,
fonts,
);
#[cfg(feature = "debugger")] if options.enable_debugger { let api = if namespace_alloc_by_client {
sender.create_api_by_client(IdNamespace::DEBUGGER)
} else {
sender.create_api()
}; crate::debugger::start(api);
}
Ok((renderer, sender))
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.47 Sekunden
(vorverarbeitet am 2026-08-22)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.