vertex.rs (22469B)
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 //! Rendering logic related to the vertex shaders and their states, uncluding 6 //! - Vertex Array Objects 7 //! - vertex layout descriptors 8 //! - textures bound at vertex stage 9 10 use std::{marker::PhantomData, mem, num::NonZeroUsize, ops}; 11 use api::units::*; 12 use crate::{ 13 device::{ 14 Device, Texture, TextureFilter, TextureUploader, UploadPBOPool, VertexUsageHint, VAO, 15 }, 16 frame_builder::Frame, 17 gpu_types::{PrimitiveHeaderI, PrimitiveHeaderF, TransformData}, 18 internal_types::Swizzle, 19 render_task::RenderTaskData, 20 }; 21 22 use crate::internal_types::FrameVec; 23 24 pub const VERTEX_TEXTURE_EXTRA_ROWS: i32 = 10; 25 26 pub const MAX_VERTEX_TEXTURE_WIDTH: usize = webrender_build::MAX_VERTEX_TEXTURE_WIDTH; 27 28 pub mod desc { 29 use crate::device::{VertexAttribute, VertexAttributeKind, VertexDescriptor}; 30 31 pub const PRIM_INSTANCES: VertexDescriptor = VertexDescriptor { 32 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 33 instance_attributes: &[VertexAttribute { 34 name: "aData", 35 count: 4, 36 kind: VertexAttributeKind::I32, 37 }], 38 }; 39 40 pub const BLUR: VertexDescriptor = VertexDescriptor { 41 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 42 instance_attributes: &[ 43 VertexAttribute::gpu_buffer_address("aBlurRenderTaskAddress"), 44 VertexAttribute::gpu_buffer_address("aBlurSourceTaskAddress"), 45 VertexAttribute::i32("aBlurDirection"), 46 VertexAttribute::i32("aBlurEdgeMode"), 47 VertexAttribute::f32x3("aBlurParams"), 48 ], 49 }; 50 51 pub const LINE: VertexDescriptor = VertexDescriptor { 52 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 53 instance_attributes: &[ 54 VertexAttribute::f32x4("aTaskRect"), 55 VertexAttribute::f32x2("aLocalSize"), 56 VertexAttribute::f32("aWavyLineThickness"), 57 VertexAttribute::i32("aStyle"), 58 VertexAttribute::f32("aAxisSelect"), 59 ], 60 }; 61 62 pub const FAST_LINEAR_GRADIENT: VertexDescriptor = VertexDescriptor { 63 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 64 instance_attributes: &[ 65 VertexAttribute::f32x4("aTaskRect"), 66 VertexAttribute::f32x4("aColor0"), 67 VertexAttribute::f32x4("aColor1"), 68 VertexAttribute::f32("aAxisSelect"), 69 ], 70 }; 71 72 pub const LINEAR_GRADIENT: VertexDescriptor = VertexDescriptor { 73 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 74 instance_attributes: &[ 75 VertexAttribute::f32x4("aTaskRect"), 76 VertexAttribute::f32x2("aStartPoint"), 77 VertexAttribute::f32x2("aEndPoint"), 78 VertexAttribute::f32x2("aScale"), 79 VertexAttribute::i32("aExtendMode"), 80 VertexAttribute::gpu_buffer_address("aGradientStopsAddress"), 81 ], 82 }; 83 84 pub const RADIAL_GRADIENT: VertexDescriptor = VertexDescriptor { 85 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 86 instance_attributes: &[ 87 VertexAttribute::f32x4("aTaskRect"), 88 VertexAttribute::f32x2("aCenter"), 89 VertexAttribute::f32x2("aScale"), 90 VertexAttribute::f32("aStartRadius"), 91 VertexAttribute::f32("aEndRadius"), 92 VertexAttribute::f32("aXYRatio"), 93 VertexAttribute::i32("aExtendMode"), 94 VertexAttribute::i32("aGradientStopsAddress"), 95 ], 96 }; 97 98 pub const CONIC_GRADIENT: VertexDescriptor = VertexDescriptor { 99 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 100 instance_attributes: &[ 101 VertexAttribute::f32x4("aTaskRect"), 102 VertexAttribute::f32x2("aCenter"), 103 VertexAttribute::f32x2("aScale"), 104 VertexAttribute::f32("aStartOffset"), 105 VertexAttribute::f32("aEndOffset"), 106 VertexAttribute::f32("aAngle"), 107 VertexAttribute::i32("aExtendMode"), 108 VertexAttribute::gpu_buffer_address("aGradientStopsAddress"), 109 ], 110 }; 111 112 pub const BORDER: VertexDescriptor = VertexDescriptor { 113 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 114 instance_attributes: &[ 115 VertexAttribute::f32x2("aTaskOrigin"), 116 VertexAttribute::f32x4("aRect"), 117 VertexAttribute::f32x4("aColor0"), 118 VertexAttribute::f32x4("aColor1"), 119 VertexAttribute::i32("aFlags"), 120 VertexAttribute::f32x2("aWidths"), 121 VertexAttribute::f32x2("aRadii"), 122 VertexAttribute::f32x4("aClipParams1"), 123 VertexAttribute::f32x4("aClipParams2"), 124 ], 125 }; 126 127 pub const SCALE: VertexDescriptor = VertexDescriptor { 128 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 129 instance_attributes: &[ 130 VertexAttribute::f32x4("aScaleTargetRect"), 131 VertexAttribute::f32x4("aScaleSourceRect"), 132 VertexAttribute::f32("aSourceRectType"), 133 ], 134 }; 135 136 pub const CLIP_RECT: VertexDescriptor = VertexDescriptor { 137 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 138 instance_attributes: &[ 139 // common clip attributes 140 VertexAttribute::f32x4("aClipDeviceArea"), 141 VertexAttribute::f32x4("aClipOrigins"), 142 VertexAttribute::f32("aDevicePixelScale"), 143 VertexAttribute::i32x2("aTransformIds"), 144 // specific clip attributes 145 VertexAttribute::f32x2("aClipLocalPos"), 146 VertexAttribute::f32x4("aClipLocalRect"), 147 VertexAttribute::f32("aClipMode"), 148 VertexAttribute::f32x4("aClipRect_TL"), 149 VertexAttribute::f32x4("aClipRadii_TL"), 150 VertexAttribute::f32x4("aClipRect_TR"), 151 VertexAttribute::f32x4("aClipRadii_TR"), 152 VertexAttribute::f32x4("aClipRect_BL"), 153 VertexAttribute::f32x4("aClipRadii_BL"), 154 VertexAttribute::f32x4("aClipRect_BR"), 155 VertexAttribute::f32x4("aClipRadii_BR"), 156 ], 157 }; 158 159 pub const CLIP_BOX_SHADOW: VertexDescriptor = VertexDescriptor { 160 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 161 instance_attributes: &[ 162 // common clip attributes 163 VertexAttribute::f32x4("aClipDeviceArea"), 164 VertexAttribute::f32x4("aClipOrigins"), 165 VertexAttribute::f32("aDevicePixelScale"), 166 VertexAttribute::i32x2("aTransformIds"), 167 // specific clip attributes 168 VertexAttribute::gpu_buffer_address("aClipDataResourceAddress"), 169 VertexAttribute::f32x2("aClipSrcRectSize"), 170 VertexAttribute::i32("aClipMode"), 171 VertexAttribute::i32x2("aStretchMode"), 172 VertexAttribute::f32x4("aClipDestRect"), 173 ], 174 }; 175 176 pub const SVG_FILTER_NODE: VertexDescriptor = VertexDescriptor { 177 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 178 instance_attributes: &[ 179 VertexAttribute::f32x4("aFilterTargetRect"), 180 VertexAttribute::f32x4("aFilterInput1ContentScaleAndOffset"), 181 VertexAttribute::f32x4("aFilterInput2ContentScaleAndOffset"), 182 VertexAttribute::gpu_buffer_address("aFilterInput1TaskAddress"), 183 VertexAttribute::gpu_buffer_address("aFilterInput2TaskAddress"), 184 VertexAttribute::u16("aFilterKind"), 185 VertexAttribute::u16("aFilterInputCount"), 186 VertexAttribute::gpu_buffer_address("aFilterExtraDataAddress"), 187 ], 188 }; 189 190 pub const MASK: VertexDescriptor = VertexDescriptor { 191 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 192 instance_attributes: &[ 193 VertexAttribute::i32x4("aData"), 194 VertexAttribute::i32x4("aClipData"), 195 ], 196 }; 197 198 pub const COMPOSITE: VertexDescriptor = VertexDescriptor { 199 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 200 instance_attributes: &[ 201 VertexAttribute::f32x4("aDeviceRect"), 202 VertexAttribute::f32x4("aDeviceClipRect"), 203 VertexAttribute::f32x4("aColor"), 204 VertexAttribute::f32x4("aParams"), 205 VertexAttribute::f32x4("aUvRect0"), 206 VertexAttribute::f32x4("aUvRect1"), 207 VertexAttribute::f32x4("aUvRect2"), 208 VertexAttribute::f32x2("aFlip"), 209 VertexAttribute::f32x4("aDeviceRoundedClipRect"), 210 VertexAttribute::f32x4("aDeviceRoundedClipRadii"), 211 ], 212 }; 213 214 pub const CLEAR: VertexDescriptor = VertexDescriptor { 215 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 216 instance_attributes: &[ 217 VertexAttribute::f32x4("aRect"), 218 VertexAttribute::f32x4("aColor"), 219 ], 220 }; 221 222 pub const COPY: VertexDescriptor = VertexDescriptor { 223 vertex_attributes: &[VertexAttribute::quad_instance_vertex()], 224 instance_attributes: &[ 225 VertexAttribute::f32x4("a_src_rect"), 226 VertexAttribute::f32x4("a_dst_rect"), 227 VertexAttribute::f32x2("a_dst_texture_size"), 228 ], 229 }; 230 } 231 232 #[derive(Debug, Copy, Clone, PartialEq)] 233 pub enum VertexArrayKind { 234 Primitive, 235 Blur, 236 ClipRect, 237 ClipBoxShadow, 238 Border, 239 Scale, 240 LineDecoration, 241 FastLinearGradient, 242 LinearGradient, 243 RadialGradient, 244 ConicGradient, 245 SvgFilterNode, 246 Composite, 247 Clear, 248 Copy, 249 Mask, 250 } 251 252 pub struct VertexDataTexture<T> { 253 texture: Option<Texture>, 254 format: api::ImageFormat, 255 _marker: PhantomData<T>, 256 } 257 258 impl<T> VertexDataTexture<T> { 259 pub fn new(format: api::ImageFormat) -> Self { 260 Self { 261 texture: None, 262 format, 263 _marker: PhantomData, 264 } 265 } 266 267 /// Returns a borrow of the GPU texture. Panics if it hasn't been initialized. 268 pub fn texture(&self) -> &Texture { 269 self.texture.as_ref().unwrap() 270 } 271 272 /// Returns an estimate of the GPU memory consumed by this VertexDataTexture. 273 pub fn size_in_bytes(&self) -> usize { 274 self.texture.as_ref().map_or(0, |t| t.size_in_bytes()) 275 } 276 277 pub fn update<'a>( 278 &'a mut self, 279 device: &mut Device, 280 texture_uploader: &mut TextureUploader<'a>, 281 data: &mut FrameVec<T>, 282 ) { 283 debug_assert!(mem::size_of::<T>() % 16 == 0); 284 let texels_per_item = mem::size_of::<T>() / 16; 285 let items_per_row = MAX_VERTEX_TEXTURE_WIDTH / texels_per_item; 286 debug_assert_ne!(items_per_row, 0); 287 288 // Ensure we always end up with a texture when leaving this method. 289 let mut len = data.len(); 290 if len == 0 { 291 if self.texture.is_some() { 292 return; 293 } 294 data.reserve(items_per_row); 295 len = items_per_row; 296 } else { 297 // Extend the data array to have enough capacity to upload at least 298 // a multiple of the row size. This ensures memory safety when the 299 // array is passed to OpenGL to upload to the GPU. 300 let extra = len % items_per_row; 301 if extra != 0 { 302 let padding = items_per_row - extra; 303 data.reserve(padding); 304 len += padding; 305 } 306 } 307 308 let needed_height = (len / items_per_row) as i32; 309 let existing_height = self 310 .texture 311 .as_ref() 312 .map_or(0, |t| t.get_dimensions().height); 313 314 // Create a new texture if needed. 315 // 316 // These textures are generally very small, which is why we don't bother 317 // with incremental updates and just re-upload every frame. For most pages 318 // they're one row each, and on stress tests like css-francine they end up 319 // in the 6-14 range. So we size the texture tightly to what we need (usually 320 // 1), and shrink it if the waste would be more than `VERTEX_TEXTURE_EXTRA_ROWS` 321 // rows. This helps with memory overhead, especially because there are several 322 // instances of these textures per Renderer. 323 if needed_height > existing_height 324 || needed_height + VERTEX_TEXTURE_EXTRA_ROWS < existing_height 325 { 326 // Drop the existing texture, if any. 327 if let Some(t) = self.texture.take() { 328 device.delete_texture(t); 329 } 330 331 let texture = device.create_texture( 332 api::ImageBufferKind::Texture2D, 333 self.format, 334 MAX_VERTEX_TEXTURE_WIDTH as i32, 335 // Ensure height is at least two to work around 336 // https://bugs.chromium.org/p/angleproject/issues/detail?id=3039 337 needed_height.max(2), 338 TextureFilter::Nearest, 339 None, 340 ); 341 self.texture = Some(texture); 342 } 343 344 // Note: the actual width can be larger than the logical one, with a few texels 345 // of each row unused at the tail. This is needed because there is still hardware 346 // (like Intel iGPUs) that prefers power-of-two sizes of textures ([1]). 347 // 348 // [1] https://software.intel.com/en-us/articles/opengl-performance-tips-power-of-two-textures-have-better-performance 349 let logical_width = if needed_height == 1 { 350 data.len() * texels_per_item 351 } else { 352 MAX_VERTEX_TEXTURE_WIDTH - (MAX_VERTEX_TEXTURE_WIDTH % texels_per_item) 353 }; 354 355 let rect = DeviceIntRect::from_size( 356 DeviceIntSize::new(logical_width as i32, needed_height), 357 ); 358 359 debug_assert!(len <= data.capacity(), "CPU copy will read out of bounds"); 360 texture_uploader.upload( 361 device, 362 self.texture(), 363 rect, 364 None, 365 None, 366 data.as_ptr(), 367 len, 368 ); 369 } 370 371 pub fn deinit(mut self, device: &mut Device) { 372 if let Some(t) = self.texture.take() { 373 device.delete_texture(t); 374 } 375 } 376 } 377 378 pub struct VertexDataTextures { 379 prim_header_f_texture: VertexDataTexture<PrimitiveHeaderF>, 380 prim_header_i_texture: VertexDataTexture<PrimitiveHeaderI>, 381 transforms_texture: VertexDataTexture<TransformData>, 382 render_task_texture: VertexDataTexture<RenderTaskData>, 383 } 384 385 impl VertexDataTextures { 386 pub fn new() -> Self { 387 VertexDataTextures { 388 prim_header_f_texture: VertexDataTexture::new(api::ImageFormat::RGBAF32), 389 prim_header_i_texture: VertexDataTexture::new(api::ImageFormat::RGBAI32), 390 transforms_texture: VertexDataTexture::new(api::ImageFormat::RGBAF32), 391 render_task_texture: VertexDataTexture::new(api::ImageFormat::RGBAF32), 392 } 393 } 394 395 pub fn update(&mut self, device: &mut Device, pbo_pool: &mut UploadPBOPool, frame: &mut Frame) { 396 let mut texture_uploader = device.upload_texture(pbo_pool); 397 self.prim_header_f_texture.update( 398 device, 399 &mut texture_uploader, 400 &mut frame.prim_headers.headers_float, 401 ); 402 self.prim_header_i_texture.update( 403 device, 404 &mut texture_uploader, 405 &mut frame.prim_headers.headers_int, 406 ); 407 self.transforms_texture 408 .update(device, &mut texture_uploader, &mut frame.transform_palette); 409 self.render_task_texture.update( 410 device, 411 &mut texture_uploader, 412 &mut frame.render_tasks.task_data, 413 ); 414 415 // Flush and drop the texture uploader now, so that 416 // we can borrow the textures to bind them. 417 texture_uploader.flush(device); 418 419 device.bind_texture( 420 super::TextureSampler::PrimitiveHeadersF, 421 &self.prim_header_f_texture.texture(), 422 Swizzle::default(), 423 ); 424 device.bind_texture( 425 super::TextureSampler::PrimitiveHeadersI, 426 &self.prim_header_i_texture.texture(), 427 Swizzle::default(), 428 ); 429 device.bind_texture( 430 super::TextureSampler::TransformPalette, 431 &self.transforms_texture.texture(), 432 Swizzle::default(), 433 ); 434 device.bind_texture( 435 super::TextureSampler::RenderTasks, 436 &self.render_task_texture.texture(), 437 Swizzle::default(), 438 ); 439 } 440 441 pub fn size_in_bytes(&self) -> usize { 442 self.prim_header_f_texture.size_in_bytes() 443 + self.prim_header_i_texture.size_in_bytes() 444 + self.transforms_texture.size_in_bytes() 445 + self.render_task_texture.size_in_bytes() 446 } 447 448 pub fn deinit(self, device: &mut Device) { 449 self.transforms_texture.deinit(device); 450 self.prim_header_f_texture.deinit(device); 451 self.prim_header_i_texture.deinit(device); 452 self.render_task_texture.deinit(device); 453 } 454 } 455 456 pub struct RendererVAOs { 457 prim_vao: VAO, 458 blur_vao: VAO, 459 clip_rect_vao: VAO, 460 clip_box_shadow_vao: VAO, 461 border_vao: VAO, 462 line_vao: VAO, 463 scale_vao: VAO, 464 fast_linear_gradient_vao: VAO, 465 linear_gradient_vao: VAO, 466 radial_gradient_vao: VAO, 467 conic_gradient_vao: VAO, 468 svg_filter_node_vao: VAO, 469 composite_vao: VAO, 470 clear_vao: VAO, 471 copy_vao: VAO, 472 mask_vao: VAO, 473 } 474 475 impl RendererVAOs { 476 pub fn new(device: &mut Device, indexed_quads: Option<NonZeroUsize>) -> Self { 477 const QUAD_INDICES: [u16; 6] = [0, 1, 2, 2, 1, 3]; 478 const QUAD_VERTICES: [[u8; 2]; 4] = [[0, 0], [0xFF, 0], [0, 0xFF], [0xFF, 0xFF]]; 479 480 let instance_divisor = if indexed_quads.is_some() { 0 } else { 1 }; 481 let prim_vao = device.create_vao(&desc::PRIM_INSTANCES, instance_divisor); 482 483 device.bind_vao(&prim_vao); 484 match indexed_quads { 485 Some(count) => { 486 assert!(count.get() < u16::MAX as usize); 487 let quad_indices = (0 .. count.get() as u16) 488 .flat_map(|instance| QUAD_INDICES.iter().map(move |&index| instance * 4 + index)) 489 .collect::<Vec<_>>(); 490 device.update_vao_indices(&prim_vao, &quad_indices, VertexUsageHint::Static); 491 let quad_vertices = (0 .. count.get() as u16) 492 .flat_map(|_| QUAD_VERTICES.iter().cloned()) 493 .collect::<Vec<_>>(); 494 device.update_vao_main_vertices(&prim_vao, &quad_vertices, VertexUsageHint::Static); 495 } 496 None => { 497 device.update_vao_indices(&prim_vao, &QUAD_INDICES, VertexUsageHint::Static); 498 device.update_vao_main_vertices(&prim_vao, &QUAD_VERTICES, VertexUsageHint::Static); 499 } 500 } 501 502 RendererVAOs { 503 blur_vao: device.create_vao_with_new_instances(&desc::BLUR, &prim_vao), 504 clip_rect_vao: device.create_vao_with_new_instances(&desc::CLIP_RECT, &prim_vao), 505 clip_box_shadow_vao: device 506 .create_vao_with_new_instances(&desc::CLIP_BOX_SHADOW, &prim_vao), 507 border_vao: device.create_vao_with_new_instances(&desc::BORDER, &prim_vao), 508 scale_vao: device.create_vao_with_new_instances(&desc::SCALE, &prim_vao), 509 line_vao: device.create_vao_with_new_instances(&desc::LINE, &prim_vao), 510 fast_linear_gradient_vao: device.create_vao_with_new_instances(&desc::FAST_LINEAR_GRADIENT, &prim_vao), 511 linear_gradient_vao: device.create_vao_with_new_instances(&desc::LINEAR_GRADIENT, &prim_vao), 512 radial_gradient_vao: device.create_vao_with_new_instances(&desc::RADIAL_GRADIENT, &prim_vao), 513 conic_gradient_vao: device.create_vao_with_new_instances(&desc::CONIC_GRADIENT, &prim_vao), 514 svg_filter_node_vao: device.create_vao_with_new_instances(&desc::SVG_FILTER_NODE, &prim_vao), 515 composite_vao: device.create_vao_with_new_instances(&desc::COMPOSITE, &prim_vao), 516 clear_vao: device.create_vao_with_new_instances(&desc::CLEAR, &prim_vao), 517 copy_vao: device.create_vao_with_new_instances(&desc::COPY, &prim_vao), 518 mask_vao: device.create_vao_with_new_instances(&desc::MASK, &prim_vao), 519 prim_vao, 520 } 521 } 522 523 pub fn deinit(self, device: &mut Device) { 524 device.delete_vao(self.prim_vao); 525 device.delete_vao(self.clip_rect_vao); 526 device.delete_vao(self.clip_box_shadow_vao); 527 device.delete_vao(self.fast_linear_gradient_vao); 528 device.delete_vao(self.linear_gradient_vao); 529 device.delete_vao(self.radial_gradient_vao); 530 device.delete_vao(self.conic_gradient_vao); 531 device.delete_vao(self.blur_vao); 532 device.delete_vao(self.line_vao); 533 device.delete_vao(self.border_vao); 534 device.delete_vao(self.scale_vao); 535 device.delete_vao(self.svg_filter_node_vao); 536 device.delete_vao(self.composite_vao); 537 device.delete_vao(self.clear_vao); 538 device.delete_vao(self.copy_vao); 539 device.delete_vao(self.mask_vao); 540 } 541 } 542 543 impl ops::Index<VertexArrayKind> for RendererVAOs { 544 type Output = VAO; 545 fn index(&self, kind: VertexArrayKind) -> &VAO { 546 match kind { 547 VertexArrayKind::Primitive => &self.prim_vao, 548 VertexArrayKind::ClipRect => &self.clip_rect_vao, 549 VertexArrayKind::ClipBoxShadow => &self.clip_box_shadow_vao, 550 VertexArrayKind::Blur => &self.blur_vao, 551 VertexArrayKind::Border => &self.border_vao, 552 VertexArrayKind::Scale => &self.scale_vao, 553 VertexArrayKind::LineDecoration => &self.line_vao, 554 VertexArrayKind::FastLinearGradient => &self.fast_linear_gradient_vao, 555 VertexArrayKind::LinearGradient => &self.linear_gradient_vao, 556 VertexArrayKind::RadialGradient => &self.radial_gradient_vao, 557 VertexArrayKind::ConicGradient => &self.conic_gradient_vao, 558 VertexArrayKind::SvgFilterNode => &self.svg_filter_node_vao, 559 VertexArrayKind::Composite => &self.composite_vao, 560 VertexArrayKind::Clear => &self.clear_vao, 561 VertexArrayKind::Copy => &self.copy_vao, 562 VertexArrayKind::Mask => &self.mask_vao, 563 } 564 } 565 }