/* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2 . 0 . If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Rendering logic related to the vertex shaders and their states, uncluding
//! - Vertex Array Objects
//! - vertex layout descriptors
//! - textures bound at vertex stage
use std::{marker::PhantomData, mem, num::NonZeroUsize, ops};
use api::units::*;
use crate ::{
device::{
Device, Texture, TextureFilter, TextureUploader, UploadPBOPool, VertexUsageHint, VAO,
},
frame_builder::Frame,
gpu_types::{PrimitiveHeaderI, PrimitiveHeaderF},
internal_types::Swizzle,
render_task::RenderTaskData,
transform::TransformData,
};
use crate ::internal_types::FrameVec;
pub const VERTEX_TEXTURE_EXTRA_ROWS: i32 =
10 ;
pub const MAX_VERTEX_TEXTURE_WIDTH: usize = webrender_build::MAX_VERTEX_TEXTURE_WIDTH
;
pub mod desc {
use crate ::device::{VertexAttribute, VertexAttributeKind, VertexDescriptor};
pub const PRIM_INSTANCES: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
instance_attributes: &[VertexAttribute {
name: "aData" ,
count: 4 ,
kind: VertexAttributeKind::I32,
}],
};
pub const BLUR: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
instance_attributes: &[
VertexAttribute::gpu_buffer_address("aBlurRenderTaskAddress" ),
VertexAttribute::gpu_buffer_address("aBlurSourceTaskAddress" ),
VertexAttribute::i32("aBlurDirection" ),
VertexAttribute::i32("aBlurEdgeMode" ),
VertexAttribute::f32x3("aBlurParams" ),
],
};
pub const LINE: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
instance_attributes: &[
VertexAttribute::f32x4("aTaskRect" ),
VertexAttribute::f32x2("aLocalSize" ),
VertexAttribute::f32("aWavyLineThickness" ),
VertexAttribute::i32("aStyle" ),
VertexAttribute::f32("aAxisSelect" ),
],
};
pub const BORDER: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
instance_attributes: &[
VertexAttribute::f32x2("aTaskOrigin" ),
VertexAttribute::f32x4("aRect" ),
VertexAttribute::f32x4("aColor0" ),
VertexAttribute::f32x4("aColor1" ),
VertexAttribute::i32("aFlags" ),
VertexAttribute::f32x2("aWidths" ),
VertexAttribute::f32x2("aRadii" ),
VertexAttribute::f32x4("aClipParams1" ),
VertexAttribute::f32x4("aClipParams2" ),
],
};
pub const SCALE: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
instance_attributes: &[
VertexAttribute::f32x4("aScaleTargetRect" ),
VertexAttribute::f32x4("aScaleSourceRect" ),
VertexAttribute::f32("aSourceRectType" ),
],
};
pub const CLIP_RECT: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
instance_attributes: &[
// common clip attributes
VertexAttribute::f32x4("aClipDeviceArea" ),
VertexAttribute::f32x4("aClipOrigins" ),
VertexAttribute::f32("aDevicePixelScale" ),
VertexAttribute::i32x2("aTransformIds" ),
// specific clip attributes
VertexAttribute::f32x2("aClipLocalPos" ),
VertexAttribute::f32x4("aClipLocalRect" ),
VertexAttribute::f32("aClipMode" ),
VertexAttribute::f32x4("aClipRect_TL" ),
VertexAttribute::f32x4("aClipRadii_TL" ),
VertexAttribute::f32x4("aClipRect_TR" ),
VertexAttribute::f32x4("aClipRadii_TR" ),
VertexAttribute::f32x4("aClipRect_BL" ),
VertexAttribute::f32x4("aClipRadii_BL" ),
VertexAttribute::f32x4("aClipRect_BR" ),
VertexAttribute::f32x4("aClipRadii_BR" ),
],
};
pub const SVG_FILTER_NODE: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
instance_attributes: &[
VertexAttribute::f32x4("aFilterTargetRect" ),
VertexAttribute::f32x4("aFilterInput1ContentScaleAndOffset" ),
VertexAttribute::f32x4("aFilterInput2ContentScaleAndOffset" ),
VertexAttribute::gpu_buffer_address("aFilterInput1TaskAddress" ),
VertexAttribute::gpu_buffer_address("aFilterInput2TaskAddress" ),
VertexAttribute::u16("aFilterKind" ),
VertexAttribute::u16("aFilterInputCount" ),
VertexAttribute::gpu_buffer_address("aFilterExtraDataAddress" ),
],
};
pub const MASK: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
instance_attributes: &[
VertexAttribute::i32x4("aData" ),
VertexAttribute::i32x4("aClipData" ),
],
};
pub const COMPOSITE: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
instance_attributes: &[
VertexAttribute::f32x4("aDeviceRect" ),
VertexAttribute::f32x4("aDeviceClipRect" ),
VertexAttribute::f32x4("aColor" ),
VertexAttribute::f32x4("aParams" ),
VertexAttribute::f32x4("aUvRect0" ),
VertexAttribute::f32x4("aUvRect1" ),
VertexAttribute::f32x4("aUvRect2" ),
VertexAttribute::f32x2("aFlip" ),
VertexAttribute::f32x4("aDeviceRoundedClipRect" ),
VertexAttribute::f32x4("aDeviceRoundedClipRadii" ),
],
};
pub const CLEAR: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
instance_attributes: &[
VertexAttribute::f32x4("aRect" ),
VertexAttribute::f32x4("aColor" ),
],
};
pub const COPY: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
instance_attributes: &[
VertexAttribute::f32x4("a_src_rect" ),
VertexAttribute::f32x4("a_dst_rect" ),
VertexAttribute::f32x2("a_dst_texture_size" ),
],
};
}
#[ derive(Debug, Copy, Clone, PartialEq)]
pub enum VertexArrayKind {
Primitive,
Blur,
ClipRect,
Border,
Scale,
LineDecoration,
SvgFilterNode,
Composite,
Clear,
Copy,
Mask,
}
pub struct VertexDataTexture<T> {
texture: Option<Texture>,
format: api::ImageFormat,
_marker: PhantomData<T>,
}
impl <T> VertexDataTexture<T> {
pub fn new(format: api::ImageFormat) -> Self {
Self {
texture: None,
format,
_marker: PhantomData,
}
}
/// Returns a borrow of the GPU texture. Panics if it hasn't been initialized.
pub fn texture(&self ) -> &Texture {
self .texture.as_ref().unwrap()
}
/// Returns an estimate of the GPU memory consumed by this VertexDataTexture.
pub fn size_in_bytes(&self ) -> usize {
self .texture.as_ref().map_or(0 , |t| t.size_in_bytes())
}
pub fn update<'a>(
&'a mut self,
device: &mut Device,
texture_uploader: &mut TextureUploader<'a>,
data: &mut FrameVec<T>,
) {
debug_assert!(mem::size_of::<T>() % 16 == 0 );
let texels_per_item = mem::size_of::<T>() / 16 ;
let items_per_row = MAX_VERTEX_TEXTURE_WIDTH / texels_per_item;
debug_assert_ne!(items_per_row, 0 );
// Ensure we always end up with a texture when leaving this method.
let mut len = data.len();
if len == 0 {
if self .texture.is_some() {
return ;
}
data.reserve(items_per_row);
len = items_per_row;
} else {
// Extend the data array to have enough capacity to upload at least
// a multiple of the row size. This ensures memory safety when the
// array is passed to OpenGL to upload to the GPU.
let extra = len % items_per_row;
if extra != 0 {
let padding = items_per_row - extra;
data.reserve(padding);
len += padding;
}
}
let needed_height = (len / items_per_row) as i32;
let existing_height = self
.texture
.as_ref()
.map_or(0 , |t| t.get_dimensions().height);
// Create a new texture if needed.
//
// These textures are generally very small, which is why we don't bother
// with incremental updates and just re-upload every frame. For most pages
// they're one row each, and on stress tests like css-francine they end up
// in the 6-14 range. So we size the texture tightly to what we need (usually
// 1), and shrink it if the waste would be more than `VERTEX_TEXTURE_EXTRA_ROWS`
// rows. This helps with memory overhead, especially because there are several
// instances of these textures per Renderer.
if needed_height > existing_height
|| needed_height + VERTEX_TEXTURE_EXTRA_ROWS < existing_height
{
// Drop the existing texture, if any.
if let Some(t) = self .texture.take() {
device.delete_texture(t);
}
let texture = device.create_texture(
api::ImageBufferKind::Texture2D,
self .format,
MAX_VERTEX_TEXTURE_WIDTH as i32,
// Ensure height is at least two to work around
// https://bugs.chromium.org/p/angleproject/issues/detail?id=3039
needed_height.max(2 ),
TextureFilter::Nearest,
None,
);
self .texture = Some(texture);
}
// Note: the actual width can be larger than the logical one, with a few texels
// of each row unused at the tail. This is needed because there is still hardware
// (like Intel iGPUs) that prefers power-of-two sizes of textures ([1]).
//
// [1] https://software.intel.com/en-us/articles/opengl-performance-tips-power-of-two-textures-have-better-performance
let logical_width = if needed_height == 1 {
data.len() * texels_per_item
} else {
MAX_VERTEX_TEXTURE_WIDTH - (MAX_VERTEX_TEXTURE_WIDTH % texels_per_item)
};
let rect = DeviceIntRect::from_size(
DeviceIntSize::new(logical_width as i32, needed_height),
);
debug_assert!(len <= data.capacity(), "CPU copy will read out of bounds" );
texture_uploader.upload(
device,
self .texture(),
rect,
None,
None,
data.as_ptr(),
len,
);
}
pub fn deinit(mut self , device: &mut Device) {
if let Some(t) = self .texture.take() {
device.delete_texture(t);
}
}
}
pub struct VertexDataTextures {
prim_header_f_texture: VertexDataTexture<PrimitiveHeaderF>,
prim_header_i_texture: VertexDataTexture<PrimitiveHeaderI>,
transforms_texture: VertexDataTexture<TransformData>,
render_task_texture: VertexDataTexture<RenderTaskData>,
}
impl VertexDataTextures {
pub fn new() -> Self {
VertexDataTextures {
prim_header_f_texture: VertexDataTexture::new(api::ImageFormat::RGBAF32),
prim_header_i_texture: VertexDataTexture::new(api::ImageFormat::RGBAI32),
transforms_texture: VertexDataTexture::new(api::ImageFormat::RGBAF32),
render_task_texture: VertexDataTexture::new(api::ImageFormat::RGBAF32),
}
}
pub fn update(&mut self , device: &mut Device, pbo_pool: &mut UploadPBOPool, frame: &mut Frame) {
let mut texture_uploader = device.upload_texture(pbo_pool);
self .prim_header_f_texture.update(
device,
&mut texture_uploader,
&mut frame.prim_headers.headers_float,
);
self .prim_header_i_texture.update(
device,
&mut texture_uploader,
&mut frame.prim_headers.headers_int,
);
self .transforms_texture
.update(device, &mut texture_uploader, &>mut frame.transform_palette);
self .render_task_texture.update(
device,
&mut texture_uploader,
&mut frame.render_tasks.task_data,
);
// Flush and drop the texture uploader now, so that
// we can borrow the textures to bind them.
texture_uploader.flush(device);
device.bind_texture(
super ::TextureSampler::PrimitiveHeadersF,
&self .prim_header_f_texture.texture(),
Swizzle::default(),
);
device.bind_texture(
super ::TextureSampler::PrimitiveHeadersI,
&self .prim_header_i_texture.texture(),
Swizzle::default(),
);
device.bind_texture(
super ::TextureSampler::TransformPalette,
&self .transforms_texture.texture(),
Swizzle::default(),
);
device.bind_texture(
super ::TextureSampler::RenderTasks,
&self .render_task_texture.texture(),
Swizzle::default(),
);
}
pub fn size_in_bytes(&self ) -> usize {
self .prim_header_f_texture.size_in_bytes()
+ self .prim_header_i_texture.size_in_bytes()
+ self .transforms_texture.size_in_bytes()
+ self .render_task_texture.size_in_bytes()
}
pub fn deinit(self , device: &mut Device) {
self .transforms_texture.deinit(device);
self .prim_header_f_texture.deinit(device);
self .prim_header_i_texture.deinit(device);
self .render_task_texture.deinit(device);
}
}
pub struct RendererVAOs {
prim_vao: VAO,
blur_vao: VAO,
clip_rect_vao: VAO,
border_vao: VAO,
line_vao: VAO,
scale_vao: VAO,
svg_filter_node_vao: VAO,
composite_vao: VAO,
clear_vao: VAO,
copy_vao: VAO,
mask_vao: VAO,
}
impl RendererVAOs {
pub fn new(device: &mut Device, indexed_quads: Option<NonZeroUsize>) -> Self {
const QUAD_INDICES: [u16; 6 ] = [0 , 1 , 2 , 2 , 1 , 3 ];
const QUAD_VERTICES: [[u8; 2 ]; 4 ] = [[0 , 0 ], [0 xFF, 0 ], [0 , 0 xFF], [0 xFF, 0 xFF]];
let instance_divisor = if indexed_quads.is_some() { 0 } else { 1 };
let prim_vao = device.create_vao(&desc::PRIM_INSTANCES, instance_divisor);
device.bind_vao(&prim_vao);
match indexed_quads {
Some(count) => {
assert!(count.get() < u16::MAX as usize);
let quad_indices = (0 .. count.get() as u16)
.flat_map(|instance| QUAD_INDICES.iter().map(move |&index| instance * 4 + index))
.collect::<Vec<_>>();
device.update_vao_indices(&prim_vao, &quad_indices, VertexUsageHint::Static );
let quad_vertices = (0 .. count.get() as u16)
.flat_map(|_| QUAD_VERTICES.iter().cloned())
.collect::<Vec<_>>();
device.update_vao_main_vertices(&prim_vao, &quad_vertices, VertexUsageHint::Static );
}
None => {
device.update_vao_indices(&prim_vao, &QUAD_INDICES, VertexUsageHint::Static );
device.update_vao_main_vertices(&prim_vao, &QUAD_VERTICES, VertexUsageHint::Static );
}
}
RendererVAOs {
blur_vao: device.create_vao_with_new_instances(&desc::BLUR, &prim_vao),
clip_rect_vao: device.create_vao_with_new_instances(&desc::CLIP_RECT, &prim_vao),
border_vao: device.create_vao_with_new_instances(&desc::BORDER, &prim_vao),
scale_vao: device.create_vao_with_new_instances(&desc::SCALE, &prim_vao),
line_vao: device.create_vao_with_new_instances(&desc::LINE, &prim_vao),
svg_filter_node_vao: device.create_vao_with_new_instances(&desc::SVG_FILTER_NODE, &prim_vao),
composite_vao: device.create_vao_with_new_instances(&desc::COMPOSITE, &prim_vao),
clear_vao: device.create_vao_with_new_instances(&desc::CLEAR, &prim_vao),
copy_vao: device.create_vao_with_new_instances(&desc::COPY, &prim_vao),
mask_vao: device.create_vao_with_new_instances(&desc::MASK, &prim_vao),
prim_vao,
}
}
pub fn deinit(self , device: &mut Device) {
device.delete_vao(self .prim_vao);
device.delete_vao(self .clip_rect_vao);
device.delete_vao(self .blur_vao);
device.delete_vao(self .line_vao);
device.delete_vao(self .border_vao);
device.delete_vao(self .scale_vao);
device.delete_vao(self .svg_filter_node_vao);
device.delete_vao(self .composite_vao);
device.delete_vao(self .clear_vao);
device.delete_vao(self .copy_vao);
device.delete_vao(self .mask_vao);
}
}
impl ops::Index<VertexArrayKind> for RendererVAOs {
type Output = VAO;
fn index(&self , kind: VertexArrayKind) -> &VAO {
match kind {
VertexArrayKind::Primitive => &self .prim_vao,
VertexArrayKind::ClipRect => &self .clip_rect_vao,
VertexArrayKind::Blur => &self .blur_vao,
VertexArrayKind::Border => &self .border_vao,
VertexArrayKind::Scale => &self .scale_vao,
VertexArrayKind::LineDecoration => &self .line_vao,
VertexArrayKind::SvgFilterNode => &self .svg_filter_node_vao,
VertexArrayKind::Composite => &self .composite_vao,
VertexArrayKind::Clear => &self .clear_vao,
VertexArrayKind::Copy => &self .copy_vao,
VertexArrayKind::Mask => &self .mask_vao,
}
}
}
Messung V0.5 in Prozent C=84 H=95 G=89
¤ Dauer der Verarbeitung: 0.6 Sekunden
¤
*© Formatika GbR, Deutschland