Skip to content

Commit

Permalink
webgl: Add feature to store backtraces for each WebGL API call for ea…
Browse files Browse the repository at this point in the history
…sier debugging.
  • Loading branch information
jdm committed Oct 1, 2018
1 parent 515afac commit 5dc80dd
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 16 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions components/canvas/Cargo.toml
Expand Up @@ -9,6 +9,9 @@ publish = false
name = "canvas"
path = "lib.rs"

[features]
webgl_backtrace = ["canvas_traits/webgl_backtrace"]

[dependencies]
azure = {git = "https://github.com/servo/rust-azure"}
canvas_traits = {path = "../canvas_traits"}
Expand Down
13 changes: 9 additions & 4 deletions components/canvas/gl_context.rs
Expand Up @@ -2,7 +2,7 @@
* 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::webgl::{WebGLCommand, WebGLVersion};
use canvas_traits::webgl::{WebGLCommand, WebGLVersion, WebGLCommandBacktrace};
use compositing::compositor_thread::{CompositorProxy, self};
use euclid::Size2D;
use gleam::gl;
Expand Down Expand Up @@ -144,13 +144,18 @@ impl GLContextWrapper {
}
}

pub fn apply_command(&self, cmd: WebGLCommand, state: &mut GLState) {
pub fn apply_command(
&self,
cmd: WebGLCommand,
backtrace: WebGLCommandBacktrace,
state: &mut GLState
) {
match *self {
GLContextWrapper::Native(ref ctx) => {
WebGLImpl::apply(ctx, state, cmd);
WebGLImpl::apply(ctx, state, cmd, backtrace);
}
GLContextWrapper::OSMesa(ref ctx) => {
WebGLImpl::apply(ctx, state, cmd);
WebGLImpl::apply(ctx, state, cmd, backtrace);
}
}
}
Expand Down
25 changes: 19 additions & 6 deletions components/canvas/webgl_thread.rs
Expand Up @@ -137,8 +137,8 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
WebGLMsg::RemoveContext(ctx_id) => {
self.remove_webgl_context(ctx_id);
},
WebGLMsg::WebGLCommand(ctx_id, command) => {
self.handle_webgl_command(ctx_id, command);
WebGLMsg::WebGLCommand(ctx_id, command, backtrace) => {
self.handle_webgl_command(ctx_id, command, backtrace);
},
WebGLMsg::WebVRCommand(ctx_id, command) => {
self.handle_webvr_command(ctx_id, command);
Expand All @@ -164,10 +164,15 @@ impl<VR: WebVRRenderHandler + 'static> WebGLThread<VR> {
}

/// Handles a WebGLCommand for a specific WebGLContext
fn handle_webgl_command(&mut self, context_id: WebGLContextId, command: WebGLCommand) {
fn handle_webgl_command(
&mut self,
context_id: WebGLContextId,
command: WebGLCommand,
backtrace: WebGLCommandBacktrace,
) {
let data = Self::make_current_if_needed_mut(context_id, &mut self.contexts, &mut self.bound_context_id);
if let Some(data) = data {
data.ctx.apply_command(command, &mut data.state);
data.ctx.apply_command(command, backtrace, &mut data.state);
}
}

Expand Down Expand Up @@ -670,7 +675,8 @@ impl WebGLImpl {
pub fn apply<Native: NativeGLContextMethods>(
ctx: &GLContext<Native>,
state: &mut GLState,
command: WebGLCommand
command: WebGLCommand,
_backtrace: WebGLCommandBacktrace,
) {
match command {
WebGLCommand::GetContextAttributes(ref sender) =>
Expand Down Expand Up @@ -1191,7 +1197,14 @@ impl WebGLImpl {
// TODO: update test expectations in order to enable debug assertions
let error = ctx.gl().get_error();
if error != gl::NO_ERROR {
error!("Last GL operation failed: {:?}", command)
error!("Last GL operation failed: {:?}", command);
#[cfg(feature = "webgl_backtrace")]
{
error!("Backtrace from failed WebGL API:\n{}", _backtrace.backtrace);
if let Some(backtrace) = _backtrace.js_backtrace {
error!("JS backtrace from failed WebGL API:\n{}", backtrace);
}
}
}
assert_eq!(error, gl::NO_ERROR, "Unexpected WebGL error: 0x{:x} ({})", error, error);
}
Expand Down
3 changes: 3 additions & 0 deletions components/canvas_traits/Cargo.toml
Expand Up @@ -9,6 +9,9 @@ publish = false
name = "canvas_traits"
path = "lib.rs"

[features]
webgl_backtrace = []

[dependencies]
cssparser = "0.24.0"
euclid = "0.19"
Expand Down
14 changes: 11 additions & 3 deletions components/canvas_traits/webgl.rs
Expand Up @@ -24,6 +24,14 @@ pub use ::webgl_channel::WebGLPipeline;
/// Entry point channel type used for sending WebGLMsg messages to the WebGL renderer.
pub use ::webgl_channel::WebGLChan;

#[derive(Clone, Deserialize, Serialize)]
pub struct WebGLCommandBacktrace {
#[cfg(feature = "webgl_backtrace")]
pub backtrace: String,
#[cfg(feature = "webgl_backtrace")]
pub js_backtrace: Option<String>,
}

/// WebGL Message API
#[derive(Deserialize, Serialize)]
pub enum WebGLMsg {
Expand All @@ -35,7 +43,7 @@ pub enum WebGLMsg {
/// Drops a WebGLContext.
RemoveContext(WebGLContextId),
/// Runs a WebGLCommand in a specific WebGLContext.
WebGLCommand(WebGLContextId, WebGLCommand),
WebGLCommand(WebGLContextId, WebGLCommand, WebGLCommandBacktrace),
/// Runs a WebVRCommand in a specific WebGLContext.
WebVRCommand(WebGLContextId, WebVRCommand),
/// Locks a specific WebGLContext. Lock messages are used for a correct synchronization
Expand Down Expand Up @@ -121,8 +129,8 @@ impl WebGLMsgSender {

/// Send a WebGLCommand message
#[inline]
pub fn send(&self, command: WebGLCommand) -> WebGLSendResult {
self.sender.send(WebGLMsg::WebGLCommand(self.ctx_id, command))
pub fn send(&self, command: WebGLCommand, backtrace: WebGLCommandBacktrace) -> WebGLSendResult {
self.sender.send(WebGLMsg::WebGLCommand(self.ctx_id, command, backtrace))
}

/// Send a WebVRCommand message
Expand Down
2 changes: 2 additions & 0 deletions components/script/Cargo.toml
Expand Up @@ -16,6 +16,7 @@ debugmozjs = ['mozjs/debugmozjs']
unstable = []
unrooted_must_root_lint = ["script_plugins/unrooted_must_root_lint"]
default = ["unrooted_must_root_lint"]
webgl_backtrace = ["backtrace", "canvas_traits/webgl_backtrace"]

[build-dependencies]
cmake = "0.1"
Expand All @@ -29,6 +30,7 @@ tinyfiledialogs = "3.0"
[dependencies]
app_units = "0.7"
audio-video-metadata = "0.1.4"
backtrace = {version = "0.3", optional = true}
base64 = "0.6"
bitflags = "1.0"
bluetooth_traits = {path = "../bluetooth_traits"}
Expand Down
28 changes: 25 additions & 3 deletions components/script/dom/webglrenderingcontext.rs
Expand Up @@ -2,9 +2,11 @@
* 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/. */

#[cfg(feature = "webgl_backtrace")]
use backtrace::Backtrace;
use byteorder::{ByteOrder, NativeEndian, WriteBytesExt};
use canvas_traits::canvas::{byte_swap, multiply_u8_pixel};
use canvas_traits::webgl::{DOMToTextureCommand, Parameter};
use canvas_traits::webgl::{DOMToTextureCommand, Parameter, WebGLCommandBacktrace};
use canvas_traits::webgl::{TexParameter, WebGLCommand, WebGLContextShareMode, WebGLError};
use canvas_traits::webgl::{WebGLFramebufferBindingRequest, WebGLMsg, WebGLMsgSender};
use canvas_traits::webgl::{WebGLProgramId, WebGLResult, WebGLSLVersion, WebGLSender};
Expand Down Expand Up @@ -316,7 +318,7 @@ impl WebGLRenderingContext {

#[inline]
pub fn send_command(&self, command: WebGLCommand) {
self.webgl_sender.send(command).unwrap();
self.webgl_sender.send(command, capture_webgl_backtrace(self)).unwrap();
}

#[inline]
Expand Down Expand Up @@ -1189,6 +1191,25 @@ impl WebGLRenderingContext {
}
}

#[cfg(not(feature = "webgl_backtrace"))]
#[inline]
pub fn capture_webgl_backtrace<T: DomObject>(_: &T) -> WebGLCommandBacktrace {
WebGLCommandBacktrace {}
}

#[cfg(feature = "webgl_backtrace")]
#[cfg_attr(feature = "webgl_backtrace", allow(unsafe_code))]
pub fn capture_webgl_backtrace<T: DomObject>(obj: &T) -> WebGLCommandBacktrace {
let bt = Backtrace::new();
unsafe {
capture_stack!(in(obj.global().get_cx()) let stack);
WebGLCommandBacktrace {
backtrace: format!("{:?}", bt),
js_backtrace: stack.and_then(|s| s.as_string(None)),
}
}
}

impl Drop for WebGLRenderingContext {
fn drop(&mut self) {
let _ = self.webgl_sender.send_remove();
Expand Down Expand Up @@ -1521,9 +1542,10 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
let (sender, receiver) = webgl_channel().unwrap();

// If the send does not succeed, assume context lost
let backtrace = capture_webgl_backtrace(self);
if self
.webgl_sender
.send(WebGLCommand::GetContextAttributes(sender))
.send(WebGLCommand::GetContextAttributes(sender), backtrace)
.is_err()
{
return None;
Expand Down
2 changes: 2 additions & 0 deletions components/script/lib.rs
Expand Up @@ -19,6 +19,8 @@

extern crate app_units;
extern crate audio_video_metadata;
#[cfg(feature = "webgl_backtrace")]
extern crate backtrace;
extern crate base64;
#[macro_use]
extern crate bitflags;
Expand Down
5 changes: 5 additions & 0 deletions components/servo/Cargo.toml
Expand Up @@ -24,6 +24,11 @@ unstable = [
"profile/unstable",
"script/unstable",
]
webgl_backtrace = [
"script/webgl_backtrace",
"canvas/webgl_backtrace",
"canvas_traits/webgl_backtrace",
]

[dependencies]
bluetooth_traits = {path = "../bluetooth_traits"}
Expand Down
1 change: 1 addition & 0 deletions ports/libsimpleservo/Cargo.toml
Expand Up @@ -42,3 +42,4 @@ debugmozjs = ["libservo/debugmozjs"]
unstable = ["libservo/unstable"]
googlevr = ["libservo/googlevr"]
oculusvr = ["libservo/oculusvr"]
webgl_backtrace = ["libservo/webgl_backtrace"]
1 change: 1 addition & 0 deletions ports/servo/Cargo.toml
Expand Up @@ -30,6 +30,7 @@ webdriver = ["libservo/webdriver"]
energy-profiling = ["libservo/energy-profiling"]
debugmozjs = ["libservo/debugmozjs"]
unstable = ["libservo/unstable"]
webgl_backtrace = ["libservo/webgl_backtrace"]

[target.'cfg(not(target_os = "android"))'.dependencies]
backtrace = "0.3"
Expand Down

0 comments on commit 5dc80dd

Please sign in to comment.