Source code

Revision control

Copy as Markdown

Other Tools

/* 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, CrashAnnotator, DocumentId, IdNamespace};
use api::{VoidPtrToSizeFn, FontRenderMode, ImageFormat};
use api::{RenderNotifier, ImageBufferKind};
use api::units::*;
use api::channel::unbounded_channel;
pub use api::DebugFlags;
use crate::bump_allocator::ChunkPool;
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::{GlyphRasterThread, GlyphRasterizer, SharedFontResources};
use crate::gpu_types::PrimitiveInstanceData;
use crate::internal_types::{FastHashMap, FastHashSet, FrameId};
use crate::{picture, Compositor2};
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 trait RenderBackendHooks {
fn init_thread(&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>>,
/// 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 supplied, composite the frame using the new experimental compositing
/// interface. If this is set, it overrides `compositor_config`. These will
/// be unified as the interface stabilises.
pub compositor2: Option<Box<dyn Compositor2>>,
}
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,
compositor2: None,
}
}
}
/// 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);
// 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 `Compositor2`.
options.surface_origin_is_top_left |= options.compositor2.is_some();
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
// 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(&mut device);
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,
max_shared_surface_size: options.max_shared_surface_size,
};
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,
options.dedicated_glyph_raster_thread,
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(),
tile_pool: api::BlobTilePool::new(),
};
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 render_backend_hooks = options.render_backend_hooks.take();
let chunk_pool = options.chunk_pool.take().unwrap_or_else(|| {
Arc::new(ChunkPool::new())
});
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 || {
if let Some(hooks) = render_backend_hooks {
hooks.init_thread();
}
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,
chunk_pool,
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.get_capabilities().supports_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,
target_frame_publish_id: None,
pending_result_msg: None,
compositor2: options.compositor2,
};
// 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))
}