From 499662fc5193e2aa47650a2c3b21f3a19c79524c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Wed, 18 Nov 2015 12:04:32 +0100 Subject: [PATCH] Add WebGL support --- Cargo.toml | 4 ++ res/gl3_common.fs.glsl | 2 +- res/gl3_common.vs.glsl | 2 +- src/batch_builder.rs | 48 +++++++++++++++++++++++- src/device.rs | 20 +++++++++- src/internal_types.rs | 1 + src/lib.rs | 1 + src/node_compiler.rs | 8 ++++ src/render_backend.rs | 85 +++++++++++++++++++++++++++++++++++++++++- src/renderer.rs | 38 ++++++++++--------- src/resource_cache.rs | 14 +++++++ src/resource_list.rs | 1 + src/texture_cache.rs | 12 +++++- 13 files changed, 212 insertions(+), 24 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d3363db51e..435a31e535 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,10 @@ git = "https://github.com/servo/ipc-channel" [dependencies.webrender_traits] git = "https://github.com/glennw/webrender_traits" +[dependencies.offscreen_gl_context] +git = "https://github.com/ecoal95/rust-offscreen-rendering-context" +branch = "webrender" + [dependencies] gleam = "*" euclid = "0.3" diff --git a/res/gl3_common.fs.glsl b/res/gl3_common.fs.glsl index 992583152a..84bed1dc86 100644 --- a/res/gl3_common.fs.glsl +++ b/res/gl3_common.fs.glsl @@ -1,4 +1,4 @@ -#version 150 +#version 130 #define SERVO_GL3 diff --git a/res/gl3_common.vs.glsl b/res/gl3_common.vs.glsl index 69e7d8c352..3ac8ea4b37 100644 --- a/res/gl3_common.vs.glsl +++ b/res/gl3_common.vs.glsl @@ -1,4 +1,4 @@ -#version 150 +#version 130 #define SERVO_GL3 diff --git a/src/batch_builder.rs b/src/batch_builder.rs index 049044e0f9..6bd9977af5 100644 --- a/src/batch_builder.rs +++ b/src/batch_builder.rs @@ -20,6 +20,7 @@ use util; use webrender_traits::{ColorF, ImageFormat, BorderStyle, BoxShadowClipMode}; use webrender_traits::{BorderRadius, BorderSide, FontKey, GlyphInstance, ImageKey}; use webrender_traits::{BorderDisplayItem, GradientStop, ComplexClipRegion, ImageRendering}; +use webrender_traits::{WebGLContextId}; const BORDER_DASH_SIZE: f32 = 3.0; @@ -58,6 +59,51 @@ impl<'a> BatchBuilder<'a> { &[*color, *color, *color, *color]) } + pub fn add_webgl_rectangle(&mut self, + matrix_index: MatrixIndex, + rect: &Rect, + clip: &CombinedClipRegion, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers, + webgl_context_id: &WebGLContextId) { + let texture_id = resource_cache.get_webgl_texture(webgl_context_id); + + let uv = RectUv { + top_left: Point2D::zero(), + top_right: Point2D::new(1.0, 0.0), + bottom_left: Point2D::new(0.0, 1.0), + bottom_right: Point2D::new(1.0, 1.0), + }; + + clipper::clip_rect_to_combined_region( + RectPolygon { + pos: *rect, + varyings: uv, + }, + &mut clip_buffers.sh_clip_buffers, + &mut clip_buffers.rect_pos_uv, + clip); + + let tile_params = TileParams { + u0: 0.0, + v0: 0.0, + u_size: 1.0, + v_size: 1.0, + }; + + for clip_region in clip_buffers.rect_pos_uv.clip_rect_to_region_result_output.drain(..) { + let mask = mask_for_clip_region(resource_cache, &clip_region, false); + let mut vertices = clip_region.make_packed_vertices_for_rect(mask); + + self.add_draw_item(matrix_index, + texture_id, + mask.texture_id, + Primitive::Rectangles, + &mut vertices, + Some(tile_params.clone())); + } + } + pub fn add_image(&mut self, matrix_index: MatrixIndex, rect: &Rect, @@ -118,7 +164,7 @@ impl<'a> BatchBuilder<'a> { size: Au, blur_radius: Au, color: &ColorF, - glyphs: &Vec, + glyphs: &[GlyphInstance], resource_cache: &ResourceCache, clip_buffers: &mut ClipBuffers, device_pixel_ratio: f32) { diff --git a/src/device.rs b/src/device.rs index 37919e737f..7a6204e31a 100644 --- a/src/device.rs +++ b/src/device.rs @@ -288,6 +288,7 @@ impl FBOId { fn bind(&self) { let FBOId(id) = *self; gl::bind_framebuffer(gl::FRAMEBUFFER, id); + gl::bind_framebuffer(gl::READ_FRAMEBUFFER, id); } } @@ -403,6 +404,7 @@ pub struct Device { // resources resource_path: PathBuf, textures: HashMap>, + raw_textures: HashMap>, programs: HashMap>, vaos: HashMap>, @@ -442,6 +444,7 @@ impl Device { default_fbo: 0, textures: HashMap::with_hash_state(Default::default()), + raw_textures: HashMap::with_hash_state(Default::default()), programs: HashMap::with_hash_state(Default::default()), vaos: HashMap::with_hash_state(Default::default()), @@ -586,8 +589,21 @@ impl Device { } pub fn get_texture_dimensions(&self, texture_id: TextureId) -> (u32, u32) { - let texture = self.textures.get(&texture_id).unwrap(); - (texture.width, texture.height) + if let Some(texture) = self.textures.get(&texture_id) { + (texture.width, texture.height) + } else { + let dimensions = self.raw_textures.get(&texture_id).unwrap(); + (dimensions.2, dimensions.3) + } + } + + pub fn update_raw_texture(&mut self, + texture_id: TextureId, + x0: u32, + y0: u32, + width: u32, + height: u32) { + self.raw_textures.insert(texture_id, (x0, y0, width, height)); } fn set_texture_parameters(&mut self, filter: TextureFilter) { diff --git a/src/internal_types.rs b/src/internal_types.rs index d53a82aff0..c1a0065dea 100644 --- a/src/internal_types.rs +++ b/src/internal_types.rs @@ -243,6 +243,7 @@ pub enum RenderTargetMode { #[derive(Debug)] pub enum TextureUpdateDetails { + Raw, Blit(Vec), Blur(Vec, Size2D, Au, TextureImage, TextureImage), /// All four corners, the tessellation index, and whether inverted, respectively. diff --git a/src/lib.rs b/src/lib.rs index 62a85a57e6..a062e3e9ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,7 @@ extern crate ipc_channel; extern crate scoped_threadpool; extern crate time; extern crate webrender_traits; +extern crate offscreen_gl_context; pub use renderer::Renderer; diff --git a/src/node_compiler.rs b/src/node_compiler.rs index f667ca7f6e..7f12f386de 100644 --- a/src/node_compiler.rs +++ b/src/node_compiler.rs @@ -60,6 +60,14 @@ impl NodeCompiler for AABBTreeNode { &display_item.clip.complex[..]); match display_item.item { + SpecificDisplayItem::WebGL(ref info) => { + builder.add_webgl_rectangle(matrix_index, + &display_item.rect, + &clip, + resource_cache, + &mut clip_buffers, + &info.context_id); + } SpecificDisplayItem::Image(ref info) => { builder.add_image(matrix_index, &display_item.rect, diff --git a/src/render_backend.rs b/src/render_backend.rs index 498949634f..f2f1a76c81 100644 --- a/src/render_backend.rs +++ b/src/render_backend.rs @@ -6,11 +6,17 @@ use profiler::BackendProfileCounters; use resource_cache::ResourceCache; use scene::Scene; use scoped_threadpool; +use std::mem; use std::collections::HashMap; use std::sync::{Arc, Mutex}; use std::sync::mpsc::Sender; use texture_cache::{TextureCache, TextureCacheItemId}; use webrender_traits::{ApiMsg, IdNamespace, RenderNotifier, ScrollLayerId}; +use webrender_traits::{WebGLContextId, WebGLCommand}; +use batch::new_id; +use device::TextureId; +use offscreen_gl_context::{NativeGLContext, GLContext, ColorAttachmentType, NativeGLContextMethods, NativeGLContextHandle}; +use gleam::gl; pub struct RenderBackend { api_rx: IpcReceiver, @@ -27,6 +33,9 @@ pub struct RenderBackend { frame: Frame, notifier: Arc>>>, + webrender_context_handle: Option, + webgl_contexts: HashMap>, + pending_webgl_commands: Vec<(WebGLContextId, WebGLCommand)>, } impl RenderBackend { @@ -38,7 +47,8 @@ impl RenderBackend { dummy_mask_image_id: TextureCacheItemId, texture_cache: TextureCache, enable_aa: bool, - notifier: Arc>>>) -> RenderBackend { + notifier: Arc>>>, + webrender_context_handle: Option) -> RenderBackend { let mut thread_pool = scoped_threadpool::Pool::new(8); let resource_cache = ResourceCache::new(&mut thread_pool, @@ -59,6 +69,9 @@ impl RenderBackend { frame: Frame::new(), next_namespace_id: IdNamespace(1), notifier: notifier, + webrender_context_handle: webrender_context_handle, + webgl_contexts: HashMap::new(), + pending_webgl_commands: Vec::new(), }; backend @@ -169,6 +182,35 @@ impl RenderBackend { Some(layer) => tx.send(point - layer.scroll_offset).unwrap(), } } + ApiMsg::RequestWebGLContext(size, attributes, tx) => { + if let Some(ref handle) = self.webrender_context_handle { + match GLContext::::new(size, attributes, ColorAttachmentType::Texture, Some(handle)) { + Ok(ctx) => { + let id = WebGLContextId(new_id()); + + let (real_size, texture_id) = { + let draw_buffer = ctx.borrow_draw_buffer().unwrap(); + (draw_buffer.size(), draw_buffer.get_bound_texture_id().unwrap()) + }; + + self.webgl_contexts.insert(id, ctx); + + self.resource_cache + .add_webgl_texture(id, TextureId(texture_id), real_size); + + tx.send(Ok(id)).unwrap(); + }, + Err(msg) => { + tx.send(Err(msg.to_owned())).unwrap(); + } + } + } else { + tx.send(Err("Not implemented yet".to_owned())).unwrap(); + } + } + ApiMsg::WebGLCommand(context_id, command) => { + self.pending_webgl_commands.push((context_id, command)); + } } } Err(..) => { @@ -178,6 +220,45 @@ impl RenderBackend { } } + fn apply_webgl_command(&self, command: WebGLCommand) { + match command { + WebGLCommand::Clear(what) + => gl::clear(what), + WebGLCommand::ClearColor(r, g, b, a) + => gl::clear_color(r, g, b, a), + } + } + + fn apply_webgl_commands(&mut self) { + if self.pending_webgl_commands.is_empty() { + return; // Early return in easy case + } + + let mut current_context_id = None; + let mut current_context: Option<&GLContext> = None; + + // Sort by context id to minimize context switching + self.pending_webgl_commands.sort_by(|a, b| a.0.cmp(&b.0)); + + let mut pending = mem::replace(&mut self.pending_webgl_commands, Vec::new()); + + for (context_id, command) in pending.drain(..) { + if current_context_id != Some(context_id) { + current_context_id = Some(context_id); + current_context = Some(self.webgl_contexts.get(&context_id).unwrap()); + current_context.unwrap().make_current().unwrap(); + } + + self.apply_webgl_command(command); + } + + // Without this the changes in the texture are not seen in the renderer. + if let Some(ctx) = current_context { + ctx.unbind().unwrap(); + } + } + + fn build_scene(&mut self) { // Flatten the stacking context hierarchy let mut new_pipeline_sizes = HashMap::new(); @@ -235,6 +316,8 @@ impl RenderBackend { } fn render(&mut self) -> RendererFrame { + self.apply_webgl_commands(); + let mut frame = self.frame.build(&self.viewport, &mut self.resource_cache, &mut self.thread_pool, diff --git a/src/renderer.rs b/src/renderer.rs index 6172fccaec..a562ffd511 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -28,6 +28,7 @@ use texture_cache::{BorderType, TextureCache, TextureInsertOp}; use time::precise_time_ns; use webrender_traits::{ColorF, Epoch, PipelineId, RenderNotifier}; use webrender_traits::{ImageFormat, MixBlendMode, RenderApiSender}; +use offscreen_gl_context::{NativeGLContext, NativeGLContextMethods}; //use util; pub const BLUR_INFLATION_FACTOR: u32 = 3; @@ -175,6 +176,10 @@ impl Renderer { let notifier = Arc::new(Mutex::new(None)); let backend_notifier = notifier.clone(); + // We need a reference to the webrender context from the render backend in order to share + // texture ids + let context_handle = NativeGLContext::current_handle(); + thread::spawn(move || { let mut backend = RenderBackend::new(api_rx, result_tx, @@ -184,7 +189,8 @@ impl Renderer { dummy_mask_image_id, texture_cache, enable_aa, - backend_notifier); + backend_notifier, + context_handle); backend.run(); }); @@ -238,23 +244,18 @@ impl Renderer { pub fn update(&mut self) { // Pull any pending results and return the most recent. - loop { - match self.result_rx.try_recv() { - Ok(msg) => { - match msg { - ResultMsg::UpdateTextureCache(update_list) => { - self.pending_texture_updates.push(update_list); - } - ResultMsg::UpdateBatches(update_list) => { - self.pending_batch_updates.push(update_list); - } - ResultMsg::NewFrame(frame, profile_counters) => { - self.backend_profile_counters = profile_counters; - self.current_frame = Some(frame); - } - } + while let Ok(msg) = self.result_rx.try_recv() { + match msg { + ResultMsg::UpdateTextureCache(update_list) => { + self.pending_texture_updates.push(update_list); + } + ResultMsg::UpdateBatches(update_list) => { + self.pending_batch_updates.push(update_list); + } + ResultMsg::NewFrame(frame, profile_counters) => { + self.backend_profile_counters = profile_counters; + self.current_frame = Some(frame); } - Err(..) => break, } } } @@ -350,6 +351,9 @@ impl Renderer { } TextureUpdateOp::Update(x, y, width, height, details) => { match details { + TextureUpdateDetails::Raw => { + self.device.update_raw_texture(update.id, x, y, width, height); + } TextureUpdateDetails::Blit(bytes) => { self.device.update_texture_for_noncomposite_operation( update.id, diff --git a/src/resource_cache.rs b/src/resource_cache.rs index 04e414adda..dfecb0f189 100644 --- a/src/resource_cache.rs +++ b/src/resource_cache.rs @@ -20,6 +20,7 @@ use std::time::Duration; use texture_cache::{TextureCache, TextureCacheItem, TextureCacheItemId}; use texture_cache::{BorderType, TextureInsertOp}; use webrender_traits::{Epoch, FontKey, ImageKey, ImageFormat, DisplayItem, ImageRendering}; +use webrender_traits::{WebGLContextId}; static FONT_CONTEXT_COUNT: AtomicUsize = ATOMIC_USIZE_INIT; @@ -47,6 +48,7 @@ pub struct ResourceCache { cached_glyphs: HashMap, DefaultState>, cached_rasters: HashMap>, cached_images: HashMap<(ImageKey, ImageRendering), CachedImageInfo, DefaultState>, + webgl_textures: HashMap>, draw_lists: FreeList, font_templates: HashMap>, @@ -88,6 +90,7 @@ impl ResourceCache { cached_glyphs: HashMap::with_hash_state(Default::default()), cached_rasters: HashMap::with_hash_state(Default::default()), cached_images: HashMap::with_hash_state(Default::default()), + webgl_textures: HashMap::with_hash_state(Default::default()), draw_lists: FreeList::new(), font_templates: HashMap::with_hash_state(Default::default()), image_templates: HashMap::with_hash_state(Default::default()), @@ -148,6 +151,11 @@ impl ResourceCache { self.image_templates.insert(image_key, resource); } + pub fn add_webgl_texture(&mut self, id: WebGLContextId, texture_id: TextureId, size: Size2D) { + self.webgl_textures.insert(id, texture_id); + self.texture_cache.add_raw_update(texture_id, size); + } + pub fn add_resource_list(&mut self, resource_list: &ResourceList) { // Update texture cache with any GPU generated procedural items. resource_list.for_each_raster(|raster_item| { @@ -328,6 +336,12 @@ impl ResourceCache { let image_id = self.cached_rasters[raster_item]; self.texture_cache.get(image_id) } + + #[inline] + pub fn get_webgl_texture(&self, + context_id: &WebGLContextId) -> TextureId { + self.webgl_textures.get(context_id).unwrap().clone() + } } fn run_raster_jobs(thread_pool: &mut scoped_threadpool::Pool, diff --git a/src/resource_list.rs b/src/resource_list.rs index e3eb314981..1221b9295f 100644 --- a/src/resource_list.rs +++ b/src/resource_list.rs @@ -147,6 +147,7 @@ impl BuildRequiredResources for AABBTreeNode { resource_list.add_glyph(info.font_key, glyph); } } + SpecificDisplayItem::WebGL(..) => {} SpecificDisplayItem::Rectangle(..) => {} SpecificDisplayItem::Gradient(..) => {} SpecificDisplayItem::BoxShadow(ref info) => { diff --git a/src/texture_cache.rs b/src/texture_cache.rs index 895fc5c3fd..a609735c5a 100644 --- a/src/texture_cache.rs +++ b/src/texture_cache.rs @@ -43,7 +43,7 @@ pub enum BorderType { } #[inline] -fn copy_pixels(src: &Vec, +fn copy_pixels(src: &[u8], target: &mut Vec, x: u32, y: u32, @@ -795,6 +795,16 @@ impl TextureCache { self.pending_updates.push(update_op); } + pub fn add_raw_update(&mut self, id: TextureId, size: Size2D) { + self.pending_updates.push(TextureUpdate { + id: id, + op: TextureUpdateOp::Update( + 0, 0, + size.width as u32, size.height as u32, + TextureUpdateDetails::Raw), + }) + } + pub fn update(&mut self, image_id: TextureCacheItemId, width: u32,