Bug 1690244 - Move WebRender initialization code out of renderer.rs. r=kvark

There is a lot going on in renderer.rs, the initialization code takes a non-negligible chunk of it and touches all aspects of WebRender including setting up all of the threads. This patch moves it into its own init module.
In addition, RendererOptions is renamed into WebRenderOptions to better reflect that it configures every aspect of the engine, and init::create_webrender_instance replaces Renderer::new.

This a rebased version of an pld patch approved by kvark a while back.

Differential Revision: https://phabricator.services.mozilla.com/D103767
This commit is contained in:
Nicolas Silva 2022-08-12 07:30:14 +00:00
parent 6b782ad093
commit 35814d5a15
16 changed files with 834 additions and 790 deletions

View File

@ -40,9 +40,9 @@ use webrender::{
api::units::*, api::*, render_api::*, set_profiler_hooks, AsyncPropertySampler, AsyncScreenshotHandle, Compositor,
CompositorCapabilities, CompositorConfig, CompositorSurfaceTransform, DebugFlags, Device, MappableCompositor,
MappedTileInfo, NativeSurfaceId, NativeSurfaceInfo, NativeTileId, PartialPresentCompositor, PipelineInfo,
ProfilerHooks, RecordedFrameHandle, Renderer, RendererOptions, RendererStats, SWGLCompositeSurfaceInfo,
ProfilerHooks, RecordedFrameHandle, Renderer, WebRenderOptions, RendererStats, SWGLCompositeSurfaceInfo,
SceneBuilderHooks, ShaderPrecacheFlags, Shaders, SharedShaders, TextureCacheConfig, UploadMethod, WindowVisibility,
ONE_TIME_USAGE_HINT,
create_webrender_instance, ONE_TIME_USAGE_HINT,
};
use wr_malloc_size_of::MallocSizeOfOps;
@ -1632,7 +1632,7 @@ pub extern "C" fn wr_window_new(
}
};
let opts = RendererOptions {
let opts = WebRenderOptions {
enable_aa: true,
force_subpixel_aa: false,
enable_subpixel_aa: cfg!(not(target_os = "android")),
@ -1688,7 +1688,7 @@ pub extern "C" fn wr_window_new(
let window_size = DeviceIntSize::new(window_width, window_height);
let notifier = Box::new(CppNotifier { window_id });
let (renderer, sender) = match Renderer::new(gl, notifier, opts, shaders.map(|sh| &sh.0)) {
let (renderer, sender) = match create_webrender_instance(gl, notifier, opts, shaders.map(|sh| &sh.0)) {
Ok((renderer, sender)) => (renderer, sender),
Err(e) => {
warn!(" Failed to create a Renderer: {:?}", e);
@ -3971,7 +3971,7 @@ pub extern "C" fn wr_shaders_new(
ShaderPrecacheFlags::ASYNC_COMPILE
};
let opts = RendererOptions {
let opts = WebRenderOptions {
precache_flags,
..Default::default()
};

View File

@ -430,12 +430,12 @@ fn main() {
partial_present: None,
}
};
let opts = webrender::RendererOptions {
let opts = webrender::WebRenderOptions {
clear_color: ColorF::new(1.0, 1.0, 1.0, 1.0),
debug_flags,
compositor_config,
surface_origin_is_top_left: false,
..webrender::RendererOptions::default()
..webrender::WebRenderOptions::default()
};
let (tx, rx) = mpsc::channel();
let notifier = Box::new(Notifier::new(tx));

View File

@ -276,7 +276,7 @@ fn main() {
let workers = Arc::new(workers.unwrap());
let opts = webrender::RendererOptions {
let opts = webrender::WebRenderOptions {
workers: Some(Arc::clone(&workers)),
// Register our blob renderer, so that WebRender integrates it in the resource cache..
// Share the same pool of worker threads between WebRender and our blob renderer.

View File

@ -102,7 +102,7 @@ pub trait Example {
pub fn main_wrapper<E: Example>(
example: &mut E,
options: Option<webrender::RendererOptions>,
options: Option<webrender::WebRenderOptions>,
) {
env_logger::init();
@ -159,13 +159,13 @@ pub fn main_wrapper<E: Example>(
println!("Loading shaders...");
let mut debug_flags = DebugFlags::ECHO_DRIVER_MESSAGES | DebugFlags::TEXTURE_CACHE_DBG;
let opts = webrender::RendererOptions {
let opts = webrender::WebRenderOptions {
resource_override_path: res_path,
precache_flags: E::PRECACHE_SHADER_FLAGS,
clear_color: ColorF::new(0.3, 0.0, 0.0, 1.0),
debug_flags,
//allow_texture_swizzling: false,
..options.unwrap_or(webrender::RendererOptions::default())
..options.unwrap_or(webrender::WebRenderOptions::default())
};
let device_size = {
@ -175,7 +175,7 @@ pub fn main_wrapper<E: Example>(
DeviceIntSize::new(size.width as i32, size.height as i32)
};
let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
let (mut renderer, sender) = webrender::Renderer::new(
let (mut renderer, sender) = webrender::create_webrender_instance(
gl.clone(),
notifier,
opts,

View File

@ -87,9 +87,9 @@ impl Window {
glutin::Api::WebGl => unimplemented!(),
};
let opts = webrender::RendererOptions {
let opts = webrender::WebRenderOptions {
clear_color,
..webrender::RendererOptions::default()
..webrender::WebRenderOptions::default()
};
let device_size = {
@ -99,7 +99,7 @@ impl Window {
DeviceIntSize::new(size.width as i32, size.height as i32)
};
let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
let (renderer, sender) = webrender::Renderer::new(gl.clone(), notifier, opts, None).unwrap();
let (renderer, sender) = webrender::create_webrender_instance(gl.clone(), notifier, opts, None).unwrap();
let mut api = sender.create_api();
let document_id = api.add_document(device_size);

View File

@ -218,7 +218,7 @@ fn main() {
current_value: 0,
};
let opts = webrender::RendererOptions {
let opts = webrender::WebRenderOptions {
..Default::default()
};

View File

@ -247,7 +247,7 @@ pub struct ResolvedExternalSurface {
pub update_params: Option<(NativeSurfaceId, DeviceIntSize)>,
}
/// Public interface specified in `RendererOptions` that configures
/// Public interface specified in `WebRenderOptions` that configures
/// how WR compositing will operate.
pub enum CompositorConfig {
/// Let WR draw tiles via normal batching. This requires no special OS support.

View File

@ -209,11 +209,12 @@ pub use crate::device::{ProgramBinary, ProgramCache, ProgramCacheObserver, Forma
pub use crate::device::Device;
pub use crate::profiler::{ProfilerHooks, set_profiler_hooks};
pub use crate::renderer::{
AsyncPropertySampler, CpuProfile, DebugFlags, GpuProfile, GraphicsApi,
GraphicsApiInfo, PipelineInfo, Renderer, RendererError, RendererOptions, RenderResults,
RendererStats, SceneBuilderHooks, Shaders, SharedShaders, ShaderPrecacheFlags,
MAX_VERTEX_TEXTURE_WIDTH, ONE_TIME_USAGE_HINT,
CpuProfile, DebugFlags, GpuProfile, GraphicsApi,
GraphicsApiInfo, PipelineInfo, Renderer, RendererError, RenderResults,
RendererStats, Shaders, SharedShaders, ShaderPrecacheFlags,
MAX_VERTEX_TEXTURE_WIDTH,
};
pub use crate::renderer::init::{WebRenderOptions, create_webrender_instance, AsyncPropertySampler, SceneBuilderHooks, ONE_TIME_USAGE_HINT};
pub use crate::hit_test::SharedHitTester;
pub use crate::internal_types::FastHashMap;
pub use crate::screen_capture::{AsyncScreenshotHandle, RecordedFrameHandle};

View File

@ -24,7 +24,7 @@ use crate::renderer::DebugRenderer;
use crate::device::query::GpuTimer;
use euclid::{Point2D, Rect, Size2D, vec2, default};
use crate::internal_types::FastHashMap;
use crate::renderer::{FullFrameStats, MAX_VERTEX_TEXTURE_WIDTH, wr_has_been_initialized};
use crate::renderer::{FullFrameStats, MAX_VERTEX_TEXTURE_WIDTH, init::wr_has_been_initialized};
use api::units::DeviceIntSize;
use std::collections::vec_deque::VecDeque;
use std::fmt::{Write, Debug};

View File

@ -1002,7 +1002,7 @@ impl RenderApiSender {
/// Creates a new resource API object with a dedicated namespace.
/// Namespace id is allocated by client.
///
/// The function could be used only when RendererOptions::namespace_alloc_by_client is true.
/// The function could be used only when WebRenderOptions::namespace_alloc_by_client is true.
/// When the option is true, create_api() could not be used to prevent namespace id conflict.
pub fn create_api_by_client(&self, namespace_id: IdNamespace) -> RenderApi {
let msg = ApiMsg::CloneApiByClient(namespace_id);

View File

@ -15,6 +15,7 @@ use api::{NotificationRequest, Checkpoint, QualitySettings};
use api::{PrimitiveKeyKind, RenderReasons};
use api::units::*;
use api::channel::{single_msg_channel, Sender, Receiver};
use crate::AsyncPropertySampler;
#[cfg(any(feature = "capture", feature = "replay"))]
use crate::render_api::CaptureBits;
#[cfg(feature = "replay")]
@ -41,7 +42,7 @@ use crate::prim_store::{PrimitiveInstanceKind, PrimTemplateCommonData};
use crate::prim_store::interned::*;
use crate::profiler::{self, TransactionProfile};
use crate::render_task_graph::RenderTaskGraphBuilder;
use crate::renderer::{AsyncPropertySampler, FullFrameStats, PipelineInfo};
use crate::renderer::{FullFrameStats, PipelineInfo};
use crate::resource_cache::ResourceCache;
#[cfg(feature = "replay")]
use crate::resource_cache::PlainCacheOwn;

View File

@ -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 crate::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,
/// Enable sub-pixel anti-aliasing if it requires a slow implementation.
pub force_subpixel_aa: bool,
pub clear_color: ColorF,
pub enable_clear_scissor: 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,
force_subpixel_aa: false,
clear_color: ColorF::new(1.0, 1.0, 1.0, 1.0),
enable_clear_scissor: true,
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");
// 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.force_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_enabled: true,
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: options.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))
}

View File

@ -34,44 +34,39 @@
//! up the scissor, are accepting already transformed coordinates, which we can get by
//! calling `DrawTarget::to_framebuffer_rect`
use api::{BlobImageHandler, ColorF, ColorU, MixBlendMode, IdNamespace};
use api::{ColorF, ColorU, MixBlendMode};
use api::{DocumentId, Epoch, ExternalImageHandler, RenderReasons};
use api::CrashAnnotator;
#[cfg(feature = "replay")]
use api::ExternalImageId;
use api::{ExternalImageSource, ExternalImageType, FontRenderMode, ImageFormat};
use api::{PipelineId, ImageRendering, Checkpoint, NotificationRequest};
use api::{VoidPtrToSizeFn, PremultipliedColorF};
use api::{RenderNotifier, ImageBufferKind};
use api::{ExternalImageSource, ExternalImageType, ImageFormat, PremultipliedColorF};
use api::{PipelineId, ImageRendering, Checkpoint, NotificationRequest, ImageBufferKind};
#[cfg(feature = "replay")]
use api::ExternalImage;
use api::units::*;
use api::channel::{unbounded_channel, Sender, Receiver};
use api::channel::{Sender, Receiver};
pub use api::DebugFlags;
use core::time::Duration;
use crate::render_api::{RenderApiSender, DebugCommand, FrameMsg, ApiMsg, MemoryReport};
use crate::render_api::{DebugCommand, ApiMsg, MemoryReport};
use crate::batch::{AlphaBatchContainer, BatchKind, BatchFeatures, BatchTextures, BrushBatchKind, ClipBatchList};
#[cfg(any(feature = "capture", feature = "replay"))]
use crate::capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
use crate::composite::{CompositeState, CompositeTileSurface, ResolvedExternalSurface, CompositorSurfaceTransform};
use crate::composite::{CompositorKind, Compositor, NativeTileId, CompositeFeatures, CompositeSurfaceFormat, ResolvedExternalSurfaceColorData};
use crate::composite::{CompositorConfig, NativeSurfaceOperationDetails, NativeSurfaceId, NativeSurfaceOperation};
use crate::composite::TileKind;
use crate::composite::{TileKind};
use crate::debug_colors;
use crate::device::{DepthFunction, Device, DrawTarget, ExternalTexture, GpuFrameId};
use crate::device::{ProgramCache, ReadTarget, ShaderError, Texture, TextureFilter, TextureFlags, TextureSlot};
use crate::device::{UploadMethod, UploadPBOPool, VertexUsageHint};
use crate::device::{DepthFunction, Device, DrawTarget, ExternalTexture, GpuFrameId, UploadPBOPool};
use crate::device::{ReadTarget, ShaderError, Texture, TextureFilter, TextureFlags, TextureSlot};
use crate::device::query::{GpuSampler, GpuTimer};
#[cfg(feature = "capture")]
use crate::device::FBOId;
use crate::debug_item::DebugItem;
use crate::frame_builder::{Frame, FrameBuilderConfig};
use crate::glyph_cache::GlyphCache;
use crate::glyph_rasterizer::{GlyphFormat, GlyphRasterizer, SharedFontResources};
use crate::frame_builder::Frame;
use crate::glyph_rasterizer::GlyphFormat;
use crate::gpu_cache::{GpuCacheUpdate, GpuCacheUpdateList};
use crate::gpu_cache::{GpuCacheDebugChunk, GpuCacheDebugCmd};
use crate::gpu_types::{PrimitiveInstanceData, ScalingInstance, SvgFilterInstance, CopyInstance};
use crate::gpu_types::{ScalingInstance, SvgFilterInstance, CopyInstance};
use crate::gpu_types::{BlurInstance, ClearInstance, CompositeInstance, ZBufferId, CompositorTransform};
use crate::internal_types::{TextureSource, ResourceCacheError, TextureCacheCategory, FrameId};
#[cfg(any(feature = "capture", feature = "replay"))]
@ -79,33 +74,31 @@ use crate::internal_types::DebugOutput;
use crate::internal_types::{CacheTextureId, FastHashMap, FastHashSet, RenderedDocument, ResultMsg};
use crate::internal_types::{TextureCacheAllocInfo, TextureCacheAllocationKind, TextureUpdateList};
use crate::internal_types::{RenderTargetInfo, Swizzle, DeferredResolveIndex};
use crate::picture::{self, ResolvedSurfaceTexture};
use crate::picture::ResolvedSurfaceTexture;
use crate::prim_store::DeferredResolve;
use crate::profiler::{self, GpuProfileTag, TransactionProfile};
use crate::profiler::{Profiler, add_event_marker, add_text_marker, thread_is_being_profiled};
use crate::device::query::{GpuProfiler, GpuDebugMethod};
use crate::render_backend::RenderBackend;
use crate::device::query::GpuProfiler;
use crate::render_target::ResolveOp;
use crate::render_task_graph::RenderTaskGraph;
use crate::render_task::{RenderTask, RenderTaskKind, ReadbackTask};
use crate::resource_cache::ResourceCache;
use crate::scene_builder_thread::{SceneBuilderThread, SceneBuilderThreadChannels, LowPrioritySceneBuilderThread};
use crate::screen_capture::AsyncScreenshotGrabber;
use crate::render_target::{AlphaRenderTarget, ColorRenderTarget, PictureCacheTarget, PictureCacheTargetKind};
use crate::render_target::{RenderTarget, TextureCacheRenderTarget};
use crate::render_target::{RenderTargetKind, BlitJob};
use crate::telemetry::Telemetry;
use crate::texture_cache::{TextureCache, TextureCacheConfig};
use crate::picture_textures::PictureTextures;
use crate::tile_cache::PictureCacheDebugInfo;
use crate::util::drain_filter;
use crate::rectangle_occlusion as occlusion;
use upload::{upload_to_texture_cache, UploadTexturePool};
use init::*;
use euclid::{rect, Transform3D, Scale, default};
use gleam::gl;
use malloc_size_of::MallocSizeOfOps;
use rayon::{ThreadPool, ThreadPoolBuilder};
#[cfg(feature = "replay")]
use std::sync::Arc;
use std::{
cell::RefCell,
@ -116,13 +109,9 @@ use std::{
num::NonZeroUsize,
path::PathBuf,
rc::Rc,
sync::Arc,
sync::atomic::{AtomicBool, Ordering},
thread,
};
#[cfg(any(feature = "capture", feature = "replay"))]
use std::collections::hash_map::Entry;
use tracy_rs::register_thread_with_profiler;
use time::precise_time_ns;
mod debug;
@ -130,23 +119,12 @@ mod gpu_cache;
mod shade;
mod vertex;
mod upload;
pub(crate) mod init;
pub use debug::DebugRenderer;
pub use shade::{Shaders, SharedShaders};
pub use vertex::{desc, VertexArrayKind, MAX_VERTEX_TEXTURE_WIDTH};
/// 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)
}
/// The size of the array of each type of vertex data texture that
/// is round-robin-ed each frame during bind_frame_data. Doing this
/// helps avoid driver stalls while updating the texture in some
@ -711,7 +689,7 @@ impl DebugOverlayState {
/// Tracks buffer damage rects over a series of frames.
#[derive(Debug, Default)]
struct BufferDamageTracker {
pub(crate) struct BufferDamageTracker {
damage_rects: [DeviceRect; 2],
current_offset: usize,
}
@ -920,523 +898,6 @@ impl From<ResourceCacheError> for RendererError {
}
impl Renderer {
/// Initializes WebRender and creates a `Renderer` and `RenderApiSender`.
///
/// # Examples
/// Initializes a `Renderer` with some reasonable values. For more information see
/// [`RendererOptions`][rendereroptions].
///
/// ```rust,ignore
/// # use webrender::renderer::Renderer;
/// # use std::path::PathBuf;
/// let opts = webrender::RendererOptions {
/// device_pixel_ratio: 1.0,
/// resource_override_path: None,
/// enable_aa: false,
/// };
/// let (renderer, sender) = Renderer::new(opts);
/// ```
/// [rendereroptions]: struct.RendererOptions.html
pub fn new(
gl: Rc<dyn gl::Gl>,
notifier: Box<dyn RenderNotifier>,
mut options: RendererOptions,
shaders: Option<&SharedShaders>,
) -> Result<(Self, 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");
// 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 =
RendererOptions::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.force_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_enabled: true,
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: RendererOptions::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: options.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))
}
pub fn device_size(&self) -> Option<DeviceIntSize> {
self.device_size
}
@ -5672,55 +5133,6 @@ impl Renderer {
}
}
/// 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);
}
bitflags! {
/// Flags that control how shaders are pre-cached, if at all.
#[derive(Default)]
@ -5736,160 +5148,6 @@ bitflags! {
}
}
pub struct RendererOptions {
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,
/// Enable sub-pixel anti-aliasing if it requires a slow implementation.
pub force_subpixel_aa: bool,
pub clear_color: ColorF,
pub enable_clear_scissor: 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 RendererOptions {
/// 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 RendererOptions {
fn default() -> Self {
RendererOptions {
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,
force_subpixel_aa: false,
clear_color: ColorF::new(1.0, 1.0, 1.0, 1.0),
enable_clear_scissor: true,
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,
}
}
}
/// The cumulative times spent in each painting phase to generate this frame.
#[derive(Debug, Default)]
pub struct FullFrameStats {

View File

@ -10,7 +10,7 @@ use euclid::default::Transform3D;
use crate::glyph_rasterizer::GlyphFormat;
use crate::renderer::{
desc,
BlendMode, DebugFlags, RendererError, RendererOptions,
BlendMode, DebugFlags, RendererError, WebRenderOptions,
TextureSampler, VertexArrayKind, ShaderPrecacheFlags,
};
use crate::profiler::{self, TransactionProfile, ns_to_ms};
@ -650,7 +650,7 @@ impl Shaders {
pub fn new(
device: &mut Device,
gl_type: GlType,
options: &RendererOptions,
options: &WebRenderOptions,
) -> Result<Self, ShaderError> {
// We have to pass a profile around a bunch but we aren't recording the initialization
// so use a dummy one.

View File

@ -28,10 +28,11 @@ use crate::prim_store::picture::Picture;
use crate::prim_store::text_run::TextRun;
use crate::profiler::{self, TransactionProfile};
use crate::render_backend::SceneView;
use crate::renderer::{FullFrameStats, PipelineInfo, SceneBuilderHooks};
use crate::renderer::{FullFrameStats, PipelineInfo};
use crate::scene::{Scene, BuiltScene, SceneStats};
use crate::spatial_tree::{SceneSpatialTree, SpatialTreeUpdates};
use crate::telemetry::Telemetry;
use crate::SceneBuilderHooks;
use std::iter;
use time::precise_time_ns;
use crate::util::drain_filter;

View File

@ -242,7 +242,7 @@ impl Wrench {
ShaderPrecacheFlags::empty()
};
let opts = webrender::RendererOptions {
let opts = webrender::WebRenderOptions {
resource_override_path: shader_override_path,
use_optimized_shaders,
enable_subpixel_aa: !no_subpixel_aa,
@ -274,7 +274,7 @@ impl Wrench {
Box::new(Notifier(data))
});
let (renderer, sender) = webrender::Renderer::new(
let (renderer, sender) = webrender::create_webrender_instance(
window.clone_gl(),
notifier,
opts,