blob.rs (10321B)
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 extern crate gleam; 6 extern crate glutin; 7 extern crate rayon; 8 extern crate webrender; 9 extern crate winit; 10 11 #[path = "common/boilerplate.rs"] 12 mod boilerplate; 13 14 use crate::boilerplate::{Example, HandyDandyRectBuilder}; 15 use rayon::{ThreadPool, ThreadPoolBuilder}; 16 use rayon::prelude::*; 17 use std::collections::HashMap; 18 use std::sync::Arc; 19 use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, PrimitiveFlags}; 20 use webrender::api::{ColorF, CommonItemProperties, SpaceAndClipInfo, ImageDescriptorFlags}; 21 use webrender::api::BlobTilePool; 22 use webrender::api::units::*; 23 use webrender::render_api::*; 24 use webrender::euclid::size2; 25 26 // This example shows how to implement a very basic BlobImageHandler that can only render 27 // a checkerboard pattern. 28 29 // The deserialized command list internally used by this example is just a color. 30 type ImageRenderingCommands = api::ColorU; 31 32 // Serialize/deserialize the blob. 33 // For real usecases you should probably use serde rather than doing it by hand. 34 35 fn serialize_blob(color: api::ColorU) -> Arc<Vec<u8>> { 36 Arc::new(vec![color.r, color.g, color.b, color.a]) 37 } 38 39 fn deserialize_blob(blob: &[u8]) -> Result<ImageRenderingCommands, ()> { 40 let mut iter = blob.iter(); 41 return match (iter.next(), iter.next(), iter.next(), iter.next()) { 42 (Some(&r), Some(&g), Some(&b), Some(&a)) => Ok(api::ColorU::new(r, g, b, a)), 43 (Some(&a), None, None, None) => Ok(api::ColorU::new(a, a, a, a)), 44 _ => Err(()), 45 }; 46 } 47 48 // This is the function that applies the deserialized drawing commands and generates 49 // actual image data. 50 fn render_blob( 51 commands: Arc<ImageRenderingCommands>, 52 descriptor: &api::BlobImageDescriptor, 53 tile: TileOffset, 54 ) -> api::BlobImageResult { 55 let color = *commands; 56 57 // Note: This implementation ignores the dirty rect which isn't incorrect 58 // but is a missed optimization. 59 60 // Allocate storage for the result. Right now the resource cache expects the 61 // tiles to have have no stride or offset. 62 let bpp = 4; 63 let mut texels = Vec::with_capacity((descriptor.rect.area() * bpp) as usize); 64 65 // Generate a per-tile pattern to see it in the demo. For a real use case it would not 66 // make sense for the rendered content to depend on its tile. 67 let tile_checker = (tile.x % 2 == 0) != (tile.y % 2 == 0); 68 69 let [w, h] = descriptor.rect.size().to_array(); 70 let offset = descriptor.rect.min; 71 72 for y in 0..h { 73 for x in 0..w { 74 // Apply the tile's offset. This is important: all drawing commands should be 75 // translated by this offset to give correct results with tiled blob images. 76 let x2 = x + offset.x; 77 let y2 = y + offset.y; 78 79 // Render a simple checkerboard pattern 80 let checker = if (x2 % 20 >= 10) != (y2 % 20 >= 10) { 81 1 82 } else { 83 0 84 }; 85 // ..nested in the per-tile checkerboard pattern 86 let tc = if tile_checker { 0 } else { (1 - checker) * 40 }; 87 88 match descriptor.format { 89 api::ImageFormat::BGRA8 => { 90 texels.push(color.b * checker + tc); 91 texels.push(color.g * checker + tc); 92 texels.push(color.r * checker + tc); 93 texels.push(color.a * checker + tc); 94 } 95 api::ImageFormat::R8 => { 96 texels.push(color.a * checker + tc); 97 } 98 _ => { 99 return Err(api::BlobImageError::Other( 100 format!("Unsupported image format"), 101 )); 102 } 103 } 104 } 105 } 106 107 Ok(api::RasterizedBlobImage { 108 data: Arc::new(texels), 109 rasterized_rect: size2(w, h).into(), 110 }) 111 } 112 113 struct CheckerboardRenderer { 114 // We are going to defer the rendering work to worker threads. 115 // Using a pre-built Arc<ThreadPool> rather than creating our own threads 116 // makes it possible to share the same thread pool as the glyph renderer (if we 117 // want to). 118 workers: Arc<ThreadPool>, 119 120 // The deserialized drawing commands. 121 // In this example we store them in Arcs. This isn't necessary since in this simplified 122 // case the command list is a simple 32 bits value and would be cheap to clone before sending 123 // to the workers. But in a more realistic scenario the commands would typically be bigger 124 // and more expensive to clone, so let's pretend it is also the case here. 125 image_cmds: HashMap<api::BlobImageKey, Arc<ImageRenderingCommands>>, 126 } 127 128 impl CheckerboardRenderer { 129 fn new(workers: Arc<ThreadPool>) -> Self { 130 CheckerboardRenderer { 131 image_cmds: HashMap::new(), 132 workers, 133 } 134 } 135 } 136 137 impl api::BlobImageHandler for CheckerboardRenderer { 138 fn create_similar(&self) -> Box<dyn api::BlobImageHandler> { 139 Box::new(CheckerboardRenderer::new(Arc::clone(&self.workers))) 140 } 141 142 fn add(&mut self, key: api::BlobImageKey, cmds: Arc<api::BlobImageData>, 143 _visible_rect: &DeviceIntRect, _: api::TileSize) { 144 self.image_cmds 145 .insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap())); 146 } 147 148 fn update(&mut self, key: api::BlobImageKey, cmds: Arc<api::BlobImageData>, 149 _visible_rect: &DeviceIntRect, _dirty_rect: &BlobDirtyRect) { 150 // Here, updating is just replacing the current version of the commands with 151 // the new one (no incremental updates). 152 self.image_cmds 153 .insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap())); 154 } 155 156 fn delete(&mut self, key: api::BlobImageKey) { 157 self.image_cmds.remove(&key); 158 } 159 160 fn prepare_resources( 161 &mut self, 162 _services: &dyn api::BlobImageResources, 163 _requests: &[api::BlobImageParams], 164 ) {} 165 166 fn enable_multithreading(&mut self, _: bool) {} 167 fn delete_font(&mut self, _font: api::FontKey) {} 168 fn delete_font_instance(&mut self, _instance: api::FontInstanceKey) {} 169 fn clear_namespace(&mut self, _namespace: api::IdNamespace) {} 170 fn create_blob_rasterizer(&mut self) -> Box<dyn api::AsyncBlobImageRasterizer> { 171 Box::new(Rasterizer { 172 workers: Arc::clone(&self.workers), 173 image_cmds: self.image_cmds.clone(), 174 }) 175 } 176 } 177 178 struct Rasterizer { 179 workers: Arc<ThreadPool>, 180 image_cmds: HashMap<api::BlobImageKey, Arc<ImageRenderingCommands>>, 181 } 182 183 impl api::AsyncBlobImageRasterizer for Rasterizer { 184 fn rasterize( 185 &mut self, 186 requests: &[api::BlobImageParams], 187 _low_priority: bool, 188 _tile_pool: &mut BlobTilePool, 189 ) -> Vec<(api::BlobImageRequest, api::BlobImageResult)> { 190 let requests: Vec<(&api::BlobImageParams, Arc<ImageRenderingCommands>)> = requests.into_iter().map(|params| { 191 (params, Arc::clone(&self.image_cmds[¶ms.request.key])) 192 }).collect(); 193 194 self.workers.install(|| { 195 requests.into_par_iter().map(|(params, commands)| { 196 (params.request, render_blob(commands, ¶ms.descriptor, params.request.tile)) 197 }).collect() 198 }) 199 } 200 } 201 202 struct App {} 203 204 impl Example for App { 205 fn render( 206 &mut self, 207 api: &mut RenderApi, 208 builder: &mut DisplayListBuilder, 209 txn: &mut Transaction, 210 _device_size: DeviceIntSize, 211 pipeline_id: PipelineId, 212 _document_id: DocumentId, 213 ) { 214 let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id); 215 216 builder.push_simple_stacking_context( 217 LayoutPoint::zero(), 218 space_and_clip.spatial_id, 219 PrimitiveFlags::IS_BACKFACE_VISIBLE, 220 ); 221 222 let size1 = DeviceIntSize::new(500, 500); 223 let blob_img1 = api.generate_blob_image_key(); 224 txn.add_blob_image( 225 blob_img1, 226 api::ImageDescriptor::new( 227 size1.width, 228 size1.height, 229 api::ImageFormat::BGRA8, 230 ImageDescriptorFlags::IS_OPAQUE, 231 ), 232 serialize_blob(api::ColorU::new(50, 50, 150, 255)), 233 size1.into(), 234 Some(128), 235 ); 236 let bounds = (30, 30).by(size1.width, size1.height); 237 builder.push_image( 238 &CommonItemProperties::new(bounds, space_and_clip), 239 bounds, 240 api::ImageRendering::Auto, 241 api::AlphaType::PremultipliedAlpha, 242 blob_img1.as_image(), 243 ColorF::WHITE, 244 ); 245 246 let size2 = DeviceIntSize::new(256, 256); 247 let blob_img2 = api.generate_blob_image_key(); 248 txn.add_blob_image( 249 blob_img2, 250 api::ImageDescriptor::new( 251 size2.width, 252 size2.height, 253 api::ImageFormat::BGRA8, 254 ImageDescriptorFlags::IS_OPAQUE, 255 ), 256 serialize_blob(api::ColorU::new(50, 150, 50, 255)), 257 size2.into(), 258 None, 259 ); 260 let bounds = (600, 600).by(size2.width, size2.height); 261 builder.push_image( 262 &CommonItemProperties::new(bounds, space_and_clip), 263 bounds, 264 api::ImageRendering::Auto, 265 api::AlphaType::PremultipliedAlpha, 266 blob_img2.as_image(), 267 ColorF::WHITE, 268 ); 269 270 builder.pop_stacking_context(); 271 } 272 } 273 274 fn main() { 275 let workers = 276 ThreadPoolBuilder::new().thread_name(|idx| format!("WebRender:Worker#{}", idx)) 277 .build(); 278 279 let workers = Arc::new(workers.unwrap()); 280 281 let opts = webrender::WebRenderOptions { 282 workers: Some(Arc::clone(&workers)), 283 // Register our blob renderer, so that WebRender integrates it in the resource cache.. 284 // Share the same pool of worker threads between WebRender and our blob renderer. 285 blob_image_handler: Some(Box::new(CheckerboardRenderer::new(Arc::clone(&workers)))), 286 ..Default::default() 287 }; 288 289 let mut app = App {}; 290 291 boilerplate::main_wrapper(&mut app, Some(opts)); 292 }