1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 //! The high-level module responsible for interfacing with the GPU.
6 //!
7 //! Much of WebRender's design is driven by separating work into different
8 //! threads. To avoid the complexities of multi-threaded GPU access, we restrict
9 //! all communication with the GPU to one thread, the render thread. But since
10 //! issuing GPU commands is often a bottleneck, we move everything else (i.e.
11 //! the computation of what commands to issue) to another thread, the
12 //! RenderBackend thread. The RenderBackend, in turn, may delegate work to other
13 //! thread (like the SceneBuilder threads or Rayon workers), but the
14 //! Render-vs-RenderBackend distinction is the most important.
15 //!
16 //! The consumer is responsible for initializing the render thread before
17 //! calling into WebRender, which means that this module also serves as the
18 //! initial entry point into WebRender, and is responsible for spawning the
19 //! various other threads discussed above. That said, WebRender initialization
20 //! returns both the `Renderer` instance as well as a channel for communicating
21 //! directly with the `RenderBackend`. Aside from a few high-level operations
22 //! like 'render now', most of interesting commands from the consumer go over
23 //! that channel and operate on the `RenderBackend`.
24 //!
25 //! ## Space conversion guidelines
26 //! At this stage, we shuld be operating with `DevicePixel` and `FramebufferPixel` only.
27 //! "Framebuffer" space represents the final destination of our rendeing,
28 //! and it happens to be Y-flipped on OpenGL. The conversion is done as follows:
29 //! - for rasterized primitives, the orthographics projection transforms
30 //! the content rectangle to -1 to 1
31 //! - the viewport transformation is setup to map the whole range to
32 //! the framebuffer rectangle provided by the document view, stored in `DrawTarget`
33 //! - all the direct framebuffer operations, like blitting, reading pixels, and setting
34 //! up the scissor, are accepting already transformed coordinates, which we can get by
35 //! calling `DrawTarget::to_framebuffer_rect`
36
37 use api::{ApiMsg, BlobImageHandler, ColorF, ColorU, MixBlendMode};
38 use api::{DocumentId, Epoch, ExternalImageHandler, ExternalImageId};
39 use api::{ExternalImageSource, ExternalImageType, FontRenderMode, FrameMsg, ImageFormat};
40 use api::{PipelineId, ImageRendering, Checkpoint, NotificationRequest, OutputImageHandler};
41 use api::{DebugCommand, MemoryReport, VoidPtrToSizeFn, PremultipliedColorF};
42 use api::{RenderApiSender, RenderNotifier, TextureTarget, SharedFontInstanceMap};
43 #[cfg(feature = "replay")]
44 use api::ExternalImage;
45 use api::units::*;
46 pub use api::DebugFlags;
47 use crate::batch::{AlphaBatchContainer, BatchKind, BatchFeatures, BatchTextures, BrushBatchKind, ClipBatchList};
48 #[cfg(any(feature = "capture", feature = "replay"))]
49 use crate::capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
50 use crate::composite::{CompositeState, CompositeTileSurface, CompositeTile, ResolvedExternalSurface};
51 use crate::composite::{CompositorKind, Compositor, NativeTileId, CompositeSurfaceFormat, ResolvedExternalSurfaceColorData};
52 use crate::composite::{CompositorConfig, NativeSurfaceOperationDetails, NativeSurfaceId, NativeSurfaceOperation};
53 use crate::debug_colors;
54 use crate::debug_render::{DebugItem, DebugRenderer};
55 use crate::device::{DepthFunction, Device, GpuFrameId, Program, UploadMethod, Texture, PBO};
56 use crate::device::{DrawTarget, ExternalTexture, FBOId, ReadTarget, TextureSlot};
57 use crate::device::{ShaderError, TextureFilter, TextureFlags,
58 VertexUsageHint, VAO, VBO, CustomVAO};
59 use crate::device::ProgramCache;
60 use crate::device::query::GpuTimer;
61 use euclid::{rect, Transform3D, Scale, default};
62 use crate::frame_builder::{Frame, ChasePrimitive, FrameBuilderConfig};
63 use gleam::gl;
64 use crate::glyph_cache::GlyphCache;
65 use crate::glyph_rasterizer::{GlyphFormat, GlyphRasterizer};
66 use crate::gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
67 use crate::gpu_cache::{GpuCacheDebugChunk, GpuCacheDebugCmd};
68 use crate::gpu_types::{PrimitiveHeaderI, PrimitiveHeaderF, ScalingInstance, SvgFilterInstance, TransformData};
69 use crate::gpu_types::{CompositeInstance, ResolveInstanceData, ZBufferId};
70 use crate::internal_types::{TextureSource, ResourceCacheError};
71 use crate::internal_types::{CacheTextureId, DebugOutput, FastHashMap, FastHashSet, LayerIndex, RenderedDocument, ResultMsg};
72 use crate::internal_types::{TextureCacheAllocationKind, TextureCacheUpdate, TextureUpdateList, TextureUpdateSource};
73 use crate::internal_types::{RenderTargetInfo, SavedTargetIndex, Swizzle};
74 use malloc_size_of::MallocSizeOfOps;
75 use crate::picture::{RecordedDirtyRegion, tile_cache_sizes, ResolvedSurfaceTexture};
76 use crate::prim_store::DeferredResolve;
77 use crate::profiler::{BackendProfileCounters, FrameProfileCounters, TimeProfileCounter,
78 GpuProfileTag, RendererProfileCounters, RendererProfileTimers};
79 use crate::profiler::{Profiler, ChangeIndicator, ProfileStyle, add_event_marker, thread_is_being_profiled};
80 use crate::device::query::{GpuProfiler, GpuDebugMethod};
81 use rayon::{ThreadPool, ThreadPoolBuilder};
82 use crate::render_backend::{FrameId, RenderBackend};
83 use crate::render_task_graph::RenderTaskGraph;
84 use crate::render_task::{RenderTask, RenderTaskData, RenderTaskKind};
85 use crate::resource_cache::ResourceCache;
86 use crate::scene_builder_thread::{SceneBuilderThread, SceneBuilderThreadChannels, LowPrioritySceneBuilderThread};
87 use crate::screen_capture::AsyncScreenshotGrabber;
88 use crate::shade::{Shaders, WrShaders};
89 use smallvec::SmallVec;
90 use crate::texture_cache::TextureCache;
91 use crate::render_target::{AlphaRenderTarget, ColorRenderTarget, PictureCacheTarget};
92 use crate::render_target::{RenderTarget, TextureCacheRenderTarget, RenderTargetList};
93 use crate::render_target::{RenderTargetKind, BlitJob, BlitJobSource};
94 use crate::render_task_graph::RenderPassKind;
95 use crate::util::drain_filter;
96 use crate::c_str;
97
98 use std;
99 use std::cmp;
100 use std::collections::VecDeque;
101 use std::collections::hash_map::Entry;
102 use std::f32;
103 use std::marker::PhantomData;
104 use std::mem;
105 use std::os::raw::c_void;
106 use std::path::PathBuf;
107 use std::rc::Rc;
108 use std::sync::Arc;
109 use std::sync::atomic::{AtomicBool, Ordering};
110 use std::sync::mpsc::{channel, Sender, Receiver};
111 use std::thread;
112 use std::cell::RefCell;
113 use tracy_rs::register_thread_with_profiler;
114 use time::precise_time_ns;
115 use std::ffi::CString;
116
117 cfg_if! {
118 if #[cfg(feature = "debugger")] {
119 use serde_json;
120 use crate::debug_server;
121 }
122 }
123
124 const DEFAULT_BATCH_LOOKBACK_COUNT: usize = 10;
125 const VERTEX_TEXTURE_EXTRA_ROWS: i32 = 10;
126
127 /// The size of the array of each type of vertex data texture that
128 /// is round-robin-ed each frame during bind_frame_data. Doing this
129 /// helps avoid driver stalls while updating the texture in some
130 /// drivers. The size of these textures are typically very small
131 /// (e.g. < 16 kB) so it's not a huge waste of memory. Despite that,
132 /// this is a short-term solution - we want to find a better way
133 /// to provide this frame data, which will likely involve some
134 /// combination of UBO/SSBO usage. Although this only affects some
135 /// platforms, it's enabled on all platforms to reduce testing
136 /// differences between platforms.
137 const VERTEX_DATA_TEXTURE_COUNT: usize = 3;
138
139 /// Is only false if no WR instances have ever been created.
140 static HAS_BEEN_INITIALIZED: AtomicBool = AtomicBool::new(false);
141
142 /// Returns true if a WR instance has ever been initialized in this process.
wr_has_been_initialized() -> bool143 pub fn wr_has_been_initialized() -> bool {
144 HAS_BEEN_INITIALIZED.load(Ordering::SeqCst)
145 }
146
147 pub const MAX_VERTEX_TEXTURE_WIDTH: usize = webrender_build::MAX_VERTEX_TEXTURE_WIDTH;
148 /// Enabling this toggle would force the GPU cache scattered texture to
149 /// be resized every frame, which enables GPU debuggers to see if this
150 /// is performed correctly.
151 const GPU_CACHE_RESIZE_TEST: bool = false;
152
153 /// Number of GPU blocks per UV rectangle provided for an image.
154 pub const BLOCKS_PER_UV_RECT: usize = 2;
155
156 const GPU_TAG_BRUSH_OPACITY: GpuProfileTag = GpuProfileTag {
157 label: "B_Opacity",
158 color: debug_colors::DARKMAGENTA,
159 };
160 const GPU_TAG_BRUSH_LINEAR_GRADIENT: GpuProfileTag = GpuProfileTag {
161 label: "B_LinearGradient",
162 color: debug_colors::POWDERBLUE,
163 };
164 const GPU_TAG_BRUSH_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag {
165 label: "B_RadialGradient",
166 color: debug_colors::LIGHTPINK,
167 };
168 const GPU_TAG_BRUSH_CONIC_GRADIENT: GpuProfileTag = GpuProfileTag {
169 label: "B_ConicGradient",
170 color: debug_colors::GREEN,
171 };
172 const GPU_TAG_BRUSH_YUV_IMAGE: GpuProfileTag = GpuProfileTag {
173 label: "B_YuvImage",
174 color: debug_colors::DARKGREEN,
175 };
176 const GPU_TAG_BRUSH_MIXBLEND: GpuProfileTag = GpuProfileTag {
177 label: "B_MixBlend",
178 color: debug_colors::MAGENTA,
179 };
180 const GPU_TAG_BRUSH_BLEND: GpuProfileTag = GpuProfileTag {
181 label: "B_Blend",
182 color: debug_colors::ORANGE,
183 };
184 const GPU_TAG_BRUSH_IMAGE: GpuProfileTag = GpuProfileTag {
185 label: "B_Image",
186 color: debug_colors::SPRINGGREEN,
187 };
188 const GPU_TAG_BRUSH_SOLID: GpuProfileTag = GpuProfileTag {
189 label: "B_Solid",
190 color: debug_colors::RED,
191 };
192 const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag {
193 label: "C_Clip",
194 color: debug_colors::PURPLE,
195 };
196 const GPU_TAG_CACHE_BORDER: GpuProfileTag = GpuProfileTag {
197 label: "C_Border",
198 color: debug_colors::CORNSILK,
199 };
200 const GPU_TAG_CACHE_LINE_DECORATION: GpuProfileTag = GpuProfileTag {
201 label: "C_LineDecoration",
202 color: debug_colors::YELLOWGREEN,
203 };
204 const GPU_TAG_CACHE_GRADIENT: GpuProfileTag = GpuProfileTag {
205 label: "C_Gradient",
206 color: debug_colors::BROWN,
207 };
208 const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag {
209 label: "target init",
210 color: debug_colors::SLATEGREY,
211 };
212 const GPU_TAG_SETUP_DATA: GpuProfileTag = GpuProfileTag {
213 label: "data init",
214 color: debug_colors::LIGHTGREY,
215 };
216 const GPU_TAG_PRIM_SPLIT_COMPOSITE: GpuProfileTag = GpuProfileTag {
217 label: "SplitComposite",
218 color: debug_colors::DARKBLUE,
219 };
220 const GPU_TAG_PRIM_TEXT_RUN: GpuProfileTag = GpuProfileTag {
221 label: "TextRun",
222 color: debug_colors::BLUE,
223 };
224 const GPU_TAG_BLUR: GpuProfileTag = GpuProfileTag {
225 label: "Blur",
226 color: debug_colors::VIOLET,
227 };
228 const GPU_TAG_BLIT: GpuProfileTag = GpuProfileTag {
229 label: "Blit",
230 color: debug_colors::LIME,
231 };
232 const GPU_TAG_SCALE: GpuProfileTag = GpuProfileTag {
233 label: "Scale",
234 color: debug_colors::GHOSTWHITE,
235 };
236 const GPU_SAMPLER_TAG_ALPHA: GpuProfileTag = GpuProfileTag {
237 label: "Alpha Targets",
238 color: debug_colors::BLACK,
239 };
240 const GPU_SAMPLER_TAG_OPAQUE: GpuProfileTag = GpuProfileTag {
241 label: "Opaque Pass",
242 color: debug_colors::BLACK,
243 };
244 const GPU_SAMPLER_TAG_TRANSPARENT: GpuProfileTag = GpuProfileTag {
245 label: "Transparent Pass",
246 color: debug_colors::BLACK,
247 };
248 const GPU_TAG_SVG_FILTER: GpuProfileTag = GpuProfileTag {
249 label: "SvgFilter",
250 color: debug_colors::LEMONCHIFFON,
251 };
252 const GPU_TAG_COMPOSITE: GpuProfileTag = GpuProfileTag {
253 label: "Composite",
254 color: debug_colors::TOMATO,
255 };
256
257 /// The clear color used for the texture cache when the debug display is enabled.
258 /// We use a shade of blue so that we can still identify completely blue items in
259 /// the texture cache.
260 const TEXTURE_CACHE_DBG_CLEAR_COLOR: [f32; 4] = [0.0, 0.0, 0.8, 1.0];
261
262 impl BatchKind {
263 #[cfg(feature = "debugger")]
debug_name(&self) -> &'static str264 fn debug_name(&self) -> &'static str {
265 match *self {
266 BatchKind::SplitComposite => "SplitComposite",
267 BatchKind::Brush(kind) => {
268 match kind {
269 BrushBatchKind::Solid => "Brush (Solid)",
270 BrushBatchKind::Image(..) => "Brush (Image)",
271 BrushBatchKind::Blend => "Brush (Blend)",
272 BrushBatchKind::MixBlend { .. } => "Brush (Composite)",
273 BrushBatchKind::YuvImage(..) => "Brush (YuvImage)",
274 BrushBatchKind::ConicGradient => "Brush (ConicGradient)",
275 BrushBatchKind::RadialGradient => "Brush (RadialGradient)",
276 BrushBatchKind::LinearGradient => "Brush (LinearGradient)",
277 BrushBatchKind::Opacity => "Brush (Opacity)",
278 }
279 }
280 BatchKind::TextRun(_) => "TextRun",
281 }
282 }
283
sampler_tag(&self) -> GpuProfileTag284 fn sampler_tag(&self) -> GpuProfileTag {
285 match *self {
286 BatchKind::SplitComposite => GPU_TAG_PRIM_SPLIT_COMPOSITE,
287 BatchKind::Brush(kind) => {
288 match kind {
289 BrushBatchKind::Solid => GPU_TAG_BRUSH_SOLID,
290 BrushBatchKind::Image(..) => GPU_TAG_BRUSH_IMAGE,
291 BrushBatchKind::Blend => GPU_TAG_BRUSH_BLEND,
292 BrushBatchKind::MixBlend { .. } => GPU_TAG_BRUSH_MIXBLEND,
293 BrushBatchKind::YuvImage(..) => GPU_TAG_BRUSH_YUV_IMAGE,
294 BrushBatchKind::ConicGradient => GPU_TAG_BRUSH_CONIC_GRADIENT,
295 BrushBatchKind::RadialGradient => GPU_TAG_BRUSH_RADIAL_GRADIENT,
296 BrushBatchKind::LinearGradient => GPU_TAG_BRUSH_LINEAR_GRADIENT,
297 BrushBatchKind::Opacity => GPU_TAG_BRUSH_OPACITY,
298 }
299 }
300 BatchKind::TextRun(_) => GPU_TAG_PRIM_TEXT_RUN,
301 }
302 }
303 }
304
flag_changed(before: DebugFlags, after: DebugFlags, select: DebugFlags) -> Option<bool>305 fn flag_changed(before: DebugFlags, after: DebugFlags, select: DebugFlags) -> Option<bool> {
306 if before & select != after & select {
307 Some(after.contains(select))
308 } else {
309 None
310 }
311 }
312
313 #[repr(C)]
314 #[derive(Copy, Clone, Debug)]
315 pub enum ShaderColorMode {
316 FromRenderPassMode = 0,
317 Alpha = 1,
318 SubpixelConstantTextColor = 2,
319 SubpixelWithBgColorPass0 = 3,
320 SubpixelWithBgColorPass1 = 4,
321 SubpixelWithBgColorPass2 = 5,
322 SubpixelDualSource = 6,
323 Bitmap = 7,
324 ColorBitmap = 8,
325 Image = 9,
326 }
327
328 impl From<GlyphFormat> for ShaderColorMode {
from(format: GlyphFormat) -> ShaderColorMode329 fn from(format: GlyphFormat) -> ShaderColorMode {
330 match format {
331 GlyphFormat::Alpha | GlyphFormat::TransformedAlpha => ShaderColorMode::Alpha,
332 GlyphFormat::Subpixel | GlyphFormat::TransformedSubpixel => {
333 panic!("Subpixel glyph formats must be handled separately.");
334 }
335 GlyphFormat::Bitmap => ShaderColorMode::Bitmap,
336 GlyphFormat::ColorBitmap => ShaderColorMode::ColorBitmap,
337 }
338 }
339 }
340
341 /// Enumeration of the texture samplers used across the various WebRender shaders.
342 ///
343 /// Each variant corresponds to a uniform declared in shader source. We only bind
344 /// the variants we need for a given shader, so not every variant is bound for every
345 /// batch.
346 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
347 pub(crate) enum TextureSampler {
348 Color0,
349 Color1,
350 Color2,
351 PrevPassAlpha,
352 PrevPassColor,
353 GpuCache,
354 TransformPalette,
355 RenderTasks,
356 Dither,
357 PrimitiveHeadersF,
358 PrimitiveHeadersI,
359 }
360
361 impl TextureSampler {
color(n: usize) -> TextureSampler362 pub(crate) fn color(n: usize) -> TextureSampler {
363 match n {
364 0 => TextureSampler::Color0,
365 1 => TextureSampler::Color1,
366 2 => TextureSampler::Color2,
367 _ => {
368 panic!("There are only 3 color samplers.");
369 }
370 }
371 }
372 }
373
374 impl Into<TextureSlot> for TextureSampler {
into(self) -> TextureSlot375 fn into(self) -> TextureSlot {
376 match self {
377 TextureSampler::Color0 => TextureSlot(0),
378 TextureSampler::Color1 => TextureSlot(1),
379 TextureSampler::Color2 => TextureSlot(2),
380 TextureSampler::PrevPassAlpha => TextureSlot(3),
381 TextureSampler::PrevPassColor => TextureSlot(4),
382 TextureSampler::GpuCache => TextureSlot(5),
383 TextureSampler::TransformPalette => TextureSlot(6),
384 TextureSampler::RenderTasks => TextureSlot(7),
385 TextureSampler::Dither => TextureSlot(8),
386 TextureSampler::PrimitiveHeadersF => TextureSlot(9),
387 TextureSampler::PrimitiveHeadersI => TextureSlot(10),
388 }
389 }
390 }
391
392 #[derive(Debug, Clone, Copy)]
393 #[repr(C)]
394 pub struct PackedVertex {
395 pub pos: [f32; 2],
396 }
397
398 pub(crate) mod desc {
399 use crate::device::{VertexAttribute, VertexAttributeKind, VertexDescriptor};
400
401 pub const PRIM_INSTANCES: VertexDescriptor = VertexDescriptor {
402 vertex_attributes: &[
403 VertexAttribute {
404 name: "aPosition",
405 count: 2,
406 kind: VertexAttributeKind::F32,
407 },
408 ],
409 instance_attributes: &[
410 VertexAttribute {
411 name: "aData",
412 count: 4,
413 kind: VertexAttributeKind::I32,
414 },
415 ],
416 };
417
418 pub const BLUR: VertexDescriptor = VertexDescriptor {
419 vertex_attributes: &[
420 VertexAttribute {
421 name: "aPosition",
422 count: 2,
423 kind: VertexAttributeKind::F32,
424 },
425 ],
426 instance_attributes: &[
427 VertexAttribute {
428 name: "aBlurRenderTaskAddress",
429 count: 1,
430 kind: VertexAttributeKind::U16,
431 },
432 VertexAttribute {
433 name: "aBlurSourceTaskAddress",
434 count: 1,
435 kind: VertexAttributeKind::U16,
436 },
437 VertexAttribute {
438 name: "aBlurDirection",
439 count: 1,
440 kind: VertexAttributeKind::I32,
441 },
442 ],
443 };
444
445 pub const LINE: VertexDescriptor = VertexDescriptor {
446 vertex_attributes: &[
447 VertexAttribute {
448 name: "aPosition",
449 count: 2,
450 kind: VertexAttributeKind::F32,
451 },
452 ],
453 instance_attributes: &[
454 VertexAttribute {
455 name: "aTaskRect",
456 count: 4,
457 kind: VertexAttributeKind::F32,
458 },
459 VertexAttribute {
460 name: "aLocalSize",
461 count: 2,
462 kind: VertexAttributeKind::F32,
463 },
464 VertexAttribute {
465 name: "aWavyLineThickness",
466 count: 1,
467 kind: VertexAttributeKind::F32,
468 },
469 VertexAttribute {
470 name: "aStyle",
471 count: 1,
472 kind: VertexAttributeKind::I32,
473 },
474 VertexAttribute {
475 name: "aAxisSelect",
476 count: 1,
477 kind: VertexAttributeKind::F32,
478 },
479 ],
480 };
481
482 pub const GRADIENT: VertexDescriptor = VertexDescriptor {
483 vertex_attributes: &[
484 VertexAttribute {
485 name: "aPosition",
486 count: 2,
487 kind: VertexAttributeKind::F32,
488 },
489 ],
490 instance_attributes: &[
491 VertexAttribute {
492 name: "aTaskRect",
493 count: 4,
494 kind: VertexAttributeKind::F32,
495 },
496 VertexAttribute {
497 name: "aStops",
498 count: 4,
499 kind: VertexAttributeKind::F32,
500 },
501 // TODO(gw): We should probably pack these as u32 colors instead
502 // of passing as full float vec4 here. It won't make much
503 // difference in real world, since these are only invoked
504 // rarely, when creating the cache.
505 VertexAttribute {
506 name: "aColor0",
507 count: 4,
508 kind: VertexAttributeKind::F32,
509 },
510 VertexAttribute {
511 name: "aColor1",
512 count: 4,
513 kind: VertexAttributeKind::F32,
514 },
515 VertexAttribute {
516 name: "aColor2",
517 count: 4,
518 kind: VertexAttributeKind::F32,
519 },
520 VertexAttribute {
521 name: "aColor3",
522 count: 4,
523 kind: VertexAttributeKind::F32,
524 },
525 VertexAttribute {
526 name: "aAxisSelect",
527 count: 1,
528 kind: VertexAttributeKind::F32,
529 },
530 VertexAttribute {
531 name: "aStartStop",
532 count: 2,
533 kind: VertexAttributeKind::F32,
534 },
535 ],
536 };
537
538 pub const BORDER: VertexDescriptor = VertexDescriptor {
539 vertex_attributes: &[
540 VertexAttribute {
541 name: "aPosition",
542 count: 2,
543 kind: VertexAttributeKind::F32,
544 },
545 ],
546 instance_attributes: &[
547 VertexAttribute {
548 name: "aTaskOrigin",
549 count: 2,
550 kind: VertexAttributeKind::F32,
551 },
552 VertexAttribute {
553 name: "aRect",
554 count: 4,
555 kind: VertexAttributeKind::F32,
556 },
557 VertexAttribute {
558 name: "aColor0",
559 count: 4,
560 kind: VertexAttributeKind::F32,
561 },
562 VertexAttribute {
563 name: "aColor1",
564 count: 4,
565 kind: VertexAttributeKind::F32,
566 },
567 VertexAttribute {
568 name: "aFlags",
569 count: 1,
570 kind: VertexAttributeKind::I32,
571 },
572 VertexAttribute {
573 name: "aWidths",
574 count: 2,
575 kind: VertexAttributeKind::F32,
576 },
577 VertexAttribute {
578 name: "aRadii",
579 count: 2,
580 kind: VertexAttributeKind::F32,
581 },
582 VertexAttribute {
583 name: "aClipParams1",
584 count: 4,
585 kind: VertexAttributeKind::F32,
586 },
587 VertexAttribute {
588 name: "aClipParams2",
589 count: 4,
590 kind: VertexAttributeKind::F32,
591 },
592 ],
593 };
594
595 pub const SCALE: VertexDescriptor = VertexDescriptor {
596 vertex_attributes: &[
597 VertexAttribute {
598 name: "aPosition",
599 count: 2,
600 kind: VertexAttributeKind::F32,
601 },
602 ],
603 instance_attributes: &[
604 VertexAttribute {
605 name: "aScaleTargetRect",
606 count: 4,
607 kind: VertexAttributeKind::F32,
608 },
609 VertexAttribute {
610 name: "aScaleSourceRect",
611 count: 4,
612 kind: VertexAttributeKind::I32,
613 },
614 VertexAttribute {
615 name: "aScaleSourceLayer",
616 count: 1,
617 kind: VertexAttributeKind::I32,
618 },
619 ],
620 };
621
622 pub const CLIP: VertexDescriptor = VertexDescriptor {
623 vertex_attributes: &[
624 VertexAttribute {
625 name: "aPosition",
626 count: 2,
627 kind: VertexAttributeKind::F32,
628 },
629 ],
630 instance_attributes: &[
631 VertexAttribute {
632 name: "aTransformIds",
633 count: 2,
634 kind: VertexAttributeKind::I32,
635 },
636 VertexAttribute {
637 name: "aClipDataResourceAddress",
638 count: 4,
639 kind: VertexAttributeKind::U16,
640 },
641 VertexAttribute {
642 name: "aClipLocalPos",
643 count: 2,
644 kind: VertexAttributeKind::F32,
645 },
646 VertexAttribute {
647 name: "aClipTileRect",
648 count: 4,
649 kind: VertexAttributeKind::F32,
650 },
651 VertexAttribute {
652 name: "aClipDeviceArea",
653 count: 4,
654 kind: VertexAttributeKind::F32,
655 },
656 VertexAttribute {
657 name: "aClipOrigins",
658 count: 4,
659 kind: VertexAttributeKind::F32,
660 },
661 VertexAttribute {
662 name: "aDevicePixelScale",
663 count: 1,
664 kind: VertexAttributeKind::F32,
665 },
666 ],
667 };
668
669 pub const GPU_CACHE_UPDATE: VertexDescriptor = VertexDescriptor {
670 vertex_attributes: &[
671 VertexAttribute {
672 name: "aPosition",
673 count: 2,
674 kind: VertexAttributeKind::U16Norm,
675 },
676 VertexAttribute {
677 name: "aValue",
678 count: 4,
679 kind: VertexAttributeKind::F32,
680 },
681 ],
682 instance_attributes: &[],
683 };
684
685 pub const RESOLVE: VertexDescriptor = VertexDescriptor {
686 vertex_attributes: &[
687 VertexAttribute {
688 name: "aPosition",
689 count: 2,
690 kind: VertexAttributeKind::F32,
691 },
692 ],
693 instance_attributes: &[
694 VertexAttribute {
695 name: "aRect",
696 count: 4,
697 kind: VertexAttributeKind::F32,
698 },
699 ],
700 };
701
702 pub const SVG_FILTER: VertexDescriptor = VertexDescriptor {
703 vertex_attributes: &[
704 VertexAttribute {
705 name: "aPosition",
706 count: 2,
707 kind: VertexAttributeKind::F32,
708 },
709 ],
710 instance_attributes: &[
711 VertexAttribute {
712 name: "aFilterRenderTaskAddress",
713 count: 1,
714 kind: VertexAttributeKind::U16,
715 },
716 VertexAttribute {
717 name: "aFilterInput1TaskAddress",
718 count: 1,
719 kind: VertexAttributeKind::U16,
720 },
721 VertexAttribute {
722 name: "aFilterInput2TaskAddress",
723 count: 1,
724 kind: VertexAttributeKind::U16,
725 },
726 VertexAttribute {
727 name: "aFilterKind",
728 count: 1,
729 kind: VertexAttributeKind::U16,
730 },
731 VertexAttribute {
732 name: "aFilterInputCount",
733 count: 1,
734 kind: VertexAttributeKind::U16,
735 },
736 VertexAttribute {
737 name: "aFilterGenericInt",
738 count: 1,
739 kind: VertexAttributeKind::U16,
740 },
741 VertexAttribute {
742 name: "aFilterExtraDataAddress",
743 count: 2,
744 kind: VertexAttributeKind::U16,
745 },
746 ],
747 };
748
749 pub const VECTOR_STENCIL: VertexDescriptor = VertexDescriptor {
750 vertex_attributes: &[
751 VertexAttribute {
752 name: "aPosition",
753 count: 2,
754 kind: VertexAttributeKind::F32,
755 },
756 ],
757 instance_attributes: &[
758 VertexAttribute {
759 name: "aFromPosition",
760 count: 2,
761 kind: VertexAttributeKind::F32,
762 },
763 VertexAttribute {
764 name: "aCtrlPosition",
765 count: 2,
766 kind: VertexAttributeKind::F32,
767 },
768 VertexAttribute {
769 name: "aToPosition",
770 count: 2,
771 kind: VertexAttributeKind::F32,
772 },
773 VertexAttribute {
774 name: "aFromNormal",
775 count: 2,
776 kind: VertexAttributeKind::F32,
777 },
778 VertexAttribute {
779 name: "aCtrlNormal",
780 count: 2,
781 kind: VertexAttributeKind::F32,
782 },
783 VertexAttribute {
784 name: "aToNormal",
785 count: 2,
786 kind: VertexAttributeKind::F32,
787 },
788 VertexAttribute {
789 name: "aPathID",
790 count: 1,
791 kind: VertexAttributeKind::U16,
792 },
793 VertexAttribute {
794 name: "aPad",
795 count: 1,
796 kind: VertexAttributeKind::U16,
797 },
798 ],
799 };
800
801 pub const VECTOR_COVER: VertexDescriptor = VertexDescriptor {
802 vertex_attributes: &[
803 VertexAttribute {
804 name: "aPosition",
805 count: 2,
806 kind: VertexAttributeKind::F32,
807 },
808 ],
809 instance_attributes: &[
810 VertexAttribute {
811 name: "aTargetRect",
812 count: 4,
813 kind: VertexAttributeKind::I32,
814 },
815 VertexAttribute {
816 name: "aStencilOrigin",
817 count: 2,
818 kind: VertexAttributeKind::I32,
819 },
820 VertexAttribute {
821 name: "aSubpixel",
822 count: 1,
823 kind: VertexAttributeKind::U16,
824 },
825 VertexAttribute {
826 name: "aPad",
827 count: 1,
828 kind: VertexAttributeKind::U16,
829 },
830 ],
831 };
832
833 pub const COMPOSITE: VertexDescriptor = VertexDescriptor {
834 vertex_attributes: &[
835 VertexAttribute {
836 name: "aPosition",
837 count: 2,
838 kind: VertexAttributeKind::F32,
839 },
840 ],
841 instance_attributes: &[
842 VertexAttribute {
843 name: "aDeviceRect",
844 count: 4,
845 kind: VertexAttributeKind::F32,
846 },
847 VertexAttribute {
848 name: "aDeviceClipRect",
849 count: 4,
850 kind: VertexAttributeKind::F32,
851 },
852 VertexAttribute {
853 name: "aColor",
854 count: 4,
855 kind: VertexAttributeKind::F32,
856 },
857 VertexAttribute {
858 name: "aParams",
859 count: 4,
860 kind: VertexAttributeKind::F32,
861 },
862 VertexAttribute {
863 name: "aUvRect0",
864 count: 4,
865 kind: VertexAttributeKind::F32,
866 },
867 VertexAttribute {
868 name: "aUvRect1",
869 count: 4,
870 kind: VertexAttributeKind::F32,
871 },
872 VertexAttribute {
873 name: "aUvRect2",
874 count: 4,
875 kind: VertexAttributeKind::F32,
876 },
877 VertexAttribute {
878 name: "aTextureLayers",
879 count: 3,
880 kind: VertexAttributeKind::F32,
881 },
882 ],
883 };
884 }
885
886 #[derive(Debug, Copy, Clone)]
887 pub(crate) enum VertexArrayKind {
888 Primitive,
889 Blur,
890 Clip,
891 VectorStencil,
892 VectorCover,
893 Border,
894 Scale,
895 LineDecoration,
896 Gradient,
897 Resolve,
898 SvgFilter,
899 Composite,
900 }
901
902 #[derive(Clone, Debug, PartialEq)]
903 pub enum GraphicsApi {
904 OpenGL,
905 }
906
907 #[derive(Clone, Debug)]
908 pub struct GraphicsApiInfo {
909 pub kind: GraphicsApi,
910 pub renderer: String,
911 pub version: String,
912 }
913
914 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
915 #[cfg_attr(feature = "capture", derive(Serialize))]
916 #[cfg_attr(feature = "replay", derive(Deserialize))]
917 pub enum ImageBufferKind {
918 Texture2D = 0,
919 TextureRect = 1,
920 TextureExternal = 2,
921 Texture2DArray = 3,
922 }
923
924 //TODO: those types are the same, so let's merge them
925 impl From<TextureTarget> for ImageBufferKind {
from(target: TextureTarget) -> Self926 fn from(target: TextureTarget) -> Self {
927 match target {
928 TextureTarget::Default => ImageBufferKind::Texture2D,
929 TextureTarget::Rect => ImageBufferKind::TextureRect,
930 TextureTarget::Array => ImageBufferKind::Texture2DArray,
931 TextureTarget::External => ImageBufferKind::TextureExternal,
932 }
933 }
934 }
935
936 #[derive(Debug)]
937 pub struct GpuProfile {
938 pub frame_id: GpuFrameId,
939 pub paint_time_ns: u64,
940 }
941
942 impl GpuProfile {
new<T>(frame_id: GpuFrameId, timers: &[GpuTimer<T>]) -> GpuProfile943 fn new<T>(frame_id: GpuFrameId, timers: &[GpuTimer<T>]) -> GpuProfile {
944 let mut paint_time_ns = 0;
945 for timer in timers {
946 paint_time_ns += timer.time_ns;
947 }
948 GpuProfile {
949 frame_id,
950 paint_time_ns,
951 }
952 }
953 }
954
955 #[derive(Debug)]
956 pub struct CpuProfile {
957 pub frame_id: GpuFrameId,
958 pub backend_time_ns: u64,
959 pub composite_time_ns: u64,
960 pub draw_calls: usize,
961 }
962
963 impl CpuProfile {
new( frame_id: GpuFrameId, backend_time_ns: u64, composite_time_ns: u64, draw_calls: usize, ) -> CpuProfile964 fn new(
965 frame_id: GpuFrameId,
966 backend_time_ns: u64,
967 composite_time_ns: u64,
968 draw_calls: usize,
969 ) -> CpuProfile {
970 CpuProfile {
971 frame_id,
972 backend_time_ns,
973 composite_time_ns,
974 draw_calls,
975 }
976 }
977 }
978
979 /// The selected partial present mode for a given frame.
980 #[derive(Debug, Copy, Clone)]
981 enum PartialPresentMode {
982 /// The device supports fewer dirty rects than the number of dirty rects
983 /// that WR produced. In this case, the WR dirty rects are union'ed into
984 /// a single dirty rect, that is provided to the caller.
985 Single {
986 dirty_rect: DeviceRect,
987 },
988 }
989
990 /// A Texture that has been initialized by the `device` module and is ready to
991 /// be used.
992 struct ActiveTexture {
993 texture: Texture,
994 saved_index: Option<SavedTargetIndex>,
995 }
996
997 /// Helper struct for resolving device Textures for use during rendering passes.
998 ///
999 /// Manages the mapping between the at-a-distance texture handles used by the
1000 /// `RenderBackend` (which does not directly interface with the GPU) and actual
1001 /// device texture handles.
1002 struct TextureResolver {
1003 /// A map to resolve texture cache IDs to native textures.
1004 texture_cache_map: FastHashMap<CacheTextureId, Texture>,
1005
1006 /// Map of external image IDs to native textures.
1007 external_images: FastHashMap<(ExternalImageId, u8), ExternalTexture>,
1008
1009 /// A special 1x1 dummy texture used for shaders that expect to work with
1010 /// the output of the previous pass but are actually running in the first
1011 /// pass.
1012 dummy_cache_texture: Texture,
1013
1014 /// The outputs of the previous pass, if applicable.
1015 prev_pass_color: Option<ActiveTexture>,
1016 prev_pass_alpha: Option<ActiveTexture>,
1017
1018 /// Saved render targets from previous passes. This is used when a pass
1019 /// needs access to the result of a pass other than the immediately-preceding
1020 /// one. In this case, the `RenderTask` will get a non-`None` `saved_index`,
1021 /// which will cause the resulting render target to be persisted in this list
1022 /// (at that index) until the end of the frame.
1023 saved_targets: Vec<Texture>,
1024
1025 /// Pool of idle render target textures ready for re-use.
1026 ///
1027 /// Naively, it would seem like we only ever need two pairs of (color,
1028 /// alpha) render targets: one for the output of the previous pass (serving
1029 /// as input to the current pass), and one for the output of the current
1030 /// pass. However, there are cases where the output of one pass is used as
1031 /// the input to multiple future passes. For example, drop-shadows draw the
1032 /// picture in pass X, then reference it in pass X+1 to create the blurred
1033 /// shadow, and pass the results of both X and X+1 to pass X+2 draw the
1034 /// actual content.
1035 ///
1036 /// See the comments in `allocate_target_texture` for more insight on why
1037 /// reuse is a win.
1038 render_target_pool: Vec<Texture>,
1039 }
1040
1041 impl TextureResolver {
new(device: &mut Device) -> TextureResolver1042 fn new(device: &mut Device) -> TextureResolver {
1043 let dummy_cache_texture = device
1044 .create_texture(
1045 TextureTarget::Array,
1046 ImageFormat::RGBA8,
1047 1,
1048 1,
1049 TextureFilter::Linear,
1050 None,
1051 1,
1052 );
1053 device.upload_texture_immediate(
1054 &dummy_cache_texture,
1055 &[0xff, 0xff, 0xff, 0xff],
1056 );
1057
1058 TextureResolver {
1059 texture_cache_map: FastHashMap::default(),
1060 external_images: FastHashMap::default(),
1061 dummy_cache_texture,
1062 prev_pass_alpha: None,
1063 prev_pass_color: None,
1064 saved_targets: Vec::default(),
1065 render_target_pool: Vec::new(),
1066 }
1067 }
1068
deinit(self, device: &mut Device)1069 fn deinit(self, device: &mut Device) {
1070 device.delete_texture(self.dummy_cache_texture);
1071
1072 for (_id, texture) in self.texture_cache_map {
1073 device.delete_texture(texture);
1074 }
1075
1076 for texture in self.render_target_pool {
1077 device.delete_texture(texture);
1078 }
1079 }
1080
begin_frame(&mut self)1081 fn begin_frame(&mut self) {
1082 assert!(self.prev_pass_color.is_none());
1083 assert!(self.prev_pass_alpha.is_none());
1084 assert!(self.saved_targets.is_empty());
1085 }
1086
end_frame(&mut self, device: &mut Device, frame_id: GpuFrameId)1087 fn end_frame(&mut self, device: &mut Device, frame_id: GpuFrameId) {
1088 // return the cached targets to the pool
1089 self.end_pass(device, None, None);
1090 // return the saved targets as well
1091 while let Some(target) = self.saved_targets.pop() {
1092 self.return_to_pool(device, target);
1093 }
1094
1095 // GC the render target pool, if it's currently > 32 MB in size.
1096 //
1097 // We use a simple scheme whereby we drop any texture that hasn't been used
1098 // in the last 60 frames, until we are below the size threshold. This should
1099 // generally prevent any sustained build-up of unused textures, unless we don't
1100 // generate frames for a long period. This can happen when the window is
1101 // minimized, and we probably want to flush all the WebRender caches in that case [1].
1102 // There is also a second "red line" memory threshold which prevents
1103 // memory exhaustion if many render targets are allocated within a small
1104 // number of frames. For now this is set at 320 MB (10x the normal memory threshold).
1105 //
1106 // [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1494099
1107 self.gc_targets(
1108 device,
1109 frame_id,
1110 32 * 1024 * 1024,
1111 32 * 1024 * 1024 * 10,
1112 60,
1113 );
1114 }
1115
1116 /// Transfers ownership of a render target back to the pool.
return_to_pool(&mut self, device: &mut Device, target: Texture)1117 fn return_to_pool(&mut self, device: &mut Device, target: Texture) {
1118 device.invalidate_render_target(&target);
1119 self.render_target_pool.push(target);
1120 }
1121
1122 /// Frees any memory possible, in the event of a memory pressure signal.
on_memory_pressure( &mut self, device: &mut Device, )1123 fn on_memory_pressure(
1124 &mut self,
1125 device: &mut Device,
1126 ) {
1127 // Clear all textures in the render target pool
1128 for target in self.render_target_pool.drain(..) {
1129 device.delete_texture(target);
1130 }
1131 }
1132
1133 /// Drops all targets from the render target pool that do not satisfy the predicate.
gc_targets( &mut self, device: &mut Device, current_frame_id: GpuFrameId, total_bytes_threshold: usize, total_bytes_red_line_threshold: usize, frames_threshold: usize, )1134 pub fn gc_targets(
1135 &mut self,
1136 device: &mut Device,
1137 current_frame_id: GpuFrameId,
1138 total_bytes_threshold: usize,
1139 total_bytes_red_line_threshold: usize,
1140 frames_threshold: usize,
1141 ) {
1142 // Get the total GPU memory size used by the current render target pool
1143 let mut rt_pool_size_in_bytes: usize = self.render_target_pool
1144 .iter()
1145 .map(|t| t.size_in_bytes())
1146 .sum();
1147
1148 // If the total size of the pool is less than the threshold, don't bother
1149 // trying to GC any targets
1150 if rt_pool_size_in_bytes <= total_bytes_threshold {
1151 return;
1152 }
1153
1154 // Sort the current pool by age, so that we remove oldest textures first
1155 self.render_target_pool.sort_by_key(|t| t.last_frame_used());
1156
1157 // We can't just use retain() because `Texture` requires manual cleanup.
1158 let mut retained_targets = SmallVec::<[Texture; 8]>::new();
1159
1160 for target in self.render_target_pool.drain(..) {
1161 // Drop oldest textures until we are under the allowed size threshold.
1162 // However, if it's been used in very recently, it is always kept around,
1163 // which ensures we don't thrash texture allocations on pages that do
1164 // require a very large render target pool and are regularly changing.
1165 if (rt_pool_size_in_bytes > total_bytes_red_line_threshold) ||
1166 (rt_pool_size_in_bytes > total_bytes_threshold &&
1167 !target.used_recently(current_frame_id, frames_threshold))
1168 {
1169 rt_pool_size_in_bytes -= target.size_in_bytes();
1170 device.delete_texture(target);
1171 } else {
1172 retained_targets.push(target);
1173 }
1174 }
1175
1176 self.render_target_pool.extend(retained_targets);
1177 }
1178
end_pass( &mut self, device: &mut Device, a8_texture: Option<ActiveTexture>, rgba8_texture: Option<ActiveTexture>, )1179 fn end_pass(
1180 &mut self,
1181 device: &mut Device,
1182 a8_texture: Option<ActiveTexture>,
1183 rgba8_texture: Option<ActiveTexture>,
1184 ) {
1185 // If we have cache textures from previous pass, return them to the pool.
1186 // Also assign the pool index of those cache textures to last pass's index because this is
1187 // the result of last pass.
1188 // Note: the order here is important, needs to match the logic in `RenderPass::build()`.
1189 if let Some(at) = self.prev_pass_color.take() {
1190 if let Some(index) = at.saved_index {
1191 assert_eq!(self.saved_targets.len(), index.0);
1192 self.saved_targets.push(at.texture);
1193 } else {
1194 self.return_to_pool(device, at.texture);
1195 }
1196 }
1197 if let Some(at) = self.prev_pass_alpha.take() {
1198 if let Some(index) = at.saved_index {
1199 assert_eq!(self.saved_targets.len(), index.0);
1200 self.saved_targets.push(at.texture);
1201 } else {
1202 self.return_to_pool(device, at.texture);
1203 }
1204 }
1205
1206 // We have another pass to process, make these textures available
1207 // as inputs to the next pass.
1208 self.prev_pass_color = rgba8_texture;
1209 self.prev_pass_alpha = a8_texture;
1210 }
1211
1212 // Bind a source texture to the device.
bind(&self, texture_id: &TextureSource, sampler: TextureSampler, device: &mut Device) -> Swizzle1213 fn bind(&self, texture_id: &TextureSource, sampler: TextureSampler, device: &mut Device) -> Swizzle {
1214 match *texture_id {
1215 TextureSource::Invalid => {
1216 Swizzle::default()
1217 }
1218 TextureSource::Dummy => {
1219 let swizzle = Swizzle::default();
1220 device.bind_texture(sampler, &self.dummy_cache_texture, swizzle);
1221 swizzle
1222 }
1223 TextureSource::PrevPassAlpha => {
1224 let texture = match self.prev_pass_alpha {
1225 Some(ref at) => &at.texture,
1226 None => &self.dummy_cache_texture,
1227 };
1228 let swizzle = Swizzle::default();
1229 device.bind_texture(sampler, texture, swizzle);
1230 swizzle
1231 }
1232 TextureSource::PrevPassColor => {
1233 let texture = match self.prev_pass_color {
1234 Some(ref at) => &at.texture,
1235 None => &self.dummy_cache_texture,
1236 };
1237 let swizzle = Swizzle::default();
1238 device.bind_texture(sampler, texture, swizzle);
1239 swizzle
1240 }
1241 TextureSource::External(external_image) => {
1242 let texture = self.external_images
1243 .get(&(external_image.id, external_image.channel_index))
1244 .expect("BUG: External image should be resolved by now");
1245 device.bind_external_texture(sampler, texture);
1246 Swizzle::default()
1247 }
1248 TextureSource::TextureCache(index, swizzle) => {
1249 let texture = &self.texture_cache_map[&index];
1250 device.bind_texture(sampler, texture, swizzle);
1251 swizzle
1252 }
1253 TextureSource::RenderTaskCache(saved_index, swizzle) => {
1254 if saved_index.0 < self.saved_targets.len() {
1255 let texture = &self.saved_targets[saved_index.0];
1256 device.bind_texture(sampler, texture, swizzle)
1257 } else {
1258 // Check if this saved index is referring to a the prev pass
1259 if Some(saved_index) == self.prev_pass_color.as_ref().and_then(|at| at.saved_index) {
1260 let texture = match self.prev_pass_color {
1261 Some(ref at) => &at.texture,
1262 None => &self.dummy_cache_texture,
1263 };
1264 device.bind_texture(sampler, texture, swizzle);
1265 } else if Some(saved_index) == self.prev_pass_alpha.as_ref().and_then(|at| at.saved_index) {
1266 let texture = match self.prev_pass_alpha {
1267 Some(ref at) => &at.texture,
1268 None => &self.dummy_cache_texture,
1269 };
1270 device.bind_texture(sampler, texture, swizzle);
1271 }
1272 }
1273 swizzle
1274 }
1275 }
1276 }
1277
1278 // Get the real (OpenGL) texture ID for a given source texture.
1279 // For a texture cache texture, the IDs are stored in a vector
1280 // map for fast access.
resolve(&self, texture_id: &TextureSource) -> Option<(&Texture, Swizzle)>1281 fn resolve(&self, texture_id: &TextureSource) -> Option<(&Texture, Swizzle)> {
1282 match *texture_id {
1283 TextureSource::Invalid => None,
1284 TextureSource::Dummy => {
1285 Some((&self.dummy_cache_texture, Swizzle::default()))
1286 }
1287 TextureSource::PrevPassAlpha => Some((
1288 match self.prev_pass_alpha {
1289 Some(ref at) => &at.texture,
1290 None => &self.dummy_cache_texture,
1291 },
1292 Swizzle::default(),
1293 )),
1294 TextureSource::PrevPassColor => Some((
1295 match self.prev_pass_color {
1296 Some(ref at) => &at.texture,
1297 None => &self.dummy_cache_texture,
1298 },
1299 Swizzle::default(),
1300 )),
1301 TextureSource::External(..) => {
1302 panic!("BUG: External textures cannot be resolved, they can only be bound.");
1303 }
1304 TextureSource::TextureCache(index, swizzle) => {
1305 Some((&self.texture_cache_map[&index], swizzle))
1306 }
1307 TextureSource::RenderTaskCache(saved_index, swizzle) => {
1308 Some((&self.saved_targets[saved_index.0], swizzle))
1309 }
1310 }
1311 }
1312
1313 // Retrieve the deferred / resolved UV rect if an external texture, otherwise
1314 // return the default supplied UV rect.
get_uv_rect( &self, source: &TextureSource, default_value: TexelRect, ) -> TexelRect1315 fn get_uv_rect(
1316 &self,
1317 source: &TextureSource,
1318 default_value: TexelRect,
1319 ) -> TexelRect {
1320 match source {
1321 TextureSource::External(ref external_image) => {
1322 let texture = self.external_images
1323 .get(&(external_image.id, external_image.channel_index))
1324 .expect("BUG: External image should be resolved by now");
1325 texture.get_uv_rect()
1326 }
1327 _ => {
1328 default_value
1329 }
1330 }
1331 }
1332
report_memory(&self) -> MemoryReport1333 fn report_memory(&self) -> MemoryReport {
1334 let mut report = MemoryReport::default();
1335
1336 // We're reporting GPU memory rather than heap-allocations, so we don't
1337 // use size_of_op.
1338 for t in self.texture_cache_map.values() {
1339 report.texture_cache_textures += t.size_in_bytes();
1340 }
1341 for t in self.render_target_pool.iter() {
1342 report.render_target_textures += t.size_in_bytes();
1343 }
1344
1345 report
1346 }
1347 }
1348
1349 #[derive(Debug, Copy, Clone, PartialEq)]
1350 #[cfg_attr(feature = "capture", derive(Serialize))]
1351 #[cfg_attr(feature = "replay", derive(Deserialize))]
1352 pub enum BlendMode {
1353 None,
1354 Alpha,
1355 PremultipliedAlpha,
1356 PremultipliedDestOut,
1357 SubpixelDualSource,
1358 SubpixelConstantTextColor(ColorF),
1359 SubpixelWithBgColor,
1360 Advanced(MixBlendMode),
1361 }
1362
1363 /// Tracks the state of each row in the GPU cache texture.
1364 struct CacheRow {
1365 /// Mirrored block data on CPU for this row. We store a copy of
1366 /// the data on the CPU side to improve upload batching.
1367 cpu_blocks: Box<[GpuBlockData; MAX_VERTEX_TEXTURE_WIDTH]>,
1368 /// The first offset in this row that is dirty.
1369 min_dirty: u16,
1370 /// The last offset in this row that is dirty.
1371 max_dirty: u16,
1372 }
1373
1374 impl CacheRow {
new() -> Self1375 fn new() -> Self {
1376 CacheRow {
1377 cpu_blocks: Box::new([GpuBlockData::EMPTY; MAX_VERTEX_TEXTURE_WIDTH]),
1378 min_dirty: MAX_VERTEX_TEXTURE_WIDTH as _,
1379 max_dirty: 0,
1380 }
1381 }
1382
is_dirty(&self) -> bool1383 fn is_dirty(&self) -> bool {
1384 return self.min_dirty < self.max_dirty;
1385 }
1386
clear_dirty(&mut self)1387 fn clear_dirty(&mut self) {
1388 self.min_dirty = MAX_VERTEX_TEXTURE_WIDTH as _;
1389 self.max_dirty = 0;
1390 }
1391
add_dirty(&mut self, block_offset: usize, block_count: usize)1392 fn add_dirty(&mut self, block_offset: usize, block_count: usize) {
1393 self.min_dirty = self.min_dirty.min(block_offset as _);
1394 self.max_dirty = self.max_dirty.max((block_offset + block_count) as _);
1395 }
1396
dirty_blocks(&self) -> &[GpuBlockData]1397 fn dirty_blocks(&self) -> &[GpuBlockData] {
1398 return &self.cpu_blocks[self.min_dirty as usize .. self.max_dirty as usize];
1399 }
1400 }
1401
1402 /// The bus over which CPU and GPU versions of the GPU cache
1403 /// get synchronized.
1404 enum GpuCacheBus {
1405 /// PBO-based updates, currently operate on a row granularity.
1406 /// Therefore, are subject to fragmentation issues.
1407 PixelBuffer {
1408 /// PBO used for transfers.
1409 buffer: PBO,
1410 /// Per-row data.
1411 rows: Vec<CacheRow>,
1412 },
1413 /// Shader-based scattering updates. Currently rendered by a set
1414 /// of points into the GPU texture, each carrying a `GpuBlockData`.
1415 Scatter {
1416 /// Special program to run the scattered update.
1417 program: Program,
1418 /// VAO containing the source vertex buffers.
1419 vao: CustomVAO,
1420 /// VBO for positional data, supplied as normalized `u16`.
1421 buf_position: VBO<[u16; 2]>,
1422 /// VBO for gpu block data.
1423 buf_value: VBO<GpuBlockData>,
1424 /// Currently stored block count.
1425 count: usize,
1426 },
1427 }
1428
1429 /// The device-specific representation of the cache texture in gpu_cache.rs
1430 struct GpuCacheTexture {
1431 texture: Option<Texture>,
1432 bus: GpuCacheBus,
1433 }
1434
1435 impl GpuCacheTexture {
1436
1437 /// Ensures that we have an appropriately-sized texture. Returns true if a
1438 /// new texture was created.
ensure_texture(&mut self, device: &mut Device, height: i32)1439 fn ensure_texture(&mut self, device: &mut Device, height: i32) {
1440 // If we already have a texture that works, we're done.
1441 if self.texture.as_ref().map_or(false, |t| t.get_dimensions().height >= height) {
1442 if GPU_CACHE_RESIZE_TEST {
1443 // Special debug mode - resize the texture even though it's fine.
1444 } else {
1445 return;
1446 }
1447 }
1448
1449 // Take the old texture, if any.
1450 let blit_source = self.texture.take();
1451
1452 // Create the new texture.
1453 assert!(height >= 2, "Height is too small for ANGLE");
1454 let new_size = DeviceIntSize::new(MAX_VERTEX_TEXTURE_WIDTH as _, height);
1455 // If glCopyImageSubData is supported, this texture doesn't need
1456 // to be a render target. This prevents GL errors due to framebuffer
1457 // incompleteness on devices that don't support RGBAF32 render targets.
1458 // TODO(gw): We still need a proper solution for the subset of devices
1459 // that don't support glCopyImageSubData *OR* rendering to a
1460 // RGBAF32 render target. These devices will currently fail
1461 // to resize the GPU cache texture.
1462 let supports_copy_image_sub_data = device.get_capabilities().supports_copy_image_sub_data;
1463 let rt_info = if supports_copy_image_sub_data {
1464 None
1465 } else {
1466 Some(RenderTargetInfo { has_depth: false })
1467 };
1468 let mut texture = device.create_texture(
1469 TextureTarget::Default,
1470 ImageFormat::RGBAF32,
1471 new_size.width,
1472 new_size.height,
1473 TextureFilter::Nearest,
1474 rt_info,
1475 1,
1476 );
1477
1478 // Blit the contents of the previous texture, if applicable.
1479 if let Some(blit_source) = blit_source {
1480 device.blit_renderable_texture(&mut texture, &blit_source);
1481 device.delete_texture(blit_source);
1482 }
1483
1484 self.texture = Some(texture);
1485 }
1486
new(device: &mut Device, use_scatter: bool) -> Result<Self, RendererError>1487 fn new(device: &mut Device, use_scatter: bool) -> Result<Self, RendererError> {
1488 let bus = if use_scatter {
1489 let program = device.create_program_linked(
1490 "gpu_cache_update",
1491 &[],
1492 &desc::GPU_CACHE_UPDATE,
1493 )?;
1494 let buf_position = device.create_vbo();
1495 let buf_value = device.create_vbo();
1496 //Note: the vertex attributes have to be supplied in the same order
1497 // as for program creation, but each assigned to a different stream.
1498 let vao = device.create_custom_vao(&[
1499 buf_position.stream_with(&desc::GPU_CACHE_UPDATE.vertex_attributes[0..1]),
1500 buf_value .stream_with(&desc::GPU_CACHE_UPDATE.vertex_attributes[1..2]),
1501 ]);
1502 GpuCacheBus::Scatter {
1503 program,
1504 vao,
1505 buf_position,
1506 buf_value,
1507 count: 0,
1508 }
1509 } else {
1510 let buffer = device.create_pbo();
1511 GpuCacheBus::PixelBuffer {
1512 buffer,
1513 rows: Vec::new(),
1514 }
1515 };
1516
1517 Ok(GpuCacheTexture {
1518 texture: None,
1519 bus,
1520 })
1521 }
1522
deinit(mut self, device: &mut Device)1523 fn deinit(mut self, device: &mut Device) {
1524 if let Some(t) = self.texture.take() {
1525 device.delete_texture(t);
1526 }
1527 match self.bus {
1528 GpuCacheBus::PixelBuffer { buffer, ..} => {
1529 device.delete_pbo(buffer);
1530 }
1531 GpuCacheBus::Scatter { program, vao, buf_position, buf_value, ..} => {
1532 device.delete_program(program);
1533 device.delete_custom_vao(vao);
1534 device.delete_vbo(buf_position);
1535 device.delete_vbo(buf_value);
1536 }
1537 }
1538 }
1539
get_height(&self) -> i321540 fn get_height(&self) -> i32 {
1541 self.texture.as_ref().map_or(0, |t| t.get_dimensions().height)
1542 }
1543
prepare_for_updates( &mut self, device: &mut Device, total_block_count: usize, max_height: i32, )1544 fn prepare_for_updates(
1545 &mut self,
1546 device: &mut Device,
1547 total_block_count: usize,
1548 max_height: i32,
1549 ) {
1550 self.ensure_texture(device, max_height);
1551 match self.bus {
1552 GpuCacheBus::PixelBuffer { .. } => {},
1553 GpuCacheBus::Scatter {
1554 ref mut buf_position,
1555 ref mut buf_value,
1556 ref mut count,
1557 ..
1558 } => {
1559 *count = 0;
1560 if total_block_count > buf_value.allocated_count() {
1561 device.allocate_vbo(buf_position, total_block_count, VertexUsageHint::Stream);
1562 device.allocate_vbo(buf_value, total_block_count, VertexUsageHint::Stream);
1563 }
1564 }
1565 }
1566 }
1567
update(&mut self, device: &mut Device, updates: &GpuCacheUpdateList)1568 fn update(&mut self, device: &mut Device, updates: &GpuCacheUpdateList) {
1569 match self.bus {
1570 GpuCacheBus::PixelBuffer { ref mut rows, .. } => {
1571 for update in &updates.updates {
1572 match *update {
1573 GpuCacheUpdate::Copy {
1574 block_index,
1575 block_count,
1576 address,
1577 } => {
1578 let row = address.v as usize;
1579
1580 // Ensure that the CPU-side shadow copy of the GPU cache data has enough
1581 // rows to apply this patch.
1582 while rows.len() <= row {
1583 // Add a new row.
1584 rows.push(CacheRow::new());
1585 }
1586
1587 // Copy the blocks from the patch array in the shadow CPU copy.
1588 let block_offset = address.u as usize;
1589 let data = &mut rows[row].cpu_blocks;
1590 for i in 0 .. block_count {
1591 data[block_offset + i] = updates.blocks[block_index + i];
1592 }
1593
1594 // This row is dirty (needs to be updated in GPU texture).
1595 rows[row].add_dirty(block_offset, block_count);
1596 }
1597 }
1598 }
1599 }
1600 GpuCacheBus::Scatter {
1601 ref buf_position,
1602 ref buf_value,
1603 ref mut count,
1604 ..
1605 } => {
1606 //TODO: re-use this heap allocation
1607 // Unused positions will be left as 0xFFFF, which translates to
1608 // (1.0, 1.0) in the vertex output position and gets culled out
1609 let mut position_data = vec![[!0u16; 2]; updates.blocks.len()];
1610 let size = self.texture.as_ref().unwrap().get_dimensions().to_usize();
1611
1612 for update in &updates.updates {
1613 match *update {
1614 GpuCacheUpdate::Copy {
1615 block_index,
1616 block_count,
1617 address,
1618 } => {
1619 // Convert the absolute texel position into normalized
1620 let y = ((2*address.v as usize + 1) << 15) / size.height;
1621 for i in 0 .. block_count {
1622 let x = ((2*address.u as usize + 2*i + 1) << 15) / size.width;
1623 position_data[block_index + i] = [x as _, y as _];
1624 }
1625 }
1626 }
1627 }
1628
1629 device.fill_vbo(buf_value, &updates.blocks, *count);
1630 device.fill_vbo(buf_position, &position_data, *count);
1631 *count += position_data.len();
1632 }
1633 }
1634 }
1635
flush(&mut self, device: &mut Device) -> usize1636 fn flush(&mut self, device: &mut Device) -> usize {
1637 let texture = self.texture.as_ref().unwrap();
1638 match self.bus {
1639 GpuCacheBus::PixelBuffer { ref buffer, ref mut rows } => {
1640 let rows_dirty = rows
1641 .iter()
1642 .filter(|row| row.is_dirty())
1643 .count();
1644 if rows_dirty == 0 {
1645 return 0
1646 }
1647
1648 let (upload_size, _) = device.required_upload_size_and_stride(
1649 DeviceIntSize::new(MAX_VERTEX_TEXTURE_WIDTH as i32, 1),
1650 texture.get_format(),
1651 );
1652
1653 let mut uploader = device.upload_texture(
1654 texture,
1655 buffer,
1656 rows_dirty * upload_size,
1657 );
1658
1659 for (row_index, row) in rows.iter_mut().enumerate() {
1660 if !row.is_dirty() {
1661 continue;
1662 }
1663
1664 let blocks = row.dirty_blocks();
1665 let rect = DeviceIntRect::new(
1666 DeviceIntPoint::new(row.min_dirty as i32, row_index as i32),
1667 DeviceIntSize::new(blocks.len() as i32, 1),
1668 );
1669
1670 uploader.upload(rect, 0, None, None, blocks.as_ptr(), blocks.len());
1671
1672 row.clear_dirty();
1673 }
1674
1675 rows_dirty
1676 }
1677 GpuCacheBus::Scatter { ref program, ref vao, count, .. } => {
1678 device.disable_depth();
1679 device.set_blend(false);
1680 device.bind_program(program);
1681 device.bind_custom_vao(vao);
1682 device.bind_draw_target(
1683 DrawTarget::from_texture(
1684 texture,
1685 0,
1686 false,
1687 ),
1688 );
1689 device.draw_nonindexed_points(0, count as _);
1690 0
1691 }
1692 }
1693 }
1694 }
1695
1696 struct VertexDataTexture<T> {
1697 texture: Option<Texture>,
1698 format: ImageFormat,
1699 pbo: PBO,
1700 _marker: PhantomData<T>,
1701 }
1702
1703 impl<T> VertexDataTexture<T> {
new( device: &mut Device, format: ImageFormat, ) -> Self1704 fn new(
1705 device: &mut Device,
1706 format: ImageFormat,
1707 ) -> Self {
1708 VertexDataTexture {
1709 texture: None,
1710 format,
1711 pbo: device.create_pbo(),
1712 _marker: PhantomData,
1713 }
1714 }
1715
1716 /// Returns a borrow of the GPU texture. Panics if it hasn't been initialized.
texture(&self) -> &Texture1717 fn texture(&self) -> &Texture {
1718 self.texture.as_ref().unwrap()
1719 }
1720
1721 /// Returns an estimate of the GPU memory consumed by this VertexDataTexture.
size_in_bytes(&self) -> usize1722 fn size_in_bytes(&self) -> usize {
1723 self.texture.as_ref().map_or(0, |t| t.size_in_bytes())
1724 }
1725
update(&mut self, device: &mut Device, data: &mut Vec<T>)1726 fn update(&mut self, device: &mut Device, data: &mut Vec<T>) {
1727 debug_assert!(mem::size_of::<T>() % 16 == 0);
1728 let texels_per_item = mem::size_of::<T>() / 16;
1729 let items_per_row = MAX_VERTEX_TEXTURE_WIDTH / texels_per_item;
1730 debug_assert_ne!(items_per_row, 0);
1731
1732 // Ensure we always end up with a texture when leaving this method.
1733 let mut len = data.len();
1734 if len == 0 {
1735 if self.texture.is_some() {
1736 return;
1737 }
1738 data.reserve(items_per_row);
1739 len = items_per_row;
1740 } else {
1741 // Extend the data array to have enough capacity to upload at least
1742 // a multiple of the row size. This ensures memory safety when the
1743 // array is passed to OpenGL to upload to the GPU.
1744 let extra = len % items_per_row;
1745 if extra != 0 {
1746 let padding = items_per_row - extra;
1747 data.reserve(padding);
1748 len += padding;
1749 }
1750 }
1751
1752 let needed_height = (len / items_per_row) as i32;
1753 let existing_height = self.texture.as_ref().map_or(0, |t| t.get_dimensions().height);
1754
1755 // Create a new texture if needed.
1756 //
1757 // These textures are generally very small, which is why we don't bother
1758 // with incremental updates and just re-upload every frame. For most pages
1759 // they're one row each, and on stress tests like css-francine they end up
1760 // in the 6-14 range. So we size the texture tightly to what we need (usually
1761 // 1), and shrink it if the waste would be more than `VERTEX_TEXTURE_EXTRA_ROWS`
1762 // rows. This helps with memory overhead, especially because there are several
1763 // instances of these textures per Renderer.
1764 if needed_height > existing_height || needed_height + VERTEX_TEXTURE_EXTRA_ROWS < existing_height {
1765 // Drop the existing texture, if any.
1766 if let Some(t) = self.texture.take() {
1767 device.delete_texture(t);
1768 }
1769
1770 let texture = device.create_texture(
1771 TextureTarget::Default,
1772 self.format,
1773 MAX_VERTEX_TEXTURE_WIDTH as i32,
1774 // Ensure height is at least two to work around
1775 // https://bugs.chromium.org/p/angleproject/issues/detail?id=3039
1776 needed_height.max(2),
1777 TextureFilter::Nearest,
1778 None,
1779 1,
1780 );
1781 self.texture = Some(texture);
1782 }
1783
1784 // Note: the actual width can be larger than the logical one, with a few texels
1785 // of each row unused at the tail. This is needed because there is still hardware
1786 // (like Intel iGPUs) that prefers power-of-two sizes of textures ([1]).
1787 //
1788 // [1] https://software.intel.com/en-us/articles/opengl-performance-tips-power-of-two-textures-have-better-performance
1789 let logical_width = if needed_height == 1 {
1790 data.len() * texels_per_item
1791 } else {
1792 MAX_VERTEX_TEXTURE_WIDTH - (MAX_VERTEX_TEXTURE_WIDTH % texels_per_item)
1793 };
1794
1795 let rect = DeviceIntRect::new(
1796 DeviceIntPoint::zero(),
1797 DeviceIntSize::new(logical_width as i32, needed_height),
1798 );
1799
1800 debug_assert!(len <= data.capacity(), "CPU copy will read out of bounds");
1801 let (upload_size, _) = device.required_upload_size_and_stride(
1802 rect.size,
1803 self.texture().get_format(),
1804 );
1805 if upload_size > 0 {
1806 device
1807 .upload_texture(self.texture(), &self.pbo, upload_size)
1808 .upload(rect, 0, None, None, data.as_ptr(), len);
1809 }
1810 }
1811
deinit(mut self, device: &mut Device)1812 fn deinit(mut self, device: &mut Device) {
1813 device.delete_pbo(self.pbo);
1814 if let Some(t) = self.texture.take() {
1815 device.delete_texture(t);
1816 }
1817 }
1818 }
1819
1820 struct FrameOutput {
1821 last_access: GpuFrameId,
1822 fbo_id: FBOId,
1823 }
1824
1825 #[derive(PartialEq)]
1826 struct TargetSelector {
1827 size: DeviceIntSize,
1828 num_layers: usize,
1829 format: ImageFormat,
1830 }
1831
1832 struct LazyInitializedDebugRenderer {
1833 debug_renderer: Option<DebugRenderer>,
1834 failed: bool,
1835 }
1836
1837 impl LazyInitializedDebugRenderer {
new() -> Self1838 pub fn new() -> Self {
1839 Self {
1840 debug_renderer: None,
1841 failed: false,
1842 }
1843 }
1844
get_mut<'a>(&'a mut self, device: &mut Device) -> Option<&'a mut DebugRenderer>1845 pub fn get_mut<'a>(&'a mut self, device: &mut Device) -> Option<&'a mut DebugRenderer> {
1846 if self.failed {
1847 return None;
1848 }
1849 if self.debug_renderer.is_none() {
1850 match DebugRenderer::new(device) {
1851 Ok(renderer) => { self.debug_renderer = Some(renderer); }
1852 Err(_) => {
1853 // The shader compilation code already logs errors.
1854 self.failed = true;
1855 }
1856 }
1857 }
1858
1859 self.debug_renderer.as_mut()
1860 }
1861
1862 /// Returns mut ref to `DebugRenderer` if one already exists, otherwise returns `None`.
try_get_mut<'a>(&'a mut self) -> Option<&'a mut DebugRenderer>1863 pub fn try_get_mut<'a>(&'a mut self) -> Option<&'a mut DebugRenderer> {
1864 self.debug_renderer.as_mut()
1865 }
1866
deinit(self, device: &mut Device)1867 pub fn deinit(self, device: &mut Device) {
1868 if let Some(debug_renderer) = self.debug_renderer {
1869 debug_renderer.deinit(device);
1870 }
1871 }
1872 }
1873
1874 // NB: If you add more VAOs here, be sure to deinitialize them in
1875 // `Renderer::deinit()` below.
1876 pub struct RendererVAOs {
1877 prim_vao: VAO,
1878 blur_vao: VAO,
1879 clip_vao: VAO,
1880 border_vao: VAO,
1881 line_vao: VAO,
1882 scale_vao: VAO,
1883 gradient_vao: VAO,
1884 resolve_vao: VAO,
1885 svg_filter_vao: VAO,
1886 composite_vao: VAO,
1887 }
1888
1889 /// Information about the state of the debugging / profiler overlay in native compositing mode.
1890 struct DebugOverlayState {
1891 /// True if any of the current debug flags will result in drawing a debug overlay.
1892 is_enabled: bool,
1893
1894 /// The current size of the debug overlay surface. None implies that the
1895 /// debug surface isn't currently allocated.
1896 current_size: Option<DeviceIntSize>,
1897 }
1898
1899 impl DebugOverlayState {
new() -> Self1900 fn new() -> Self {
1901 DebugOverlayState {
1902 is_enabled: false,
1903 current_size: None,
1904 }
1905 }
1906 }
1907
1908 pub struct VertexDataTextures {
1909 prim_header_f_texture: VertexDataTexture<PrimitiveHeaderF>,
1910 prim_header_i_texture: VertexDataTexture<PrimitiveHeaderI>,
1911 transforms_texture: VertexDataTexture<TransformData>,
1912 render_task_texture: VertexDataTexture<RenderTaskData>,
1913 }
1914
1915 impl VertexDataTextures {
new( device: &mut Device, ) -> Self1916 fn new(
1917 device: &mut Device,
1918 ) -> Self {
1919 VertexDataTextures {
1920 prim_header_f_texture: VertexDataTexture::new(device, ImageFormat::RGBAF32),
1921 prim_header_i_texture: VertexDataTexture::new(device, ImageFormat::RGBAI32),
1922 transforms_texture: VertexDataTexture::new(device, ImageFormat::RGBAF32),
1923 render_task_texture: VertexDataTexture::new(device, ImageFormat::RGBAF32),
1924 }
1925 }
1926
update( &mut self, device: &mut Device, frame: &mut Frame, )1927 fn update(
1928 &mut self,
1929 device: &mut Device,
1930 frame: &mut Frame,
1931 ) {
1932 self.prim_header_f_texture.update(
1933 device,
1934 &mut frame.prim_headers.headers_float,
1935 );
1936 device.bind_texture(
1937 TextureSampler::PrimitiveHeadersF,
1938 &self.prim_header_f_texture.texture(),
1939 Swizzle::default(),
1940 );
1941
1942 self.prim_header_i_texture.update(
1943 device,
1944 &mut frame.prim_headers.headers_int,
1945 );
1946 device.bind_texture(
1947 TextureSampler::PrimitiveHeadersI,
1948 &self.prim_header_i_texture.texture(),
1949 Swizzle::default(),
1950 );
1951
1952 self.transforms_texture.update(
1953 device,
1954 &mut frame.transform_palette,
1955 );
1956 device.bind_texture(
1957 TextureSampler::TransformPalette,
1958 &self.transforms_texture.texture(),
1959 Swizzle::default(),
1960 );
1961
1962 self.render_task_texture.update(
1963 device,
1964 &mut frame.render_tasks.task_data,
1965 );
1966 device.bind_texture(
1967 TextureSampler::RenderTasks,
1968 &self.render_task_texture.texture(),
1969 Swizzle::default(),
1970 );
1971 }
1972
size_in_bytes(&self) -> usize1973 fn size_in_bytes(&self) -> usize {
1974 self.prim_header_f_texture.size_in_bytes() +
1975 self.prim_header_i_texture.size_in_bytes() +
1976 self.transforms_texture.size_in_bytes() +
1977 self.render_task_texture.size_in_bytes()
1978 }
1979
deinit( self, device: &mut Device, )1980 fn deinit(
1981 self,
1982 device: &mut Device,
1983 ) {
1984 self.transforms_texture.deinit(device);
1985 self.prim_header_f_texture.deinit(device);
1986 self.prim_header_i_texture.deinit(device);
1987 self.render_task_texture.deinit(device);
1988 }
1989 }
1990
1991 /// The renderer is responsible for submitting to the GPU the work prepared by the
1992 /// RenderBackend.
1993 ///
1994 /// We have a separate `Renderer` instance for each instance of WebRender (generally
1995 /// one per OS window), and all instances share the same thread.
1996 pub struct Renderer {
1997 result_rx: Receiver<ResultMsg>,
1998 debug_server: Box<dyn DebugServer>,
1999 pub device: Device,
2000 pending_texture_updates: Vec<TextureUpdateList>,
2001 /// True if there are any TextureCacheUpdate pending.
2002 pending_texture_cache_updates: bool,
2003 pending_native_surface_updates: Vec<NativeSurfaceOperation>,
2004 pending_gpu_cache_updates: Vec<GpuCacheUpdateList>,
2005 pending_gpu_cache_clear: bool,
2006 pending_shader_updates: Vec<PathBuf>,
2007 active_documents: Vec<(DocumentId, RenderedDocument)>,
2008
2009 shaders: Rc<RefCell<Shaders>>,
2010
2011 max_recorded_profiles: usize,
2012
2013 clear_color: Option<ColorF>,
2014 enable_clear_scissor: bool,
2015 enable_advanced_blend_barriers: bool,
2016
2017 debug: LazyInitializedDebugRenderer,
2018 debug_flags: DebugFlags,
2019 backend_profile_counters: BackendProfileCounters,
2020 profile_counters: RendererProfileCounters,
2021 resource_upload_time: u64,
2022 gpu_cache_upload_time: u64,
2023 profiler: Profiler,
2024 new_frame_indicator: ChangeIndicator,
2025 new_scene_indicator: ChangeIndicator,
2026 slow_frame_indicator: ChangeIndicator,
2027 slow_txn_indicator: ChangeIndicator,
2028
2029 last_time: u64,
2030
2031 pub gpu_profile: GpuProfiler<GpuProfileTag>,
2032 vaos: RendererVAOs,
2033
2034 gpu_cache_texture: GpuCacheTexture,
2035 vertex_data_textures: Vec<VertexDataTextures>,
2036 current_vertex_data_textures: usize,
2037
2038 /// When the GPU cache debugger is enabled, we keep track of the live blocks
2039 /// in the GPU cache so that we can use them for the debug display. This
2040 /// member stores those live blocks, indexed by row.
2041 gpu_cache_debug_chunks: Vec<Vec<GpuCacheDebugChunk>>,
2042
2043 gpu_cache_frame_id: FrameId,
2044 gpu_cache_overflow: bool,
2045
2046 pipeline_info: PipelineInfo,
2047
2048 // Manages and resolves source textures IDs to real texture IDs.
2049 texture_resolver: TextureResolver,
2050
2051 // A PBO used to do asynchronous texture cache uploads.
2052 texture_cache_upload_pbo: PBO,
2053
2054 dither_matrix_texture: Option<Texture>,
2055
2056 /// Optional trait object that allows the client
2057 /// application to provide external buffers for image data.
2058 external_image_handler: Option<Box<dyn ExternalImageHandler>>,
2059
2060 /// Optional trait object that allows the client
2061 /// application to provide a texture handle to
2062 /// copy the WR output to.
2063 output_image_handler: Option<Box<dyn OutputImageHandler>>,
2064
2065 /// Optional function pointers for measuring memory used by a given
2066 /// heap-allocated pointer.
2067 size_of_ops: Option<MallocSizeOfOps>,
2068
2069 // Currently allocated FBOs for output frames.
2070 output_targets: FastHashMap<u32, FrameOutput>,
2071
2072 pub renderer_errors: Vec<RendererError>,
2073
2074 pub(in crate) async_frame_recorder: Option<AsyncScreenshotGrabber>,
2075 pub(in crate) async_screenshots: Option<AsyncScreenshotGrabber>,
2076
2077 /// List of profile results from previous frames. Can be retrieved
2078 /// via get_frame_profiles().
2079 cpu_profiles: VecDeque<CpuProfile>,
2080 gpu_profiles: VecDeque<GpuProfile>,
2081
2082 /// Notification requests to be fulfilled after rendering.
2083 notifications: Vec<NotificationRequest>,
2084
2085 device_size: Option<DeviceIntSize>,
2086
2087 /// A lazily created texture for the zoom debugging widget.
2088 zoom_debug_texture: Option<Texture>,
2089
2090 /// The current mouse position. This is used for debugging
2091 /// functionality only, such as the debug zoom widget.
2092 cursor_position: DeviceIntPoint,
2093
2094 /// Guards to check if we might be rendering a frame with expired texture
2095 /// cache entries.
2096 shared_texture_cache_cleared: bool,
2097
2098 /// The set of documents which we've seen a publish for since last render.
2099 documents_seen: FastHashSet<DocumentId>,
2100
2101 #[cfg(feature = "capture")]
2102 read_fbo: FBOId,
2103 #[cfg(feature = "replay")]
2104 owned_external_images: FastHashMap<(ExternalImageId, u8), ExternalTexture>,
2105
2106 /// The compositing config, affecting how WR composites into the final scene.
2107 compositor_config: CompositorConfig,
2108
2109 current_compositor_kind: CompositorKind,
2110
2111 /// Maintains a set of allocated native composite surfaces. This allows any
2112 /// currently allocated surfaces to be cleaned up as soon as deinit() is
2113 /// called (the normal bookkeeping for native surfaces exists in the
2114 /// render backend thread).
2115 allocated_native_surfaces: FastHashSet<NativeSurfaceId>,
2116
2117 /// If true, partial present state has been reset and everything needs to
2118 /// be drawn on the next render.
2119 force_redraw: bool,
2120
2121 /// State related to the debug / profiling overlays
2122 debug_overlay_state: DebugOverlayState,
2123
2124 /// The dirty rectangle from the previous frame, used on platforms that
2125 /// require keeping the front buffer fully correct when doing
2126 /// partial present (e.g. unix desktop with EGL_EXT_buffer_age).
2127 prev_dirty_rect: DeviceRect,
2128 }
2129
2130 #[derive(Debug)]
2131 pub enum RendererError {
2132 Shader(ShaderError),
2133 Thread(std::io::Error),
2134 Resource(ResourceCacheError),
2135 MaxTextureSize,
2136 }
2137
2138 impl From<ShaderError> for RendererError {
from(err: ShaderError) -> Self2139 fn from(err: ShaderError) -> Self {
2140 RendererError::Shader(err)
2141 }
2142 }
2143
2144 impl From<std::io::Error> for RendererError {
from(err: std::io::Error) -> Self2145 fn from(err: std::io::Error) -> Self {
2146 RendererError::Thread(err)
2147 }
2148 }
2149
2150 impl From<ResourceCacheError> for RendererError {
from(err: ResourceCacheError) -> Self2151 fn from(err: ResourceCacheError) -> Self {
2152 RendererError::Resource(err)
2153 }
2154 }
2155
2156 impl Renderer {
2157 /// Initializes WebRender and creates a `Renderer` and `RenderApiSender`.
2158 ///
2159 /// # Examples
2160 /// Initializes a `Renderer` with some reasonable values. For more information see
2161 /// [`RendererOptions`][rendereroptions].
2162 ///
2163 /// ```rust,ignore
2164 /// # use webrender::renderer::Renderer;
2165 /// # use std::path::PathBuf;
2166 /// let opts = webrender::RendererOptions {
2167 /// device_pixel_ratio: 1.0,
2168 /// resource_override_path: None,
2169 /// enable_aa: false,
2170 /// };
2171 /// let (renderer, sender) = Renderer::new(opts);
2172 /// ```
2173 /// [rendereroptions]: struct.RendererOptions.html
new( gl: Rc<dyn gl::Gl>, notifier: Box<dyn RenderNotifier>, mut options: RendererOptions, shaders: Option<&mut WrShaders>, start_size: DeviceIntSize, ) -> Result<(Self, RenderApiSender), RendererError>2174 pub fn new(
2175 gl: Rc<dyn gl::Gl>,
2176 notifier: Box<dyn RenderNotifier>,
2177 mut options: RendererOptions,
2178 shaders: Option<&mut WrShaders>,
2179 start_size: DeviceIntSize,
2180 ) -> Result<(Self, RenderApiSender), RendererError> {
2181 if !wr_has_been_initialized() {
2182 // If the profiler feature is enabled, try to load the profiler shared library
2183 // if the path was provided.
2184 #[cfg(feature = "profiler")]
2185 unsafe {
2186 if let Ok(ref tracy_path) = std::env::var("WR_TRACY_PATH") {
2187 let ok = tracy_rs::load(tracy_path);
2188 println!("Load tracy from {} -> {}", tracy_path, ok);
2189 }
2190 }
2191
2192 register_thread_with_profiler("Compositor".to_owned());
2193 }
2194
2195 HAS_BEEN_INITIALIZED.store(true, Ordering::SeqCst);
2196
2197 let (api_tx, api_rx) = channel();
2198 let (result_tx, result_rx) = channel();
2199 let gl_type = gl.get_type();
2200
2201 let debug_server = new_debug_server(options.start_debug_server, api_tx.clone());
2202
2203 let mut device = Device::new(
2204 gl,
2205 options.resource_override_path.clone(),
2206 options.use_optimized_shaders,
2207 options.upload_method.clone(),
2208 options.cached_programs.take(),
2209 options.allow_pixel_local_storage_support,
2210 options.allow_texture_storage_support,
2211 options.allow_texture_swizzling,
2212 options.dump_shader_source.take(),
2213 options.surface_origin_is_top_left,
2214 options.panic_on_gl_error,
2215 );
2216
2217 let color_cache_formats = device.preferred_color_formats();
2218 let swizzle_settings = device.swizzle_settings();
2219 let use_dual_source_blending =
2220 device.get_capabilities().supports_dual_source_blending &&
2221 options.allow_dual_source_blending &&
2222 // If using pixel local storage, subpixel AA isn't supported (we disable it on all
2223 // mobile devices explicitly anyway).
2224 !device.get_capabilities().supports_pixel_local_storage;
2225 let ext_blend_equation_advanced =
2226 options.allow_advanced_blend_equation &&
2227 device.get_capabilities().supports_advanced_blend_equation;
2228 let ext_blend_equation_advanced_coherent =
2229 device.supports_extension("GL_KHR_blend_equation_advanced_coherent");
2230
2231 // 512 is the minimum that the texture cache can work with.
2232 const MIN_TEXTURE_SIZE: i32 = 512;
2233 if let Some(user_limit) = options.max_texture_size {
2234 assert!(user_limit >= MIN_TEXTURE_SIZE);
2235 device.clamp_max_texture_size(user_limit);
2236 }
2237 if device.max_texture_size() < MIN_TEXTURE_SIZE {
2238 // Broken GL contexts can return a max texture size of zero (See #1260).
2239 // Better to gracefully fail now than panic as soon as a texture is allocated.
2240 error!(
2241 "Device reporting insufficient max texture size ({})",
2242 device.max_texture_size()
2243 );
2244 return Err(RendererError::MaxTextureSize);
2245 }
2246 let max_texture_size = device.max_texture_size();
2247 let max_texture_layers = device.max_texture_layers();
2248
2249 device.begin_frame();
2250
2251 let shaders = match shaders {
2252 Some(shaders) => Rc::clone(&shaders.shaders),
2253 None => Rc::new(RefCell::new(Shaders::new(&mut device, gl_type, &options)?)),
2254 };
2255
2256 let backend_profile_counters = BackendProfileCounters::new();
2257
2258 let dither_matrix_texture = if options.enable_dithering {
2259 let dither_matrix: [u8; 64] = [
2260 0,
2261 48,
2262 12,
2263 60,
2264 3,
2265 51,
2266 15,
2267 63,
2268 32,
2269 16,
2270 44,
2271 28,
2272 35,
2273 19,
2274 47,
2275 31,
2276 8,
2277 56,
2278 4,
2279 52,
2280 11,
2281 59,
2282 7,
2283 55,
2284 40,
2285 24,
2286 36,
2287 20,
2288 43,
2289 27,
2290 39,
2291 23,
2292 2,
2293 50,
2294 14,
2295 62,
2296 1,
2297 49,
2298 13,
2299 61,
2300 34,
2301 18,
2302 46,
2303 30,
2304 33,
2305 17,
2306 45,
2307 29,
2308 10,
2309 58,
2310 6,
2311 54,
2312 9,
2313 57,
2314 5,
2315 53,
2316 42,
2317 26,
2318 38,
2319 22,
2320 41,
2321 25,
2322 37,
2323 21,
2324 ];
2325
2326 let texture = device.create_texture(
2327 TextureTarget::Default,
2328 ImageFormat::R8,
2329 8,
2330 8,
2331 TextureFilter::Nearest,
2332 None,
2333 1,
2334 );
2335 device.upload_texture_immediate(&texture, &dither_matrix);
2336
2337 Some(texture)
2338 } else {
2339 None
2340 };
2341
2342 let x0 = 0.0;
2343 let y0 = 0.0;
2344 let x1 = 1.0;
2345 let y1 = 1.0;
2346
2347 let quad_indices: [u16; 6] = [0, 1, 2, 2, 1, 3];
2348 let quad_vertices = [
2349 PackedVertex { pos: [x0, y0] },
2350 PackedVertex { pos: [x1, y0] },
2351 PackedVertex { pos: [x0, y1] },
2352 PackedVertex { pos: [x1, y1] },
2353 ];
2354
2355 let prim_vao = device.create_vao(&desc::PRIM_INSTANCES);
2356 device.bind_vao(&prim_vao);
2357 device.update_vao_indices(&prim_vao, &quad_indices, VertexUsageHint::Static);
2358 device.update_vao_main_vertices(&prim_vao, &quad_vertices, VertexUsageHint::Static);
2359
2360 let blur_vao = device.create_vao_with_new_instances(&desc::BLUR, &prim_vao);
2361 let clip_vao = device.create_vao_with_new_instances(&desc::CLIP, &prim_vao);
2362 let border_vao = device.create_vao_with_new_instances(&desc::BORDER, &prim_vao);
2363 let scale_vao = device.create_vao_with_new_instances(&desc::SCALE, &prim_vao);
2364 let line_vao = device.create_vao_with_new_instances(&desc::LINE, &prim_vao);
2365 let gradient_vao = device.create_vao_with_new_instances(&desc::GRADIENT, &prim_vao);
2366 let resolve_vao = device.create_vao_with_new_instances(&desc::RESOLVE, &prim_vao);
2367 let svg_filter_vao = device.create_vao_with_new_instances(&desc::SVG_FILTER, &prim_vao);
2368 let composite_vao = device.create_vao_with_new_instances(&desc::COMPOSITE, &prim_vao);
2369 let texture_cache_upload_pbo = device.create_pbo();
2370
2371 let texture_resolver = TextureResolver::new(&mut device);
2372
2373 let mut vertex_data_textures = Vec::new();
2374 for _ in 0 .. VERTEX_DATA_TEXTURE_COUNT {
2375 vertex_data_textures.push(VertexDataTextures::new(&mut device));
2376 }
2377
2378 // On some (mostly older, integrated) GPUs, the normal GPU texture cache update path
2379 // doesn't work well when running on ANGLE, causing CPU stalls inside D3D and/or the
2380 // GPU driver. See https://bugzilla.mozilla.org/show_bug.cgi?id=1576637 for much
2381 // more detail. To reduce the number of code paths we have active that require testing,
2382 // we will enable the GPU cache scatter update path on all devices running with ANGLE.
2383 // We want a better solution long-term, but for now this is a significant performance
2384 // improvement on HD4600 era GPUs, and shouldn't hurt performance in a noticeable
2385 // way on other systems running under ANGLE.
2386 let is_angle = device.get_capabilities().renderer_name.contains("ANGLE");
2387
2388 let gpu_cache_texture = GpuCacheTexture::new(
2389 &mut device,
2390 is_angle,
2391 )?;
2392
2393 device.end_frame();
2394
2395 let backend_notifier = notifier.clone();
2396
2397 let prefer_subpixel_aa = options.force_subpixel_aa || (options.enable_subpixel_aa && use_dual_source_blending);
2398 let default_font_render_mode = match (options.enable_aa, prefer_subpixel_aa) {
2399 (true, true) => FontRenderMode::Subpixel,
2400 (true, false) => FontRenderMode::Alpha,
2401 (false, _) => FontRenderMode::Mono,
2402 };
2403
2404 let compositor_kind = match options.compositor_config {
2405 CompositorConfig::Draw { max_partial_present_rects, draw_previous_partial_present_regions } => {
2406 CompositorKind::Draw { max_partial_present_rects, draw_previous_partial_present_regions }
2407 }
2408 CompositorConfig::Native { ref compositor, max_update_rects, .. } => {
2409 let capabilities = compositor.get_capabilities();
2410
2411 CompositorKind::Native {
2412 max_update_rects,
2413 virtual_surface_size: capabilities.virtual_surface_size,
2414 }
2415 }
2416 };
2417
2418 let config = FrameBuilderConfig {
2419 default_font_render_mode,
2420 dual_source_blending_is_enabled: true,
2421 dual_source_blending_is_supported: use_dual_source_blending,
2422 chase_primitive: options.chase_primitive,
2423 global_enable_picture_caching: options.enable_picture_caching,
2424 testing: options.testing,
2425 gpu_supports_fast_clears: options.gpu_supports_fast_clears,
2426 gpu_supports_advanced_blend: ext_blend_equation_advanced,
2427 advanced_blend_is_coherent: ext_blend_equation_advanced_coherent,
2428 batch_lookback_count: options.batch_lookback_count,
2429 background_color: options.clear_color,
2430 compositor_kind,
2431 tile_size_override: None,
2432 max_depth_ids: device.max_depth_ids(),
2433 max_target_size: max_texture_size,
2434 };
2435 info!("WR {:?}", config);
2436
2437 let device_pixel_ratio = options.device_pixel_ratio;
2438 let debug_flags = options.debug_flags;
2439 let size_of_op = options.size_of_op;
2440 let enclosing_size_of_op = options.enclosing_size_of_op;
2441 let make_size_of_ops =
2442 move || size_of_op.map(|o| MallocSizeOfOps::new(o, enclosing_size_of_op));
2443 let thread_listener = Arc::new(options.thread_listener);
2444 let thread_listener_for_rayon_start = thread_listener.clone();
2445 let thread_listener_for_rayon_end = thread_listener.clone();
2446 let workers = options
2447 .workers
2448 .take()
2449 .unwrap_or_else(|| {
2450 let worker = ThreadPoolBuilder::new()
2451 .thread_name(|idx|{ format!("WRWorker#{}", idx) })
2452 .start_handler(move |idx| {
2453 register_thread_with_profiler(format!("WRWorker#{}", idx));
2454 if let Some(ref thread_listener) = *thread_listener_for_rayon_start {
2455 thread_listener.thread_started(&format!("WRWorker#{}", idx));
2456 }
2457 })
2458 .exit_handler(move |idx| {
2459 if let Some(ref thread_listener) = *thread_listener_for_rayon_end {
2460 thread_listener.thread_stopped(&format!("WRWorker#{}", idx));
2461 }
2462 })
2463 .build();
2464 Arc::new(worker.unwrap())
2465 });
2466 let sampler = options.sampler;
2467 let namespace_alloc_by_client = options.namespace_alloc_by_client;
2468 let max_glyph_cache_size = options.max_glyph_cache_size.unwrap_or(GlyphCache::DEFAULT_MAX_BYTES_USED);
2469
2470 let font_instances = SharedFontInstanceMap::new();
2471
2472 let blob_image_handler = options.blob_image_handler.take();
2473 let thread_listener_for_render_backend = thread_listener.clone();
2474 let thread_listener_for_scene_builder = thread_listener.clone();
2475 let thread_listener_for_lp_scene_builder = thread_listener.clone();
2476 let scene_builder_hooks = options.scene_builder_hooks;
2477 let rb_thread_name = format!("WRRenderBackend#{}", options.renderer_id.unwrap_or(0));
2478 let scene_thread_name = format!("WRSceneBuilder#{}", options.renderer_id.unwrap_or(0));
2479 let lp_scene_thread_name = format!("WRSceneBuilderLP#{}", options.renderer_id.unwrap_or(0));
2480 let glyph_rasterizer = GlyphRasterizer::new(workers)?;
2481
2482 let (scene_builder_channels, scene_tx, backend_scene_tx, scene_rx) =
2483 SceneBuilderThreadChannels::new(api_tx.clone());
2484
2485 let sb_font_instances = font_instances.clone();
2486
2487 thread::Builder::new().name(scene_thread_name.clone()).spawn(move || {
2488 register_thread_with_profiler(scene_thread_name.clone());
2489 if let Some(ref thread_listener) = *thread_listener_for_scene_builder {
2490 thread_listener.thread_started(&scene_thread_name);
2491 }
2492
2493 let mut scene_builder = SceneBuilderThread::new(
2494 config,
2495 device_pixel_ratio,
2496 sb_font_instances,
2497 make_size_of_ops(),
2498 scene_builder_hooks,
2499 scene_builder_channels,
2500 );
2501 scene_builder.run();
2502
2503 if let Some(ref thread_listener) = *thread_listener_for_scene_builder {
2504 thread_listener.thread_stopped(&scene_thread_name);
2505 }
2506 })?;
2507
2508 let low_priority_scene_tx = if options.support_low_priority_transactions {
2509 let (low_priority_scene_tx, low_priority_scene_rx) = channel();
2510 let lp_builder = LowPrioritySceneBuilderThread {
2511 rx: low_priority_scene_rx,
2512 tx: scene_tx.clone(),
2513 simulate_slow_ms: 0,
2514 };
2515
2516 thread::Builder::new().name(lp_scene_thread_name.clone()).spawn(move || {
2517 register_thread_with_profiler(lp_scene_thread_name.clone());
2518 if let Some(ref thread_listener) = *thread_listener_for_lp_scene_builder {
2519 thread_listener.thread_started(&lp_scene_thread_name);
2520 }
2521
2522 let mut scene_builder = lp_builder;
2523 scene_builder.run();
2524
2525 if let Some(ref thread_listener) = *thread_listener_for_lp_scene_builder {
2526 thread_listener.thread_stopped(&lp_scene_thread_name);
2527 }
2528 })?;
2529
2530 low_priority_scene_tx
2531 } else {
2532 scene_tx.clone()
2533 };
2534
2535 let backend_blob_handler = blob_image_handler
2536 .as_ref()
2537 .map(|handler| handler.create_similar());
2538
2539 let rb_font_instances = font_instances.clone();
2540 let enable_multithreading = options.enable_multithreading;
2541 thread::Builder::new().name(rb_thread_name.clone()).spawn(move || {
2542 register_thread_with_profiler(rb_thread_name.clone());
2543 if let Some(ref thread_listener) = *thread_listener_for_render_backend {
2544 thread_listener.thread_started(&rb_thread_name);
2545 }
2546
2547 let texture_cache = TextureCache::new(
2548 max_texture_size,
2549 max_texture_layers,
2550 if config.global_enable_picture_caching {
2551 tile_cache_sizes(config.testing)
2552 } else {
2553 &[]
2554 },
2555 start_size,
2556 color_cache_formats,
2557 swizzle_settings,
2558 );
2559
2560 let glyph_cache = GlyphCache::new(max_glyph_cache_size);
2561
2562 let mut resource_cache = ResourceCache::new(
2563 texture_cache,
2564 glyph_rasterizer,
2565 glyph_cache,
2566 rb_font_instances,
2567 );
2568
2569 resource_cache.enable_multithreading(enable_multithreading);
2570
2571 let mut backend = RenderBackend::new(
2572 api_rx,
2573 result_tx,
2574 scene_tx,
2575 low_priority_scene_tx,
2576 backend_scene_tx,
2577 scene_rx,
2578 device_pixel_ratio,
2579 resource_cache,
2580 backend_notifier,
2581 backend_blob_handler,
2582 config,
2583 sampler,
2584 make_size_of_ops(),
2585 debug_flags,
2586 namespace_alloc_by_client,
2587 );
2588 backend.run(backend_profile_counters);
2589 if let Some(ref thread_listener) = *thread_listener_for_render_backend {
2590 thread_listener.thread_stopped(&rb_thread_name);
2591 }
2592 })?;
2593
2594 let debug_method = if !options.enable_gpu_markers {
2595 // The GPU markers are disabled.
2596 GpuDebugMethod::None
2597 } else if device.supports_extension("GL_KHR_debug") {
2598 GpuDebugMethod::KHR
2599 } else if device.supports_extension("GL_EXT_debug_marker") {
2600 GpuDebugMethod::MarkerEXT
2601 } else {
2602 println!("Warning: asking to enable_gpu_markers but no supporting extension was found");
2603 GpuDebugMethod::None
2604 };
2605
2606 info!("using {:?}", debug_method);
2607
2608 let gpu_profile = GpuProfiler::new(Rc::clone(device.rc_gl()), debug_method);
2609 #[cfg(feature = "capture")]
2610 let read_fbo = device.create_fbo();
2611
2612 let mut renderer = Renderer {
2613 result_rx,
2614 debug_server,
2615 device,
2616 active_documents: Vec::new(),
2617 pending_texture_updates: Vec::new(),
2618 pending_texture_cache_updates: false,
2619 pending_native_surface_updates: Vec::new(),
2620 pending_gpu_cache_updates: Vec::new(),
2621 pending_gpu_cache_clear: false,
2622 pending_shader_updates: Vec::new(),
2623 shaders,
2624 debug: LazyInitializedDebugRenderer::new(),
2625 debug_flags: DebugFlags::empty(),
2626 backend_profile_counters: BackendProfileCounters::new(),
2627 profile_counters: RendererProfileCounters::new(),
2628 resource_upload_time: 0,
2629 gpu_cache_upload_time: 0,
2630 profiler: Profiler::new(),
2631 new_frame_indicator: ChangeIndicator::new(),
2632 new_scene_indicator: ChangeIndicator::new(),
2633 slow_frame_indicator: ChangeIndicator::new(),
2634 slow_txn_indicator: ChangeIndicator::new(),
2635 max_recorded_profiles: options.max_recorded_profiles,
2636 clear_color: options.clear_color,
2637 enable_clear_scissor: options.enable_clear_scissor,
2638 enable_advanced_blend_barriers: !ext_blend_equation_advanced_coherent,
2639 last_time: 0,
2640 gpu_profile,
2641 vaos: RendererVAOs {
2642 prim_vao,
2643 blur_vao,
2644 clip_vao,
2645 border_vao,
2646 scale_vao,
2647 gradient_vao,
2648 resolve_vao,
2649 line_vao,
2650 svg_filter_vao,
2651 composite_vao,
2652 },
2653 vertex_data_textures,
2654 current_vertex_data_textures: 0,
2655 pipeline_info: PipelineInfo::default(),
2656 dither_matrix_texture,
2657 external_image_handler: None,
2658 output_image_handler: None,
2659 size_of_ops: make_size_of_ops(),
2660 output_targets: FastHashMap::default(),
2661 cpu_profiles: VecDeque::new(),
2662 gpu_profiles: VecDeque::new(),
2663 gpu_cache_texture,
2664 gpu_cache_debug_chunks: Vec::new(),
2665 gpu_cache_frame_id: FrameId::INVALID,
2666 gpu_cache_overflow: false,
2667 texture_cache_upload_pbo,
2668 texture_resolver,
2669 renderer_errors: Vec::new(),
2670 async_frame_recorder: None,
2671 async_screenshots: None,
2672 #[cfg(feature = "capture")]
2673 read_fbo,
2674 #[cfg(feature = "replay")]
2675 owned_external_images: FastHashMap::default(),
2676 notifications: Vec::new(),
2677 device_size: None,
2678 zoom_debug_texture: None,
2679 cursor_position: DeviceIntPoint::zero(),
2680 shared_texture_cache_cleared: false,
2681 documents_seen: FastHashSet::default(),
2682 force_redraw: true,
2683 compositor_config: options.compositor_config,
2684 current_compositor_kind: compositor_kind,
2685 allocated_native_surfaces: FastHashSet::default(),
2686 debug_overlay_state: DebugOverlayState::new(),
2687 prev_dirty_rect: DeviceRect::zero(),
2688 };
2689
2690 // We initially set the flags to default and then now call set_debug_flags
2691 // to ensure any potential transition when enabling a flag is run.
2692 renderer.set_debug_flags(debug_flags);
2693
2694 let sender = RenderApiSender::new(api_tx, blob_image_handler, font_instances);
2695 Ok((renderer, sender))
2696 }
2697
device_size(&self) -> Option<DeviceIntSize>2698 pub fn device_size(&self) -> Option<DeviceIntSize> {
2699 self.device_size
2700 }
2701
2702 /// Update the current position of the debug cursor.
set_cursor_position( &mut self, position: DeviceIntPoint, )2703 pub fn set_cursor_position(
2704 &mut self,
2705 position: DeviceIntPoint,
2706 ) {
2707 self.cursor_position = position;
2708 }
2709
get_max_texture_size(&self) -> i322710 pub fn get_max_texture_size(&self) -> i32 {
2711 self.device.max_texture_size()
2712 }
2713
get_graphics_api_info(&self) -> GraphicsApiInfo2714 pub fn get_graphics_api_info(&self) -> GraphicsApiInfo {
2715 GraphicsApiInfo {
2716 kind: GraphicsApi::OpenGL,
2717 version: self.device.gl().get_string(gl::VERSION),
2718 renderer: self.device.gl().get_string(gl::RENDERER),
2719 }
2720 }
2721
preferred_color_format(&self) -> ImageFormat2722 pub fn preferred_color_format(&self) -> ImageFormat {
2723 self.device.preferred_color_formats().external
2724 }
2725
optimal_texture_stride_alignment(&self, format: ImageFormat) -> usize2726 pub fn optimal_texture_stride_alignment(&self, format: ImageFormat) -> usize {
2727 self.device.optimal_pbo_stride().num_bytes(format).get()
2728 }
2729
flush_pipeline_info(&mut self) -> PipelineInfo2730 pub fn flush_pipeline_info(&mut self) -> PipelineInfo {
2731 mem::replace(&mut self.pipeline_info, PipelineInfo::default())
2732 }
2733
2734 /// Returns the Epoch of the current frame in a pipeline.
current_epoch(&self, document_id: DocumentId, pipeline_id: PipelineId) -> Option<Epoch>2735 pub fn current_epoch(&self, document_id: DocumentId, pipeline_id: PipelineId) -> Option<Epoch> {
2736 self.pipeline_info.epochs.get(&(pipeline_id, document_id)).cloned()
2737 }
2738
2739 /// Processes the result queue.
2740 ///
2741 /// Should be called before `render()`, as texture cache updates are done here.
update(&mut self)2742 pub fn update(&mut self) {
2743 profile_scope!("update");
2744 // Pull any pending results and return the most recent.
2745 while let Ok(msg) = self.result_rx.try_recv() {
2746 match msg {
2747 ResultMsg::PublishPipelineInfo(mut pipeline_info) => {
2748 for ((pipeline_id, document_id), epoch) in pipeline_info.epochs {
2749 self.pipeline_info.epochs.insert((pipeline_id, document_id), epoch);
2750 }
2751 self.pipeline_info.removed_pipelines.extend(pipeline_info.removed_pipelines.drain(..));
2752 }
2753 ResultMsg::PublishDocument(
2754 document_id,
2755 doc,
2756 resource_update_list,
2757 profile_counters,
2758 ) => {
2759 if doc.is_new_scene {
2760 self.new_scene_indicator.changed();
2761 }
2762
2763 // Add a new document to the active set, expressed as a `Vec` in order
2764 // to re-order based on `DocumentLayer` during rendering.
2765 match self.active_documents.iter().position(|&(id, _)| id == document_id) {
2766 Some(pos) => {
2767 // If the document we are replacing must be drawn
2768 // (in order to update the texture cache), issue
2769 // a render just to off-screen targets.
2770 if self.active_documents[pos].1.frame.must_be_drawn() {
2771 let device_size = self.device_size;
2772 self.render_impl(device_size).ok();
2773 }
2774
2775 self.active_documents[pos].1 = doc;
2776 }
2777 None => self.active_documents.push((document_id, doc)),
2778 }
2779
2780 // IMPORTANT: The pending texture cache updates must be applied
2781 // *after* the previous frame has been rendered above
2782 // (if neceessary for a texture cache update). For
2783 // an example of why this is required:
2784 // 1) Previous frame contains a render task that
2785 // targets Texture X.
2786 // 2) New frame contains a texture cache update which
2787 // frees Texture X.
2788 // 3) bad stuff happens.
2789
2790 //TODO: associate `document_id` with target window
2791 self.pending_texture_cache_updates |= !resource_update_list.texture_updates.updates.is_empty();
2792 self.pending_texture_updates.push(resource_update_list.texture_updates);
2793 self.pending_native_surface_updates.extend(resource_update_list.native_surface_updates);
2794 self.backend_profile_counters = profile_counters;
2795 self.documents_seen.insert(document_id);
2796 }
2797 ResultMsg::UpdateGpuCache(mut list) => {
2798 if list.clear {
2799 self.pending_gpu_cache_clear = true;
2800 }
2801 if list.clear {
2802 self.gpu_cache_debug_chunks = Vec::new();
2803 }
2804 for cmd in mem::replace(&mut list.debug_commands, Vec::new()) {
2805 match cmd {
2806 GpuCacheDebugCmd::Alloc(chunk) => {
2807 let row = chunk.address.v as usize;
2808 if row >= self.gpu_cache_debug_chunks.len() {
2809 self.gpu_cache_debug_chunks.resize(row + 1, Vec::new());
2810 }
2811 self.gpu_cache_debug_chunks[row].push(chunk);
2812 },
2813 GpuCacheDebugCmd::Free(address) => {
2814 let chunks = &mut self.gpu_cache_debug_chunks[address.v as usize];
2815 let pos = chunks.iter()
2816 .position(|x| x.address == address).unwrap();
2817 chunks.remove(pos);
2818 },
2819 }
2820 }
2821 self.pending_gpu_cache_updates.push(list);
2822 }
2823 ResultMsg::UpdateResources {
2824 resource_updates,
2825 memory_pressure,
2826 } => {
2827 if memory_pressure {
2828 // If a memory pressure event arrives _after_ a new scene has
2829 // been published that writes persistent targets (i.e. cached
2830 // render tasks to the texture cache, or picture cache tiles)
2831 // but _before_ the next update/render loop, those targets
2832 // will not be updated due to the active_documents list being
2833 // cleared at the end of this message. To work around that,
2834 // if any of the existing documents have not rendered yet, and
2835 // have picture/texture cache targets, force a render so that
2836 // those targets are updated.
2837 let must_be_drawn = self.active_documents
2838 .iter()
2839 .any(|(_, doc)| {
2840 doc.frame.must_be_drawn()
2841 });
2842
2843 if must_be_drawn {
2844 let device_size = self.device_size;
2845 self.render_impl(device_size).ok();
2846 }
2847 }
2848
2849 self.pending_texture_cache_updates |= !resource_updates.texture_updates.updates.is_empty();
2850 self.pending_texture_updates.push(resource_updates.texture_updates);
2851 self.pending_native_surface_updates.extend(resource_updates.native_surface_updates);
2852 self.device.begin_frame();
2853
2854 self.update_texture_cache();
2855 self.update_native_surfaces();
2856
2857 // Flush the render target pool on memory pressure.
2858 //
2859 // This needs to be separate from the block below because
2860 // the device module asserts if we delete textures while
2861 // not in a frame.
2862 if memory_pressure {
2863 self.texture_resolver.on_memory_pressure(
2864 &mut self.device,
2865 );
2866 }
2867
2868 self.device.end_frame();
2869 // If we receive a `PublishDocument` message followed by this one
2870 // within the same update we need to cancel the frame because we
2871 // might have deleted the resources in use in the frame due to a
2872 // memory pressure event.
2873 if memory_pressure {
2874 self.active_documents.clear();
2875 }
2876 }
2877 ResultMsg::AppendNotificationRequests(mut notifications) => {
2878 // We need to know specifically if there are any pending
2879 // TextureCacheUpdate updates in any of the entries in
2880 // pending_texture_updates. They may simply be nops, which do not
2881 // need to prevent issuing the notification, and if so, may not
2882 // cause a timely frame render to occur to wake up any listeners.
2883 if !self.pending_texture_cache_updates {
2884 drain_filter(
2885 &mut notifications,
2886 |n| { n.when() == Checkpoint::FrameTexturesUpdated },
2887 |n| { n.notify(); },
2888 );
2889 }
2890 self.notifications.append(&mut notifications);
2891 }
2892 ResultMsg::ForceRedraw => {
2893 self.force_redraw = true;
2894 }
2895 ResultMsg::RefreshShader(path) => {
2896 self.pending_shader_updates.push(path);
2897 }
2898 ResultMsg::DebugOutput(output) => match output {
2899 DebugOutput::FetchDocuments(string) |
2900 DebugOutput::FetchClipScrollTree(string) => {
2901 self.debug_server.send(string);
2902 }
2903 #[cfg(feature = "capture")]
2904 DebugOutput::SaveCapture(config, deferred) => {
2905 self.save_capture(config, deferred);
2906 }
2907 #[cfg(feature = "replay")]
2908 DebugOutput::LoadCapture(config, plain_externals) => {
2909 self.active_documents.clear();
2910 self.load_capture(config, plain_externals);
2911 }
2912 },
2913 ResultMsg::DebugCommand(command) => {
2914 self.handle_debug_command(command);
2915 }
2916 }
2917 }
2918 }
2919
2920 #[cfg(not(feature = "debugger"))]
get_screenshot_for_debugger(&mut self) -> String2921 fn get_screenshot_for_debugger(&mut self) -> String {
2922 // Avoid unused param warning.
2923 let _ = &self.debug_server;
2924 String::new()
2925 }
2926
2927 #[cfg(feature = "debugger")]
get_screenshot_for_debugger(&mut self) -> String2928 fn get_screenshot_for_debugger(&mut self) -> String {
2929 use api::{ImageDescriptor, ImageDescriptorFlags};
2930
2931 let desc = ImageDescriptor::new(1024, 768, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE);
2932 let data = self.device.read_pixels(&desc);
2933 let screenshot = debug_server::Screenshot::new(desc.size, data);
2934
2935 serde_json::to_string(&screenshot).unwrap()
2936 }
2937
2938 #[cfg(not(feature = "debugger"))]
get_passes_for_debugger(&self) -> String2939 fn get_passes_for_debugger(&self) -> String {
2940 // Avoid unused param warning.
2941 let _ = &self.debug_server;
2942 String::new()
2943 }
2944
2945 #[cfg(feature = "debugger")]
debug_alpha_target(target: &AlphaRenderTarget) -> debug_server::Target2946 fn debug_alpha_target(target: &AlphaRenderTarget) -> debug_server::Target {
2947 let mut debug_target = debug_server::Target::new("A8");
2948
2949 debug_target.add(
2950 debug_server::BatchKind::Cache,
2951 "Scalings",
2952 target.scalings.len(),
2953 );
2954 debug_target.add(
2955 debug_server::BatchKind::Cache,
2956 "Zero Clears",
2957 target.zero_clears.len(),
2958 );
2959 debug_target.add(
2960 debug_server::BatchKind::Cache,
2961 "One Clears",
2962 target.one_clears.len(),
2963 );
2964 debug_target.add(
2965 debug_server::BatchKind::Clip,
2966 "BoxShadows [p]",
2967 target.clip_batcher.primary_clips.box_shadows.len(),
2968 );
2969 debug_target.add(
2970 debug_server::BatchKind::Clip,
2971 "BoxShadows [s]",
2972 target.clip_batcher.secondary_clips.box_shadows.len(),
2973 );
2974 debug_target.add(
2975 debug_server::BatchKind::Cache,
2976 "Vertical Blur",
2977 target.vertical_blurs.len(),
2978 );
2979 debug_target.add(
2980 debug_server::BatchKind::Cache,
2981 "Horizontal Blur",
2982 target.horizontal_blurs.len(),
2983 );
2984 debug_target.add(
2985 debug_server::BatchKind::Clip,
2986 "Slow Rectangles [p]",
2987 target.clip_batcher.primary_clips.slow_rectangles.len(),
2988 );
2989 debug_target.add(
2990 debug_server::BatchKind::Clip,
2991 "Fast Rectangles [p]",
2992 target.clip_batcher.primary_clips.fast_rectangles.len(),
2993 );
2994 debug_target.add(
2995 debug_server::BatchKind::Clip,
2996 "Slow Rectangles [s]",
2997 target.clip_batcher.secondary_clips.slow_rectangles.len(),
2998 );
2999 debug_target.add(
3000 debug_server::BatchKind::Clip,
3001 "Fast Rectangles [s]",
3002 target.clip_batcher.secondary_clips.fast_rectangles.len(),
3003 );
3004 for (_, items) in target.clip_batcher.primary_clips.images.iter() {
3005 debug_target.add(debug_server::BatchKind::Clip, "Image mask [p]", items.len());
3006 }
3007 for (_, items) in target.clip_batcher.secondary_clips.images.iter() {
3008 debug_target.add(debug_server::BatchKind::Clip, "Image mask [s]", items.len());
3009 }
3010
3011 debug_target
3012 }
3013
3014 #[cfg(feature = "debugger")]
debug_color_target(target: &ColorRenderTarget) -> debug_server::Target3015 fn debug_color_target(target: &ColorRenderTarget) -> debug_server::Target {
3016 let mut debug_target = debug_server::Target::new("RGBA8");
3017
3018 debug_target.add(
3019 debug_server::BatchKind::Cache,
3020 "Scalings",
3021 target.scalings.len(),
3022 );
3023 debug_target.add(
3024 debug_server::BatchKind::Cache,
3025 "Readbacks",
3026 target.readbacks.len(),
3027 );
3028 debug_target.add(
3029 debug_server::BatchKind::Cache,
3030 "Vertical Blur",
3031 target.vertical_blurs.len(),
3032 );
3033 debug_target.add(
3034 debug_server::BatchKind::Cache,
3035 "Horizontal Blur",
3036 target.horizontal_blurs.len(),
3037 );
3038 debug_target.add(
3039 debug_server::BatchKind::Cache,
3040 "SVG Filters",
3041 target.svg_filters.iter().map(|(_, batch)| batch.len()).sum(),
3042 );
3043
3044 for alpha_batch_container in &target.alpha_batch_containers {
3045 for batch in alpha_batch_container.opaque_batches.iter().rev() {
3046 debug_target.add(
3047 debug_server::BatchKind::Opaque,
3048 batch.key.kind.debug_name(),
3049 batch.instances.len(),
3050 );
3051 }
3052
3053 for batch in &alpha_batch_container.alpha_batches {
3054 debug_target.add(
3055 debug_server::BatchKind::Alpha,
3056 batch.key.kind.debug_name(),
3057 batch.instances.len(),
3058 );
3059 }
3060 }
3061
3062 debug_target
3063 }
3064
3065 #[cfg(feature = "debugger")]
debug_texture_cache_target(target: &TextureCacheRenderTarget) -> debug_server::Target3066 fn debug_texture_cache_target(target: &TextureCacheRenderTarget) -> debug_server::Target {
3067 let mut debug_target = debug_server::Target::new("Texture Cache");
3068
3069 debug_target.add(
3070 debug_server::BatchKind::Cache,
3071 "Horizontal Blur",
3072 target.horizontal_blurs.len(),
3073 );
3074
3075 debug_target
3076 }
3077
3078 #[cfg(feature = "debugger")]
get_passes_for_debugger(&self) -> String3079 fn get_passes_for_debugger(&self) -> String {
3080 let mut debug_passes = debug_server::PassList::new();
3081
3082 for &(_, ref render_doc) in &self.active_documents {
3083 for pass in &render_doc.frame.passes {
3084 let mut debug_targets = Vec::new();
3085 match pass.kind {
3086 RenderPassKind::MainFramebuffer { ref main_target, .. } => {
3087 debug_targets.push(Self::debug_color_target(main_target));
3088 }
3089 RenderPassKind::OffScreen { ref alpha, ref color, ref texture_cache, .. } => {
3090 debug_targets.extend(alpha.targets.iter().map(Self::debug_alpha_target));
3091 debug_targets.extend(color.targets.iter().map(Self::debug_color_target));
3092 debug_targets.extend(texture_cache.iter().map(|(_, target)| Self::debug_texture_cache_target(target)));
3093 }
3094 }
3095
3096 debug_passes.add(debug_server::Pass { targets: debug_targets });
3097 }
3098 }
3099
3100 serde_json::to_string(&debug_passes).unwrap()
3101 }
3102
3103 #[cfg(not(feature = "debugger"))]
get_render_tasks_for_debugger(&self) -> String3104 fn get_render_tasks_for_debugger(&self) -> String {
3105 String::new()
3106 }
3107
3108 #[cfg(feature = "debugger")]
get_render_tasks_for_debugger(&self) -> String3109 fn get_render_tasks_for_debugger(&self) -> String {
3110 let mut debug_root = debug_server::RenderTaskList::new();
3111
3112 for &(_, ref render_doc) in &self.active_documents {
3113 let debug_node = debug_server::TreeNode::new("document render tasks");
3114 let mut builder = debug_server::TreeNodeBuilder::new(debug_node);
3115
3116 let render_tasks = &render_doc.frame.render_tasks;
3117 match render_tasks.tasks.first() {
3118 Some(main_task) => main_task.print_with(&mut builder, render_tasks),
3119 None => continue,
3120 };
3121
3122 debug_root.add(builder.build());
3123 }
3124
3125 serde_json::to_string(&debug_root).unwrap()
3126 }
3127
handle_debug_command(&mut self, command: DebugCommand)3128 fn handle_debug_command(&mut self, command: DebugCommand) {
3129 match command {
3130 DebugCommand::EnableDualSourceBlending(_) |
3131 DebugCommand::SetPictureTileSize(_) => {
3132 panic!("Should be handled by render backend");
3133 }
3134 DebugCommand::FetchDocuments |
3135 DebugCommand::FetchClipScrollTree => {}
3136 DebugCommand::FetchRenderTasks => {
3137 let json = self.get_render_tasks_for_debugger();
3138 self.debug_server.send(json);
3139 }
3140 DebugCommand::FetchPasses => {
3141 let json = self.get_passes_for_debugger();
3142 self.debug_server.send(json);
3143 }
3144 DebugCommand::FetchScreenshot => {
3145 let json = self.get_screenshot_for_debugger();
3146 self.debug_server.send(json);
3147 }
3148 DebugCommand::SaveCapture(..) |
3149 DebugCommand::LoadCapture(..) |
3150 DebugCommand::StartCaptureSequence(..) |
3151 DebugCommand::StopCaptureSequence => {
3152 panic!("Capture commands are not welcome here! Did you build with 'capture' feature?")
3153 }
3154 DebugCommand::ClearCaches(_)
3155 | DebugCommand::SimulateLongSceneBuild(_)
3156 | DebugCommand::SimulateLongLowPrioritySceneBuild(_)
3157 | DebugCommand::EnableNativeCompositor(_)
3158 | DebugCommand::SetBatchingLookback(_)
3159 | DebugCommand::EnableMultithreading(_) => {}
3160 DebugCommand::InvalidateGpuCache => {
3161 match self.gpu_cache_texture.bus {
3162 GpuCacheBus::PixelBuffer { ref mut rows, .. } => {
3163 info!("Invalidating GPU caches");
3164 for row in rows {
3165 row.add_dirty(0, MAX_VERTEX_TEXTURE_WIDTH);
3166 }
3167 }
3168 GpuCacheBus::Scatter { .. } => {
3169 warn!("Unable to invalidate scattered GPU cache");
3170 }
3171 }
3172 }
3173 DebugCommand::SetFlags(flags) => {
3174 self.set_debug_flags(flags);
3175 }
3176 }
3177 }
3178
3179 /// Set a callback for handling external images.
set_external_image_handler(&mut self, handler: Box<dyn ExternalImageHandler>)3180 pub fn set_external_image_handler(&mut self, handler: Box<dyn ExternalImageHandler>) {
3181 self.external_image_handler = Some(handler);
3182 }
3183
3184 /// Set a callback for handling external outputs.
set_output_image_handler(&mut self, handler: Box<dyn OutputImageHandler>)3185 pub fn set_output_image_handler(&mut self, handler: Box<dyn OutputImageHandler>) {
3186 self.output_image_handler = Some(handler);
3187 }
3188
3189 /// Retrieve (and clear) the current list of recorded frame profiles.
get_frame_profiles(&mut self) -> (Vec<CpuProfile>, Vec<GpuProfile>)3190 pub fn get_frame_profiles(&mut self) -> (Vec<CpuProfile>, Vec<GpuProfile>) {
3191 let cpu_profiles = self.cpu_profiles.drain(..).collect();
3192 let gpu_profiles = self.gpu_profiles.drain(..).collect();
3193 (cpu_profiles, gpu_profiles)
3194 }
3195
3196 /// Reset the current partial present state. This forces the entire framebuffer
3197 /// to be refreshed next time `render` is called.
force_redraw(&mut self)3198 pub fn force_redraw(&mut self) {
3199 self.force_redraw = true;
3200 }
3201
3202 /// Renders the current frame.
3203 ///
3204 /// A Frame is supplied by calling [`generate_frame()`][webrender_api::Transaction::generate_frame].
render( &mut self, device_size: DeviceIntSize, ) -> Result<RenderResults, Vec<RendererError>>3205 pub fn render(
3206 &mut self,
3207 device_size: DeviceIntSize,
3208 ) -> Result<RenderResults, Vec<RendererError>> {
3209 self.device_size = Some(device_size);
3210
3211 let result = self.render_impl(Some(device_size));
3212
3213 drain_filter(
3214 &mut self.notifications,
3215 |n| { n.when() == Checkpoint::FrameRendered },
3216 |n| { n.notify(); },
3217 );
3218
3219 // This is the end of the rendering pipeline. If some notifications are is still there,
3220 // just clear them and they will autimatically fire the Checkpoint::TransactionDropped
3221 // event. Otherwise they would just pile up in this vector forever.
3222 self.notifications.clear();
3223
3224 tracy_frame_marker!();
3225
3226 result
3227 }
3228
3229 /// Update the state of any debug / profiler overlays. This is currently only needed
3230 /// when running with the native compositor enabled.
update_debug_overlay(&mut self, framebuffer_size: DeviceIntSize)3231 fn update_debug_overlay(&mut self, framebuffer_size: DeviceIntSize) {
3232 // If any of the following debug flags are set, something will be drawn on the debug overlay.
3233 self.debug_overlay_state.is_enabled = self.debug_flags.intersects(
3234 DebugFlags::PROFILER_DBG |
3235 DebugFlags::RENDER_TARGET_DBG |
3236 DebugFlags::TEXTURE_CACHE_DBG |
3237 DebugFlags::EPOCHS |
3238 DebugFlags::NEW_FRAME_INDICATOR |
3239 DebugFlags::NEW_SCENE_INDICATOR |
3240 DebugFlags::GPU_CACHE_DBG |
3241 DebugFlags::SLOW_FRAME_INDICATOR |
3242 DebugFlags::PICTURE_CACHING_DBG |
3243 DebugFlags::PRIMITIVE_DBG |
3244 DebugFlags::ZOOM_DBG
3245 );
3246
3247 // Update the debug overlay surface, if we are running in native compositor mode.
3248 if let CompositorKind::Native { .. } = self.current_compositor_kind {
3249 let compositor = self.compositor_config.compositor().unwrap();
3250
3251 // If there is a current surface, destroy it if we don't need it for this frame, or if
3252 // the size has changed.
3253 if let Some(current_size) = self.debug_overlay_state.current_size {
3254 if !self.debug_overlay_state.is_enabled || current_size != framebuffer_size {
3255 compositor.destroy_surface(NativeSurfaceId::DEBUG_OVERLAY);
3256 self.debug_overlay_state.current_size = None;
3257 }
3258 }
3259
3260 // Allocate a new surface, if we need it and there isn't one.
3261 if self.debug_overlay_state.is_enabled && self.debug_overlay_state.current_size.is_none() {
3262 compositor.create_surface(
3263 NativeSurfaceId::DEBUG_OVERLAY,
3264 DeviceIntPoint::zero(),
3265 framebuffer_size,
3266 false,
3267 );
3268 compositor.create_tile(
3269 NativeTileId::DEBUG_OVERLAY,
3270 );
3271 self.debug_overlay_state.current_size = Some(framebuffer_size);
3272 }
3273 }
3274 }
3275
3276 /// Bind a draw target for the debug / profiler overlays, if required.
bind_debug_overlay(&mut self)3277 fn bind_debug_overlay(&mut self) {
3278 // Debug overlay setup are only required in native compositing mode
3279 if self.debug_overlay_state.is_enabled {
3280 if let CompositorKind::Native { .. } = self.current_compositor_kind {
3281 let compositor = self.compositor_config.compositor().unwrap();
3282 let surface_size = self.debug_overlay_state.current_size.unwrap();
3283
3284 // Bind the native surface
3285 let surface_info = compositor.bind(
3286 NativeTileId::DEBUG_OVERLAY,
3287 DeviceIntRect::new(
3288 DeviceIntPoint::zero(),
3289 surface_size,
3290 ),
3291 DeviceIntRect::new(
3292 DeviceIntPoint::zero(),
3293 surface_size,
3294 ),
3295 );
3296
3297 // Bind the native surface to current FBO target
3298 let draw_target = DrawTarget::NativeSurface {
3299 offset: surface_info.origin,
3300 external_fbo_id: surface_info.fbo_id,
3301 dimensions: surface_size,
3302 };
3303 self.device.bind_draw_target(draw_target);
3304
3305 // When native compositing, clear the debug overlay each frame.
3306 self.device.clear_target(
3307 Some([0.0, 0.0, 0.0, 0.0]),
3308 Some(1.0),
3309 None,
3310 );
3311 }
3312 }
3313 }
3314
3315 /// Unbind the draw target for debug / profiler overlays, if required.
unbind_debug_overlay(&mut self)3316 fn unbind_debug_overlay(&mut self) {
3317 // Debug overlay setup are only required in native compositing mode
3318 if self.debug_overlay_state.is_enabled {
3319 if let CompositorKind::Native { .. } = self.current_compositor_kind {
3320 let compositor = self.compositor_config.compositor().unwrap();
3321 // Unbind the draw target and add it to the visual tree to be composited
3322 compositor.unbind();
3323
3324 compositor.add_surface(
3325 NativeSurfaceId::DEBUG_OVERLAY,
3326 DeviceIntPoint::zero(),
3327 DeviceIntRect::new(
3328 DeviceIntPoint::zero(),
3329 self.debug_overlay_state.current_size.unwrap(),
3330 ),
3331 );
3332 }
3333 }
3334 }
3335
3336 // If device_size is None, don't render
3337 // to the main frame buffer. This is useful
3338 // to update texture cache render tasks but
3339 // avoid doing a full frame render.
render_impl( &mut self, device_size: Option<DeviceIntSize>, ) -> Result<RenderResults, Vec<RendererError>>3340 fn render_impl(
3341 &mut self,
3342 device_size: Option<DeviceIntSize>,
3343 ) -> Result<RenderResults, Vec<RendererError>> {
3344 profile_scope!("render");
3345 let mut results = RenderResults::default();
3346 if self.active_documents.is_empty() {
3347 self.last_time = precise_time_ns();
3348 return Ok(results);
3349 }
3350
3351 let compositor_kind = self.active_documents[0].1.frame.composite_state.compositor_kind;
3352 // CompositorKind is updated
3353 if self.current_compositor_kind != compositor_kind {
3354 let enable = match (self.current_compositor_kind, compositor_kind) {
3355 (CompositorKind::Native { .. }, CompositorKind::Draw { .. }) => {
3356 if self.debug_overlay_state.current_size.is_some() {
3357 self.compositor_config
3358 .compositor()
3359 .unwrap()
3360 .destroy_surface(NativeSurfaceId::DEBUG_OVERLAY);
3361 self.debug_overlay_state.current_size = None;
3362 }
3363 false
3364 }
3365 (CompositorKind::Draw { .. }, CompositorKind::Native { .. }) => {
3366 true
3367 }
3368 (_, _) => {
3369 unreachable!();
3370 }
3371 };
3372
3373 self.compositor_config
3374 .compositor()
3375 .unwrap()
3376 .enable_native_compositor(enable);
3377 self.current_compositor_kind = compositor_kind;
3378 }
3379
3380 let mut frame_profiles = Vec::new();
3381 let mut profile_timers = RendererProfileTimers::new();
3382
3383 // The texture resolver scope should be outside of any rendering, including
3384 // debug rendering. This ensures that when we return render targets to the
3385 // pool via glInvalidateFramebuffer, we don't do any debug rendering after
3386 // that point. Otherwise, the bind / invalidate / bind logic trips up the
3387 // render pass logic in tiled / mobile GPUs, resulting in an extra copy /
3388 // resolve step when the debug overlay is enabled.
3389 self.texture_resolver.begin_frame();
3390
3391 let profile_samplers = {
3392 let _gm = self.gpu_profile.start_marker("build samples");
3393 // Block CPU waiting for last frame's GPU profiles to arrive.
3394 // In general this shouldn't block unless heavily GPU limited.
3395 let (gpu_frame_id, timers, samplers) = self.gpu_profile.build_samples();
3396
3397 if self.max_recorded_profiles > 0 {
3398 while self.gpu_profiles.len() >= self.max_recorded_profiles {
3399 self.gpu_profiles.pop_front();
3400 }
3401 self.gpu_profiles
3402 .push_back(GpuProfile::new(gpu_frame_id, &timers));
3403 }
3404 profile_timers.gpu_samples = timers;
3405 samplers
3406 };
3407
3408
3409 let cpu_frame_id = profile_timers.cpu_time.profile(|| {
3410 let _gm = self.gpu_profile.start_marker("begin frame");
3411 let frame_id = self.device.begin_frame();
3412 self.gpu_profile.begin_frame(frame_id);
3413
3414 self.device.disable_scissor();
3415 self.device.disable_depth();
3416 self.set_blend(false, FramebufferKind::Main);
3417 //self.update_shaders();
3418
3419 self.update_texture_cache();
3420 self.update_native_surfaces();
3421
3422 frame_id
3423 });
3424
3425 // Inform the client that we are starting a composition transaction if native
3426 // compositing is enabled. This needs to be done early in the frame, so that
3427 // we can create debug overlays after drawing the main surfaces.
3428 if let CompositorKind::Native { .. } = self.current_compositor_kind {
3429 let compositor = self.compositor_config.compositor().unwrap();
3430 compositor.begin_frame();
3431 }
3432
3433 profile_timers.cpu_time.profile(|| {
3434 //Note: another borrowck dance
3435 let mut active_documents = mem::replace(&mut self.active_documents, Vec::default());
3436 // sort by the document layer id
3437 active_documents.sort_by_key(|&(_, ref render_doc)| render_doc.frame.layer);
3438
3439 #[cfg(feature = "replay")]
3440 self.texture_resolver.external_images.extend(
3441 self.owned_external_images.iter().map(|(key, value)| (*key, value.clone()))
3442 );
3443
3444 let last_document_index = active_documents.len() - 1;
3445 for (doc_index, (document_id, RenderedDocument { ref mut frame, .. })) in active_documents.iter_mut().enumerate() {
3446 assert!(self.current_compositor_kind == frame.composite_state.compositor_kind);
3447
3448 if self.shared_texture_cache_cleared {
3449 assert!(self.documents_seen.contains(&document_id),
3450 "Cleared texture cache without sending new document frame.");
3451 }
3452
3453 frame.profile_counters.reset_targets();
3454 if let Err(e) = self.prepare_gpu_cache(frame) {
3455 self.renderer_errors.push(e);
3456 continue;
3457 }
3458 assert!(frame.gpu_cache_frame_id <= self.gpu_cache_frame_id,
3459 "Received frame depends on a later GPU cache epoch ({:?}) than one we received last via `UpdateGpuCache` ({:?})",
3460 frame.gpu_cache_frame_id, self.gpu_cache_frame_id);
3461
3462 {
3463 profile_scope!("gl.flush");
3464 self.device.gl().flush(); // early start on gpu cache updates
3465 }
3466
3467 self.draw_frame(
3468 frame,
3469 device_size,
3470 cpu_frame_id,
3471 &mut results,
3472 doc_index == 0,
3473 );
3474
3475 // Profile marker for the number of invalidated picture cache
3476 if thread_is_being_profiled() {
3477 let num_invalidated = self.profile_counters.rendered_picture_cache_tiles.get_accum();
3478 let message = format!("NumPictureCacheInvalidated: {}", num_invalidated);
3479 add_event_marker(&(CString::new(message).unwrap()));
3480 }
3481
3482 if device_size.is_some() {
3483 self.draw_frame_debug_items(&frame.debug_items);
3484 }
3485 if self.debug_flags.contains(DebugFlags::PROFILER_DBG) {
3486 frame_profiles.push(frame.profile_counters.clone());
3487 }
3488
3489 let dirty_regions =
3490 mem::replace(&mut frame.recorded_dirty_regions, Vec::new());
3491 results.recorded_dirty_regions.extend(dirty_regions);
3492
3493 // If we're the last document, don't call end_pass here, because we'll
3494 // be moving on to drawing the debug overlays. See the comment above
3495 // the end_pass call in draw_frame about debug draw overlays
3496 // for a bit more context.
3497 if doc_index != last_document_index {
3498 self.texture_resolver.end_pass(&mut self.device, None, None);
3499 }
3500 }
3501
3502 self.unlock_external_images();
3503 self.active_documents = active_documents;
3504
3505 let _gm = self.gpu_profile.start_marker("end frame");
3506 self.gpu_profile.end_frame();
3507 });
3508
3509 if let Some(device_size) = device_size {
3510 // Update the state of the debug overlay surface, ensuring that
3511 // the compositor mode has a suitable surface to draw to, if required.
3512 self.update_debug_overlay(device_size);
3513
3514 // Bind a surface to draw the debug / profiler information to.
3515 self.bind_debug_overlay();
3516
3517 self.draw_render_target_debug(device_size);
3518 self.draw_texture_cache_debug(device_size);
3519 self.draw_gpu_cache_debug(device_size);
3520 self.draw_zoom_debug(device_size);
3521 self.draw_epoch_debug();
3522 }
3523
3524 let current_time = precise_time_ns();
3525 if device_size.is_some() {
3526 let ns = current_time - self.last_time;
3527 self.profile_counters.frame_time.set(ns);
3528 }
3529
3530 let frame_cpu_time_ns = self.backend_profile_counters.total_time.get()
3531 + profile_timers.cpu_time.get();
3532 let frame_cpu_time_ms = frame_cpu_time_ns as f64 / 1000000.0;
3533 if frame_cpu_time_ms > 16.0 {
3534 self.slow_frame_indicator.changed();
3535 }
3536
3537 if self.backend_profile_counters.scene_changed {
3538 let txn_time_ns = self.backend_profile_counters.txn.total_send_time.get()
3539 + self.backend_profile_counters.txn.display_list_build_time.get()
3540 + self.backend_profile_counters.txn.scene_build_time.get();
3541 let txn_time_ms = txn_time_ns as f64 / 1000000.0;
3542 if txn_time_ms > 100.0 {
3543 self.slow_txn_indicator.changed();
3544 }
3545 }
3546
3547 if self.max_recorded_profiles > 0 {
3548 while self.cpu_profiles.len() >= self.max_recorded_profiles {
3549 self.cpu_profiles.pop_front();
3550 }
3551 let cpu_profile = CpuProfile::new(
3552 cpu_frame_id,
3553 self.backend_profile_counters.total_time.get(),
3554 profile_timers.cpu_time.get(),
3555 self.profile_counters.draw_calls.get(),
3556 );
3557 self.cpu_profiles.push_back(cpu_profile);
3558 }
3559
3560 if self.debug_flags.contains(DebugFlags::PROFILER_DBG) {
3561 if let Some(device_size) = device_size {
3562 //TODO: take device/pixel ratio into equation?
3563 if let Some(debug_renderer) = self.debug.get_mut(&mut self.device) {
3564 let style = if self.debug_flags.contains(DebugFlags::SMART_PROFILER) {
3565 ProfileStyle::Smart
3566 } else if self.debug_flags.contains(DebugFlags::COMPACT_PROFILER) {
3567 ProfileStyle::Compact
3568 } else {
3569 ProfileStyle::Full
3570 };
3571
3572 let screen_fraction = 1.0 / device_size.to_f32().area();
3573 self.profiler.draw_profile(
3574 &frame_profiles,
3575 &self.backend_profile_counters,
3576 &self.profile_counters,
3577 &mut profile_timers,
3578 &profile_samplers,
3579 screen_fraction,
3580 debug_renderer,
3581 style,
3582 );
3583 }
3584 }
3585 }
3586
3587 let mut x = 0.0;
3588 if self.debug_flags.contains(DebugFlags::NEW_FRAME_INDICATOR) {
3589 if let Some(debug_renderer) = self.debug.get_mut(&mut self.device) {
3590 self.new_frame_indicator.changed();
3591 self.new_frame_indicator.draw(
3592 x, 0.0,
3593 ColorU::new(0, 110, 220, 255),
3594 debug_renderer,
3595 );
3596 x += ChangeIndicator::width();
3597 }
3598 }
3599
3600 if self.debug_flags.contains(DebugFlags::NEW_SCENE_INDICATOR) {
3601 if let Some(debug_renderer) = self.debug.get_mut(&mut self.device) {
3602 self.new_scene_indicator.draw(
3603 x, 0.0,
3604 ColorU::new(0, 220, 110, 255),
3605 debug_renderer,
3606 );
3607 x += ChangeIndicator::width();
3608 }
3609 }
3610
3611 if self.debug_flags.contains(DebugFlags::SLOW_FRAME_INDICATOR) {
3612 if let Some(debug_renderer) = self.debug.get_mut(&mut self.device) {
3613 self.slow_txn_indicator.draw(
3614 x, 0.0,
3615 ColorU::new(250, 80, 80, 255),
3616 debug_renderer,
3617 );
3618 self.slow_frame_indicator.draw(
3619 x, 10.0,
3620 ColorU::new(220, 30, 10, 255),
3621 debug_renderer,
3622 );
3623 }
3624 }
3625
3626 if self.debug_flags.contains(DebugFlags::ECHO_DRIVER_MESSAGES) {
3627 self.device.echo_driver_messages();
3628 }
3629
3630 results.stats.texture_upload_kb = self.profile_counters.texture_data_uploaded.get();
3631 self.backend_profile_counters.reset();
3632 self.profile_counters.reset();
3633 self.profile_counters.frame_counter.inc();
3634 results.stats.resource_upload_time = self.resource_upload_time;
3635 self.resource_upload_time = 0;
3636 results.stats.gpu_cache_upload_time = self.gpu_cache_upload_time;
3637 self.gpu_cache_upload_time = 0;
3638
3639 profile_timers.cpu_time.profile(|| {
3640 if let Some(debug_renderer) = self.debug.try_get_mut() {
3641 let small_screen = self.debug_flags.contains(DebugFlags::SMALL_SCREEN);
3642 let scale = if small_screen { 1.6 } else { 1.0 };
3643 // TODO(gw): Tidy this up so that compositor config integrates better
3644 // with the (non-compositor) surface y-flip options.
3645 let surface_origin_is_top_left = match self.current_compositor_kind {
3646 CompositorKind::Native { .. } => true,
3647 CompositorKind::Draw { .. } => self.device.surface_origin_is_top_left(),
3648 };
3649 debug_renderer.render(
3650 &mut self.device,
3651 device_size,
3652 scale,
3653 surface_origin_is_top_left,
3654 );
3655 }
3656 // See comment for texture_resolver.begin_frame() for explanation
3657 // of why this must be done after all rendering, including debug
3658 // overlays. The end_frame() call implicitly calls end_pass(), which
3659 // should ensure any left over render targets get invalidated and
3660 // returned to the pool correctly.
3661 self.texture_resolver.end_frame(&mut self.device, cpu_frame_id);
3662 self.device.end_frame();
3663 });
3664
3665 if device_size.is_some() {
3666 self.last_time = current_time;
3667
3668 // Unbind the target for the debug overlay. No debug or profiler drawing
3669 // can occur afer this point.
3670 self.unbind_debug_overlay();
3671 }
3672
3673 // Inform the client that we are finished this composition transaction if native
3674 // compositing is enabled. This must be called after any debug / profiling compositor
3675 // surfaces have been drawn and added to the visual tree.
3676 if let CompositorKind::Native { .. } = self.current_compositor_kind {
3677 profile_scope!("compositor.end_frame");
3678 let compositor = self.compositor_config.compositor().unwrap();
3679 compositor.end_frame();
3680 }
3681
3682 self.documents_seen.clear();
3683 self.shared_texture_cache_cleared = false;
3684
3685 if self.renderer_errors.is_empty() {
3686 Ok(results)
3687 } else {
3688 Err(mem::replace(&mut self.renderer_errors, Vec::new()))
3689 }
3690 }
3691
update_gpu_cache(&mut self)3692 fn update_gpu_cache(&mut self) {
3693 let _gm = self.gpu_profile.start_marker("gpu cache update");
3694
3695 // For an artificial stress test of GPU cache resizing,
3696 // always pass an extra update list with at least one block in it.
3697 let gpu_cache_height = self.gpu_cache_texture.get_height();
3698 if gpu_cache_height != 0 && GPU_CACHE_RESIZE_TEST {
3699 self.pending_gpu_cache_updates.push(GpuCacheUpdateList {
3700 frame_id: FrameId::INVALID,
3701 clear: false,
3702 height: gpu_cache_height,
3703 blocks: vec![[1f32; 4].into()],
3704 updates: Vec::new(),
3705 debug_commands: Vec::new(),
3706 });
3707 }
3708
3709 let (updated_blocks, max_requested_height) = self
3710 .pending_gpu_cache_updates
3711 .iter()
3712 .fold((0, gpu_cache_height), |(count, height), list| {
3713 (count + list.blocks.len(), cmp::max(height, list.height))
3714 });
3715
3716 if max_requested_height > self.get_max_texture_size() && !self.gpu_cache_overflow {
3717 self.gpu_cache_overflow = true;
3718 self.renderer_errors.push(RendererError::MaxTextureSize);
3719 }
3720
3721 // Note: if we decide to switch to scatter-style GPU cache update
3722 // permanently, we can have this code nicer with `BufferUploader` kind
3723 // of helper, similarly to how `TextureUploader` API is used.
3724 self.gpu_cache_texture.prepare_for_updates(
3725 &mut self.device,
3726 updated_blocks,
3727 max_requested_height,
3728 );
3729
3730 for update_list in self.pending_gpu_cache_updates.drain(..) {
3731 assert!(update_list.height <= max_requested_height);
3732 if update_list.frame_id > self.gpu_cache_frame_id {
3733 self.gpu_cache_frame_id = update_list.frame_id
3734 }
3735 self.gpu_cache_texture
3736 .update(&mut self.device, &update_list);
3737 }
3738
3739 let mut upload_time = TimeProfileCounter::new("GPU cache upload time", false, Some(0.0..2.0));
3740 let updated_rows = upload_time.profile(|| {
3741 self.gpu_cache_texture.flush(&mut self.device)
3742 });
3743 self.gpu_cache_upload_time += upload_time.get();
3744
3745 let counters = &mut self.backend_profile_counters.resources.gpu_cache;
3746 counters.updated_rows.set(updated_rows);
3747 counters.updated_blocks.set(updated_blocks);
3748 }
3749
prepare_gpu_cache(&mut self, frame: &Frame) -> Result<(), RendererError>3750 fn prepare_gpu_cache(&mut self, frame: &Frame) -> Result<(), RendererError> {
3751 if self.pending_gpu_cache_clear {
3752 let use_scatter =
3753 matches!(self.gpu_cache_texture.bus, GpuCacheBus::Scatter { .. });
3754 let new_cache = GpuCacheTexture::new(&mut self.device, use_scatter)?;
3755 let old_cache = mem::replace(&mut self.gpu_cache_texture, new_cache);
3756 old_cache.deinit(&mut self.device);
3757 self.pending_gpu_cache_clear = false;
3758 }
3759
3760 let deferred_update_list = self.update_deferred_resolves(&frame.deferred_resolves);
3761 self.pending_gpu_cache_updates.extend(deferred_update_list);
3762
3763 self.update_gpu_cache();
3764
3765 // Note: the texture might have changed during the `update`,
3766 // so we need to bind it here.
3767 self.device.bind_texture(
3768 TextureSampler::GpuCache,
3769 self.gpu_cache_texture.texture.as_ref().unwrap(),
3770 Swizzle::default(),
3771 );
3772
3773 Ok(())
3774 }
3775
update_texture_cache(&mut self)3776 fn update_texture_cache(&mut self) {
3777 profile_scope!("update_texture_cache");
3778
3779 let _gm = self.gpu_profile.start_marker("texture cache update");
3780 let mut pending_texture_updates = mem::replace(&mut self.pending_texture_updates, vec![]);
3781 self.pending_texture_cache_updates = false;
3782
3783 let mut upload_time = TimeProfileCounter::new("Resource upload time", false, Some(0.0..2.0));
3784 upload_time.profile(|| {
3785 for update_list in pending_texture_updates.drain(..) {
3786 for allocation in update_list.allocations {
3787 match allocation.kind {
3788 TextureCacheAllocationKind::Alloc(_) => add_event_marker(c_str!("TextureCacheAlloc")),
3789 TextureCacheAllocationKind::Realloc(_) => add_event_marker(c_str!("TextureCacheRealloc")),
3790 TextureCacheAllocationKind::Reset(_) => add_event_marker(c_str!("TextureCacheReset")),
3791 TextureCacheAllocationKind::Free => add_event_marker(c_str!("TextureCacheFree")),
3792 };
3793 let old = match allocation.kind {
3794 TextureCacheAllocationKind::Alloc(ref info) |
3795 TextureCacheAllocationKind::Realloc(ref info) |
3796 TextureCacheAllocationKind::Reset(ref info) => {
3797 // Create a new native texture, as requested by the texture cache.
3798 //
3799 // Ensure no PBO is bound when creating the texture storage,
3800 // or GL will attempt to read data from there.
3801 let mut texture = self.device.create_texture(
3802 TextureTarget::Array,
3803 info.format,
3804 info.width,
3805 info.height,
3806 info.filter,
3807 // This needs to be a render target because some render
3808 // tasks get rendered into the texture cache.
3809 Some(RenderTargetInfo { has_depth: info.has_depth }),
3810 info.layer_count,
3811 );
3812
3813 if info.is_shared_cache {
3814 texture.flags_mut()
3815 .insert(TextureFlags::IS_SHARED_TEXTURE_CACHE);
3816
3817 // Textures in the cache generally don't need to be cleared,
3818 // but we do so if the debug display is active to make it
3819 // easier to identify unallocated regions.
3820 if self.debug_flags.contains(DebugFlags::TEXTURE_CACHE_DBG) {
3821 self.clear_texture(&texture, TEXTURE_CACHE_DBG_CLEAR_COLOR);
3822 }
3823 }
3824
3825 self.texture_resolver.texture_cache_map.insert(allocation.id, texture)
3826 }
3827 TextureCacheAllocationKind::Free => {
3828 self.texture_resolver.texture_cache_map.remove(&allocation.id)
3829 }
3830 };
3831
3832 match allocation.kind {
3833 TextureCacheAllocationKind::Alloc(_) => {
3834 assert!(old.is_none(), "Renderer and backend disagree!");
3835 }
3836 TextureCacheAllocationKind::Realloc(_) => {
3837 self.device.blit_renderable_texture(
3838 self.texture_resolver.texture_cache_map.get_mut(&allocation.id).unwrap(),
3839 old.as_ref().unwrap(),
3840 );
3841 }
3842 TextureCacheAllocationKind::Reset(_) |
3843 TextureCacheAllocationKind::Free => {
3844 assert!(old.is_some(), "Renderer and backend disagree!");
3845 }
3846 }
3847
3848 if let Some(old) = old {
3849 self.device.delete_texture(old);
3850 }
3851 }
3852
3853 for (texture_id, updates) in update_list.updates {
3854 let texture = &self.texture_resolver.texture_cache_map[&texture_id];
3855 let device = &mut self.device;
3856
3857 // Calculate the total size of buffer required to upload all updates.
3858 let required_size = updates.iter().map(|update| {
3859 // Perform any debug clears now. As this requires a mutable borrow of device,
3860 // it must be done before all the updates which require a TextureUploader.
3861 if let TextureUpdateSource::DebugClear = update.source {
3862 let draw_target = DrawTarget::from_texture(
3863 texture,
3864 update.layer_index as usize,
3865 false,
3866 );
3867 device.bind_draw_target(draw_target);
3868 device.clear_target(
3869 Some(TEXTURE_CACHE_DBG_CLEAR_COLOR),
3870 None,
3871 Some(draw_target.to_framebuffer_rect(update.rect.to_i32()))
3872 );
3873
3874 0
3875 } else {
3876 let (upload_size, _) = device.required_upload_size_and_stride(
3877 update.rect.size,
3878 texture.get_format(),
3879 );
3880 upload_size
3881 }
3882 }).sum();
3883
3884 if required_size == 0 {
3885 continue;
3886 }
3887
3888 // For best performance we use a single TextureUploader for all uploads.
3889 // Using individual TextureUploaders was causing performance issues on some drivers
3890 // due to allocating too many PBOs.
3891 let mut uploader = device.upload_texture(
3892 texture,
3893 &self.texture_cache_upload_pbo,
3894 required_size
3895 );
3896
3897 for update in updates {
3898 let TextureCacheUpdate { rect, stride, offset, layer_index, format_override, source } = update;
3899
3900 let bytes_uploaded = match source {
3901 TextureUpdateSource::Bytes { data } => {
3902 let data = &data[offset as usize ..];
3903 uploader.upload(
3904 rect,
3905 layer_index,
3906 stride,
3907 format_override,
3908 data.as_ptr(),
3909 data.len(),
3910 )
3911 }
3912 TextureUpdateSource::External { id, channel_index } => {
3913 let handler = self.external_image_handler
3914 .as_mut()
3915 .expect("Found external image, but no handler set!");
3916 // The filter is only relevant for NativeTexture external images.
3917 let dummy_data;
3918 let data = match handler.lock(id, channel_index, ImageRendering::Auto).source {
3919 ExternalImageSource::RawData(data) => {
3920 &data[offset as usize ..]
3921 }
3922 ExternalImageSource::Invalid => {
3923 // Create a local buffer to fill the pbo.
3924 let bpp = texture.get_format().bytes_per_pixel();
3925 let width = stride.unwrap_or(rect.size.width * bpp);
3926 let total_size = width * rect.size.height;
3927 // WR haven't support RGBAF32 format in texture_cache, so
3928 // we use u8 type here.
3929 dummy_data = vec![0xFFu8; total_size as usize];
3930 &dummy_data
3931 }
3932 ExternalImageSource::NativeTexture(eid) => {
3933 panic!("Unexpected external texture {:?} for the texture cache update of {:?}", eid, id);
3934 }
3935 };
3936 let size = uploader.upload(
3937 rect,
3938 layer_index,
3939 stride,
3940 format_override,
3941 data.as_ptr(),
3942 data.len()
3943 );
3944 handler.unlock(id, channel_index);
3945 size
3946 }
3947 TextureUpdateSource::DebugClear => {
3948 // DebugClear updates are handled separately.
3949 0
3950 }
3951 };
3952 self.profile_counters.texture_data_uploaded.add(bytes_uploaded >> 10);
3953 }
3954 }
3955
3956 if update_list.clears_shared_cache {
3957 self.shared_texture_cache_cleared = true;
3958 }
3959 }
3960
3961 drain_filter(
3962 &mut self.notifications,
3963 |n| { n.when() == Checkpoint::FrameTexturesUpdated },
3964 |n| { n.notify(); },
3965 );
3966 });
3967 self.resource_upload_time += upload_time.get();
3968 }
3969
draw_instanced_batch<T>( &mut self, data: &[T], vertex_array_kind: VertexArrayKind, textures: &BatchTextures, stats: &mut RendererStats, )3970 pub(crate) fn draw_instanced_batch<T>(
3971 &mut self,
3972 data: &[T],
3973 vertex_array_kind: VertexArrayKind,
3974 textures: &BatchTextures,
3975 stats: &mut RendererStats,
3976 ) {
3977 let mut swizzles = [Swizzle::default(); 3];
3978 for i in 0 .. textures.colors.len() {
3979 let swizzle = self.texture_resolver.bind(
3980 &textures.colors[i],
3981 TextureSampler::color(i),
3982 &mut self.device,
3983 );
3984 if cfg!(debug_assertions) {
3985 swizzles[i] = swizzle;
3986 for j in 0 .. i {
3987 if textures.colors[j] == textures.colors[i] && swizzles[j] != swizzle {
3988 error!("Swizzling conflict in {:?}", textures);
3989 }
3990 }
3991 }
3992 }
3993
3994 // TODO: this probably isn't the best place for this.
3995 if let Some(ref texture) = self.dither_matrix_texture {
3996 self.device.bind_texture(TextureSampler::Dither, texture, Swizzle::default());
3997 }
3998
3999 self.draw_instanced_batch_with_previously_bound_textures(data, vertex_array_kind, stats)
4000 }
4001
draw_instanced_batch_with_previously_bound_textures<T>( &mut self, data: &[T], vertex_array_kind: VertexArrayKind, stats: &mut RendererStats, )4002 pub(crate) fn draw_instanced_batch_with_previously_bound_textures<T>(
4003 &mut self,
4004 data: &[T],
4005 vertex_array_kind: VertexArrayKind,
4006 stats: &mut RendererStats,
4007 ) {
4008 // If we end up with an empty draw call here, that means we have
4009 // probably introduced unnecessary batch breaks during frame
4010 // building - so we should be catching this earlier and removing
4011 // the batch.
4012 debug_assert!(!data.is_empty());
4013
4014 let vao = get_vao(vertex_array_kind, &self.vaos);
4015
4016 self.device.bind_vao(vao);
4017
4018 let batched = !self.debug_flags.contains(DebugFlags::DISABLE_BATCHING);
4019
4020 if batched {
4021 self.device
4022 .update_vao_instances(vao, data, VertexUsageHint::Stream);
4023 self.device
4024 .draw_indexed_triangles_instanced_u16(6, data.len() as i32);
4025 self.profile_counters.draw_calls.inc();
4026 stats.total_draw_calls += 1;
4027 } else {
4028 for i in 0 .. data.len() {
4029 self.device
4030 .update_vao_instances(vao, &data[i .. i + 1], VertexUsageHint::Stream);
4031 self.device.draw_triangles_u16(0, 6);
4032 self.profile_counters.draw_calls.inc();
4033 stats.total_draw_calls += 1;
4034 }
4035 }
4036
4037 self.profile_counters.vertices.add(6 * data.len());
4038 }
4039
handle_readback_composite( &mut self, draw_target: DrawTarget, uses_scissor: bool, source: &RenderTask, backdrop: &RenderTask, readback: &RenderTask, )4040 fn handle_readback_composite(
4041 &mut self,
4042 draw_target: DrawTarget,
4043 uses_scissor: bool,
4044 source: &RenderTask,
4045 backdrop: &RenderTask,
4046 readback: &RenderTask,
4047 ) {
4048 if uses_scissor {
4049 self.device.disable_scissor();
4050 }
4051
4052 let (cache_texture, _) = self.texture_resolver
4053 .resolve(&TextureSource::PrevPassColor)
4054 .unwrap();
4055
4056 // Before submitting the composite batch, do the
4057 // framebuffer readbacks that are needed for each
4058 // composite operation in this batch.
4059 let (readback_rect, readback_layer) = readback.get_target_rect();
4060 let (backdrop_rect, _) = backdrop.get_target_rect();
4061 let (backdrop_screen_origin, backdrop_scale) = match backdrop.kind {
4062 RenderTaskKind::Picture(ref task_info) => (task_info.content_origin, task_info.device_pixel_scale),
4063 _ => panic!("bug: composite on non-picture?"),
4064 };
4065 let (source_screen_origin, source_scale) = match source.kind {
4066 RenderTaskKind::Picture(ref task_info) => (task_info.content_origin, task_info.device_pixel_scale),
4067 _ => panic!("bug: composite on non-picture?"),
4068 };
4069
4070 // Bind the FBO to blit the backdrop to.
4071 // Called per-instance in case the layer (and therefore FBO)
4072 // changes. The device will skip the GL call if the requested
4073 // target is already bound.
4074 let cache_draw_target = DrawTarget::from_texture(
4075 cache_texture,
4076 readback_layer.0 as usize,
4077 false,
4078 );
4079
4080 let source_in_backdrop_space = source_screen_origin.to_f32() * (backdrop_scale.0 / source_scale.0);
4081
4082 let mut src = DeviceIntRect::new(
4083 (source_in_backdrop_space + (backdrop_rect.origin - backdrop_screen_origin).to_f32()).to_i32(),
4084 readback_rect.size,
4085 );
4086 let mut dest = readback_rect.to_i32();
4087 let device_to_framebuffer = Scale::new(1i32);
4088
4089 // Need to invert the y coordinates and flip the image vertically when
4090 // reading back from the framebuffer.
4091 if draw_target.is_default() {
4092 src.origin.y = draw_target.dimensions().height as i32 - src.size.height - src.origin.y;
4093 dest.origin.y += dest.size.height;
4094 dest.size.height = -dest.size.height;
4095 }
4096
4097 self.device.blit_render_target(
4098 draw_target.into(),
4099 src * device_to_framebuffer,
4100 cache_draw_target,
4101 dest * device_to_framebuffer,
4102 TextureFilter::Linear,
4103 );
4104
4105 // Restore draw target to current pass render target + layer, and reset
4106 // the read target.
4107 self.device.bind_draw_target(draw_target);
4108 self.device.reset_read_target();
4109
4110 if uses_scissor {
4111 self.device.enable_scissor();
4112 }
4113 }
4114
handle_blits( &mut self, blits: &[BlitJob], render_tasks: &RenderTaskGraph, draw_target: DrawTarget, content_origin: &DeviceIntPoint, )4115 fn handle_blits(
4116 &mut self,
4117 blits: &[BlitJob],
4118 render_tasks: &RenderTaskGraph,
4119 draw_target: DrawTarget,
4120 content_origin: &DeviceIntPoint,
4121 ) {
4122 if blits.is_empty() {
4123 return;
4124 }
4125
4126 let _timer = self.gpu_profile.start_timer(GPU_TAG_BLIT);
4127
4128 // TODO(gw): For now, we don't bother batching these by source texture.
4129 // If if ever shows up as an issue, we can easily batch them.
4130 for blit in blits {
4131 let (source, layer, source_rect) = match blit.source {
4132 BlitJobSource::Texture(texture_id, layer, source_rect) => {
4133 // A blit from a texture into this target.
4134 (texture_id, layer as usize, source_rect)
4135 }
4136 BlitJobSource::RenderTask(task_id) => {
4137 // A blit from the child render task into this target.
4138 // TODO(gw): Support R8 format here once we start
4139 // creating mips for alpha masks.
4140 let source = &render_tasks[task_id];
4141 let (source_rect, layer) = source.get_target_rect();
4142 (TextureSource::PrevPassColor, layer.0, source_rect)
4143 }
4144 };
4145
4146 debug_assert_eq!(source_rect.size, blit.target_rect.size);
4147 let (texture, swizzle) = self.texture_resolver
4148 .resolve(&source)
4149 .expect("BUG: invalid source texture");
4150
4151 if swizzle != Swizzle::default() {
4152 error!("Swizzle {:?} can't be handled by a blit", swizzle);
4153 }
4154
4155 let read_target = DrawTarget::from_texture(
4156 texture,
4157 layer,
4158 false,
4159 );
4160
4161 self.device.blit_render_target(
4162 read_target.into(),
4163 read_target.to_framebuffer_rect(source_rect),
4164 draw_target,
4165 draw_target.to_framebuffer_rect(blit.target_rect.translate(-content_origin.to_vector())),
4166 TextureFilter::Linear,
4167 );
4168 }
4169 }
4170
handle_scaling( &mut self, scalings: &FastHashMap<TextureSource, Vec<ScalingInstance>>, projection: &default::Transform3D<f32>, stats: &mut RendererStats, )4171 fn handle_scaling(
4172 &mut self,
4173 scalings: &FastHashMap<TextureSource, Vec<ScalingInstance>>,
4174 projection: &default::Transform3D<f32>,
4175 stats: &mut RendererStats,
4176 ) {
4177 if scalings.is_empty() {
4178 return
4179 }
4180
4181 let _timer = self.gpu_profile.start_timer(GPU_TAG_SCALE);
4182
4183 self.shaders
4184 .borrow_mut()
4185 .cs_scale
4186 .bind(
4187 &mut self.device,
4188 &projection,
4189 &mut self.renderer_errors,
4190 );
4191
4192 for (source, instances) in scalings {
4193 self.draw_instanced_batch(
4194 instances,
4195 VertexArrayKind::Scale,
4196 &BatchTextures::color(*source),
4197 stats,
4198 );
4199 }
4200 }
4201
handle_svg_filters( &mut self, textures: &BatchTextures, svg_filters: &[SvgFilterInstance], projection: &default::Transform3D<f32>, stats: &mut RendererStats, )4202 fn handle_svg_filters(
4203 &mut self,
4204 textures: &BatchTextures,
4205 svg_filters: &[SvgFilterInstance],
4206 projection: &default::Transform3D<f32>,
4207 stats: &mut RendererStats,
4208 ) {
4209 if svg_filters.is_empty() {
4210 return;
4211 }
4212
4213 let _timer = self.gpu_profile.start_timer(GPU_TAG_SVG_FILTER);
4214
4215 self.shaders.borrow_mut().cs_svg_filter.bind(
4216 &mut self.device,
4217 &projection,
4218 &mut self.renderer_errors
4219 );
4220
4221 self.draw_instanced_batch(
4222 &svg_filters,
4223 VertexArrayKind::SvgFilter,
4224 textures,
4225 stats,
4226 );
4227 }
4228
draw_picture_cache_target( &mut self, target: &PictureCacheTarget, draw_target: DrawTarget, content_origin: DeviceIntPoint, projection: &default::Transform3D<f32>, render_tasks: &RenderTaskGraph, stats: &mut RendererStats, )4229 fn draw_picture_cache_target(
4230 &mut self,
4231 target: &PictureCacheTarget,
4232 draw_target: DrawTarget,
4233 content_origin: DeviceIntPoint,
4234 projection: &default::Transform3D<f32>,
4235 render_tasks: &RenderTaskGraph,
4236 stats: &mut RendererStats,
4237 ) {
4238 profile_scope!("draw_picture_cache_target");
4239
4240 self.profile_counters.rendered_picture_cache_tiles.inc();
4241 let _gm = self.gpu_profile.start_marker("picture cache target");
4242 let framebuffer_kind = FramebufferKind::Other;
4243
4244 {
4245 let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_TARGET);
4246 self.device.bind_draw_target(draw_target);
4247 self.device.disable_depth();
4248 self.device.enable_depth_write();
4249 self.set_blend(false, framebuffer_kind);
4250
4251 // If updating only a dirty rect within a picture cache target, the
4252 // clear must also be scissored to that dirty region.
4253 let scissor_rect = target.alpha_batch_container.task_scissor_rect.map(|rect| {
4254 draw_target.build_scissor_rect(
4255 Some(rect),
4256 content_origin,
4257 )
4258 });
4259
4260 self.device.clear_target(
4261 target.clear_color.map(|c| c.to_array()),
4262 Some(1.0),
4263 scissor_rect,
4264 );
4265
4266 self.device.disable_depth_write();
4267 }
4268
4269 self.draw_alpha_batch_container(
4270 &target.alpha_batch_container,
4271 draw_target,
4272 content_origin,
4273 framebuffer_kind,
4274 projection,
4275 render_tasks,
4276 stats,
4277 );
4278 }
4279
4280 /// Draw an alpha batch container into a given draw target. This is used
4281 /// by both color and picture cache target kinds.
draw_alpha_batch_container( &mut self, alpha_batch_container: &AlphaBatchContainer, draw_target: DrawTarget, content_origin: DeviceIntPoint, framebuffer_kind: FramebufferKind, projection: &default::Transform3D<f32>, render_tasks: &RenderTaskGraph, stats: &mut RendererStats, )4282 fn draw_alpha_batch_container(
4283 &mut self,
4284 alpha_batch_container: &AlphaBatchContainer,
4285 draw_target: DrawTarget,
4286 content_origin: DeviceIntPoint,
4287 framebuffer_kind: FramebufferKind,
4288 projection: &default::Transform3D<f32>,
4289 render_tasks: &RenderTaskGraph,
4290 stats: &mut RendererStats,
4291 ) {
4292 let uses_scissor = alpha_batch_container.task_scissor_rect.is_some();
4293
4294 if uses_scissor {
4295 self.device.enable_scissor();
4296 let scissor_rect = draw_target.build_scissor_rect(
4297 alpha_batch_container.task_scissor_rect,
4298 content_origin,
4299 );
4300 self.device.set_scissor_rect(scissor_rect)
4301 }
4302
4303 if !alpha_batch_container.opaque_batches.is_empty()
4304 && !self.debug_flags.contains(DebugFlags::DISABLE_OPAQUE_PASS) {
4305 let _gl = self.gpu_profile.start_marker("opaque batches");
4306 let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE);
4307 self.set_blend(false, framebuffer_kind);
4308 //Note: depth equality is needed for split planes
4309 self.device.set_depth_func(DepthFunction::LessEqual);
4310 self.device.enable_depth();
4311 self.device.enable_depth_write();
4312
4313 // Draw opaque batches front-to-back for maximum
4314 // z-buffer efficiency!
4315 for batch in alpha_batch_container
4316 .opaque_batches
4317 .iter()
4318 .rev()
4319 {
4320 if should_skip_batch(&batch.key.kind, self.debug_flags) {
4321 continue;
4322 }
4323
4324 self.shaders.borrow_mut()
4325 .get(&batch.key, batch.features, self.debug_flags)
4326 .bind(
4327 &mut self.device, projection,
4328 &mut self.renderer_errors,
4329 );
4330
4331 let _timer = self.gpu_profile.start_timer(batch.key.kind.sampler_tag());
4332 self.draw_instanced_batch(
4333 &batch.instances,
4334 VertexArrayKind::Primitive,
4335 &batch.key.textures,
4336 stats
4337 );
4338 }
4339
4340 self.device.disable_depth_write();
4341 self.gpu_profile.finish_sampler(opaque_sampler);
4342 }
4343
4344 if !alpha_batch_container.alpha_batches.is_empty()
4345 && !self.debug_flags.contains(DebugFlags::DISABLE_ALPHA_PASS) {
4346 let _gl = self.gpu_profile.start_marker("alpha batches");
4347 let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
4348 self.set_blend(true, framebuffer_kind);
4349
4350 let mut prev_blend_mode = BlendMode::None;
4351 let shaders_rc = self.shaders.clone();
4352
4353 // If the device supports pixel local storage, initialize the PLS buffer for
4354 // the transparent pass. This involves reading the current framebuffer value
4355 // and storing that in PLS.
4356 // TODO(gw): This is quite expensive and relies on framebuffer fetch being
4357 // available. We can probably switch the opaque pass over to use
4358 // PLS too, and remove this pass completely.
4359 if self.device.get_capabilities().supports_pixel_local_storage {
4360 // TODO(gw): If using PLS, the fixed function blender is disabled. It's possible
4361 // we could take advantage of this by skipping batching on the blend
4362 // mode in these cases.
4363 self.init_pixel_local_storage(
4364 alpha_batch_container.task_rect,
4365 projection,
4366 stats,
4367 );
4368 }
4369
4370 for batch in &alpha_batch_container.alpha_batches {
4371 if should_skip_batch(&batch.key.kind, self.debug_flags) {
4372 continue;
4373 }
4374
4375 let mut shaders = shaders_rc.borrow_mut();
4376 let shader = shaders.get(
4377 &batch.key,
4378 batch.features | BatchFeatures::ALPHA_PASS,
4379 self.debug_flags,
4380 );
4381
4382 if batch.key.blend_mode != prev_blend_mode {
4383 match batch.key.blend_mode {
4384 _ if self.debug_flags.contains(DebugFlags::SHOW_OVERDRAW) &&
4385 framebuffer_kind == FramebufferKind::Main => {
4386 self.device.set_blend_mode_show_overdraw();
4387 }
4388 BlendMode::None => {
4389 unreachable!("bug: opaque blend in alpha pass");
4390 }
4391 BlendMode::Alpha => {
4392 self.device.set_blend_mode_alpha();
4393 }
4394 BlendMode::PremultipliedAlpha => {
4395 self.device.set_blend_mode_premultiplied_alpha();
4396 }
4397 BlendMode::PremultipliedDestOut => {
4398 self.device.set_blend_mode_premultiplied_dest_out();
4399 }
4400 BlendMode::SubpixelDualSource => {
4401 self.device.set_blend_mode_subpixel_dual_source();
4402 }
4403 BlendMode::SubpixelConstantTextColor(color) => {
4404 self.device.set_blend_mode_subpixel_constant_text_color(color);
4405 }
4406 BlendMode::SubpixelWithBgColor => {
4407 // Using the three pass "component alpha with font smoothing
4408 // background color" rendering technique:
4409 //
4410 // /webrender/doc/text-rendering.md
4411 //
4412 self.device.set_blend_mode_subpixel_with_bg_color_pass0();
4413 // need to make sure the shader is bound
4414 shader.bind(
4415 &mut self.device,
4416 projection,
4417 &mut self.renderer_errors,
4418 );
4419 self.device.switch_mode(ShaderColorMode::SubpixelWithBgColorPass0 as _);
4420 }
4421 BlendMode::Advanced(mode) => {
4422 if self.enable_advanced_blend_barriers {
4423 self.device.gl().blend_barrier_khr();
4424 }
4425 self.device.set_blend_mode_advanced(mode);
4426 }
4427 }
4428 prev_blend_mode = batch.key.blend_mode;
4429 }
4430
4431 // Handle special case readback for composites.
4432 if let BatchKind::Brush(BrushBatchKind::MixBlend { task_id, source_id, backdrop_id }) = batch.key.kind {
4433 // composites can't be grouped together because
4434 // they may overlap and affect each other.
4435 debug_assert_eq!(batch.instances.len(), 1);
4436 self.handle_readback_composite(
4437 draw_target,
4438 uses_scissor,
4439 &render_tasks[source_id],
4440 &render_tasks[task_id],
4441 &render_tasks[backdrop_id],
4442 );
4443 }
4444
4445 let _timer = self.gpu_profile.start_timer(batch.key.kind.sampler_tag());
4446 shader.bind(
4447 &mut self.device,
4448 projection,
4449 &mut self.renderer_errors,
4450 );
4451
4452 self.draw_instanced_batch(
4453 &batch.instances,
4454 VertexArrayKind::Primitive,
4455 &batch.key.textures,
4456 stats
4457 );
4458
4459 if batch.key.blend_mode == BlendMode::SubpixelWithBgColor {
4460 self.set_blend_mode_subpixel_with_bg_color_pass1(framebuffer_kind);
4461 // re-binding the shader after the blend mode change
4462 shader.bind(
4463 &mut self.device,
4464 projection,
4465 &mut self.renderer_errors,
4466 );
4467 self.device.switch_mode(ShaderColorMode::SubpixelWithBgColorPass1 as _);
4468
4469 // When drawing the 2nd and 3rd passes, we know that the VAO, textures etc
4470 // are all set up from the previous draw_instanced_batch call,
4471 // so just issue a draw call here to avoid re-uploading the
4472 // instances and re-binding textures etc.
4473 self.device
4474 .draw_indexed_triangles_instanced_u16(6, batch.instances.len() as i32);
4475
4476 self.set_blend_mode_subpixel_with_bg_color_pass2(framebuffer_kind);
4477 // re-binding the shader after the blend mode change
4478 shader.bind(
4479 &mut self.device,
4480 projection,
4481 &mut self.renderer_errors,
4482 );
4483 self.device.switch_mode(ShaderColorMode::SubpixelWithBgColorPass2 as _);
4484
4485 self.device
4486 .draw_indexed_triangles_instanced_u16(6, batch.instances.len() as i32);
4487 }
4488
4489 if batch.key.blend_mode == BlendMode::SubpixelWithBgColor {
4490 prev_blend_mode = BlendMode::None;
4491 }
4492 }
4493
4494 // If the device supports pixel local storage, resolve the PLS values.
4495 // This pass reads the final PLS color value, and writes it to a normal
4496 // fragment output.
4497 if self.device.get_capabilities().supports_pixel_local_storage {
4498 self.resolve_pixel_local_storage(
4499 alpha_batch_container.task_rect,
4500 projection,
4501 stats,
4502 );
4503 }
4504
4505 self.device.disable_depth();
4506 self.set_blend(false, framebuffer_kind);
4507 self.gpu_profile.finish_sampler(transparent_sampler);
4508 }
4509
4510 if uses_scissor {
4511 self.device.disable_scissor();
4512 }
4513 }
4514
4515 /// Rasterize any external compositor surfaces that require updating
update_external_native_surfaces( &mut self, external_surfaces: &[ResolvedExternalSurface], results: &mut RenderResults, )4516 fn update_external_native_surfaces(
4517 &mut self,
4518 external_surfaces: &[ResolvedExternalSurface],
4519 results: &mut RenderResults,
4520 ) {
4521 if external_surfaces.is_empty() {
4522 return;
4523 }
4524
4525 let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE);
4526
4527 self.device.disable_depth();
4528 self.set_blend(false, FramebufferKind::Main);
4529
4530 for surface in external_surfaces {
4531 // See if this surface needs to be updated
4532 let (native_surface_id, surface_size) = match surface.update_params {
4533 Some(params) => params,
4534 None => continue,
4535 };
4536
4537 // When updating an external surface, the entire surface rect is used
4538 // for all of the draw, dirty, valid and clip rect parameters.
4539 let surface_rect = surface_size.into();
4540
4541 // Bind the native compositor surface to update
4542 let surface_info = self.compositor_config
4543 .compositor()
4544 .unwrap()
4545 .bind(
4546 NativeTileId {
4547 surface_id: native_surface_id,
4548 x: 0,
4549 y: 0,
4550 },
4551 surface_rect,
4552 surface_rect,
4553 );
4554
4555 // Bind the native surface to current FBO target
4556 let draw_target = DrawTarget::NativeSurface {
4557 offset: surface_info.origin,
4558 external_fbo_id: surface_info.fbo_id,
4559 dimensions: surface_size,
4560 };
4561 self.device.bind_draw_target(draw_target);
4562
4563 let projection = Transform3D::ortho(
4564 0.0,
4565 surface_size.width as f32,
4566 0.0,
4567 surface_size.height as f32,
4568 self.device.ortho_near_plane(),
4569 self.device.ortho_far_plane(),
4570 );
4571
4572 let ( textures, instance ) = match surface.color_data {
4573 ResolvedExternalSurfaceColorData::Yuv{
4574 ref planes, color_space, format, rescale, .. } => {
4575
4576 // Bind an appropriate YUV shader for the texture format kind
4577 self.shaders
4578 .borrow_mut()
4579 .get_composite_shader(
4580 CompositeSurfaceFormat::Yuv,
4581 surface.image_buffer_kind,
4582 ).bind(
4583 &mut self.device,
4584 &projection,
4585 &mut self.renderer_errors
4586 );
4587
4588 let textures = BatchTextures {
4589 colors: [
4590 planes[0].texture,
4591 planes[1].texture,
4592 planes[2].texture,
4593 ],
4594 };
4595
4596 // When the texture is an external texture, the UV rect is not known when
4597 // the external surface descriptor is created, because external textures
4598 // are not resolved until the lock() callback is invoked at the start of
4599 // the frame render. To handle this, query the texture resolver for the
4600 // UV rect if it's an external texture, otherwise use the default UV rect.
4601 let uv_rects = [
4602 self.texture_resolver.get_uv_rect(&textures.colors[0], planes[0].uv_rect),
4603 self.texture_resolver.get_uv_rect(&textures.colors[1], planes[1].uv_rect),
4604 self.texture_resolver.get_uv_rect(&textures.colors[2], planes[2].uv_rect),
4605 ];
4606
4607 let instance = CompositeInstance::new_yuv(
4608 surface_rect.to_f32(),
4609 surface_rect.to_f32(),
4610 // z-id is not relevant when updating a native compositor surface.
4611 // TODO(gw): Support compositor surfaces without z-buffer, for memory / perf win here.
4612 ZBufferId(0),
4613 color_space,
4614 format,
4615 rescale,
4616 [
4617 planes[0].texture_layer as f32,
4618 planes[1].texture_layer as f32,
4619 planes[2].texture_layer as f32,
4620 ],
4621 uv_rects,
4622 );
4623
4624 ( textures, instance )
4625 },
4626 ResolvedExternalSurfaceColorData::Rgb{ ref plane, flip_y, .. } => {
4627
4628 self.shaders
4629 .borrow_mut()
4630 .get_composite_shader(
4631 CompositeSurfaceFormat::Rgba,
4632 surface.image_buffer_kind,
4633 ).bind(
4634 &mut self.device,
4635 &projection,
4636 &mut self.renderer_errors
4637 );
4638
4639 let textures = BatchTextures::color(plane.texture);
4640 let mut uv_rect = self.texture_resolver.get_uv_rect(&textures.colors[0], plane.uv_rect);
4641 if flip_y {
4642 let y = uv_rect.uv0.y;
4643 uv_rect.uv0.y = uv_rect.uv1.y;
4644 uv_rect.uv1.y = y;
4645 }
4646
4647 let instance = CompositeInstance::new_rgb(
4648 surface_rect.to_f32(),
4649 surface_rect.to_f32(),
4650 PremultipliedColorF::WHITE,
4651 plane.texture_layer as f32,
4652 ZBufferId(0),
4653 uv_rect,
4654 );
4655
4656 ( textures, instance )
4657 },
4658 };
4659
4660 self.draw_instanced_batch(
4661 &[instance],
4662 VertexArrayKind::Composite,
4663 &textures,
4664 &mut results.stats,
4665 );
4666
4667 self.compositor_config
4668 .compositor()
4669 .unwrap()
4670 .unbind();
4671 }
4672
4673 self.gpu_profile.finish_sampler(opaque_sampler);
4674 }
4675
4676 /// Draw a list of tiles to the framebuffer
draw_tile_list<'a, I: Iterator<Item = &'a CompositeTile>>( &mut self, tiles_iter: I, external_surfaces: &[ResolvedExternalSurface], projection: &default::Transform3D<f32>, partial_present_mode: Option<PartialPresentMode>, stats: &mut RendererStats, )4677 fn draw_tile_list<'a, I: Iterator<Item = &'a CompositeTile>>(
4678 &mut self,
4679 tiles_iter: I,
4680 external_surfaces: &[ResolvedExternalSurface],
4681 projection: &default::Transform3D<f32>,
4682 partial_present_mode: Option<PartialPresentMode>,
4683 stats: &mut RendererStats,
4684 ) {
4685 self.shaders
4686 .borrow_mut()
4687 .get_composite_shader(
4688 CompositeSurfaceFormat::Rgba,
4689 ImageBufferKind::Texture2DArray,
4690 ).bind(
4691 &mut self.device,
4692 projection,
4693 &mut self.renderer_errors
4694 );
4695
4696 let mut current_shader_params = (CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2DArray);
4697 let mut current_textures = BatchTextures::no_texture();
4698 let mut instances = Vec::new();
4699
4700 for tile in tiles_iter {
4701 // Determine a clip rect to apply to this tile, depending on what
4702 // the partial present mode is.
4703 let partial_clip_rect = match partial_present_mode {
4704 Some(PartialPresentMode::Single { dirty_rect }) => dirty_rect,
4705 None => tile.rect,
4706 };
4707
4708 let clip_rect = match partial_clip_rect.intersection(&tile.clip_rect) {
4709 Some(rect) => rect,
4710 None => continue,
4711 };
4712
4713 // Simple compositor needs the valid rect in device space to match clip rect
4714 let valid_device_rect = tile.valid_rect.translate(
4715 tile.rect.origin.to_vector()
4716 );
4717
4718 // Only composite the part of the tile that contains valid pixels
4719 let clip_rect = match clip_rect.intersection(&valid_device_rect) {
4720 Some(rect) => rect,
4721 None => continue,
4722 };
4723
4724 // Work out the draw params based on the tile surface
4725 let (instance, textures, shader_params) = match tile.surface {
4726 CompositeTileSurface::Color { color } => {
4727 (
4728 CompositeInstance::new(
4729 tile.rect,
4730 clip_rect,
4731 color.premultiplied(),
4732 0.0,
4733 tile.z_id,
4734 ),
4735 BatchTextures::color(TextureSource::Dummy),
4736 (CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2DArray),
4737 )
4738 }
4739 CompositeTileSurface::Clear => {
4740 (
4741 CompositeInstance::new(
4742 tile.rect,
4743 clip_rect,
4744 PremultipliedColorF::BLACK,
4745 0.0,
4746 tile.z_id,
4747 ),
4748 BatchTextures::color(TextureSource::Dummy),
4749 (CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2DArray),
4750 )
4751 }
4752 CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::TextureCache { texture, layer } } => {
4753 (
4754 CompositeInstance::new(
4755 tile.rect,
4756 clip_rect,
4757 PremultipliedColorF::WHITE,
4758 layer as f32,
4759 tile.z_id,
4760 ),
4761 BatchTextures::color(texture),
4762 (CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2DArray),
4763 )
4764 }
4765 CompositeTileSurface::ExternalSurface { external_surface_index } => {
4766 let surface = &external_surfaces[external_surface_index.0];
4767
4768 match surface.color_data {
4769 ResolvedExternalSurfaceColorData::Yuv{ ref planes, color_space, format, rescale, .. } => {
4770
4771 let textures = BatchTextures {
4772 colors: [
4773 planes[0].texture,
4774 planes[1].texture,
4775 planes[2].texture,
4776 ],
4777 };
4778
4779 // When the texture is an external texture, the UV rect is not known when
4780 // the external surface descriptor is created, because external textures
4781 // are not resolved until the lock() callback is invoked at the start of
4782 // the frame render. To handle this, query the texture resolver for the
4783 // UV rect if it's an external texture, otherwise use the default UV rect.
4784 let uv_rects = [
4785 self.texture_resolver.get_uv_rect(&textures.colors[0], planes[0].uv_rect),
4786 self.texture_resolver.get_uv_rect(&textures.colors[1], planes[1].uv_rect),
4787 self.texture_resolver.get_uv_rect(&textures.colors[2], planes[2].uv_rect),
4788 ];
4789
4790 (
4791 CompositeInstance::new_yuv(
4792 tile.rect,
4793 clip_rect,
4794 tile.z_id,
4795 color_space,
4796 format,
4797 rescale,
4798 [
4799 planes[0].texture_layer as f32,
4800 planes[1].texture_layer as f32,
4801 planes[2].texture_layer as f32,
4802 ],
4803 uv_rects,
4804 ),
4805 textures,
4806 (CompositeSurfaceFormat::Yuv, surface.image_buffer_kind),
4807 )
4808 },
4809 ResolvedExternalSurfaceColorData::Rgb{ ref plane, flip_y, .. } => {
4810
4811 let mut uv_rect = self.texture_resolver.get_uv_rect(&plane.texture, plane.uv_rect);
4812 if flip_y {
4813 let y = uv_rect.uv0.y;
4814 uv_rect.uv0.y = uv_rect.uv1.y;
4815 uv_rect.uv1.y = y;
4816 }
4817
4818 (
4819 CompositeInstance::new_rgb(
4820 tile.rect,
4821 clip_rect,
4822 PremultipliedColorF::WHITE,
4823 plane.texture_layer as f32,
4824 tile.z_id,
4825 uv_rect,
4826 ),
4827 BatchTextures::color(plane.texture),
4828 (CompositeSurfaceFormat::Rgba, surface.image_buffer_kind),
4829 )
4830 },
4831 }
4832 }
4833 CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::Native { .. } } => {
4834 unreachable!("bug: found native surface in simple composite path");
4835 }
4836 };
4837
4838 // Flush batch if shader params or textures changed
4839 let flush_batch = !current_textures.is_compatible_with(&textures) ||
4840 shader_params != current_shader_params;
4841
4842 if flush_batch {
4843 if !instances.is_empty() {
4844 self.draw_instanced_batch(
4845 &instances,
4846 VertexArrayKind::Composite,
4847 ¤t_textures,
4848 stats,
4849 );
4850 instances.clear();
4851 }
4852 }
4853
4854 if shader_params != current_shader_params {
4855 self.shaders
4856 .borrow_mut()
4857 .get_composite_shader(shader_params.0, shader_params.1)
4858 .bind(
4859 &mut self.device,
4860 projection,
4861 &mut self.renderer_errors
4862 );
4863
4864 current_shader_params = shader_params;
4865 }
4866
4867 current_textures = textures;
4868
4869 // Add instance to current batch
4870 instances.push(instance);
4871 }
4872
4873 // Flush the last batch
4874 if !instances.is_empty() {
4875 self.draw_instanced_batch(
4876 &instances,
4877 VertexArrayKind::Composite,
4878 ¤t_textures,
4879 stats,
4880 );
4881 }
4882 }
4883
4884 /// Composite picture cache tiles into the framebuffer. This is currently
4885 /// the only way that picture cache tiles get drawn. In future, the tiles
4886 /// will often be handed to the OS compositor, and this method will be
4887 /// rarely used.
composite_simple( &mut self, composite_state: &CompositeState, clear_framebuffer: bool, draw_target: DrawTarget, projection: &default::Transform3D<f32>, results: &mut RenderResults, max_partial_present_rects: usize, draw_previous_partial_present_regions: bool, )4888 fn composite_simple(
4889 &mut self,
4890 composite_state: &CompositeState,
4891 clear_framebuffer: bool,
4892 draw_target: DrawTarget,
4893 projection: &default::Transform3D<f32>,
4894 results: &mut RenderResults,
4895 max_partial_present_rects: usize,
4896 draw_previous_partial_present_regions: bool,
4897 ) {
4898 let _gm = self.gpu_profile.start_marker("framebuffer");
4899 let _timer = self.gpu_profile.start_timer(GPU_TAG_COMPOSITE);
4900
4901 self.device.bind_draw_target(draw_target);
4902 self.device.enable_depth();
4903 self.device.enable_depth_write();
4904
4905 // Determine the partial present mode for this frame, which is used during
4906 // framebuffer clears and calculating the clip rect for each tile that is drawn.
4907 let mut partial_present_mode = None;
4908
4909 if max_partial_present_rects > 0 {
4910 // We can only use partial present if we have valid dirty rects and the
4911 // client hasn't reset partial present state since last frame.
4912 if composite_state.dirty_rects_are_valid && !self.force_redraw {
4913 let mut combined_dirty_rect = DeviceRect::zero();
4914
4915 // Work out how many dirty rects WR produced, and if that's more than
4916 // what the device supports.
4917 for tile in composite_state.opaque_tiles.iter().chain(composite_state.alpha_tiles.iter()) {
4918 let dirty_rect = tile.dirty_rect.translate(tile.rect.origin.to_vector());
4919 combined_dirty_rect = combined_dirty_rect.union(&dirty_rect);
4920 }
4921
4922 let combined_dirty_rect = combined_dirty_rect.round();
4923 let combined_dirty_rect_i32 = combined_dirty_rect.to_i32();
4924 // If nothing has changed, don't return any dirty rects at all (the client
4925 // can use this as a signal to skip present completely).
4926 if !combined_dirty_rect.is_empty() {
4927 results.dirty_rects.push(combined_dirty_rect_i32);
4928 }
4929
4930 // If the implementation requires manually keeping the buffer consistent,
4931 // combine the previous frame's damage for tile clipping.
4932 // (Not for the returned region though, that should be from this frame only)
4933 partial_present_mode = Some(PartialPresentMode::Single {
4934 dirty_rect: if draw_previous_partial_present_regions {
4935 combined_dirty_rect.union(&self.prev_dirty_rect)
4936 } else { combined_dirty_rect },
4937 });
4938
4939 if draw_previous_partial_present_regions {
4940 self.prev_dirty_rect = combined_dirty_rect;
4941 }
4942 } else {
4943 // If we don't have a valid partial present scenario, return a single
4944 // dirty rect to the client that covers the entire framebuffer.
4945 let fb_rect = DeviceIntRect::new(
4946 DeviceIntPoint::zero(),
4947 draw_target.dimensions(),
4948 );
4949 results.dirty_rects.push(fb_rect);
4950
4951 if draw_previous_partial_present_regions {
4952 self.prev_dirty_rect = fb_rect.to_f32();
4953 }
4954 }
4955
4956 self.force_redraw = false;
4957 }
4958
4959 // Clear the framebuffer, if required
4960 if clear_framebuffer {
4961 let clear_color = self.clear_color.map(|color| color.to_array());
4962
4963 match partial_present_mode {
4964 Some(PartialPresentMode::Single { dirty_rect }) => {
4965 // We have a single dirty rect, so clear only that
4966 self.device.clear_target(clear_color,
4967 Some(1.0),
4968 Some(draw_target.to_framebuffer_rect(dirty_rect.to_i32())));
4969 }
4970 None => {
4971 // Partial present is disabled, so clear the entire framebuffer
4972 self.device.clear_target(clear_color,
4973 Some(1.0),
4974 None);
4975 }
4976 }
4977 }
4978
4979 // We are only interested in tiles backed with actual cached pixels so we don't
4980 // count clear tiles here.
4981 let num_tiles = composite_state.opaque_tiles.len()
4982 + composite_state.alpha_tiles.len();
4983 self.profile_counters.total_picture_cache_tiles.set(num_tiles);
4984
4985 // Draw opaque tiles first, front-to-back to get maxmum
4986 // z-reject efficiency.
4987 if !composite_state.opaque_tiles.is_empty() {
4988 let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE);
4989 self.device.enable_depth_write();
4990 self.set_blend(false, FramebufferKind::Main);
4991 self.draw_tile_list(
4992 composite_state.opaque_tiles.iter().rev(),
4993 &composite_state.external_surfaces,
4994 projection,
4995 partial_present_mode,
4996 &mut results.stats,
4997 );
4998 self.gpu_profile.finish_sampler(opaque_sampler);
4999 }
5000
5001 if !composite_state.clear_tiles.is_empty() {
5002 let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
5003 self.device.disable_depth_write();
5004 self.set_blend(true, FramebufferKind::Main);
5005 self.device.set_blend_mode_premultiplied_dest_out();
5006 self.draw_tile_list(
5007 composite_state.clear_tiles.iter(),
5008 &composite_state.external_surfaces,
5009 projection,
5010 partial_present_mode,
5011 &mut results.stats,
5012 );
5013 self.gpu_profile.finish_sampler(transparent_sampler);
5014 }
5015
5016 // Draw alpha tiles
5017 if !composite_state.alpha_tiles.is_empty() {
5018 let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
5019 self.device.disable_depth_write();
5020 self.set_blend(true, FramebufferKind::Main);
5021 self.set_blend_mode_premultiplied_alpha(FramebufferKind::Main);
5022 self.draw_tile_list(
5023 composite_state.alpha_tiles.iter(),
5024 &composite_state.external_surfaces,
5025 projection,
5026 partial_present_mode,
5027 &mut results.stats,
5028 );
5029 self.gpu_profile.finish_sampler(transparent_sampler);
5030 }
5031 }
5032
draw_color_target( &mut self, draw_target: DrawTarget, target: &ColorRenderTarget, content_origin: DeviceIntPoint, clear_color: Option<[f32; 4]>, clear_depth: Option<f32>, render_tasks: &RenderTaskGraph, projection: &default::Transform3D<f32>, frame_id: GpuFrameId, stats: &mut RendererStats, )5033 fn draw_color_target(
5034 &mut self,
5035 draw_target: DrawTarget,
5036 target: &ColorRenderTarget,
5037 content_origin: DeviceIntPoint,
5038 clear_color: Option<[f32; 4]>,
5039 clear_depth: Option<f32>,
5040 render_tasks: &RenderTaskGraph,
5041 projection: &default::Transform3D<f32>,
5042 frame_id: GpuFrameId,
5043 stats: &mut RendererStats,
5044 ) {
5045 profile_scope!("draw_color_target");
5046
5047 self.profile_counters.color_passes.inc();
5048 let _gm = self.gpu_profile.start_marker("color target");
5049
5050 // sanity check for the depth buffer
5051 if let DrawTarget::Texture { with_depth, .. } = draw_target {
5052 assert!(with_depth >= target.needs_depth());
5053 }
5054
5055 let framebuffer_kind = if draw_target.is_default() {
5056 FramebufferKind::Main
5057 } else {
5058 FramebufferKind::Other
5059 };
5060
5061 {
5062 let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_TARGET);
5063 self.device.bind_draw_target(draw_target);
5064 self.device.disable_depth();
5065 self.set_blend(false, framebuffer_kind);
5066
5067 if clear_depth.is_some() {
5068 self.device.enable_depth_write();
5069 }
5070
5071 let clear_rect = match draw_target {
5072 DrawTarget::NativeSurface { .. } => {
5073 unreachable!("bug: native compositor surface in child target");
5074 }
5075 DrawTarget::Default { rect, total_size, .. } if rect.origin == FramebufferIntPoint::zero() && rect.size == total_size => {
5076 // whole screen is covered, no need for scissor
5077 None
5078 }
5079 DrawTarget::Default { rect, .. } => {
5080 Some(rect)
5081 }
5082 DrawTarget::Texture { .. } if self.enable_clear_scissor => {
5083 // TODO(gw): Applying a scissor rect and minimal clear here
5084 // is a very large performance win on the Intel and nVidia
5085 // GPUs that I have tested with. It's possible it may be a
5086 // performance penalty on other GPU types - we should test this
5087 // and consider different code paths.
5088 //
5089 // Note: The above measurements were taken when render
5090 // target slices were minimum 2048x2048. Now that we size
5091 // them adaptively, this may be less of a win (except perhaps
5092 // on a mostly-unused last slice of a large texture array).
5093 Some(draw_target.to_framebuffer_rect(target.used_rect()))
5094 }
5095 DrawTarget::Texture { .. } | DrawTarget::External { .. } => {
5096 None
5097 }
5098 };
5099
5100 self.device.clear_target(
5101 clear_color,
5102 clear_depth,
5103 clear_rect,
5104 );
5105
5106 if clear_depth.is_some() {
5107 self.device.disable_depth_write();
5108 }
5109 }
5110
5111 // Handle any blits from the texture cache to this target.
5112 self.handle_blits(
5113 &target.blits, render_tasks, draw_target, &content_origin,
5114 );
5115
5116 // Draw any blurs for this target.
5117 // Blurs are rendered as a standard 2-pass
5118 // separable implementation.
5119 // TODO(gw): In the future, consider having
5120 // fast path blur shaders for common
5121 // blur radii with fixed weights.
5122 if !target.vertical_blurs.is_empty() || !target.horizontal_blurs.is_empty() {
5123 let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR);
5124
5125 self.set_blend(false, framebuffer_kind);
5126 self.shaders.borrow_mut().cs_blur_rgba8
5127 .bind(&mut self.device, projection, &mut self.renderer_errors);
5128
5129 if !target.vertical_blurs.is_empty() {
5130 self.draw_instanced_batch(
5131 &target.vertical_blurs,
5132 VertexArrayKind::Blur,
5133 &BatchTextures::no_texture(),
5134 stats,
5135 );
5136 }
5137
5138 if !target.horizontal_blurs.is_empty() {
5139 self.draw_instanced_batch(
5140 &target.horizontal_blurs,
5141 VertexArrayKind::Blur,
5142 &BatchTextures::no_texture(),
5143 stats,
5144 );
5145 }
5146 }
5147
5148 self.handle_scaling(
5149 &target.scalings,
5150 projection,
5151 stats,
5152 );
5153
5154 for (ref textures, ref filters) in &target.svg_filters {
5155 self.handle_svg_filters(
5156 textures,
5157 filters,
5158 projection,
5159 stats,
5160 );
5161 }
5162
5163 for alpha_batch_container in &target.alpha_batch_containers {
5164 self.draw_alpha_batch_container(
5165 alpha_batch_container,
5166 draw_target,
5167 content_origin,
5168 framebuffer_kind,
5169 projection,
5170 render_tasks,
5171 stats,
5172 );
5173 }
5174
5175 // For any registered image outputs on this render target,
5176 // get the texture from caller and blit it.
5177 for output in &target.outputs {
5178 let handler = self.output_image_handler
5179 .as_mut()
5180 .expect("Found output image, but no handler set!");
5181 if let Some((texture_id, output_size)) = handler.lock(output.pipeline_id) {
5182 let fbo_id = match self.output_targets.entry(texture_id) {
5183 Entry::Vacant(entry) => {
5184 let fbo_id = self.device.create_fbo_for_external_texture(texture_id);
5185 entry.insert(FrameOutput {
5186 fbo_id,
5187 last_access: frame_id,
5188 });
5189 fbo_id
5190 }
5191 Entry::Occupied(mut entry) => {
5192 let target = entry.get_mut();
5193 target.last_access = frame_id;
5194 target.fbo_id
5195 }
5196 };
5197 let (src_rect, _) = render_tasks[output.task_id].get_target_rect();
5198 if !self.device.surface_origin_is_top_left() {
5199 self.device.blit_render_target_invert_y(
5200 draw_target.into(),
5201 draw_target.to_framebuffer_rect(src_rect.translate(-content_origin.to_vector())),
5202 DrawTarget::External { fbo: fbo_id, size: output_size },
5203 output_size.into(),
5204 );
5205 } else {
5206 self.device.blit_render_target(
5207 draw_target.into(),
5208 draw_target.to_framebuffer_rect(src_rect.translate(-content_origin.to_vector())),
5209 DrawTarget::External { fbo: fbo_id, size: output_size },
5210 output_size.into(),
5211 TextureFilter::Linear,
5212 );
5213 }
5214 handler.unlock(output.pipeline_id);
5215 }
5216 }
5217 }
5218
5219 /// Draw all the instances in a clip batcher list to the current target.
draw_clip_batch_list( &mut self, list: &ClipBatchList, projection: &default::Transform3D<f32>, stats: &mut RendererStats, )5220 fn draw_clip_batch_list(
5221 &mut self,
5222 list: &ClipBatchList,
5223 projection: &default::Transform3D<f32>,
5224 stats: &mut RendererStats,
5225 ) {
5226 if self.debug_flags.contains(DebugFlags::DISABLE_CLIP_MASKS) {
5227 return;
5228 }
5229
5230 // draw rounded cornered rectangles
5231 if !list.slow_rectangles.is_empty() {
5232 let _gm2 = self.gpu_profile.start_marker("slow clip rectangles");
5233 self.shaders.borrow_mut().cs_clip_rectangle_slow.bind(
5234 &mut self.device,
5235 projection,
5236 &mut self.renderer_errors,
5237 );
5238 self.draw_instanced_batch(
5239 &list.slow_rectangles,
5240 VertexArrayKind::Clip,
5241 &BatchTextures::no_texture(),
5242 stats,
5243 );
5244 }
5245 if !list.fast_rectangles.is_empty() {
5246 let _gm2 = self.gpu_profile.start_marker("fast clip rectangles");
5247 self.shaders.borrow_mut().cs_clip_rectangle_fast.bind(
5248 &mut self.device,
5249 projection,
5250 &mut self.renderer_errors,
5251 );
5252 self.draw_instanced_batch(
5253 &list.fast_rectangles,
5254 VertexArrayKind::Clip,
5255 &BatchTextures::no_texture(),
5256 stats,
5257 );
5258 }
5259 // draw box-shadow clips
5260 for (mask_texture_id, items) in list.box_shadows.iter() {
5261 let _gm2 = self.gpu_profile.start_marker("box-shadows");
5262 let textures = BatchTextures {
5263 colors: [
5264 *mask_texture_id,
5265 TextureSource::Invalid,
5266 TextureSource::Invalid,
5267 ],
5268 };
5269 self.shaders.borrow_mut().cs_clip_box_shadow
5270 .bind(&mut self.device, projection, &mut self.renderer_errors);
5271 self.draw_instanced_batch(
5272 items,
5273 VertexArrayKind::Clip,
5274 &textures,
5275 stats,
5276 );
5277 }
5278
5279 // draw image masks
5280 for (mask_texture_id, items) in list.images.iter() {
5281 let _gm2 = self.gpu_profile.start_marker("clip images");
5282 let textures = BatchTextures {
5283 colors: [
5284 *mask_texture_id,
5285 TextureSource::Invalid,
5286 TextureSource::Invalid,
5287 ],
5288 };
5289 self.shaders.borrow_mut().cs_clip_image
5290 .bind(&mut self.device, projection, &mut self.renderer_errors);
5291 self.draw_instanced_batch(
5292 items,
5293 VertexArrayKind::Clip,
5294 &textures,
5295 stats,
5296 );
5297 }
5298 }
5299
draw_alpha_target( &mut self, draw_target: DrawTarget, target: &AlphaRenderTarget, projection: &default::Transform3D<f32>, render_tasks: &RenderTaskGraph, stats: &mut RendererStats, )5300 fn draw_alpha_target(
5301 &mut self,
5302 draw_target: DrawTarget,
5303 target: &AlphaRenderTarget,
5304 projection: &default::Transform3D<f32>,
5305 render_tasks: &RenderTaskGraph,
5306 stats: &mut RendererStats,
5307 ) {
5308 profile_scope!("draw_alpha_target");
5309
5310 self.profile_counters.alpha_passes.inc();
5311 let _gm = self.gpu_profile.start_marker("alpha target");
5312 let alpha_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_ALPHA);
5313
5314 {
5315 let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_TARGET);
5316 self.device.bind_draw_target(draw_target);
5317 self.device.disable_depth();
5318 self.device.disable_depth_write();
5319 self.set_blend(false, FramebufferKind::Other);
5320
5321 // TODO(gw): Applying a scissor rect and minimal clear here
5322 // is a very large performance win on the Intel and nVidia
5323 // GPUs that I have tested with. It's possible it may be a
5324 // performance penalty on other GPU types - we should test this
5325 // and consider different code paths.
5326
5327 let zero_color = [0.0, 0.0, 0.0, 0.0];
5328 for &task_id in &target.zero_clears {
5329 let (rect, _) = render_tasks[task_id].get_target_rect();
5330 self.device.clear_target(
5331 Some(zero_color),
5332 None,
5333 Some(draw_target.to_framebuffer_rect(rect)),
5334 );
5335 }
5336
5337 let one_color = [1.0, 1.0, 1.0, 1.0];
5338 for &task_id in &target.one_clears {
5339 let (rect, _) = render_tasks[task_id].get_target_rect();
5340 self.device.clear_target(
5341 Some(one_color),
5342 None,
5343 Some(draw_target.to_framebuffer_rect(rect)),
5344 );
5345 }
5346 }
5347
5348 // Draw any blurs for this target.
5349 // Blurs are rendered as a standard 2-pass
5350 // separable implementation.
5351 // TODO(gw): In the future, consider having
5352 // fast path blur shaders for common
5353 // blur radii with fixed weights.
5354 if !target.vertical_blurs.is_empty() || !target.horizontal_blurs.is_empty() {
5355 let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR);
5356
5357 self.shaders.borrow_mut().cs_blur_a8
5358 .bind(&mut self.device, projection, &mut self.renderer_errors);
5359
5360 if !target.vertical_blurs.is_empty() {
5361 self.draw_instanced_batch(
5362 &target.vertical_blurs,
5363 VertexArrayKind::Blur,
5364 &BatchTextures::no_texture(),
5365 stats,
5366 );
5367 }
5368
5369 if !target.horizontal_blurs.is_empty() {
5370 self.draw_instanced_batch(
5371 &target.horizontal_blurs,
5372 VertexArrayKind::Blur,
5373 &BatchTextures::no_texture(),
5374 stats,
5375 );
5376 }
5377 }
5378
5379 self.handle_scaling(
5380 &target.scalings,
5381 projection,
5382 stats,
5383 );
5384
5385 // Draw the clip items into the tiled alpha mask.
5386 {
5387 let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_CLIP);
5388
5389 // TODO(gw): Consider grouping multiple clip masks per shader
5390 // invocation here to reduce memory bandwith further?
5391
5392 // Draw the primary clip mask - since this is the first mask
5393 // for the task, we can disable blending, knowing that it will
5394 // overwrite every pixel in the mask area.
5395 self.set_blend(false, FramebufferKind::Other);
5396 self.draw_clip_batch_list(
5397 &target.clip_batcher.primary_clips,
5398 projection,
5399 stats,
5400 );
5401
5402 // switch to multiplicative blending for secondary masks, using
5403 // multiplicative blending to accumulate clips into the mask.
5404 self.set_blend(true, FramebufferKind::Other);
5405 self.set_blend_mode_multiply(FramebufferKind::Other);
5406 self.draw_clip_batch_list(
5407 &target.clip_batcher.secondary_clips,
5408 projection,
5409 stats,
5410 );
5411 }
5412
5413 self.gpu_profile.finish_sampler(alpha_sampler);
5414 }
5415
draw_texture_cache_target( &mut self, texture: &CacheTextureId, layer: LayerIndex, target: &TextureCacheRenderTarget, render_tasks: &RenderTaskGraph, stats: &mut RendererStats, )5416 fn draw_texture_cache_target(
5417 &mut self,
5418 texture: &CacheTextureId,
5419 layer: LayerIndex,
5420 target: &TextureCacheRenderTarget,
5421 render_tasks: &RenderTaskGraph,
5422 stats: &mut RendererStats,
5423 ) {
5424 profile_scope!("draw_texture_cache_target");
5425
5426 let texture_source = TextureSource::TextureCache(*texture, Swizzle::default());
5427 let projection = {
5428 let (texture, _) = self.texture_resolver
5429 .resolve(&texture_source)
5430 .expect("BUG: invalid target texture");
5431 let target_size = texture.get_dimensions();
5432
5433 Transform3D::ortho(
5434 0.0,
5435 target_size.width as f32,
5436 0.0,
5437 target_size.height as f32,
5438 self.device.ortho_near_plane(),
5439 self.device.ortho_far_plane(),
5440 )
5441 };
5442
5443 self.device.disable_depth();
5444 self.device.disable_depth_write();
5445
5446 self.set_blend(false, FramebufferKind::Other);
5447
5448 {
5449 let (texture, _) = self.texture_resolver
5450 .resolve(&texture_source)
5451 .expect("BUG: invalid target texture");
5452 let draw_target = DrawTarget::from_texture(
5453 texture,
5454 layer,
5455 false,
5456 );
5457 self.device.bind_draw_target(draw_target);
5458
5459 self.device.disable_depth();
5460 self.device.disable_depth_write();
5461 self.set_blend(false, FramebufferKind::Other);
5462
5463 for rect in &target.clears {
5464 self.device.clear_target(
5465 Some([0.0, 0.0, 0.0, 0.0]),
5466 None,
5467 Some(draw_target.to_framebuffer_rect(*rect)),
5468 );
5469 }
5470
5471 // Handle any blits to this texture from child tasks.
5472 self.handle_blits(
5473 &target.blits, render_tasks, draw_target, &DeviceIntPoint::zero(),
5474 );
5475 }
5476
5477 // Draw any borders for this target.
5478 if !target.border_segments_solid.is_empty() ||
5479 !target.border_segments_complex.is_empty()
5480 {
5481 let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_BORDER);
5482
5483 self.set_blend(true, FramebufferKind::Other);
5484 self.set_blend_mode_premultiplied_alpha(FramebufferKind::Other);
5485
5486 if !target.border_segments_solid.is_empty() {
5487 self.shaders.borrow_mut().cs_border_solid.bind(
5488 &mut self.device,
5489 &projection,
5490 &mut self.renderer_errors,
5491 );
5492
5493 self.draw_instanced_batch(
5494 &target.border_segments_solid,
5495 VertexArrayKind::Border,
5496 &BatchTextures::no_texture(),
5497 stats,
5498 );
5499 }
5500
5501 if !target.border_segments_complex.is_empty() {
5502 self.shaders.borrow_mut().cs_border_segment.bind(
5503 &mut self.device,
5504 &projection,
5505 &mut self.renderer_errors,
5506 );
5507
5508 self.draw_instanced_batch(
5509 &target.border_segments_complex,
5510 VertexArrayKind::Border,
5511 &BatchTextures::no_texture(),
5512 stats,
5513 );
5514 }
5515
5516 self.set_blend(false, FramebufferKind::Other);
5517 }
5518
5519 // Draw any line decorations for this target.
5520 if !target.line_decorations.is_empty() {
5521 let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_LINE_DECORATION);
5522
5523 self.set_blend(true, FramebufferKind::Other);
5524 self.set_blend_mode_premultiplied_alpha(FramebufferKind::Other);
5525
5526 self.shaders.borrow_mut().cs_line_decoration.bind(
5527 &mut self.device,
5528 &projection,
5529 &mut self.renderer_errors,
5530 );
5531
5532 self.draw_instanced_batch(
5533 &target.line_decorations,
5534 VertexArrayKind::LineDecoration,
5535 &BatchTextures::no_texture(),
5536 stats,
5537 );
5538
5539 self.set_blend(false, FramebufferKind::Other);
5540 }
5541
5542 // Draw any gradients for this target.
5543 if !target.gradients.is_empty() {
5544 let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_GRADIENT);
5545
5546 self.set_blend(false, FramebufferKind::Other);
5547
5548 self.shaders.borrow_mut().cs_gradient.bind(
5549 &mut self.device,
5550 &projection,
5551 &mut self.renderer_errors,
5552 );
5553
5554 self.draw_instanced_batch(
5555 &target.gradients,
5556 VertexArrayKind::Gradient,
5557 &BatchTextures::no_texture(),
5558 stats,
5559 );
5560 }
5561
5562 // Draw any blurs for this target.
5563 if !target.horizontal_blurs.is_empty() {
5564 let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR);
5565
5566 {
5567 let mut shaders = self.shaders.borrow_mut();
5568 match target.target_kind {
5569 RenderTargetKind::Alpha => &mut shaders.cs_blur_a8,
5570 RenderTargetKind::Color => &mut shaders.cs_blur_rgba8,
5571 }.bind(&mut self.device, &projection, &mut self.renderer_errors);
5572 }
5573
5574 self.draw_instanced_batch(
5575 &target.horizontal_blurs,
5576 VertexArrayKind::Blur,
5577 &BatchTextures::no_texture(),
5578 stats,
5579 );
5580 }
5581 }
5582
update_deferred_resolves(&mut self, deferred_resolves: &[DeferredResolve]) -> Option<GpuCacheUpdateList>5583 fn update_deferred_resolves(&mut self, deferred_resolves: &[DeferredResolve]) -> Option<GpuCacheUpdateList> {
5584 // The first thing we do is run through any pending deferred
5585 // resolves, and use a callback to get the UV rect for this
5586 // custom item. Then we patch the resource_rects structure
5587 // here before it's uploaded to the GPU.
5588 if deferred_resolves.is_empty() {
5589 return None;
5590 }
5591
5592 let handler = self.external_image_handler
5593 .as_mut()
5594 .expect("Found external image, but no handler set!");
5595
5596 let mut list = GpuCacheUpdateList {
5597 frame_id: FrameId::INVALID,
5598 clear: false,
5599 height: self.gpu_cache_texture.get_height(),
5600 blocks: Vec::new(),
5601 updates: Vec::new(),
5602 debug_commands: Vec::new(),
5603 };
5604
5605 for deferred_resolve in deferred_resolves {
5606 self.gpu_profile.place_marker("deferred resolve");
5607 let props = &deferred_resolve.image_properties;
5608 let ext_image = props
5609 .external_image
5610 .expect("BUG: Deferred resolves must be external images!");
5611 // Provide rendering information for NativeTexture external images.
5612 let image = handler.lock(ext_image.id, ext_image.channel_index, deferred_resolve.rendering);
5613 let texture_target = match ext_image.image_type {
5614 ExternalImageType::TextureHandle(target) => target,
5615 ExternalImageType::Buffer => {
5616 panic!("not a suitable image type in update_deferred_resolves()");
5617 }
5618 };
5619
5620 // In order to produce the handle, the external image handler may call into
5621 // the GL context and change some states.
5622 self.device.reset_state();
5623
5624 let texture = match image.source {
5625 ExternalImageSource::NativeTexture(texture_id) => {
5626 ExternalTexture::new(
5627 texture_id,
5628 texture_target,
5629 Swizzle::default(),
5630 image.uv,
5631 )
5632 }
5633 ExternalImageSource::Invalid => {
5634 warn!("Invalid ext-image");
5635 debug!(
5636 "For ext_id:{:?}, channel:{}.",
5637 ext_image.id,
5638 ext_image.channel_index
5639 );
5640 // Just use 0 as the gl handle for this failed case.
5641 ExternalTexture::new(
5642 0,
5643 texture_target,
5644 Swizzle::default(),
5645 image.uv,
5646 )
5647 }
5648 ExternalImageSource::RawData(_) => {
5649 panic!("Raw external data is not expected for deferred resolves!");
5650 }
5651 };
5652
5653 self.texture_resolver
5654 .external_images
5655 .insert((ext_image.id, ext_image.channel_index), texture);
5656
5657 list.updates.push(GpuCacheUpdate::Copy {
5658 block_index: list.blocks.len(),
5659 block_count: BLOCKS_PER_UV_RECT,
5660 address: deferred_resolve.address,
5661 });
5662 list.blocks.push(image.uv.into());
5663 list.blocks.push([0f32; 4].into());
5664 }
5665
5666 Some(list)
5667 }
5668
unlock_external_images(&mut self)5669 fn unlock_external_images(&mut self) {
5670 if !self.texture_resolver.external_images.is_empty() {
5671 let handler = self.external_image_handler
5672 .as_mut()
5673 .expect("Found external image, but no handler set!");
5674
5675 for (ext_data, _) in self.texture_resolver.external_images.drain() {
5676 handler.unlock(ext_data.0, ext_data.1);
5677 }
5678 }
5679 }
5680
5681 /// Allocates a texture to be used as the output for a rendering pass.
5682 ///
5683 /// We make an effort to reuse render targe textures across passes and
5684 /// across frames when the format and dimensions match. Because we use
5685 /// immutable storage, we can't resize textures.
5686 ///
5687 /// We could consider approaches to re-use part of a larger target, if
5688 /// available. However, we'd need to be careful about eviction. Currently,
5689 /// render targets are freed if they haven't been used in 30 frames. If we
5690 /// used partial targets, we'd need to track how _much_ of the target has
5691 /// been used in the last 30 frames, since we could otherwise end up
5692 /// keeping an enormous target alive indefinitely by constantly using it
5693 /// in situations where a much smaller target would suffice.
allocate_target_texture<T: RenderTarget>( &mut self, list: &mut RenderTargetList<T>, counters: &mut FrameProfileCounters, ) -> Option<ActiveTexture>5694 fn allocate_target_texture<T: RenderTarget>(
5695 &mut self,
5696 list: &mut RenderTargetList<T>,
5697 counters: &mut FrameProfileCounters,
5698 ) -> Option<ActiveTexture> {
5699 if list.targets.is_empty() {
5700 return None
5701 }
5702
5703 // Get a bounding rect of all the layers, and round it up to a multiple
5704 // of 256. This improves render target reuse when resizing the window,
5705 // since we don't need to create a new render target for each slightly-
5706 // larger frame.
5707 let mut bounding_rect = DeviceIntRect::zero();
5708 for t in list.targets.iter() {
5709 bounding_rect = t.used_rect().union(&bounding_rect);
5710 }
5711 debug_assert_eq!(bounding_rect.origin, DeviceIntPoint::zero());
5712 let dimensions = DeviceIntSize::new(
5713 (bounding_rect.size.width + 255) & !255,
5714 (bounding_rect.size.height + 255) & !255,
5715 );
5716
5717 counters.targets_used.inc();
5718
5719 // Try finding a match in the existing pool. If there's no match, we'll
5720 // create a new texture.
5721 let selector = TargetSelector {
5722 size: dimensions,
5723 num_layers: list.targets.len(),
5724 format: list.format,
5725 };
5726 let index = self.texture_resolver.render_target_pool
5727 .iter()
5728 .position(|texture| {
5729 selector == TargetSelector {
5730 size: texture.get_dimensions(),
5731 num_layers: texture.get_layer_count() as usize,
5732 format: texture.get_format(),
5733 }
5734 });
5735
5736 let rt_info = RenderTargetInfo { has_depth: list.needs_depth() };
5737 let texture = if let Some(idx) = index {
5738 let mut t = self.texture_resolver.render_target_pool.swap_remove(idx);
5739 self.device.reuse_render_target::<u8>(&mut t, rt_info);
5740 t
5741 } else {
5742 counters.targets_created.inc();
5743 self.device.create_texture(
5744 TextureTarget::Array,
5745 list.format,
5746 dimensions.width,
5747 dimensions.height,
5748 TextureFilter::Linear,
5749 Some(rt_info),
5750 list.targets.len() as _,
5751 )
5752 };
5753
5754 list.check_ready(&texture);
5755 Some(ActiveTexture {
5756 texture,
5757 saved_index: list.saved_index.clone(),
5758 })
5759 }
5760
bind_frame_data(&mut self, frame: &mut Frame)5761 fn bind_frame_data(&mut self, frame: &mut Frame) {
5762 profile_scope!("bind_frame_data");
5763
5764 let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_DATA);
5765
5766 self.vertex_data_textures[self.current_vertex_data_textures].update(
5767 &mut self.device,
5768 frame,
5769 );
5770 self.current_vertex_data_textures =
5771 (self.current_vertex_data_textures + 1) % VERTEX_DATA_TEXTURE_COUNT;
5772
5773 debug_assert!(self.texture_resolver.prev_pass_alpha.is_none());
5774 debug_assert!(self.texture_resolver.prev_pass_color.is_none());
5775 }
5776
update_native_surfaces(&mut self)5777 fn update_native_surfaces(&mut self) {
5778 profile_scope!("update_native_surfaces");
5779
5780 match self.compositor_config {
5781 CompositorConfig::Native { ref mut compositor, .. } => {
5782 for op in self.pending_native_surface_updates.drain(..) {
5783 match op.details {
5784 NativeSurfaceOperationDetails::CreateSurface { id, virtual_offset, tile_size, is_opaque } => {
5785 let _inserted = self.allocated_native_surfaces.insert(id);
5786 debug_assert!(_inserted, "bug: creating existing surface");
5787 compositor.create_surface(
5788 id,
5789 virtual_offset,
5790 tile_size,
5791 is_opaque,
5792 );
5793 }
5794 NativeSurfaceOperationDetails::DestroySurface { id } => {
5795 let _existed = self.allocated_native_surfaces.remove(&id);
5796 debug_assert!(_existed, "bug: removing unknown surface");
5797 compositor.destroy_surface(id);
5798 }
5799 NativeSurfaceOperationDetails::CreateTile { id } => {
5800 compositor.create_tile(id);
5801 }
5802 NativeSurfaceOperationDetails::DestroyTile { id } => {
5803 compositor.destroy_tile(id);
5804 }
5805 }
5806 }
5807 }
5808 CompositorConfig::Draw { .. } => {
5809 // Ensure nothing is added in simple composite mode, since otherwise
5810 // memory will leak as this doesn't get drained
5811 debug_assert!(self.pending_native_surface_updates.is_empty());
5812 }
5813 }
5814 }
5815
draw_frame( &mut self, frame: &mut Frame, device_size: Option<DeviceIntSize>, frame_id: GpuFrameId, results: &mut RenderResults, clear_framebuffer: bool, )5816 fn draw_frame(
5817 &mut self,
5818 frame: &mut Frame,
5819 device_size: Option<DeviceIntSize>,
5820 frame_id: GpuFrameId,
5821 results: &mut RenderResults,
5822 clear_framebuffer: bool,
5823 ) {
5824 profile_scope!("draw_frame");
5825
5826 // These markers seem to crash a lot on Android, see bug 1559834
5827 #[cfg(not(target_os = "android"))]
5828 let _gm = self.gpu_profile.start_marker("draw frame");
5829
5830 if frame.passes.is_empty() {
5831 frame.has_been_rendered = true;
5832 return;
5833 }
5834
5835 self.device.disable_depth_write();
5836 self.set_blend(false, FramebufferKind::Other);
5837 self.device.disable_stencil();
5838
5839 self.bind_frame_data(frame);
5840
5841 for (_pass_index, pass) in frame.passes.iter_mut().enumerate() {
5842 #[cfg(not(target_os = "android"))]
5843 let _gm = self.gpu_profile.start_marker(&format!("pass {}", _pass_index));
5844
5845 self.texture_resolver.bind(
5846 &TextureSource::PrevPassAlpha,
5847 TextureSampler::PrevPassAlpha,
5848 &mut self.device,
5849 );
5850 self.texture_resolver.bind(
5851 &TextureSource::PrevPassColor,
5852 TextureSampler::PrevPassColor,
5853 &mut self.device,
5854 );
5855
5856 match pass.kind {
5857 RenderPassKind::MainFramebuffer { ref main_target, .. } => {
5858 profile_scope!("main target");
5859
5860 if let Some(device_size) = device_size {
5861 results.stats.color_target_count += 1;
5862
5863 let offset = frame.content_origin.to_f32();
5864 let size = frame.device_rect.size.to_f32();
5865 let surface_origin_is_top_left = self.device.surface_origin_is_top_left();
5866 let (bottom, top) = if surface_origin_is_top_left {
5867 (offset.y, offset.y + size.height)
5868 } else {
5869 (offset.y + size.height, offset.y)
5870 };
5871
5872 let projection = Transform3D::ortho(
5873 offset.x,
5874 offset.x + size.width,
5875 bottom,
5876 top,
5877 self.device.ortho_near_plane(),
5878 self.device.ortho_far_plane(),
5879 );
5880
5881 let fb_scale = Scale::<_, _, FramebufferPixel>::new(1i32);
5882 let mut fb_rect = frame.device_rect * fb_scale;
5883
5884 if !surface_origin_is_top_left {
5885 fb_rect.origin.y = device_size.height - fb_rect.origin.y - fb_rect.size.height;
5886 }
5887
5888 let draw_target = DrawTarget::Default {
5889 rect: fb_rect,
5890 total_size: device_size * fb_scale,
5891 surface_origin_is_top_left,
5892 };
5893
5894 // Picture caching can be enabled / disabled dynamically from frame to
5895 // frame. This is determined by what the frame builder selected, and is
5896 // passed to the renderer via the composite state.
5897 if frame.composite_state.picture_caching_is_enabled {
5898 // If we have a native OS compositor, then make use of that interface
5899 // to specify how to composite each of the picture cache surfaces.
5900 match self.current_compositor_kind {
5901 CompositorKind::Native { .. } => {
5902 self.update_external_native_surfaces(
5903 &frame.composite_state.external_surfaces,
5904 results,
5905 );
5906 let compositor = self.compositor_config.compositor().unwrap();
5907 frame.composite_state.composite_native(&mut **compositor);
5908 }
5909 CompositorKind::Draw { max_partial_present_rects, draw_previous_partial_present_regions, .. } => {
5910 self.composite_simple(
5911 &frame.composite_state,
5912 clear_framebuffer,
5913 draw_target,
5914 &projection,
5915 results,
5916 max_partial_present_rects,
5917 draw_previous_partial_present_regions,
5918 );
5919 }
5920 }
5921 } else {
5922 if clear_framebuffer {
5923 let clear_color = self.clear_color.map(|color| color.to_array());
5924 self.device.bind_draw_target(draw_target);
5925 self.device.enable_depth_write();
5926 self.device.clear_target(clear_color,
5927 Some(1.0),
5928 None);
5929 }
5930
5931 // If picture caching is disabled, we will be drawing the entire
5932 // framebuffer. In that case, we need to push a screen size dirty
5933 // rect, in case partial present is enabled (an empty array of
5934 // dirty rects when partial present is enabled is interpreted by
5935 // Gecko as meaning nothing has changed and a swap is not required).
5936 results.dirty_rects.push(frame.device_rect);
5937
5938 self.draw_color_target(
5939 draw_target,
5940 main_target,
5941 frame.content_origin,
5942 None,
5943 None,
5944 &frame.render_tasks,
5945 &projection,
5946 frame_id,
5947 &mut results.stats,
5948 );
5949 }
5950 }
5951 }
5952 RenderPassKind::OffScreen {
5953 ref mut alpha,
5954 ref mut color,
5955 ref mut texture_cache,
5956 ref mut picture_cache,
5957 } => {
5958 profile_scope!("offscreen target");
5959
5960 let alpha_tex = self.allocate_target_texture(alpha, &mut frame.profile_counters);
5961 let color_tex = self.allocate_target_texture(color, &mut frame.profile_counters);
5962
5963 // If this frame has already been drawn, then any texture
5964 // cache targets have already been updated and can be
5965 // skipped this time.
5966 if !frame.has_been_rendered {
5967 for (&(texture_id, target_index), target) in texture_cache {
5968 self.draw_texture_cache_target(
5969 &texture_id,
5970 target_index,
5971 target,
5972 &frame.render_tasks,
5973 &mut results.stats,
5974 );
5975 }
5976
5977 if !picture_cache.is_empty() {
5978 self.profile_counters.color_passes.inc();
5979 }
5980
5981 // Draw picture caching tiles for this pass.
5982 for picture_target in picture_cache {
5983 results.stats.color_target_count += 1;
5984
5985 let draw_target = match picture_target.surface {
5986 ResolvedSurfaceTexture::TextureCache { ref texture, layer } => {
5987 let (texture, _) = self.texture_resolver
5988 .resolve(texture)
5989 .expect("bug");
5990
5991 DrawTarget::from_texture(
5992 texture,
5993 layer as usize,
5994 true,
5995 )
5996 }
5997 ResolvedSurfaceTexture::Native { id, size } => {
5998 let surface_info = match self.current_compositor_kind {
5999 CompositorKind::Native { .. } => {
6000 let compositor = self.compositor_config.compositor().unwrap();
6001 compositor.bind(
6002 id,
6003 picture_target.dirty_rect,
6004 picture_target.valid_rect,
6005 )
6006 }
6007 CompositorKind::Draw { .. } => {
6008 unreachable!();
6009 }
6010 };
6011
6012 DrawTarget::NativeSurface {
6013 offset: surface_info.origin,
6014 external_fbo_id: surface_info.fbo_id,
6015 dimensions: size,
6016 }
6017 }
6018 };
6019
6020 let projection = Transform3D::ortho(
6021 0.0,
6022 draw_target.dimensions().width as f32,
6023 0.0,
6024 draw_target.dimensions().height as f32,
6025 self.device.ortho_near_plane(),
6026 self.device.ortho_far_plane(),
6027 );
6028
6029 self.draw_picture_cache_target(
6030 picture_target,
6031 draw_target,
6032 frame.content_origin,
6033 &projection,
6034 &frame.render_tasks,
6035 &mut results.stats,
6036 );
6037
6038 // Native OS surfaces must be unbound at the end of drawing to them
6039 if let ResolvedSurfaceTexture::Native { .. } = picture_target.surface {
6040 match self.current_compositor_kind {
6041 CompositorKind::Native { .. } => {
6042 let compositor = self.compositor_config.compositor().unwrap();
6043 compositor.unbind();
6044 }
6045 CompositorKind::Draw { .. } => {
6046 unreachable!();
6047 }
6048 }
6049 }
6050 }
6051 }
6052
6053 for (target_index, target) in alpha.targets.iter().enumerate() {
6054 results.stats.alpha_target_count += 1;
6055 let draw_target = DrawTarget::from_texture(
6056 &alpha_tex.as_ref().unwrap().texture,
6057 target_index,
6058 false,
6059 );
6060
6061 let projection = Transform3D::ortho(
6062 0.0,
6063 draw_target.dimensions().width as f32,
6064 0.0,
6065 draw_target.dimensions().height as f32,
6066 self.device.ortho_near_plane(),
6067 self.device.ortho_far_plane(),
6068 );
6069
6070 self.draw_alpha_target(
6071 draw_target,
6072 target,
6073 &projection,
6074 &frame.render_tasks,
6075 &mut results.stats,
6076 );
6077 }
6078
6079 for (target_index, target) in color.targets.iter().enumerate() {
6080 results.stats.color_target_count += 1;
6081 let draw_target = DrawTarget::from_texture(
6082 &color_tex.as_ref().unwrap().texture,
6083 target_index,
6084 target.needs_depth(),
6085 );
6086
6087 let projection = Transform3D::ortho(
6088 0.0,
6089 draw_target.dimensions().width as f32,
6090 0.0,
6091 draw_target.dimensions().height as f32,
6092 self.device.ortho_near_plane(),
6093 self.device.ortho_far_plane(),
6094 );
6095
6096 let clear_depth = if target.needs_depth() {
6097 Some(1.0)
6098 } else {
6099 None
6100 };
6101
6102 self.draw_color_target(
6103 draw_target,
6104 target,
6105 frame.content_origin,
6106 Some([0.0, 0.0, 0.0, 0.0]),
6107 clear_depth,
6108 &frame.render_tasks,
6109 &projection,
6110 frame_id,
6111 &mut results.stats,
6112 );
6113 }
6114
6115 // Only end the pass here and invalidate previous textures for
6116 // off-screen targets. Deferring return of the inputs to the
6117 // frame buffer until the implicit end_pass in end_frame allows
6118 // debug draw overlays to be added without triggering a copy
6119 // resolve stage in mobile / tiled GPUs.
6120 self.texture_resolver.end_pass(
6121 &mut self.device,
6122 alpha_tex,
6123 color_tex,
6124 );
6125 }
6126 }
6127 {
6128 profile_scope!("gl.flush");
6129 self.device.gl().flush();
6130 }
6131 }
6132
6133 if let Some(device_size) = device_size {
6134 self.draw_frame_debug_items(&frame.debug_items);
6135 self.draw_render_target_debug(device_size);
6136 self.draw_texture_cache_debug(device_size);
6137 self.draw_gpu_cache_debug(device_size);
6138 self.draw_zoom_debug(device_size);
6139 }
6140 self.draw_epoch_debug();
6141
6142 // Garbage collect any frame outputs that weren't used this frame.
6143 let device = &mut self.device;
6144 self.output_targets
6145 .retain(|_, target| if target.last_access != frame_id {
6146 device.delete_fbo(target.fbo_id);
6147 false
6148 } else {
6149 true
6150 });
6151
6152 frame.has_been_rendered = true;
6153 }
6154
6155 /// Initialize the PLS block, by reading the current framebuffer color.
init_pixel_local_storage( &mut self, task_rect: DeviceIntRect, projection: &default::Transform3D<f32>, stats: &mut RendererStats, )6156 pub fn init_pixel_local_storage(
6157 &mut self,
6158 task_rect: DeviceIntRect,
6159 projection: &default::Transform3D<f32>,
6160 stats: &mut RendererStats,
6161 ) {
6162 self.device.enable_pixel_local_storage(true);
6163
6164 self.shaders
6165 .borrow_mut()
6166 .pls_init
6167 .as_mut()
6168 .unwrap()
6169 .bind(
6170 &mut self.device,
6171 projection,
6172 &mut self.renderer_errors,
6173 );
6174
6175 let instances = [
6176 ResolveInstanceData::new(task_rect),
6177 ];
6178
6179 self.draw_instanced_batch(
6180 &instances,
6181 VertexArrayKind::Resolve,
6182 &BatchTextures::no_texture(),
6183 stats,
6184 );
6185 }
6186
6187 /// Resolve the current PLS structure, writing it to a fragment color output.
resolve_pixel_local_storage( &mut self, task_rect: DeviceIntRect, projection: &default::Transform3D<f32>, stats: &mut RendererStats, )6188 pub fn resolve_pixel_local_storage(
6189 &mut self,
6190 task_rect: DeviceIntRect,
6191 projection: &default::Transform3D<f32>,
6192 stats: &mut RendererStats,
6193 ) {
6194 self.shaders
6195 .borrow_mut()
6196 .pls_resolve
6197 .as_mut()
6198 .unwrap()
6199 .bind(
6200 &mut self.device,
6201 projection,
6202 &mut self.renderer_errors,
6203 );
6204
6205 let instances = [
6206 ResolveInstanceData::new(task_rect),
6207 ];
6208
6209 self.draw_instanced_batch(
6210 &instances,
6211 VertexArrayKind::Resolve,
6212 &BatchTextures::no_texture(),
6213 stats,
6214 );
6215
6216 self.device.enable_pixel_local_storage(false);
6217 }
6218
debug_renderer(&mut self) -> Option<&mut DebugRenderer>6219 pub fn debug_renderer(&mut self) -> Option<&mut DebugRenderer> {
6220 self.debug.get_mut(&mut self.device)
6221 }
6222
get_debug_flags(&self) -> DebugFlags6223 pub fn get_debug_flags(&self) -> DebugFlags {
6224 self.debug_flags
6225 }
6226
set_debug_flags(&mut self, flags: DebugFlags)6227 pub fn set_debug_flags(&mut self, flags: DebugFlags) {
6228 if let Some(enabled) = flag_changed(self.debug_flags, flags, DebugFlags::GPU_TIME_QUERIES) {
6229 if enabled {
6230 self.gpu_profile.enable_timers();
6231 } else {
6232 self.gpu_profile.disable_timers();
6233 }
6234 }
6235 if let Some(enabled) = flag_changed(self.debug_flags, flags, DebugFlags::GPU_SAMPLE_QUERIES) {
6236 if enabled {
6237 self.gpu_profile.enable_samplers();
6238 } else {
6239 self.gpu_profile.disable_samplers();
6240 }
6241 }
6242
6243 self.debug_flags = flags;
6244 }
6245
draw_frame_debug_items(&mut self, items: &[DebugItem])6246 fn draw_frame_debug_items(&mut self, items: &[DebugItem]) {
6247 if items.is_empty() {
6248 return;
6249 }
6250
6251 let debug_renderer = match self.debug.get_mut(&mut self.device) {
6252 Some(render) => render,
6253 None => return,
6254 };
6255
6256 for item in items {
6257 match item {
6258 DebugItem::Rect { rect, outer_color, inner_color } => {
6259 debug_renderer.add_quad(
6260 rect.origin.x,
6261 rect.origin.y,
6262 rect.origin.x + rect.size.width,
6263 rect.origin.y + rect.size.height,
6264 (*inner_color).into(),
6265 (*inner_color).into(),
6266 );
6267
6268 debug_renderer.add_rect(
6269 &rect.to_i32(),
6270 (*outer_color).into(),
6271 );
6272 }
6273 DebugItem::Text { ref msg, position, color } => {
6274 debug_renderer.add_text(
6275 position.x,
6276 position.y,
6277 msg,
6278 (*color).into(),
6279 None,
6280 );
6281 }
6282 }
6283 }
6284 }
6285
draw_render_target_debug(&mut self, device_size: DeviceIntSize)6286 fn draw_render_target_debug(&mut self, device_size: DeviceIntSize) {
6287 if !self.debug_flags.contains(DebugFlags::RENDER_TARGET_DBG) {
6288 return;
6289 }
6290
6291 let debug_renderer = match self.debug.get_mut(&mut self.device) {
6292 Some(render) => render,
6293 None => return,
6294 };
6295
6296 let textures =
6297 self.texture_resolver.render_target_pool.iter().collect::<Vec<&Texture>>();
6298
6299 Self::do_debug_blit(
6300 &mut self.device,
6301 debug_renderer,
6302 textures,
6303 device_size,
6304 0,
6305 &|_| [0.0, 1.0, 0.0, 1.0], // Use green for all RTs.
6306 );
6307 }
6308
draw_zoom_debug( &mut self, device_size: DeviceIntSize, )6309 fn draw_zoom_debug(
6310 &mut self,
6311 device_size: DeviceIntSize,
6312 ) {
6313 if !self.debug_flags.contains(DebugFlags::ZOOM_DBG) {
6314 return;
6315 }
6316
6317 let debug_renderer = match self.debug.get_mut(&mut self.device) {
6318 Some(render) => render,
6319 None => return,
6320 };
6321
6322 let source_size = DeviceIntSize::new(64, 64);
6323 let target_size = DeviceIntSize::new(1024, 1024);
6324
6325 let source_origin = DeviceIntPoint::new(
6326 (self.cursor_position.x - source_size.width / 2)
6327 .min(device_size.width - source_size.width)
6328 .max(0),
6329 (self.cursor_position.y - source_size.height / 2)
6330 .min(device_size.height - source_size.height)
6331 .max(0),
6332 );
6333
6334 let source_rect = DeviceIntRect::new(
6335 source_origin,
6336 source_size,
6337 );
6338
6339 let target_rect = DeviceIntRect::new(
6340 DeviceIntPoint::new(
6341 device_size.width - target_size.width - 64,
6342 device_size.height - target_size.height - 64,
6343 ),
6344 target_size,
6345 );
6346
6347 let texture_rect = FramebufferIntRect::new(
6348 FramebufferIntPoint::zero(),
6349 source_rect.size.cast_unit(),
6350 );
6351
6352 debug_renderer.add_rect(
6353 &target_rect.inflate(1, 1),
6354 debug_colors::RED.into(),
6355 );
6356
6357 if self.zoom_debug_texture.is_none() {
6358 let texture = self.device.create_texture(
6359 TextureTarget::Default,
6360 ImageFormat::BGRA8,
6361 source_rect.size.width,
6362 source_rect.size.height,
6363 TextureFilter::Nearest,
6364 Some(RenderTargetInfo { has_depth: false }),
6365 1,
6366 );
6367
6368 self.zoom_debug_texture = Some(texture);
6369 }
6370
6371 // Copy frame buffer into the zoom texture
6372 let read_target = DrawTarget::new_default(device_size, self.device.surface_origin_is_top_left());
6373 self.device.blit_render_target(
6374 read_target.into(),
6375 read_target.to_framebuffer_rect(source_rect),
6376 DrawTarget::from_texture(
6377 self.zoom_debug_texture.as_ref().unwrap(),
6378 0,
6379 false,
6380 ),
6381 texture_rect,
6382 TextureFilter::Nearest,
6383 );
6384
6385 // Draw the zoom texture back to the framebuffer
6386 self.device.blit_render_target(
6387 ReadTarget::from_texture(
6388 self.zoom_debug_texture.as_ref().unwrap(),
6389 0,
6390 ),
6391 texture_rect,
6392 read_target,
6393 read_target.to_framebuffer_rect(target_rect),
6394 TextureFilter::Nearest,
6395 );
6396 }
6397
draw_texture_cache_debug(&mut self, device_size: DeviceIntSize)6398 fn draw_texture_cache_debug(&mut self, device_size: DeviceIntSize) {
6399 if !self.debug_flags.contains(DebugFlags::TEXTURE_CACHE_DBG) {
6400 return;
6401 }
6402
6403 let debug_renderer = match self.debug.get_mut(&mut self.device) {
6404 Some(render) => render,
6405 None => return,
6406 };
6407
6408 let textures =
6409 self.texture_resolver.texture_cache_map.values().collect::<Vec<&Texture>>();
6410
6411 fn select_color(texture: &Texture) -> [f32; 4] {
6412 if texture.flags().contains(TextureFlags::IS_SHARED_TEXTURE_CACHE) {
6413 [1.0, 0.5, 0.0, 1.0] // Orange for shared.
6414 } else {
6415 [1.0, 0.0, 1.0, 1.0] // Fuchsia for standalone.
6416 }
6417 }
6418
6419 Self::do_debug_blit(
6420 &mut self.device,
6421 debug_renderer,
6422 textures,
6423 device_size,
6424 if self.debug_flags.contains(DebugFlags::RENDER_TARGET_DBG) { 544 } else { 0 },
6425 &select_color,
6426 );
6427 }
6428
do_debug_blit( device: &mut Device, debug_renderer: &mut DebugRenderer, mut textures: Vec<&Texture>, device_size: DeviceIntSize, bottom: i32, select_color: &dyn Fn(&Texture) -> [f32; 4], )6429 fn do_debug_blit(
6430 device: &mut Device,
6431 debug_renderer: &mut DebugRenderer,
6432 mut textures: Vec<&Texture>,
6433 device_size: DeviceIntSize,
6434 bottom: i32,
6435 select_color: &dyn Fn(&Texture) -> [f32; 4],
6436 ) {
6437 let mut spacing = 16;
6438 let mut size = 512;
6439
6440 let fb_width = device_size.width;
6441 let fb_height = device_size.height;
6442 let num_layers: i32 = textures.iter()
6443 .map(|texture| texture.get_layer_count())
6444 .sum();
6445
6446 if num_layers * (size + spacing) > fb_width {
6447 let factor = fb_width as f32 / (num_layers * (size + spacing)) as f32;
6448 size = (size as f32 * factor) as i32;
6449 spacing = (spacing as f32 * factor) as i32;
6450 }
6451
6452 // Sort the display by layer size (in bytes), so that left-to-right is
6453 // largest-to-smallest.
6454 //
6455 // Note that the vec here is in increasing order, because the elements
6456 // get drawn right-to-left.
6457 textures.sort_by_key(|t| t.layer_size_in_bytes());
6458
6459 let mut i = 0;
6460 for texture in textures.iter() {
6461 let y = spacing + bottom;
6462 let dimensions = texture.get_dimensions();
6463 let src_rect = FramebufferIntRect::new(
6464 FramebufferIntPoint::zero(),
6465 FramebufferIntSize::new(dimensions.width as i32, dimensions.height as i32),
6466 );
6467
6468 let layer_count = texture.get_layer_count() as usize;
6469 for layer in 0 .. layer_count {
6470 let x = fb_width - (spacing + size) * (i as i32 + 1);
6471
6472 // If we have more targets than fit on one row in screen, just early exit.
6473 if x > fb_width {
6474 return;
6475 }
6476
6477 //TODO: properly use FramebufferPixel coordinates
6478
6479 // Draw the info tag.
6480 let text_margin = 1;
6481 let text_height = 14; // Visually aproximated.
6482 let tag_height = text_height + text_margin * 2;
6483 let tag_rect = rect(x, y, size, tag_height);
6484 let tag_color = select_color(texture);
6485 device.clear_target(
6486 Some(tag_color),
6487 None,
6488 Some(tag_rect.cast_unit()),
6489 );
6490
6491 // Draw the dimensions onto the tag.
6492 let dim = texture.get_dimensions();
6493 let mut text_rect = tag_rect;
6494 text_rect.origin.y =
6495 fb_height - text_rect.origin.y - text_rect.size.height; // Top-relative.
6496 debug_renderer.add_text(
6497 (x + text_margin) as f32,
6498 (fb_height - y - text_margin) as f32, // Top-relative.
6499 &format!("{}x{}", dim.width, dim.height),
6500 ColorU::new(0, 0, 0, 255),
6501 Some(text_rect.to_f32())
6502 );
6503
6504 // Blit the contents of the layer. We need to invert Y because
6505 // we're blitting from a texture to the main framebuffer, which
6506 // use different conventions.
6507 let dest_rect = rect(x, y + tag_height, size, size);
6508 if !device.surface_origin_is_top_left() {
6509 device.blit_render_target_invert_y(
6510 ReadTarget::from_texture(texture, layer),
6511 src_rect,
6512 DrawTarget::new_default(device_size, device.surface_origin_is_top_left()),
6513 FramebufferIntRect::from_untyped(&dest_rect),
6514 );
6515 } else {
6516 device.blit_render_target(
6517 ReadTarget::from_texture(texture, layer),
6518 src_rect,
6519 DrawTarget::new_default(device_size, device.surface_origin_is_top_left()),
6520 FramebufferIntRect::from_untyped(&dest_rect),
6521 TextureFilter::Linear,
6522 );
6523 }
6524 i += 1;
6525 }
6526 }
6527 }
6528
draw_epoch_debug(&mut self)6529 fn draw_epoch_debug(&mut self) {
6530 if !self.debug_flags.contains(DebugFlags::EPOCHS) {
6531 return;
6532 }
6533
6534 let debug_renderer = match self.debug.get_mut(&mut self.device) {
6535 Some(render) => render,
6536 None => return,
6537 };
6538
6539 let dy = debug_renderer.line_height();
6540 let x0: f32 = 30.0;
6541 let y0: f32 = 30.0;
6542 let mut y = y0;
6543 let mut text_width = 0.0;
6544 for ((pipeline, document_id), epoch) in &self.pipeline_info.epochs {
6545 y += dy;
6546 let w = debug_renderer.add_text(
6547 x0, y,
6548 &format!("({:?}, {:?}): {:?}", pipeline, document_id, epoch),
6549 ColorU::new(255, 255, 0, 255),
6550 None,
6551 ).size.width;
6552 text_width = f32::max(text_width, w);
6553 }
6554
6555 let margin = 10.0;
6556 debug_renderer.add_quad(
6557 x0 - margin,
6558 y0 - margin,
6559 x0 + text_width + margin,
6560 y + margin,
6561 ColorU::new(25, 25, 25, 200),
6562 ColorU::new(51, 51, 51, 200),
6563 );
6564 }
6565
draw_gpu_cache_debug(&mut self, device_size: DeviceIntSize)6566 fn draw_gpu_cache_debug(&mut self, device_size: DeviceIntSize) {
6567 if !self.debug_flags.contains(DebugFlags::GPU_CACHE_DBG) {
6568 return;
6569 }
6570
6571 let debug_renderer = match self.debug.get_mut(&mut self.device) {
6572 Some(render) => render,
6573 None => return,
6574 };
6575
6576 let (x_off, y_off) = (30f32, 30f32);
6577 let height = self.gpu_cache_texture.texture
6578 .as_ref().map_or(0, |t| t.get_dimensions().height)
6579 .min(device_size.height - (y_off as i32) * 2) as usize;
6580 debug_renderer.add_quad(
6581 x_off,
6582 y_off,
6583 x_off + MAX_VERTEX_TEXTURE_WIDTH as f32,
6584 y_off + height as f32,
6585 ColorU::new(80, 80, 80, 80),
6586 ColorU::new(80, 80, 80, 80),
6587 );
6588
6589 let upper = self.gpu_cache_debug_chunks.len().min(height);
6590 for chunk in self.gpu_cache_debug_chunks[0..upper].iter().flatten() {
6591 let color = ColorU::new(250, 0, 0, 200);
6592 debug_renderer.add_quad(
6593 x_off + chunk.address.u as f32,
6594 y_off + chunk.address.v as f32,
6595 x_off + chunk.address.u as f32 + chunk.size as f32,
6596 y_off + chunk.address.v as f32 + 1.0,
6597 color,
6598 color,
6599 );
6600 }
6601 }
6602
6603 /// Pass-through to `Device::read_pixels_into`, used by Gecko's WR bindings.
read_pixels_into(&mut self, rect: FramebufferIntRect, format: ImageFormat, output: &mut [u8])6604 pub fn read_pixels_into(&mut self, rect: FramebufferIntRect, format: ImageFormat, output: &mut [u8]) {
6605 self.device.read_pixels_into(rect, format, output);
6606 }
6607
read_pixels_rgba8(&mut self, rect: FramebufferIntRect) -> Vec<u8>6608 pub fn read_pixels_rgba8(&mut self, rect: FramebufferIntRect) -> Vec<u8> {
6609 let mut pixels = vec![0; (rect.size.width * rect.size.height * 4) as usize];
6610 self.device.read_pixels_into(rect, ImageFormat::RGBA8, &mut pixels);
6611 pixels
6612 }
6613
read_gpu_cache(&mut self) -> (DeviceIntSize, Vec<u8>)6614 pub fn read_gpu_cache(&mut self) -> (DeviceIntSize, Vec<u8>) {
6615 let texture = self.gpu_cache_texture.texture.as_ref().unwrap();
6616 let size = device_size_as_framebuffer_size(texture.get_dimensions());
6617 let mut texels = vec![0; (size.width * size.height * 16) as usize];
6618 self.device.begin_frame();
6619 self.device.bind_read_target(ReadTarget::from_texture(texture, 0));
6620 self.device.read_pixels_into(
6621 size.into(),
6622 ImageFormat::RGBAF32,
6623 &mut texels,
6624 );
6625 self.device.reset_read_target();
6626 self.device.end_frame();
6627 (texture.get_dimensions(), texels)
6628 }
6629
6630 // De-initialize the Renderer safely, assuming the GL is still alive and active.
deinit(mut self)6631 pub fn deinit(mut self) {
6632 //Note: this is a fake frame, only needed because texture deletion is require to happen inside a frame
6633 self.device.begin_frame();
6634 // If we are using a native compositor, ensure that any remaining native
6635 // surfaces are freed.
6636 if let CompositorConfig::Native { mut compositor, .. } = self.compositor_config {
6637 for id in self.allocated_native_surfaces.drain() {
6638 compositor.destroy_surface(id);
6639 }
6640 // Destroy the debug overlay surface, if currently allocated.
6641 if self.debug_overlay_state.current_size.is_some() {
6642 compositor.destroy_surface(NativeSurfaceId::DEBUG_OVERLAY);
6643 }
6644 compositor.deinit();
6645 }
6646 self.gpu_cache_texture.deinit(&mut self.device);
6647 if let Some(dither_matrix_texture) = self.dither_matrix_texture {
6648 self.device.delete_texture(dither_matrix_texture);
6649 }
6650 if let Some(zoom_debug_texture) = self.zoom_debug_texture {
6651 self.device.delete_texture(zoom_debug_texture);
6652 }
6653 for textures in self.vertex_data_textures.drain(..) {
6654 textures.deinit(&mut self.device);
6655 }
6656 self.device.delete_pbo(self.texture_cache_upload_pbo);
6657 self.texture_resolver.deinit(&mut self.device);
6658 self.device.delete_vao(self.vaos.prim_vao);
6659 self.device.delete_vao(self.vaos.resolve_vao);
6660 self.device.delete_vao(self.vaos.clip_vao);
6661 self.device.delete_vao(self.vaos.gradient_vao);
6662 self.device.delete_vao(self.vaos.blur_vao);
6663 self.device.delete_vao(self.vaos.line_vao);
6664 self.device.delete_vao(self.vaos.border_vao);
6665 self.device.delete_vao(self.vaos.scale_vao);
6666 self.device.delete_vao(self.vaos.svg_filter_vao);
6667 self.device.delete_vao(self.vaos.composite_vao);
6668
6669 self.debug.deinit(&mut self.device);
6670
6671 for (_, target) in self.output_targets {
6672 self.device.delete_fbo(target.fbo_id);
6673 }
6674 if let Ok(shaders) = Rc::try_unwrap(self.shaders) {
6675 shaders.into_inner().deinit(&mut self.device);
6676 }
6677
6678 if let Some(async_screenshots) = self.async_screenshots.take() {
6679 async_screenshots.deinit(&mut self.device);
6680 }
6681
6682 if let Some(async_frame_recorder) = self.async_frame_recorder.take() {
6683 async_frame_recorder.deinit(&mut self.device);
6684 }
6685
6686 #[cfg(feature = "capture")]
6687 self.device.delete_fbo(self.read_fbo);
6688 #[cfg(feature = "replay")]
6689 for (_, ext) in self.owned_external_images {
6690 self.device.delete_external_texture(ext);
6691 }
6692 self.device.end_frame();
6693 }
6694
size_of<T>(&self, ptr: *const T) -> usize6695 fn size_of<T>(&self, ptr: *const T) -> usize {
6696 let op = self.size_of_ops.as_ref().unwrap().size_of_op;
6697 unsafe { op(ptr as *const c_void) }
6698 }
6699
6700 /// Collects a memory report.
report_memory(&self) -> MemoryReport6701 pub fn report_memory(&self) -> MemoryReport {
6702 let mut report = MemoryReport::default();
6703
6704 // GPU cache CPU memory.
6705 if let GpuCacheBus::PixelBuffer{ref rows, ..} = self.gpu_cache_texture.bus {
6706 for row in rows.iter() {
6707 report.gpu_cache_cpu_mirror += self.size_of(&*row.cpu_blocks as *const _);
6708 }
6709 }
6710
6711 // GPU cache GPU memory.
6712 report.gpu_cache_textures +=
6713 self.gpu_cache_texture.texture.as_ref().map_or(0, |t| t.size_in_bytes());
6714
6715 // Render task CPU memory.
6716 for (_id, doc) in &self.active_documents {
6717 report.render_tasks += self.size_of(doc.frame.render_tasks.tasks.as_ptr());
6718 report.render_tasks += self.size_of(doc.frame.render_tasks.task_data.as_ptr());
6719 }
6720
6721 // Vertex data GPU memory.
6722 for textures in &self.vertex_data_textures {
6723 report.vertex_data_textures += textures.size_in_bytes();
6724 }
6725
6726 // Texture cache and render target GPU memory.
6727 report += self.texture_resolver.report_memory();
6728
6729 // Textures held internally within the device layer.
6730 report += self.device.report_memory();
6731
6732 report
6733 }
6734
6735 // Sets the blend mode. Blend is unconditionally set if the "show overdraw" debugging mode is
6736 // enabled.
set_blend(&mut self, mut blend: bool, framebuffer_kind: FramebufferKind)6737 fn set_blend(&mut self, mut blend: bool, framebuffer_kind: FramebufferKind) {
6738 if framebuffer_kind == FramebufferKind::Main &&
6739 self.debug_flags.contains(DebugFlags::SHOW_OVERDRAW) {
6740 blend = true
6741 }
6742 self.device.set_blend(blend)
6743 }
6744
set_blend_mode_multiply(&mut self, framebuffer_kind: FramebufferKind)6745 fn set_blend_mode_multiply(&mut self, framebuffer_kind: FramebufferKind) {
6746 if framebuffer_kind == FramebufferKind::Main &&
6747 self.debug_flags.contains(DebugFlags::SHOW_OVERDRAW) {
6748 self.device.set_blend_mode_show_overdraw();
6749 } else {
6750 self.device.set_blend_mode_multiply();
6751 }
6752 }
6753
set_blend_mode_premultiplied_alpha(&mut self, framebuffer_kind: FramebufferKind)6754 fn set_blend_mode_premultiplied_alpha(&mut self, framebuffer_kind: FramebufferKind) {
6755 if framebuffer_kind == FramebufferKind::Main &&
6756 self.debug_flags.contains(DebugFlags::SHOW_OVERDRAW) {
6757 self.device.set_blend_mode_show_overdraw();
6758 } else {
6759 self.device.set_blend_mode_premultiplied_alpha();
6760 }
6761 }
6762
set_blend_mode_subpixel_with_bg_color_pass1(&mut self, framebuffer_kind: FramebufferKind)6763 fn set_blend_mode_subpixel_with_bg_color_pass1(&mut self, framebuffer_kind: FramebufferKind) {
6764 if framebuffer_kind == FramebufferKind::Main &&
6765 self.debug_flags.contains(DebugFlags::SHOW_OVERDRAW) {
6766 self.device.set_blend_mode_show_overdraw();
6767 } else {
6768 self.device.set_blend_mode_subpixel_with_bg_color_pass1();
6769 }
6770 }
6771
set_blend_mode_subpixel_with_bg_color_pass2(&mut self, framebuffer_kind: FramebufferKind)6772 fn set_blend_mode_subpixel_with_bg_color_pass2(&mut self, framebuffer_kind: FramebufferKind) {
6773 if framebuffer_kind == FramebufferKind::Main &&
6774 self.debug_flags.contains(DebugFlags::SHOW_OVERDRAW) {
6775 self.device.set_blend_mode_show_overdraw();
6776 } else {
6777 self.device.set_blend_mode_subpixel_with_bg_color_pass2();
6778 }
6779 }
6780
6781 /// Clears all the layers of a texture with a given color.
clear_texture(&mut self, texture: &Texture, color: [f32; 4])6782 fn clear_texture(&mut self, texture: &Texture, color: [f32; 4]) {
6783 for i in 0..texture.get_layer_count() {
6784 self.device.bind_draw_target(DrawTarget::from_texture(
6785 &texture,
6786 i as usize,
6787 false,
6788 ));
6789 self.device.clear_target(Some(color), None, None);
6790 }
6791 }
6792 }
6793
6794 pub trait ThreadListener {
thread_started(&self, thread_name: &str)6795 fn thread_started(&self, thread_name: &str);
thread_stopped(&self, thread_name: &str)6796 fn thread_stopped(&self, thread_name: &str);
6797 }
6798
6799 /// Allows callers to hook in at certain points of the async scene build. These
6800 /// functions are all called from the scene builder thread.
6801 pub trait SceneBuilderHooks {
6802 /// This is called exactly once, when the scene builder thread is started
6803 /// and before it processes anything.
register(&self)6804 fn register(&self);
6805 /// This is called before each scene build starts.
pre_scene_build(&self)6806 fn pre_scene_build(&self);
6807 /// This is called before each scene swap occurs.
pre_scene_swap(&self, scenebuild_time: u64)6808 fn pre_scene_swap(&self, scenebuild_time: u64);
6809 /// This is called after each scene swap occurs. The PipelineInfo contains
6810 /// the updated epochs and pipelines removed in the new scene compared to
6811 /// the old scene.
post_scene_swap(&self, document_id: &Vec<DocumentId>, info: PipelineInfo, sceneswap_time: u64)6812 fn post_scene_swap(&self, document_id: &Vec<DocumentId>, info: PipelineInfo, sceneswap_time: u64);
6813 /// This is called after a resource update operation on the scene builder
6814 /// thread, in the case where resource updates were applied without a scene
6815 /// build.
post_resource_update(&self, document_ids: &Vec<DocumentId>)6816 fn post_resource_update(&self, document_ids: &Vec<DocumentId>);
6817 /// This is called after a scene build completes without any changes being
6818 /// made. We guarantee that each pre_scene_build call will be matched with
6819 /// exactly one of post_scene_swap, post_resource_update or
6820 /// post_empty_scene_build.
post_empty_scene_build(&self)6821 fn post_empty_scene_build(&self);
6822 /// This is a generic callback which provides an opportunity to run code
6823 /// on the scene builder thread. This is called as part of the main message
6824 /// loop of the scene builder thread, but outside of any specific message
6825 /// handler.
poke(&self)6826 fn poke(&self);
6827 /// This is called exactly once, when the scene builder thread is about to
6828 /// terminate.
deregister(&self)6829 fn deregister(&self);
6830 }
6831
6832 /// Allows callers to hook into the main render_backend loop and provide
6833 /// additional frame ops for generate_frame transactions. These functions
6834 /// are all called from the render backend thread.
6835 pub trait AsyncPropertySampler {
6836 /// This is called exactly once, when the render backend thread is started
6837 /// and before it processes anything.
register(&self)6838 fn register(&self);
6839 /// This is called for each transaction with the generate_frame flag set
6840 /// (i.e. that will trigger a render). The list of frame messages returned
6841 /// are processed as though they were part of the original transaction.
sample(&self, document_id: DocumentId, doc: &FastHashMap<PipelineId, Epoch>) -> Vec<FrameMsg>6842 fn sample(&self, document_id: DocumentId,
6843 doc: &FastHashMap<PipelineId, Epoch>) -> Vec<FrameMsg>;
6844 /// This is called exactly once, when the render backend thread is about to
6845 /// terminate.
deregister(&self)6846 fn deregister(&self);
6847 }
6848
6849 bitflags! {
6850 /// Flags that control how shaders are pre-cached, if at all.
6851 #[derive(Default)]
6852 pub struct ShaderPrecacheFlags: u32 {
6853 /// Needed for const initialization
6854 const EMPTY = 0;
6855
6856 /// Only start async compile
6857 const ASYNC_COMPILE = 1 << 2;
6858
6859 /// Do a full compile/link during startup
6860 const FULL_COMPILE = 1 << 3;
6861 }
6862 }
6863
6864 pub struct RendererOptions {
6865 pub device_pixel_ratio: f32,
6866 pub resource_override_path: Option<PathBuf>,
6867 /// Whether to use shaders that have been optimized at build time.
6868 pub use_optimized_shaders: bool,
6869 pub enable_aa: bool,
6870 pub enable_dithering: bool,
6871 pub max_recorded_profiles: usize,
6872 pub precache_flags: ShaderPrecacheFlags,
6873 /// Enable sub-pixel anti-aliasing if a fast implementation is available.
6874 pub enable_subpixel_aa: bool,
6875 /// Enable sub-pixel anti-aliasing if it requires a slow implementation.
6876 pub force_subpixel_aa: bool,
6877 pub clear_color: Option<ColorF>,
6878 pub enable_clear_scissor: bool,
6879 pub max_texture_size: Option<i32>,
6880 pub max_glyph_cache_size: Option<usize>,
6881 pub upload_method: UploadMethod,
6882 pub workers: Option<Arc<ThreadPool>>,
6883 pub enable_multithreading: bool,
6884 pub blob_image_handler: Option<Box<dyn BlobImageHandler>>,
6885 pub thread_listener: Option<Box<dyn ThreadListener + Send + Sync>>,
6886 pub size_of_op: Option<VoidPtrToSizeFn>,
6887 pub enclosing_size_of_op: Option<VoidPtrToSizeFn>,
6888 pub cached_programs: Option<Rc<ProgramCache>>,
6889 pub debug_flags: DebugFlags,
6890 pub renderer_id: Option<u64>,
6891 pub scene_builder_hooks: Option<Box<dyn SceneBuilderHooks + Send>>,
6892 pub sampler: Option<Box<dyn AsyncPropertySampler + Send>>,
6893 pub chase_primitive: ChasePrimitive,
6894 pub support_low_priority_transactions: bool,
6895 pub namespace_alloc_by_client: bool,
6896 pub enable_picture_caching: bool,
6897 pub testing: bool,
6898 /// Set to true if this GPU supports hardware fast clears as a performance
6899 /// optimization. Likely requires benchmarking on various GPUs to see if
6900 /// it is a performance win. The default is false, which tends to be best
6901 /// performance on lower end / integrated GPUs.
6902 pub gpu_supports_fast_clears: bool,
6903 pub allow_dual_source_blending: bool,
6904 pub allow_advanced_blend_equation: bool,
6905 /// If true, allow WR to use pixel local storage if the device supports it.
6906 /// For now, this defaults to false since the code is still experimental
6907 /// and not complete. This option will probably be removed once support is
6908 /// complete, and WR can implicitly choose whether to make use of PLS.
6909 pub allow_pixel_local_storage_support: bool,
6910 /// If true, allow textures to be initialized with glTexStorage.
6911 /// This affects VRAM consumption and data upload paths.
6912 pub allow_texture_storage_support: bool,
6913 /// If true, we allow the data uploaded in a different format from the
6914 /// one expected by the driver, pretending the format is matching, and
6915 /// swizzling the components on all the shader sampling.
6916 pub allow_texture_swizzling: bool,
6917 /// Number of batches to look back in history for adding the current
6918 /// transparent instance into.
6919 pub batch_lookback_count: usize,
6920 /// Start the debug server for this renderer.
6921 pub start_debug_server: bool,
6922 /// Output the source of the shader with the given name.
6923 pub dump_shader_source: Option<String>,
6924 pub surface_origin_is_top_left: bool,
6925 /// The configuration options defining how WR composites the final scene.
6926 pub compositor_config: CompositorConfig,
6927 pub enable_gpu_markers: bool,
6928 /// If true, panic whenever a GL error occurs. This has a significant
6929 /// performance impact, so only use when debugging specific problems!
6930 pub panic_on_gl_error: bool,
6931 }
6932
6933 impl Default for RendererOptions {
default() -> Self6934 fn default() -> Self {
6935 RendererOptions {
6936 device_pixel_ratio: 1.0,
6937 resource_override_path: None,
6938 use_optimized_shaders: false,
6939 enable_aa: true,
6940 enable_dithering: false,
6941 debug_flags: DebugFlags::empty(),
6942 max_recorded_profiles: 0,
6943 precache_flags: ShaderPrecacheFlags::empty(),
6944 enable_subpixel_aa: false,
6945 force_subpixel_aa: false,
6946 clear_color: Some(ColorF::new(1.0, 1.0, 1.0, 1.0)),
6947 enable_clear_scissor: true,
6948 max_texture_size: None,
6949 max_glyph_cache_size: None,
6950 // This is best as `Immediate` on Angle, or `Pixelbuffer(Dynamic)` on GL,
6951 // but we are unable to make this decision here, so picking the reasonable medium.
6952 upload_method: UploadMethod::PixelBuffer(VertexUsageHint::Stream),
6953 workers: None,
6954 enable_multithreading: true,
6955 blob_image_handler: None,
6956 thread_listener: None,
6957 size_of_op: None,
6958 enclosing_size_of_op: None,
6959 renderer_id: None,
6960 cached_programs: None,
6961 scene_builder_hooks: None,
6962 sampler: None,
6963 chase_primitive: ChasePrimitive::Nothing,
6964 support_low_priority_transactions: false,
6965 namespace_alloc_by_client: false,
6966 enable_picture_caching: false,
6967 testing: false,
6968 gpu_supports_fast_clears: false,
6969 allow_dual_source_blending: true,
6970 allow_advanced_blend_equation: false,
6971 allow_pixel_local_storage_support: false,
6972 allow_texture_storage_support: true,
6973 allow_texture_swizzling: true,
6974 batch_lookback_count: DEFAULT_BATCH_LOOKBACK_COUNT,
6975 // For backwards compatibility we set this to true by default, so
6976 // that if the debugger feature is enabled, the debug server will
6977 // be started automatically. Users can explicitly disable this as
6978 // needed.
6979 start_debug_server: true,
6980 dump_shader_source: None,
6981 surface_origin_is_top_left: false,
6982 compositor_config: CompositorConfig::default(),
6983 enable_gpu_markers: true,
6984 panic_on_gl_error: false,
6985 }
6986 }
6987 }
6988
6989 pub trait DebugServer {
send(&mut self, _message: String)6990 fn send(&mut self, _message: String);
6991 }
6992
6993 struct NoopDebugServer;
6994
6995 impl NoopDebugServer {
new(_: Sender<ApiMsg>) -> Self6996 fn new(_: Sender<ApiMsg>) -> Self {
6997 NoopDebugServer
6998 }
6999 }
7000
7001 impl DebugServer for NoopDebugServer {
send(&mut self, _: String)7002 fn send(&mut self, _: String) {}
7003 }
7004
7005 #[cfg(feature = "debugger")]
new_debug_server(enable: bool, api_tx: Sender<ApiMsg>) -> Box<dyn DebugServer>7006 fn new_debug_server(enable: bool, api_tx: Sender<ApiMsg>) -> Box<dyn DebugServer> {
7007 if enable {
7008 Box::new(debug_server::DebugServerImpl::new(api_tx))
7009 } else {
7010 Box::new(NoopDebugServer::new(api_tx))
7011 }
7012 }
7013
7014 #[cfg(not(feature = "debugger"))]
new_debug_server(_enable: bool, api_tx: Sender<ApiMsg>) -> Box<dyn DebugServer>7015 fn new_debug_server(_enable: bool, api_tx: Sender<ApiMsg>) -> Box<dyn DebugServer> {
7016 Box::new(NoopDebugServer::new(api_tx))
7017 }
7018
7019 /// Some basic statistics about the rendered scene, used in Gecko, as
7020 /// well as in wrench reftests to ensure that tests are batching and/or
7021 /// allocating on render targets as we expect them to.
7022 #[repr(C)]
7023 #[derive(Debug, Default)]
7024 pub struct RendererStats {
7025 pub total_draw_calls: usize,
7026 pub alpha_target_count: usize,
7027 pub color_target_count: usize,
7028 pub texture_upload_kb: usize,
7029 pub resource_upload_time: u64,
7030 pub gpu_cache_upload_time: u64,
7031 }
7032
7033 /// Return type from render(), which contains some repr(C) statistics as well as
7034 /// some non-repr(C) data.
7035 #[derive(Debug, Default)]
7036 pub struct RenderResults {
7037 /// Statistics about the frame that was rendered.
7038 pub stats: RendererStats,
7039
7040 /// A list of dirty world rects. This is only currently
7041 /// useful to test infrastructure.
7042 /// TODO(gw): This needs to be refactored / removed.
7043 pub recorded_dirty_regions: Vec<RecordedDirtyRegion>,
7044
7045 /// A list of the device dirty rects that were updated
7046 /// this frame.
7047 /// TODO(gw): This is an initial interface, likely to change in future.
7048 /// TODO(gw): The dirty rects here are currently only useful when scrolling
7049 /// is not occurring. They are still correct in the case of
7050 /// scrolling, but will be very large (until we expose proper
7051 /// OS compositor support where the dirty rects apply to a
7052 /// specific picture cache slice / OS compositor surface).
7053 pub dirty_rects: Vec<DeviceIntRect>,
7054 }
7055
7056 #[cfg(any(feature = "capture", feature = "replay"))]
7057 #[cfg_attr(feature = "capture", derive(Serialize))]
7058 #[cfg_attr(feature = "replay", derive(Deserialize))]
7059 struct PlainTexture {
7060 data: String,
7061 size: (DeviceIntSize, i32),
7062 format: ImageFormat,
7063 filter: TextureFilter,
7064 has_depth: bool,
7065 }
7066
7067
7068 #[cfg(any(feature = "capture", feature = "replay"))]
7069 #[cfg_attr(feature = "capture", derive(Serialize))]
7070 #[cfg_attr(feature = "replay", derive(Deserialize))]
7071 struct PlainRenderer {
7072 device_size: Option<DeviceIntSize>,
7073 gpu_cache: PlainTexture,
7074 gpu_cache_frame_id: FrameId,
7075 textures: FastHashMap<CacheTextureId, PlainTexture>,
7076 }
7077
7078 #[cfg(any(feature = "capture", feature = "replay"))]
7079 #[cfg_attr(feature = "capture", derive(Serialize))]
7080 #[cfg_attr(feature = "replay", derive(Deserialize))]
7081 struct PlainExternalResources {
7082 images: Vec<ExternalCaptureImage>
7083 }
7084
7085 #[cfg(feature = "replay")]
7086 enum CapturedExternalImageData {
7087 NativeTexture(gl::GLuint),
7088 Buffer(Arc<Vec<u8>>),
7089 }
7090
7091 #[cfg(feature = "replay")]
7092 struct DummyExternalImageHandler {
7093 data: FastHashMap<(ExternalImageId, u8), (CapturedExternalImageData, TexelRect)>,
7094 }
7095
7096 #[cfg(feature = "replay")]
7097 impl ExternalImageHandler for DummyExternalImageHandler {
lock(&mut self, key: ExternalImageId, channel_index: u8, _rendering: ImageRendering) -> ExternalImage7098 fn lock(&mut self, key: ExternalImageId, channel_index: u8, _rendering: ImageRendering) -> ExternalImage {
7099 let (ref captured_data, ref uv) = self.data[&(key, channel_index)];
7100 ExternalImage {
7101 uv: *uv,
7102 source: match *captured_data {
7103 CapturedExternalImageData::NativeTexture(tid) => ExternalImageSource::NativeTexture(tid),
7104 CapturedExternalImageData::Buffer(ref arc) => ExternalImageSource::RawData(&*arc),
7105 }
7106 }
7107 }
unlock(&mut self, _key: ExternalImageId, _channel_index: u8)7108 fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {}
7109 }
7110
7111 #[cfg(feature = "replay")]
7112 struct VoidHandler;
7113
7114 #[cfg(feature = "replay")]
7115 impl OutputImageHandler for VoidHandler {
lock(&mut self, _: PipelineId) -> Option<(u32, FramebufferIntSize)>7116 fn lock(&mut self, _: PipelineId) -> Option<(u32, FramebufferIntSize)> {
7117 None
7118 }
unlock(&mut self, _: PipelineId)7119 fn unlock(&mut self, _: PipelineId) {
7120 unreachable!()
7121 }
7122 }
7123
7124 #[derive(Default)]
7125 pub struct PipelineInfo {
7126 pub epochs: FastHashMap<(PipelineId, DocumentId), Epoch>,
7127 pub removed_pipelines: Vec<(PipelineId, DocumentId)>,
7128 }
7129
7130 impl Renderer {
7131 #[cfg(feature = "capture")]
save_texture( texture: &Texture, name: &str, root: &PathBuf, device: &mut Device ) -> PlainTexture7132 fn save_texture(
7133 texture: &Texture, name: &str, root: &PathBuf, device: &mut Device
7134 ) -> PlainTexture {
7135 use std::fs;
7136 use std::io::Write;
7137
7138 let short_path = format!("textures/{}.raw", name);
7139
7140 let bytes_per_pixel = texture.get_format().bytes_per_pixel();
7141 let read_format = texture.get_format();
7142 let rect_size = texture.get_dimensions();
7143
7144 let mut file = fs::File::create(root.join(&short_path))
7145 .expect(&format!("Unable to create {}", short_path));
7146 let bytes_per_layer = (rect_size.width * rect_size.height * bytes_per_pixel) as usize;
7147 let mut data = vec![0; bytes_per_layer];
7148
7149 //TODO: instead of reading from an FBO with `read_pixels*`, we could
7150 // read from textures directly with `get_tex_image*`.
7151
7152 for layer_id in 0 .. texture.get_layer_count() {
7153 let rect = device_size_as_framebuffer_size(rect_size).into();
7154
7155 device.attach_read_texture(texture, layer_id);
7156 #[cfg(feature = "png")]
7157 {
7158 let mut png_data;
7159 let (data_ref, format) = match texture.get_format() {
7160 ImageFormat::RGBAF32 => {
7161 png_data = vec![0; (rect_size.width * rect_size.height * 4) as usize];
7162 device.read_pixels_into(rect, ImageFormat::RGBA8, &mut png_data);
7163 (&png_data, ImageFormat::RGBA8)
7164 }
7165 fm => (&data, fm),
7166 };
7167 CaptureConfig::save_png(
7168 root.join(format!("textures/{}-{}.png", name, layer_id)),
7169 rect_size, format,
7170 None,
7171 data_ref,
7172 );
7173 }
7174 device.read_pixels_into(rect, read_format, &mut data);
7175 file.write_all(&data)
7176 .unwrap();
7177 }
7178
7179 PlainTexture {
7180 data: short_path,
7181 size: (rect_size, texture.get_layer_count()),
7182 format: texture.get_format(),
7183 filter: texture.get_filter(),
7184 has_depth: texture.supports_depth(),
7185 }
7186 }
7187
7188 #[cfg(feature = "replay")]
load_texture( target: TextureTarget, plain: &PlainTexture, rt_info: Option<RenderTargetInfo>, root: &PathBuf, device: &mut Device ) -> (Texture, Vec<u8>)7189 fn load_texture(
7190 target: TextureTarget,
7191 plain: &PlainTexture,
7192 rt_info: Option<RenderTargetInfo>,
7193 root: &PathBuf,
7194 device: &mut Device
7195 ) -> (Texture, Vec<u8>)
7196 {
7197 use std::fs::File;
7198 use std::io::Read;
7199
7200 let mut texels = Vec::new();
7201 File::open(root.join(&plain.data))
7202 .expect(&format!("Unable to open texture at {}", plain.data))
7203 .read_to_end(&mut texels)
7204 .unwrap();
7205
7206 let texture = device.create_texture(
7207 target,
7208 plain.format,
7209 plain.size.0.width,
7210 plain.size.0.height,
7211 plain.filter,
7212 rt_info,
7213 plain.size.1,
7214 );
7215 device.upload_texture_immediate(&texture, &texels);
7216
7217 (texture, texels)
7218 }
7219
7220 #[cfg(feature = "capture")]
save_capture( &mut self, config: CaptureConfig, deferred_images: Vec<ExternalCaptureImage>, )7221 fn save_capture(
7222 &mut self,
7223 config: CaptureConfig,
7224 deferred_images: Vec<ExternalCaptureImage>,
7225 ) {
7226 use std::fs;
7227 use std::io::Write;
7228 use api::{CaptureBits, ExternalImageData};
7229
7230 let root = config.resource_root();
7231
7232 self.device.begin_frame();
7233 let _gm = self.gpu_profile.start_marker("read GPU data");
7234 self.device.bind_read_target_impl(self.read_fbo);
7235
7236 if config.bits.contains(CaptureBits::EXTERNAL_RESOURCES) && !deferred_images.is_empty() {
7237 info!("saving external images");
7238 let mut arc_map = FastHashMap::<*const u8, String>::default();
7239 let mut tex_map = FastHashMap::<u32, String>::default();
7240 let handler = self.external_image_handler
7241 .as_mut()
7242 .expect("Unable to lock the external image handler!");
7243 for def in &deferred_images {
7244 info!("\t{}", def.short_path);
7245 let ExternalImageData { id, channel_index, image_type } = def.external;
7246 // The image rendering parameter is irrelevant because no filtering happens during capturing.
7247 let ext_image = handler.lock(id, channel_index, ImageRendering::Auto);
7248 let (data, short_path) = match ext_image.source {
7249 ExternalImageSource::RawData(data) => {
7250 let arc_id = arc_map.len() + 1;
7251 match arc_map.entry(data.as_ptr()) {
7252 Entry::Occupied(e) => {
7253 (None, e.get().clone())
7254 }
7255 Entry::Vacant(e) => {
7256 let short_path = format!("externals/d{}.raw", arc_id);
7257 (Some(data.to_vec()), e.insert(short_path).clone())
7258 }
7259 }
7260 }
7261 ExternalImageSource::NativeTexture(gl_id) => {
7262 let tex_id = tex_map.len() + 1;
7263 match tex_map.entry(gl_id) {
7264 Entry::Occupied(e) => {
7265 (None, e.get().clone())
7266 }
7267 Entry::Vacant(e) => {
7268 let target = match image_type {
7269 ExternalImageType::TextureHandle(target) => target,
7270 ExternalImageType::Buffer => unreachable!(),
7271 };
7272 info!("\t\tnative texture of target {:?}", target);
7273 let layer_index = 0; //TODO: what about layered textures?
7274 self.device.attach_read_texture_external(gl_id, target, layer_index);
7275 let data = self.device.read_pixels(&def.descriptor);
7276 let short_path = format!("externals/t{}.raw", tex_id);
7277 (Some(data), e.insert(short_path).clone())
7278 }
7279 }
7280 }
7281 ExternalImageSource::Invalid => {
7282 info!("\t\tinvalid source!");
7283 (None, String::new())
7284 }
7285 };
7286 if let Some(bytes) = data {
7287 fs::File::create(root.join(&short_path))
7288 .expect(&format!("Unable to create {}", short_path))
7289 .write_all(&bytes)
7290 .unwrap();
7291 #[cfg(feature = "png")]
7292 CaptureConfig::save_png(
7293 root.join(&short_path).with_extension("png"),
7294 def.descriptor.size,
7295 def.descriptor.format,
7296 def.descriptor.stride,
7297 &bytes,
7298 );
7299 }
7300 let plain = PlainExternalImage {
7301 data: short_path,
7302 external: def.external,
7303 uv: ext_image.uv,
7304 };
7305 config.serialize_for_resource(&plain, &def.short_path);
7306 }
7307 for def in &deferred_images {
7308 handler.unlock(def.external.id, def.external.channel_index);
7309 }
7310 let plain_external = PlainExternalResources {
7311 images: deferred_images,
7312 };
7313 config.serialize_for_resource(&plain_external, "external_resources");
7314 }
7315
7316 if config.bits.contains(CaptureBits::FRAME) {
7317 let path_textures = root.join("textures");
7318 if !path_textures.is_dir() {
7319 fs::create_dir(&path_textures).unwrap();
7320 }
7321
7322 info!("saving GPU cache");
7323 self.update_gpu_cache(); // flush pending updates
7324 let mut plain_self = PlainRenderer {
7325 device_size: self.device_size,
7326 gpu_cache: Self::save_texture(
7327 &self.gpu_cache_texture.texture.as_ref().unwrap(),
7328 "gpu", &root, &mut self.device,
7329 ),
7330 gpu_cache_frame_id: self.gpu_cache_frame_id,
7331 textures: FastHashMap::default(),
7332 };
7333
7334 info!("saving cached textures");
7335 for (id, texture) in &self.texture_resolver.texture_cache_map {
7336 let file_name = format!("cache-{}", plain_self.textures.len() + 1);
7337 info!("\t{}", file_name);
7338 let plain = Self::save_texture(texture, &file_name, &root, &mut self.device);
7339 plain_self.textures.insert(*id, plain);
7340 }
7341
7342 config.serialize_for_resource(&plain_self, "renderer");
7343 }
7344
7345 self.device.reset_read_target();
7346 self.device.end_frame();
7347 info!("done.");
7348 }
7349
7350 #[cfg(feature = "replay")]
load_capture( &mut self, config: CaptureConfig, plain_externals: Vec<PlainExternalImage>, )7351 fn load_capture(
7352 &mut self,
7353 config: CaptureConfig,
7354 plain_externals: Vec<PlainExternalImage>,
7355 ) {
7356 use std::fs::File;
7357 use std::io::Read;
7358 use std::slice;
7359
7360 info!("loading external buffer-backed images");
7361 assert!(self.texture_resolver.external_images.is_empty());
7362 let mut raw_map = FastHashMap::<String, Arc<Vec<u8>>>::default();
7363 let mut image_handler = DummyExternalImageHandler {
7364 data: FastHashMap::default(),
7365 };
7366
7367 let root = config.resource_root();
7368
7369 // Note: this is a `SCENE` level population of the external image handlers
7370 // It would put both external buffers and texture into the map.
7371 // But latter are going to be overwritten later in this function
7372 // if we are in the `FRAME` level.
7373 for plain_ext in plain_externals {
7374 let data = match raw_map.entry(plain_ext.data) {
7375 Entry::Occupied(e) => e.get().clone(),
7376 Entry::Vacant(e) => {
7377 let mut buffer = Vec::new();
7378 File::open(root.join(e.key()))
7379 .expect(&format!("Unable to open {}", e.key()))
7380 .read_to_end(&mut buffer)
7381 .unwrap();
7382 e.insert(Arc::new(buffer)).clone()
7383 }
7384 };
7385 let ext = plain_ext.external;
7386 let value = (CapturedExternalImageData::Buffer(data), plain_ext.uv);
7387 image_handler.data.insert((ext.id, ext.channel_index), value);
7388 }
7389
7390 if let Some(external_resources) = config.deserialize_for_resource::<PlainExternalResources, _>("external_resources") {
7391 info!("loading external texture-backed images");
7392 let mut native_map = FastHashMap::<String, gl::GLuint>::default();
7393 for ExternalCaptureImage { short_path, external, descriptor } in external_resources.images {
7394 let target = match external.image_type {
7395 ExternalImageType::TextureHandle(target) => target,
7396 ExternalImageType::Buffer => continue,
7397 };
7398 let plain_ext = config.deserialize_for_resource::<PlainExternalImage, _>(&short_path)
7399 .expect(&format!("Unable to read {}.ron", short_path));
7400 let key = (external.id, external.channel_index);
7401
7402 let tid = match native_map.entry(plain_ext.data) {
7403 Entry::Occupied(e) => e.get().clone(),
7404 Entry::Vacant(e) => {
7405 //TODO: provide a way to query both the layer count and the filter from external images
7406 let (layer_count, filter) = (1, TextureFilter::Linear);
7407 let plain_tex = PlainTexture {
7408 data: e.key().clone(),
7409 size: (descriptor.size, layer_count),
7410 format: descriptor.format,
7411 filter,
7412 has_depth: false,
7413 };
7414 let t = Self::load_texture(
7415 target,
7416 &plain_tex,
7417 None,
7418 &root,
7419 &mut self.device
7420 );
7421 let extex = t.0.into_external();
7422 self.owned_external_images.insert(key, extex.clone());
7423 e.insert(extex.internal_id()).clone()
7424 }
7425 };
7426
7427 let value = (CapturedExternalImageData::NativeTexture(tid), plain_ext.uv);
7428 image_handler.data.insert(key, value);
7429 }
7430 }
7431
7432 if let Some(renderer) = config.deserialize_for_resource::<PlainRenderer, _>("renderer") {
7433 info!("loading cached textures");
7434 self.device_size = renderer.device_size;
7435 self.device.begin_frame();
7436
7437 for (_id, texture) in self.texture_resolver.texture_cache_map.drain() {
7438 self.device.delete_texture(texture);
7439 }
7440 for (id, texture) in renderer.textures {
7441 info!("\t{}", texture.data);
7442 let t = Self::load_texture(
7443 TextureTarget::Array,
7444 &texture,
7445 Some(RenderTargetInfo { has_depth: texture.has_depth }),
7446 &root,
7447 &mut self.device
7448 );
7449 self.texture_resolver.texture_cache_map.insert(id, t.0);
7450 }
7451
7452 info!("loading gpu cache");
7453 if let Some(t) = self.gpu_cache_texture.texture.take() {
7454 self.device.delete_texture(t);
7455 }
7456 let (t, gpu_cache_data) = Self::load_texture(
7457 TextureTarget::Default,
7458 &renderer.gpu_cache,
7459 Some(RenderTargetInfo { has_depth: false }),
7460 &root,
7461 &mut self.device,
7462 );
7463 self.gpu_cache_texture.texture = Some(t);
7464 match self.gpu_cache_texture.bus {
7465 GpuCacheBus::PixelBuffer { ref mut rows, .. } => {
7466 let dim = self.gpu_cache_texture.texture.as_ref().unwrap().get_dimensions();
7467 let blocks = unsafe {
7468 slice::from_raw_parts(
7469 gpu_cache_data.as_ptr() as *const GpuBlockData,
7470 gpu_cache_data.len() / mem::size_of::<GpuBlockData>(),
7471 )
7472 };
7473 // fill up the CPU cache from the contents we just loaded
7474 rows.clear();
7475 rows.extend((0 .. dim.height).map(|_| CacheRow::new()));
7476 let chunks = blocks.chunks(MAX_VERTEX_TEXTURE_WIDTH);
7477 debug_assert_eq!(chunks.len(), rows.len());
7478 for (row, chunk) in rows.iter_mut().zip(chunks) {
7479 row.cpu_blocks.copy_from_slice(chunk);
7480 }
7481 }
7482 GpuCacheBus::Scatter { .. } => {}
7483 }
7484 self.gpu_cache_frame_id = renderer.gpu_cache_frame_id;
7485
7486 self.device.end_frame();
7487 } else {
7488 info!("loading cached textures");
7489 self.device.begin_frame();
7490 for (_id, texture) in self.texture_resolver.texture_cache_map.drain() {
7491 self.device.delete_texture(texture);
7492 }
7493
7494 info!("loading gpu cache");
7495 if let Some(t) = self.gpu_cache_texture.texture.take() {
7496 self.device.delete_texture(t);
7497 }
7498 self.device.end_frame();
7499 }
7500
7501 self.output_image_handler = Some(Box::new(VoidHandler) as Box<_>);
7502 self.external_image_handler = Some(Box::new(image_handler) as Box<_>);
7503 info!("done.");
7504 }
7505 }
7506
get_vao(vertex_array_kind: VertexArrayKind, vaos: &RendererVAOs) -> &VAO7507 fn get_vao(vertex_array_kind: VertexArrayKind, vaos: &RendererVAOs) -> &VAO {
7508 match vertex_array_kind {
7509 VertexArrayKind::Primitive => &vaos.prim_vao,
7510 VertexArrayKind::Clip => &vaos.clip_vao,
7511 VertexArrayKind::Blur => &vaos.blur_vao,
7512 VertexArrayKind::VectorStencil | VertexArrayKind::VectorCover => unreachable!(),
7513 VertexArrayKind::Border => &vaos.border_vao,
7514 VertexArrayKind::Scale => &vaos.scale_vao,
7515 VertexArrayKind::LineDecoration => &vaos.line_vao,
7516 VertexArrayKind::Gradient => &vaos.gradient_vao,
7517 VertexArrayKind::Resolve => &vaos.resolve_vao,
7518 VertexArrayKind::SvgFilter => &vaos.svg_filter_vao,
7519 VertexArrayKind::Composite => &vaos.composite_vao,
7520 }
7521 }
7522 #[derive(Clone, Copy, PartialEq)]
7523 enum FramebufferKind {
7524 Main,
7525 Other,
7526 }
7527
should_skip_batch(kind: &BatchKind, flags: DebugFlags) -> bool7528 fn should_skip_batch(kind: &BatchKind, flags: DebugFlags) -> bool {
7529 match kind {
7530 BatchKind::TextRun(_) => {
7531 flags.contains(DebugFlags::DISABLE_TEXT_PRIMS)
7532 }
7533 BatchKind::Brush(BrushBatchKind::ConicGradient) |
7534 BatchKind::Brush(BrushBatchKind::RadialGradient) |
7535 BatchKind::Brush(BrushBatchKind::LinearGradient) => {
7536 flags.contains(DebugFlags::DISABLE_GRADIENT_PRIMS)
7537 }
7538 _ => false,
7539 }
7540 }
7541
7542 impl CompositeState {
7543 /// Use the client provided native compositor interface to add all picture
7544 /// cache tiles to the OS compositor
composite_native( &self, compositor: &mut dyn Compositor, )7545 fn composite_native(
7546 &self,
7547 compositor: &mut dyn Compositor,
7548 ) {
7549 // Add each surface to the visual tree. z-order is implicit based on
7550 // order added. Offset and clip rect apply to all tiles within this
7551 // surface.
7552 for surface in &self.descriptor.surfaces {
7553 compositor.add_surface(
7554 surface.surface_id.expect("bug: no native surface allocated"),
7555 surface.offset.to_i32(),
7556 surface.clip_rect.to_i32(),
7557 );
7558 }
7559 }
7560 }
7561