diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2017-08-15 16:00:10 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-15 16:00:10 -0500 |
commit | 90f55ea4580e2a15f7d70d0491444f18b972d450 (patch) | |
tree | c85f3cb5d55babab03d56dac8b0d10d588b0a0f9 /components/canvas/webgl_paint_thread.rs | |
parent | 2e60b27a2186a8cba4b952960155dfcf3f47d7db (diff) | |
parent | 703962fe61d673536eb982b45795ae13748f0f6a (diff) | |
download | servo-90f55ea4580e2a15f7d70d0491444f18b972d450.tar.gz servo-90f55ea4580e2a15f7d70d0491444f18b972d450.zip |
Auto merge of #17891 - MortimerGoro:webgl_move, r=glennw,emilio
Improved WebGL architecture
<!-- Please describe your changes on the following line: -->
Info about the big picture and the goals of the WebGL refactor in this thread: https://groups.google.com/forum/#!topic/mozilla.dev.servo/0WMGz60kKzQ
I tried to reduce this PR as much as possible as requested in the thread. I'll do separate PRs for other features (e.g.: Batch messages or use shared memory to improve frame times) or fixes.
Some tips to ease the review process:
- Most changes in DOM objects follow the same pattern (remove CanvasMsg wrapper and use the new sender method).
- WebGLCommands are the same ones as before (moved from webrender_api). So those lines are already reviewed.
- See WebGL traits in [components/canvas_traits/webgl.rs](https://github.com/servo/servo/pull/17891/files#diff-8701045d01505418701d0631d4d45562)
- See WebGLThread and WR External Image bridge in [components/canvas/webgl_thread.rs](https://github.com/servo/servo/pull/17891/files#diff-281554879f39a2a041f7a69d442a5d2e)
- The implementation submitted in this PR creates a single `WebGLThread` for all ScriptThread/Pipelines. See that in [components/canvas/webgl_mode/inprocess.rs](https://github.com/servo/servo/pull/17891/files#diff-250070c6c5a38c7f9fa0f5b3c101f68b)
The conformance tests will help to guarantee that we don't miss anything.
---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [ ] These changes fix #__ (github issue number if applicable).
<!-- Either: -->
- [x] There are tests for these changes OR
- [ ] These changes do not require tests because _____
<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->
<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17891)
<!-- Reviewable:end -->
Diffstat (limited to 'components/canvas/webgl_paint_thread.rs')
-rw-r--r-- | components/canvas/webgl_paint_thread.rs | 379 |
1 files changed, 0 insertions, 379 deletions
diff --git a/components/canvas/webgl_paint_thread.rs b/components/canvas/webgl_paint_thread.rs deleted file mode 100644 index 2b6819effba..00000000000 --- a/components/canvas/webgl_paint_thread.rs +++ /dev/null @@ -1,379 +0,0 @@ -/* 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/. */ - -use canvas_traits::{CanvasCommonMsg, CanvasData, CanvasMsg, CanvasImageData}; -use canvas_traits::{FromLayoutMsg, FromScriptMsg, byte_swap}; -use euclid::Size2D; -use gleam::gl; -use ipc_channel::ipc::{self, IpcSender}; -use offscreen_gl_context::{ColorAttachmentType, GLContext, GLLimits}; -use offscreen_gl_context::{GLContextAttributes, NativeGLContext, OSMesaContext}; -use servo_config::opts; -use std::borrow::ToOwned; -use std::mem; -use std::sync::Arc; -use std::sync::mpsc::channel; -use std::thread; -use webrender_api; - -enum GLContextWrapper { - Native(GLContext<NativeGLContext>), - OSMesa(GLContext<OSMesaContext>), -} - -impl GLContextWrapper { - fn new(size: Size2D<i32>, - attributes: GLContextAttributes, - gl_type: gl::GlType) -> Result<GLContextWrapper, &'static str> { - if opts::get().should_use_osmesa() { - let ctx = GLContext::<OSMesaContext>::new(size, - attributes, - ColorAttachmentType::Texture, - gl_type, - None); - ctx.map(GLContextWrapper::OSMesa) - } else { - let ctx = GLContext::<NativeGLContext>::new(size, - attributes, - ColorAttachmentType::Texture, - gl_type, - None); - ctx.map(GLContextWrapper::Native) - } - } - - pub fn get_limits(&self) -> GLLimits { - match *self { - GLContextWrapper::Native(ref ctx) => { - ctx.borrow_limits().clone() - } - GLContextWrapper::OSMesa(ref ctx) => { - ctx.borrow_limits().clone() - } - } - } - - fn resize(&mut self, size: Size2D<i32>) -> Result<Size2D<i32>, &'static str> { - match *self { - GLContextWrapper::Native(ref mut ctx) => { - ctx.resize(size)?; - Ok(ctx.borrow_draw_buffer().unwrap().size()) - } - GLContextWrapper::OSMesa(ref mut ctx) => { - ctx.resize(size)?; - Ok(ctx.borrow_draw_buffer().unwrap().size()) - } - } - } - - fn gl(&self) -> &gl::Gl { - match *self { - GLContextWrapper::Native(ref ctx) => { - ctx.gl() - } - GLContextWrapper::OSMesa(ref ctx) => { - ctx.gl() - } - } - } - - pub fn make_current(&self) { - match *self { - GLContextWrapper::Native(ref ctx) => { - ctx.make_current().unwrap(); - } - GLContextWrapper::OSMesa(ref ctx) => { - ctx.make_current().unwrap(); - } - } - } - - pub fn apply_command(&self, cmd: webrender_api::WebGLCommand) { - match *self { - GLContextWrapper::Native(ref ctx) => { - cmd.apply(ctx); - } - GLContextWrapper::OSMesa(ref ctx) => { - cmd.apply(ctx); - } - } - } -} - -enum WebGLPaintTaskData { - WebRender(webrender_api::RenderApi, webrender_api::WebGLContextId), - Readback { - context: GLContextWrapper, - webrender_api: webrender_api::RenderApi, - image_key: Option<webrender_api::ImageKey>, - /// An old webrender image key that can be deleted when the next epoch ends. - old_image_key: Option<webrender_api::ImageKey>, - /// An old webrender image key that can be deleted when the current epoch ends. - very_old_image_key: Option<webrender_api::ImageKey>, - }, -} - -pub struct WebGLPaintThread { - size: Size2D<i32>, - data: WebGLPaintTaskData, -} - -fn create_readback_painter(size: Size2D<i32>, - attrs: GLContextAttributes, - webrender_api: webrender_api::RenderApi, - gl_type: gl::GlType) - -> Result<(WebGLPaintThread, GLLimits), String> { - let context = GLContextWrapper::new(size, attrs, gl_type)?; - let limits = context.get_limits(); - let painter = WebGLPaintThread { - size: size, - data: WebGLPaintTaskData::Readback { - context: context, - webrender_api: webrender_api, - image_key: None, - old_image_key: None, - very_old_image_key: None, - }, - }; - - Ok((painter, limits)) -} - -impl WebGLPaintThread { - fn new(size: Size2D<i32>, - attrs: GLContextAttributes, - webrender_api_sender: webrender_api::RenderApiSender, - gl_type: gl::GlType) - -> Result<(WebGLPaintThread, GLLimits), String> { - let wr_api = webrender_api_sender.create_api(); - let device_size = webrender_api::DeviceIntSize::from_untyped(&size); - match wr_api.request_webgl_context(&device_size, attrs) { - Ok((id, limits)) => { - let painter = WebGLPaintThread { - data: WebGLPaintTaskData::WebRender(wr_api, id), - size: size - }; - Ok((painter, limits)) - }, - Err(msg) => { - warn!("Initial context creation failed, falling back to readback: {}", msg); - create_readback_painter(size, attrs, wr_api, gl_type) - } - } - } - - fn handle_webgl_message(&self, message: webrender_api::WebGLCommand) { - debug!("WebGL message: {:?}", message); - match self.data { - WebGLPaintTaskData::WebRender(ref api, id) => { - api.send_webgl_command(id, message); - } - WebGLPaintTaskData::Readback { ref context, .. } => { - context.apply_command(message); - } - } - } - - fn handle_webvr_message(&self, message: webrender_api::VRCompositorCommand) { - match self.data { - WebGLPaintTaskData::WebRender(ref api, id) => { - api.send_vr_compositor_command(id, message); - } - WebGLPaintTaskData::Readback { .. } => { - error!("Webrender is required for WebVR implementation"); - } - } - } - - - /// Creates a new `WebGLPaintThread` and returns an `IpcSender` to - /// communicate with it. - pub fn start(size: Size2D<i32>, - attrs: GLContextAttributes, - webrender_api_sender: webrender_api::RenderApiSender) - -> Result<(IpcSender<CanvasMsg>, GLLimits), String> { - let (sender, receiver) = ipc::channel::<CanvasMsg>().unwrap(); - let (result_chan, result_port) = channel(); - thread::Builder::new().name("WebGLThread".to_owned()).spawn(move || { - let gl_type = gl::GlType::default(); - let mut painter = match WebGLPaintThread::new(size, attrs, webrender_api_sender, gl_type) { - Ok((thread, limits)) => { - result_chan.send(Ok(limits)).unwrap(); - thread - }, - Err(e) => { - result_chan.send(Err(e)).unwrap(); - return - } - }; - painter.init(); - loop { - match receiver.recv().unwrap() { - CanvasMsg::WebGL(message) => painter.handle_webgl_message(message), - CanvasMsg::Common(message) => { - match message { - CanvasCommonMsg::Close => break, - // TODO(emilio): handle error nicely - CanvasCommonMsg::Recreate(size) => painter.recreate(size).unwrap(), - } - }, - CanvasMsg::FromScript(message) => { - match message { - FromScriptMsg::SendPixels(chan) =>{ - // Read the comment on - // HTMLCanvasElement::fetch_all_data. - chan.send(None).unwrap(); - } - } - } - CanvasMsg::FromLayout(message) => { - match message { - FromLayoutMsg::SendData(chan) => - painter.send_data(chan), - } - } - CanvasMsg::Canvas2d(_) => panic!("Wrong message sent to WebGLThread"), - CanvasMsg::WebVR(message) => painter.handle_webvr_message(message) - } - } - }).expect("Thread spawning failed"); - - result_port.recv().unwrap().map(|limits| (sender, limits)) - } - - fn send_data(&mut self, chan: IpcSender<CanvasData>) { - match self.data { - WebGLPaintTaskData::Readback { - ref context, - ref webrender_api, - ref mut image_key, - ref mut old_image_key, - ref mut very_old_image_key, - } => { - let width = self.size.width as usize; - let height = self.size.height as usize; - - let mut pixels = context.gl().read_pixels(0, 0, - self.size.width as gl::GLsizei, - self.size.height as gl::GLsizei, - gl::RGBA, gl::UNSIGNED_BYTE); - // flip image vertically (texture is upside down) - let orig_pixels = pixels.clone(); - let stride = width * 4; - for y in 0..height { - let dst_start = y * stride; - let src_start = (height - y - 1) * stride; - let src_slice = &orig_pixels[src_start .. src_start + stride]; - (&mut pixels[dst_start .. dst_start + stride]).clone_from_slice(&src_slice[..stride]); - } - - // rgba -> bgra - byte_swap(&mut pixels); - - let descriptor = webrender_api::ImageDescriptor { - width: width as u32, - height: height as u32, - stride: None, - format: webrender_api::ImageFormat::BGRA8, - offset: 0, - is_opaque: false, - }; - let data = webrender_api::ImageData::Raw(Arc::new(pixels)); - - let mut updates = webrender_api::ResourceUpdates::new(); - - match *image_key { - Some(image_key) => { - updates.update_image(image_key, - descriptor, - data, - None); - } - None => { - *image_key = Some(webrender_api.generate_image_key()); - updates.add_image(image_key.unwrap(), - descriptor, - data, - None); - } - } - - if let Some(image_key) = mem::replace(very_old_image_key, old_image_key.take()) { - updates.delete_image(image_key); - } - - webrender_api.update_resources(updates); - - let image_data = CanvasImageData { - image_key: image_key.unwrap(), - }; - - chan.send(CanvasData::Image(image_data)).unwrap(); - } - WebGLPaintTaskData::WebRender(_, id) => { - chan.send(CanvasData::WebGL(id)).unwrap(); - } - } - } - - #[allow(unsafe_code)] - fn recreate(&mut self, size: Size2D<i32>) -> Result<(), &'static str> { - match self.data { - WebGLPaintTaskData::Readback { ref mut context, ref mut image_key, ref mut old_image_key, .. } => { - if size.width > self.size.width || - size.height > self.size.height { - self.size = context.resize(size)?; - } else { - self.size = size; - context.gl().scissor(0, 0, size.width, size.height); - } - // Webrender doesn't let images change size, so we clear the webrender image key. - if let Some(image_key) = image_key.take() { - // If this executes, then we are in a new epoch since we last recreated the canvas, - // so `old_image_key` must be `None`. - debug_assert!(old_image_key.is_none()); - *old_image_key = Some(image_key); - } - } - WebGLPaintTaskData::WebRender(ref api, id) => { - let device_size = webrender_api::DeviceIntSize::from_untyped(&size); - api.resize_webgl_context(id, &device_size); - } - } - - Ok(()) - } - - fn init(&mut self) { - if let WebGLPaintTaskData::Readback { ref context, .. } = self.data { - context.make_current(); - } - } -} - -impl Drop for WebGLPaintThread { - fn drop(&mut self) { - if let WebGLPaintTaskData::Readback { - ref mut webrender_api, - image_key, - old_image_key, - very_old_image_key, - .. - } = self.data { - let mut updates = webrender_api::ResourceUpdates::new(); - - if let Some(image_key) = image_key { - updates.delete_image(image_key); - } - if let Some(image_key) = old_image_key { - updates.delete_image(image_key); - } - if let Some(image_key) = very_old_image_key { - updates.delete_image(image_key); - } - - webrender_api.update_resources(updates); - } - } -} |