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 use api::{
6 AlphaType, ColorDepth, ColorF, ColorU,
7 ImageKey as ApiImageKey, ImageRendering,
8 PremultipliedColorF, Shadow, YuvColorSpace, ColorRange, YuvFormat,
9 };
10 use api::units::*;
11 use crate::scene_building::{CreateShadow, IsVisible};
12 use crate::frame_builder::FrameBuildingState;
13 use crate::gpu_cache::{GpuCache, GpuDataRequest};
14 use crate::intern::{Internable, InternDebug, Handle as InternHandle};
15 use crate::internal_types::{LayoutPrimitiveInfo};
16 use crate::prim_store::{
17 EdgeAaSegmentMask, OpacityBindingIndex, PrimitiveInstanceKind,
18 PrimitiveOpacity, PrimKey,
19 PrimTemplate, PrimTemplateCommonData, PrimitiveStore, SegmentInstanceIndex,
20 SizeKey, InternablePrimitive,
21 };
22 use crate::render_target::RenderTargetKind;
23 use crate::render_task::{BlitSource, RenderTask};
24 use crate::render_task_cache::{
25 RenderTaskCacheEntryHandle, RenderTaskCacheKey, RenderTaskCacheKeyKind
26 };
27 use crate::resource_cache::{ImageRequest, ResourceCache};
28 use crate::util::pack_as_float;
29
30 #[derive(Debug)]
31 #[cfg_attr(feature = "capture", derive(Serialize))]
32 #[cfg_attr(feature = "replay", derive(Deserialize))]
33 pub struct VisibleImageTile {
34 pub tile_offset: TileOffset,
35 pub edge_flags: EdgeAaSegmentMask,
36 pub local_rect: LayoutRect,
37 pub local_clip_rect: LayoutRect,
38 }
39
40 // Key that identifies a unique (partial) image that is being
41 // stored in the render task cache.
42 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
43 #[cfg_attr(feature = "capture", derive(Serialize))]
44 #[cfg_attr(feature = "replay", derive(Deserialize))]
45 pub struct ImageCacheKey {
46 pub request: ImageRequest,
47 pub texel_rect: Option<DeviceIntRect>,
48 }
49
50 /// Instance specific fields for an image primitive. These are
51 /// currently stored in a separate array to avoid bloating the
52 /// size of PrimitiveInstance. In the future, we should be able
53 /// to remove this and store the information inline, by:
54 /// (a) Removing opacity collapse / binding support completely.
55 /// Once we have general picture caching, we don't need this.
56 /// (b) Change visible_tiles to use Storage in the primitive
57 /// scratch buffer. This will reduce the size of the
58 /// visible_tiles field here, and save memory allocation
59 /// when image tiling is used. I've left it as a Vec for
60 /// now to reduce the number of changes, and because image
61 /// tiling is very rare on real pages.
62 #[derive(Debug)]
63 #[cfg_attr(feature = "capture", derive(Serialize))]
64 pub struct ImageInstance {
65 pub opacity_binding_index: OpacityBindingIndex,
66 pub segment_instance_index: SegmentInstanceIndex,
67 pub tight_local_clip_rect: LayoutRect,
68 pub visible_tiles: Vec<VisibleImageTile>,
69 }
70
71 #[cfg_attr(feature = "capture", derive(Serialize))]
72 #[cfg_attr(feature = "replay", derive(Deserialize))]
73 #[derive(Debug, Clone, Eq, PartialEq, MallocSizeOf, Hash)]
74 pub struct Image {
75 pub key: ApiImageKey,
76 pub stretch_size: SizeKey,
77 pub tile_spacing: SizeKey,
78 pub color: ColorU,
79 pub image_rendering: ImageRendering,
80 pub alpha_type: AlphaType,
81 }
82
83 pub type ImageKey = PrimKey<Image>;
84
85 impl ImageKey {
new( info: &LayoutPrimitiveInfo, image: Image, ) -> Self86 pub fn new(
87 info: &LayoutPrimitiveInfo,
88 image: Image,
89 ) -> Self {
90 ImageKey {
91 common: info.into(),
92 kind: image,
93 }
94 }
95 }
96
97 impl InternDebug for ImageKey {}
98
99 // Where to find the texture data for an image primitive.
100 #[cfg_attr(feature = "capture", derive(Serialize))]
101 #[cfg_attr(feature = "replay", derive(Deserialize))]
102 #[derive(Debug, MallocSizeOf)]
103 pub enum ImageSource {
104 // A normal image - just reference the texture cache.
105 Default,
106 // An image that is pre-rendered into the texture cache
107 // via a render task.
108 Cache {
109 size: DeviceIntSize,
110 handle: Option<RenderTaskCacheEntryHandle>,
111 },
112 }
113
114 #[cfg_attr(feature = "capture", derive(Serialize))]
115 #[cfg_attr(feature = "replay", derive(Deserialize))]
116 #[derive(Debug, MallocSizeOf)]
117 pub struct ImageData {
118 pub key: ApiImageKey,
119 pub stretch_size: LayoutSize,
120 pub tile_spacing: LayoutSize,
121 pub color: ColorF,
122 pub source: ImageSource,
123 pub image_rendering: ImageRendering,
124 pub alpha_type: AlphaType,
125 }
126
127 impl From<Image> for ImageData {
from(image: Image) -> Self128 fn from(image: Image) -> Self {
129 ImageData {
130 key: image.key,
131 color: image.color.into(),
132 stretch_size: image.stretch_size.into(),
133 tile_spacing: image.tile_spacing.into(),
134 source: ImageSource::Default,
135 image_rendering: image.image_rendering,
136 alpha_type: image.alpha_type,
137 }
138 }
139 }
140
141 impl ImageData {
142 /// Update the GPU cache for a given primitive template. This may be called multiple
143 /// times per frame, by each primitive reference that refers to this interned
144 /// template. The initial request call to the GPU cache ensures that work is only
145 /// done if the cache entry is invalid (due to first use or eviction).
update( &mut self, common: &mut PrimTemplateCommonData, frame_state: &mut FrameBuildingState, )146 pub fn update(
147 &mut self,
148 common: &mut PrimTemplateCommonData,
149 frame_state: &mut FrameBuildingState,
150 ) {
151 if let Some(mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) {
152 self.write_prim_gpu_blocks(&mut request);
153 }
154
155 common.opacity = {
156 let image_properties = frame_state
157 .resource_cache
158 .get_image_properties(self.key);
159
160 match image_properties {
161 Some(image_properties) => {
162 let is_tiled = image_properties.tiling.is_some();
163
164 if self.tile_spacing != LayoutSize::zero() && !is_tiled {
165 self.source = ImageSource::Cache {
166 // Size in device-pixels we need to allocate in render task cache.
167 size: image_properties.descriptor.size.to_i32(),
168 handle: None,
169 };
170 }
171
172 let mut is_opaque = image_properties.descriptor.is_opaque();
173 let request = ImageRequest {
174 key: self.key,
175 rendering: self.image_rendering,
176 tile: None,
177 };
178
179 // Every frame, for cached items, we need to request the render
180 // task cache item. The closure will be invoked on the first
181 // time through, and any time the render task output has been
182 // evicted from the texture cache.
183 match self.source {
184 ImageSource::Cache { ref mut size, ref mut handle } => {
185 let padding = DeviceIntSideOffsets::new(
186 0,
187 (self.tile_spacing.width * size.width as f32 / self.stretch_size.width) as i32,
188 (self.tile_spacing.height * size.height as f32 / self.stretch_size.height) as i32,
189 0,
190 );
191
192 size.width += padding.horizontal();
193 size.height += padding.vertical();
194
195 is_opaque &= padding == DeviceIntSideOffsets::zero();
196
197 let image_cache_key = ImageCacheKey {
198 request,
199 texel_rect: None,
200 };
201 let target_kind = if image_properties.descriptor.format.bytes_per_pixel() == 1 {
202 RenderTargetKind::Alpha
203 } else {
204 RenderTargetKind::Color
205 };
206
207 // Request a pre-rendered image task.
208 *handle = Some(frame_state.resource_cache.request_render_task(
209 RenderTaskCacheKey {
210 size: *size,
211 kind: RenderTaskCacheKeyKind::Image(image_cache_key),
212 },
213 frame_state.gpu_cache,
214 frame_state.render_tasks,
215 None,
216 image_properties.descriptor.is_opaque(),
217 |render_tasks| {
218 // Create a task to blit from the texture cache to
219 // a normal transient render task surface. This will
220 // copy only the sub-rect, if specified.
221 // TODO: figure out if/when we can do a blit instead.
222 let cache_to_target_task_id = RenderTask::new_scaling_with_padding(
223 BlitSource::Image { key: image_cache_key },
224 render_tasks,
225 target_kind,
226 *size,
227 padding,
228 );
229
230 // Create a task to blit the rect from the child render
231 // task above back into the right spot in the persistent
232 // render target cache.
233 render_tasks.add().init(RenderTask::new_blit(
234 *size,
235 BlitSource::RenderTask {
236 task_id: cache_to_target_task_id,
237 },
238 ))
239 }
240 ));
241 }
242 ImageSource::Default => {}
243 }
244
245 if is_opaque {
246 PrimitiveOpacity::from_alpha(self.color.a)
247 } else {
248 PrimitiveOpacity::translucent()
249 }
250 }
251 None => {
252 PrimitiveOpacity::opaque()
253 }
254 }
255 };
256 }
257
write_prim_gpu_blocks(&self, request: &mut GpuDataRequest)258 pub fn write_prim_gpu_blocks(&self, request: &mut GpuDataRequest) {
259 // Images are drawn as a white color, modulated by the total
260 // opacity coming from any collapsed property bindings.
261 // Size has to match `VECS_PER_SPECIFIC_BRUSH` from `brush_image.glsl` exactly.
262 request.push(self.color.premultiplied());
263 request.push(PremultipliedColorF::WHITE);
264 request.push([
265 self.stretch_size.width + self.tile_spacing.width,
266 self.stretch_size.height + self.tile_spacing.height,
267 0.0,
268 0.0,
269 ]);
270 }
271 }
272
273 pub type ImageTemplate = PrimTemplate<ImageData>;
274
275 impl From<ImageKey> for ImageTemplate {
from(image: ImageKey) -> Self276 fn from(image: ImageKey) -> Self {
277 let common = PrimTemplateCommonData::with_key_common(image.common);
278
279 ImageTemplate {
280 common,
281 kind: image.kind.into(),
282 }
283 }
284 }
285
286 pub type ImageDataHandle = InternHandle<Image>;
287
288 impl Internable for Image {
289 type Key = ImageKey;
290 type StoreData = ImageTemplate;
291 type InternData = ();
292 }
293
294 impl InternablePrimitive for Image {
into_key( self, info: &LayoutPrimitiveInfo, ) -> ImageKey295 fn into_key(
296 self,
297 info: &LayoutPrimitiveInfo,
298 ) -> ImageKey {
299 ImageKey::new(info, self)
300 }
301
make_instance_kind( _key: ImageKey, data_handle: ImageDataHandle, prim_store: &mut PrimitiveStore, _reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind302 fn make_instance_kind(
303 _key: ImageKey,
304 data_handle: ImageDataHandle,
305 prim_store: &mut PrimitiveStore,
306 _reference_frame_relative_offset: LayoutVector2D,
307 ) -> PrimitiveInstanceKind {
308 // TODO(gw): Refactor this to not need a separate image
309 // instance (see ImageInstance struct).
310 let image_instance_index = prim_store.images.push(ImageInstance {
311 opacity_binding_index: OpacityBindingIndex::INVALID,
312 segment_instance_index: SegmentInstanceIndex::INVALID,
313 tight_local_clip_rect: LayoutRect::zero(),
314 visible_tiles: Vec::new(),
315 });
316
317 PrimitiveInstanceKind::Image {
318 data_handle,
319 image_instance_index,
320 is_compositor_surface: false,
321 }
322 }
323 }
324
325 impl CreateShadow for Image {
create_shadow(&self, shadow: &Shadow) -> Self326 fn create_shadow(&self, shadow: &Shadow) -> Self {
327 Image {
328 tile_spacing: self.tile_spacing,
329 stretch_size: self.stretch_size,
330 key: self.key,
331 image_rendering: self.image_rendering,
332 alpha_type: self.alpha_type,
333 color: shadow.color.into(),
334 }
335 }
336 }
337
338 impl IsVisible for Image {
is_visible(&self) -> bool339 fn is_visible(&self) -> bool {
340 true
341 }
342 }
343
344 ////////////////////////////////////////////////////////////////////////////////
345
346 #[cfg_attr(feature = "capture", derive(Serialize))]
347 #[cfg_attr(feature = "replay", derive(Deserialize))]
348 #[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
349 pub struct YuvImage {
350 pub color_depth: ColorDepth,
351 pub yuv_key: [ApiImageKey; 3],
352 pub format: YuvFormat,
353 pub color_space: YuvColorSpace,
354 pub color_range: ColorRange,
355 pub image_rendering: ImageRendering,
356 }
357
358 pub type YuvImageKey = PrimKey<YuvImage>;
359
360 impl YuvImageKey {
new( info: &LayoutPrimitiveInfo, yuv_image: YuvImage, ) -> Self361 pub fn new(
362 info: &LayoutPrimitiveInfo,
363 yuv_image: YuvImage,
364 ) -> Self {
365 YuvImageKey {
366 common: info.into(),
367 kind: yuv_image,
368 }
369 }
370 }
371
372 impl InternDebug for YuvImageKey {}
373
374 #[cfg_attr(feature = "capture", derive(Serialize))]
375 #[cfg_attr(feature = "replay", derive(Deserialize))]
376 #[derive(MallocSizeOf)]
377 pub struct YuvImageData {
378 pub color_depth: ColorDepth,
379 pub yuv_key: [ApiImageKey; 3],
380 pub format: YuvFormat,
381 pub color_space: YuvColorSpace,
382 pub color_range: ColorRange,
383 pub image_rendering: ImageRendering,
384 }
385
386 impl From<YuvImage> for YuvImageData {
from(image: YuvImage) -> Self387 fn from(image: YuvImage) -> Self {
388 YuvImageData {
389 color_depth: image.color_depth,
390 yuv_key: image.yuv_key,
391 format: image.format,
392 color_space: image.color_space,
393 color_range: image.color_range,
394 image_rendering: image.image_rendering,
395 }
396 }
397 }
398
399 impl YuvImageData {
400 /// Update the GPU cache for a given primitive template. This may be called multiple
401 /// times per frame, by each primitive reference that refers to this interned
402 /// template. The initial request call to the GPU cache ensures that work is only
403 /// done if the cache entry is invalid (due to first use or eviction).
update( &mut self, common: &mut PrimTemplateCommonData, frame_state: &mut FrameBuildingState, )404 pub fn update(
405 &mut self,
406 common: &mut PrimTemplateCommonData,
407 frame_state: &mut FrameBuildingState,
408 ) {
409 if let Some(mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) {
410 self.write_prim_gpu_blocks(&mut request);
411 };
412
413 // YUV images never have transparency
414 common.opacity = PrimitiveOpacity::opaque();
415 }
416
request_resources( &mut self, resource_cache: &mut ResourceCache, gpu_cache: &mut GpuCache, )417 pub fn request_resources(
418 &mut self,
419 resource_cache: &mut ResourceCache,
420 gpu_cache: &mut GpuCache,
421 ) {
422 let channel_num = self.format.get_plane_num();
423 debug_assert!(channel_num <= 3);
424 for channel in 0 .. channel_num {
425 resource_cache.request_image(
426 ImageRequest {
427 key: self.yuv_key[channel],
428 rendering: self.image_rendering,
429 tile: None,
430 },
431 gpu_cache,
432 );
433 }
434 }
435
write_prim_gpu_blocks(&self, request: &mut GpuDataRequest)436 pub fn write_prim_gpu_blocks(&self, request: &mut GpuDataRequest) {
437 request.push([
438 self.color_depth.rescaling_factor(),
439 pack_as_float(self.color_space as u32),
440 pack_as_float(self.format as u32),
441 0.0
442 ]);
443 }
444 }
445
446 pub type YuvImageTemplate = PrimTemplate<YuvImageData>;
447
448 impl From<YuvImageKey> for YuvImageTemplate {
from(image: YuvImageKey) -> Self449 fn from(image: YuvImageKey) -> Self {
450 let common = PrimTemplateCommonData::with_key_common(image.common);
451
452 YuvImageTemplate {
453 common,
454 kind: image.kind.into(),
455 }
456 }
457 }
458
459 pub type YuvImageDataHandle = InternHandle<YuvImage>;
460
461 impl Internable for YuvImage {
462 type Key = YuvImageKey;
463 type StoreData = YuvImageTemplate;
464 type InternData = ();
465 }
466
467 impl InternablePrimitive for YuvImage {
into_key( self, info: &LayoutPrimitiveInfo, ) -> YuvImageKey468 fn into_key(
469 self,
470 info: &LayoutPrimitiveInfo,
471 ) -> YuvImageKey {
472 YuvImageKey::new(info, self)
473 }
474
make_instance_kind( _key: YuvImageKey, data_handle: YuvImageDataHandle, _prim_store: &mut PrimitiveStore, _reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind475 fn make_instance_kind(
476 _key: YuvImageKey,
477 data_handle: YuvImageDataHandle,
478 _prim_store: &mut PrimitiveStore,
479 _reference_frame_relative_offset: LayoutVector2D,
480 ) -> PrimitiveInstanceKind {
481 PrimitiveInstanceKind::YuvImage {
482 data_handle,
483 segment_instance_index: SegmentInstanceIndex::INVALID,
484 is_compositor_surface: false,
485 }
486 }
487 }
488
489 impl IsVisible for YuvImage {
is_visible(&self) -> bool490 fn is_visible(&self) -> bool {
491 true
492 }
493 }
494
495 #[test]
496 #[cfg(target_pointer_width = "64")]
test_struct_sizes()497 fn test_struct_sizes() {
498 use std::mem;
499 // The sizes of these structures are critical for performance on a number of
500 // talos stress tests. If you get a failure here on CI, there's two possibilities:
501 // (a) You made a structure smaller than it currently is. Great work! Update the
502 // test expectations and move on.
503 // (b) You made a structure larger. This is not necessarily a problem, but should only
504 // be done with care, and after checking if talos performance regresses badly.
505 assert_eq!(mem::size_of::<Image>(), 32, "Image size changed");
506 assert_eq!(mem::size_of::<ImageTemplate>(), 92, "ImageTemplate size changed");
507 assert_eq!(mem::size_of::<ImageKey>(), 52, "ImageKey size changed");
508 assert_eq!(mem::size_of::<YuvImage>(), 32, "YuvImage size changed");
509 assert_eq!(mem::size_of::<YuvImageTemplate>(), 60, "YuvImageTemplate size changed");
510 assert_eq!(mem::size_of::<YuvImageKey>(), 52, "YuvImageKey size changed");
511 }
512