From a3515114639ab7fab9c0aa0182534b5bcde0d042 Mon Sep 17 00:00:00 2001 From: Victor Porof Date: Tue, 26 Nov 2013 10:51:20 +0200 Subject: [PATCH] Bug 938549 - Recompiling shaders when highlighting is very heavy and loses cached state, r=rcampbell --HG-- rename : browser/devtools/shadereditor/test/browser_se_programs-blackbox.js => browser/devtools/shadereditor/test/browser_se_programs-blackbox-01.js rename : browser/devtools/shadereditor/test/browser_se_programs-highlight.js => browser/devtools/shadereditor/test/browser_se_programs-highlight-01.js --- browser/devtools/shadereditor/shadereditor.js | 4 +- .../devtools/shadereditor/test/browser.ini | 7 +- ....js => browser_se_programs-blackbox-01.js} | 10 +- .../test/browser_se_programs-blackbox-02.js | 67 ++++ ...js => browser_se_programs-highlight-01.js} | 4 +- .../test/browser_se_programs-highlight-02.js | 53 +++ .../test/browser_webgl-actor-test-06.js | 6 +- .../test/browser_webgl-actor-test-09.js | 6 +- .../test/browser_webgl-actor-test-10.js | 13 +- .../test/browser_webgl-actor-test-15.js | 13 +- .../test/browser_webgl-actor-test-16.js | 13 +- .../test/doc_blended-geometry.html | 136 +++++++ browser/devtools/shadereditor/test/head.js | 1 + toolkit/devtools/server/actors/webgl.js | 359 +++++++++++++++--- 14 files changed, 616 insertions(+), 76 deletions(-) rename browser/devtools/shadereditor/test/{browser_se_programs-blackbox.js => browser_se_programs-blackbox-01.js} (95%) create mode 100644 browser/devtools/shadereditor/test/browser_se_programs-blackbox-02.js rename browser/devtools/shadereditor/test/{browser_se_programs-highlight.js => browser_se_programs-highlight-01.js} (95%) create mode 100644 browser/devtools/shadereditor/test/browser_se_programs-highlight-02.js create mode 100644 browser/devtools/shadereditor/test/doc_blended-geometry.html diff --git a/browser/devtools/shadereditor/shadereditor.js b/browser/devtools/shadereditor/shadereditor.js index 615e4fd88233..b887c0946fca 100644 --- a/browser/devtools/shadereditor/shadereditor.js +++ b/browser/devtools/shadereditor/shadereditor.js @@ -32,7 +32,7 @@ const EVENTS = { }; const STRINGS_URI = "chrome://browser/locale/devtools/shadereditor.properties" -const HIGHLIGHT_COLOR = [1, 0, 0, 1]; // rgba +const HIGHLIGHT_TINT = [1, 0, 0.25, 1]; // rgba const TYPING_MAX_DELAY = 500; // ms const SHADERS_AUTOGROW_ITEMS = 4; const GUTTER_ERROR_PANEL_OFFSET_X = 7; // px @@ -297,7 +297,7 @@ let ShadersListView = Heritage.extend(WidgetMethods, { _onProgramMouseEnter: function(e) { let sourceItem = this.getItemForElement(e.target, { noSiblings: true }); if (sourceItem && !sourceItem.attachment.isBlackBoxed) { - sourceItem.attachment.programActor.highlight(HIGHLIGHT_COLOR); + sourceItem.attachment.programActor.highlight(HIGHLIGHT_TINT); if (e instanceof Event) { e.preventDefault(); diff --git a/browser/devtools/shadereditor/test/browser.ini b/browser/devtools/shadereditor/test/browser.ini index 7efa1dd6587e..c90e31cd656e 100644 --- a/browser/devtools/shadereditor/test/browser.ini +++ b/browser/devtools/shadereditor/test/browser.ini @@ -1,5 +1,6 @@ [DEFAULT] support-files = + doc_blended-geometry.html doc_multiple-contexts.html doc_overlapping-geometry.html doc_shader-order.html @@ -14,9 +15,11 @@ support-files = [browser_se_editors-lazy-init.js] [browser_se_first-run.js] [browser_se_navigation.js] -[browser_se_programs-blackbox.js] +[browser_se_programs-blackbox-01.js] +[browser_se_programs-blackbox-02.js] [browser_se_programs-cache.js] -[browser_se_programs-highlight.js] +[browser_se_programs-highlight-01.js] +[browser_se_programs-highlight-02.js] [browser_se_programs-list.js] [browser_se_shaders-edit-01.js] [browser_se_shaders-edit-02.js] diff --git a/browser/devtools/shadereditor/test/browser_se_programs-blackbox.js b/browser/devtools/shadereditor/test/browser_se_programs-blackbox-01.js similarity index 95% rename from browser/devtools/shadereditor/test/browser_se_programs-blackbox.js rename to browser/devtools/shadereditor/test/browser_se_programs-blackbox-01.js index e39f8a543943..0e6a3e693af2 100644 --- a/browser/devtools/shadereditor/test/browser_se_programs-blackbox.js +++ b/browser/devtools/shadereditor/test/browser_se_programs-blackbox-01.js @@ -84,7 +84,7 @@ function ifWebGLSupported() { yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2"); - ok(true, "Highlighting didn't work while blackboxed (1)."); + ok(true, "Highlighting shouldn't work while blackboxed (1)."); ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 0) }); ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) }); @@ -93,7 +93,7 @@ function ifWebGLSupported() { yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2"); - ok(true, "Highlighting didn't work while blackboxed (2)."); + ok(true, "Highlighting shouldn't work while blackboxed (2)."); ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) }); @@ -101,7 +101,7 @@ function ifWebGLSupported() { yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas1"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 0, a: 255 }, true, "#canvas2"); - ok(true, "Highlighting didn't work while blackboxed (3)."); + ok(true, "Highlighting shouldn't work while blackboxed (3)."); getBlackBoxCheckbox(panel, 0).click(); getBlackBoxCheckbox(panel, 1).click(); @@ -133,9 +133,9 @@ function ifWebGLSupported() { ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) }); yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1"); - yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2"); + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1"); - yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2"); + yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2"); ok(true, "The second program was correctly highlighted."); ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) }); diff --git a/browser/devtools/shadereditor/test/browser_se_programs-blackbox-02.js b/browser/devtools/shadereditor/test/browser_se_programs-blackbox-02.js new file mode 100644 index 000000000000..71202aa6f61f --- /dev/null +++ b/browser/devtools/shadereditor/test/browser_se_programs-blackbox-02.js @@ -0,0 +1,67 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if blackboxing a program works properly in tandem with blended + * overlapping geometry. + */ + +function ifWebGLSupported() { + let [target, debuggee, panel] = yield initShaderEditor(BLENDED_GEOMETRY_CANVAS_URL); + let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin; + + reload(target); + let firstProgramActor = yield once(gFront, "program-linked"); + let secondProgramActor = yield once(gFront, "program-linked"); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true); + ok(true, "The canvas was correctly drawn."); + + getBlackBoxCheckbox(panel, 0).click(); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 0, b: 0, a: 127 }, true); + ok(true, "The first program was correctly blackboxed."); + + getBlackBoxCheckbox(panel, 0).click(); + getBlackBoxCheckbox(panel, 1).click(); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 127, g: 127, b: 127, a: 255 }, true); + ok(true, "The second program was correctly blackboxed."); + + getBlackBoxCheckbox(panel, 1).click(); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true); + ok(true, "The two programs were correctly unblackboxed."); + + getBlackBoxCheckbox(panel, 0).click(); + getBlackBoxCheckbox(panel, 1).click(); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 0, b: 0, a: 255 }, true); + ok(true, "The two programs were correctly blackboxed again."); + + getBlackBoxCheckbox(panel, 0).click(); + getBlackBoxCheckbox(panel, 1).click(); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true); + ok(true, "The two programs were correctly unblackboxed again."); + + yield teardown(panel); + finish(); +} + +function getBlackBoxCheckbox(aPanel, aIndex) { + return aPanel.panelWin.document.querySelectorAll( + ".side-menu-widget-item-checkbox")[aIndex]; +} + +function once(aTarget, aEvent) { + let deferred = promise.defer(); + aTarget.once(aEvent, deferred.resolve); + return deferred.promise; +} diff --git a/browser/devtools/shadereditor/test/browser_se_programs-highlight.js b/browser/devtools/shadereditor/test/browser_se_programs-highlight-01.js similarity index 95% rename from browser/devtools/shadereditor/test/browser_se_programs-highlight.js rename to browser/devtools/shadereditor/test/browser_se_programs-highlight-01.js index 5e0eb6c35367..6f54f7734bf1 100644 --- a/browser/devtools/shadereditor/test/browser_se_programs-highlight.js +++ b/browser/devtools/shadereditor/test/browser_se_programs-highlight-01.js @@ -47,9 +47,9 @@ function ifWebGLSupported() { ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) }); yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1"); - yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2"); + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2"); yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 255, b: 0, a: 255 }, true, "#canvas1"); - yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 255, g: 0, b: 0, a: 255 }, true, "#canvas2"); + yield ensurePixelIs(debuggee, { x: 127, y: 127 }, { r: 0, g: 0, b: 64, a: 255 }, true, "#canvas2"); ok(true, "The second program was correctly highlighted."); ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) }); diff --git a/browser/devtools/shadereditor/test/browser_se_programs-highlight-02.js b/browser/devtools/shadereditor/test/browser_se_programs-highlight-02.js new file mode 100644 index 000000000000..d5d13464100b --- /dev/null +++ b/browser/devtools/shadereditor/test/browser_se_programs-highlight-02.js @@ -0,0 +1,53 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests if highlighting a program works properly in tandem with blended + * overlapping geometry. + */ + +function ifWebGLSupported() { + let [target, debuggee, panel] = yield initShaderEditor(BLENDED_GEOMETRY_CANVAS_URL); + let { gFront, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin; + + reload(target); + let firstProgramActor = yield once(gFront, "program-linked"); + let secondProgramActor = yield once(gFront, "program-linked"); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true); + ok(true, "The canvas was correctly drawn."); + + ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 0) }); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 0, b: 32, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 0, b: 32, a: 127 }, true); + ok(true, "The first program was correctly highlighted."); + + ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 0) }); + ShadersListView._onProgramMouseEnter({ target: getItemLabel(panel, 1) }); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 255, g: 0, b: 64, a: 255 }, true); + ok(true, "The second program was correctly highlighted."); + + ShadersListView._onProgramMouseLeave({ target: getItemLabel(panel, 1) }); + + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 127, g: 127, b: 127, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 64, y: 64 }, { r: 0, g: 127, b: 127, a: 127 }, true); + ok(true, "The two programs were correctly unhighlighted."); + + yield teardown(panel); + finish(); +} + +function getItemLabel(aPanel, aIndex) { + return aPanel.panelWin.document.querySelectorAll( + ".side-menu-widget-item-label")[aIndex]; +} + +function once(aTarget, aEvent) { + let deferred = promise.defer(); + aTarget.once(aEvent, deferred.resolve); + return deferred.promise; +} diff --git a/browser/devtools/shadereditor/test/browser_webgl-actor-test-06.js b/browser/devtools/shadereditor/test/browser_webgl-actor-test-06.js index 5a516b188d8b..024a6ca7c945 100644 --- a/browser/devtools/shadereditor/test/browser_webgl-actor-test-06.js +++ b/browser/devtools/shadereditor/test/browser_webgl-actor-test-06.js @@ -19,9 +19,9 @@ function ifWebGLSupported() { yield checkShaderSource("The shader sources are correct before highlighting."); ok(true, "The corner pixel colors are correct before highlighting."); - yield programActor.highlight([0, 0, 1, 1]); - yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true); - yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 0, b: 255, a: 255 }, true); + yield programActor.highlight([0, 1, 0, 1]); + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); yield checkShaderSource("The shader sources are preserved after highlighting."); ok(true, "The corner pixel colors are correct after highlighting."); diff --git a/browser/devtools/shadereditor/test/browser_webgl-actor-test-09.js b/browser/devtools/shadereditor/test/browser_webgl-actor-test-09.js index 925fde818140..61bd306054c8 100644 --- a/browser/devtools/shadereditor/test/browser_webgl-actor-test-09.js +++ b/browser/devtools/shadereditor/test/browser_webgl-actor-test-09.js @@ -70,9 +70,9 @@ function ifWebGLSupported() { ok(fragSource.contains("vec3 vFragmentColor;"), "The previous correct fragment shader source was preserved."); - yield programActor.highlight([0, 0, 1, 1]); - yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true); - yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 0, b: 255, a: 255 }, true); + yield programActor.highlight([0, 1, 0, 1]); + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); ok(true, "Highlighting worked after setting a defective fragment source."); yield programActor.unhighlight(); diff --git a/browser/devtools/shadereditor/test/browser_webgl-actor-test-10.js b/browser/devtools/shadereditor/test/browser_webgl-actor-test-10.js index 070bf6e886e2..f9f4c7f774cc 100644 --- a/browser/devtools/shadereditor/test/browser_webgl-actor-test-10.js +++ b/browser/devtools/shadereditor/test/browser_webgl-actor-test-10.js @@ -27,15 +27,18 @@ function ifWebGLSupported() { function testHighlighting(programActor) { return Task.spawn(function() { yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true); - ok(true, "The top left pixel color was correct before highlighting."); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct before highlighting."); - yield programActor.highlight([0, 0, 1, 1]); - yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true); - ok(true, "The top left pixel color is correct after highlighting."); + yield programActor.highlight([0, 1, 0, 1]); + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct after highlighting."); yield programActor.unhighlight(); yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true); - ok(true, "The top left pixel color is correct after unhighlighting."); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct after unhighlighting."); }); } } diff --git a/browser/devtools/shadereditor/test/browser_webgl-actor-test-15.js b/browser/devtools/shadereditor/test/browser_webgl-actor-test-15.js index d26e3f3b5bc1..580d91ffecfa 100644 --- a/browser/devtools/shadereditor/test/browser_webgl-actor-test-15.js +++ b/browser/devtools/shadereditor/test/browser_webgl-actor-test-15.js @@ -81,15 +81,18 @@ function ifWebGLSupported() { function checkHighlightingInTheFirstPage(programActor) { return Task.spawn(function() { yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true); - ok(true, "The top left pixel color was correct before highlighting."); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct before highlighting."); - yield programActor.highlight([0, 0, 1, 1]); - yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true); - ok(true, "The top left pixel color is correct after highlighting."); + yield programActor.highlight([0, 1, 0, 1]); + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct after highlighting."); yield programActor.unhighlight(); yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true); - ok(true, "The top left pixel color is correct after unhighlighting."); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct after unhighlighting."); }); } diff --git a/browser/devtools/shadereditor/test/browser_webgl-actor-test-16.js b/browser/devtools/shadereditor/test/browser_webgl-actor-test-16.js index 96b449d2baf1..6fa445c67760 100644 --- a/browser/devtools/shadereditor/test/browser_webgl-actor-test-16.js +++ b/browser/devtools/shadereditor/test/browser_webgl-actor-test-16.js @@ -81,15 +81,18 @@ function ifWebGLSupported() { function checkHighlightingInTheFirstPage(programActor) { return Task.spawn(function() { yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true); - ok(true, "The top left pixel color was correct before highlighting."); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct before highlighting."); - yield programActor.highlight([0, 0, 1, 1]); - yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 255, a: 255 }, true); - ok(true, "The top left pixel color is correct after highlighting."); + yield programActor.highlight([0, 1, 0, 1]); + yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 0, g: 0, b: 0, a: 255 }, true); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct after highlighting."); yield programActor.unhighlight(); yield ensurePixelIs(debuggee, { x: 0, y: 0 }, { r: 255, g: 0, b: 0, a: 255 }, true); - ok(true, "The top left pixel color is correct after unhighlighting."); + yield ensurePixelIs(debuggee, { x: 511, y: 511 }, { r: 0, g: 255, b: 0, a: 255 }, true); + ok(true, "The corner pixel colors are correct after unhighlighting."); }); } diff --git a/browser/devtools/shadereditor/test/doc_blended-geometry.html b/browser/devtools/shadereditor/test/doc_blended-geometry.html new file mode 100644 index 000000000000..7137026effe1 --- /dev/null +++ b/browser/devtools/shadereditor/test/doc_blended-geometry.html @@ -0,0 +1,136 @@ + + + + + + + WebGL editor test page + + + + + + + + + + + + + + + diff --git a/browser/devtools/shadereditor/test/head.js b/browser/devtools/shadereditor/test/head.js index 0da165722282..bd5da2f22399 100644 --- a/browser/devtools/shadereditor/test/head.js +++ b/browser/devtools/shadereditor/test/head.js @@ -28,6 +28,7 @@ const SIMPLE_CANVAS_URL = EXAMPLE_URL + "doc_simple-canvas.html"; const SHADER_ORDER_URL = EXAMPLE_URL + "doc_shader-order.html"; const MULTIPLE_CONTEXTS_URL = EXAMPLE_URL + "doc_multiple-contexts.html"; const OVERLAPPING_GEOMETRY_CANVAS_URL = EXAMPLE_URL + "doc_overlapping-geometry.html"; +const BLENDED_GEOMETRY_CANVAS_URL = EXAMPLE_URL + "doc_blended-geometry.html"; // All tests are asynchronous. waitForExplicitFinish(); diff --git a/toolkit/devtools/server/actors/webgl.js b/toolkit/devtools/server/actors/webgl.js index f5bfdb890b0a..e52b44507208 100644 --- a/toolkit/devtools/server/actors/webgl.js +++ b/toolkit/devtools/server/actors/webgl.js @@ -15,16 +15,10 @@ const { method, Arg, Option, RetVal } = protocol; const WEBGL_CONTEXT_NAMES = ["webgl", "experimental-webgl", "moz-webgl"]; -const HIGHLIGHT_FRAG_SHADER = [ - "precision lowp float;", - "void main() {", - "gl_FragColor.rgba = vec4(%color);", - "}" -].join("\n"); - // These traits are bit masks. Make sure they're powers of 2. const PROGRAM_DEFAULT_TRAITS = 0; const PROGRAM_BLACKBOX_TRAIT = 1; +const PROGRAM_HIGHLIGHT_TRAIT = 2; exports.register = function(handle) { handle.addTabActor(WebGLActor, "webglActor"); @@ -160,26 +154,21 @@ let ProgramActor = protocol.ActorClass({ }), /** - * Replaces this program's fragment shader with an temporary - * easy-to-distinguish alternative. See HIGHLIGHT_FRAG_SHADER. + * Highlights any geometry rendered using this program. */ - highlight: method(function(color) { - let shaderActor = this._getShaderActor("fragment"); - let oldText = shaderActor.text; - let newText = HIGHLIGHT_FRAG_SHADER.replace("%color", color) - shaderActor.compile(newText); - shaderActor.text = oldText; + highlight: method(function(tint) { + this.linkedProxy.highlightTint = tint; + this.linkedCache.setProgramTrait(this.program, PROGRAM_HIGHLIGHT_TRAIT); }, { - request: { color: Arg(0, "array:string") }, + request: { tint: Arg(0, "array:number") }, oneway: true }), /** - * Reverts this program's fragment shader to the latest user-defined source. + * Allows geometry to be rendered normally using this program. */ unhighlight: method(function() { - let shaderActor = this._getShaderActor("fragment"); - shaderActor.compile(shaderActor.text); + this.linkedCache.unsetProgramTrait(this.program, PROGRAM_HIGHLIGHT_TRAIT); }, { oneway: true }), @@ -477,29 +466,31 @@ let WebGLInstrumenter = { * The targeted WebGL context instance. * @param string funcName * The function to override. - * @param string callbackName [optional] - * A custom callback function name in the observer. If unspecified, - * it will default to the name of the function to override. + * @param array callbackName [optional] + * The two callback function names in the observer, corresponding to + * the "before" and "after" invocation times. If unspecified, they will + * default to the name of the function to override. * @param number timing [optional] * When to issue the callback in relation to the actual context - * function call. Availalble values are 0 for "before" (default) - * and 1 for "after". + * function call. Availalble values are -1 for "before" (default) + * 1 for "after" and 0 for "before and after". */ - _instrument: function(observer, context, funcName, callbackName, timing = 0) { + _instrument: function(observer, context, funcName, callbackName = [], timing = -1) { let { cache, proxy } = observer.for(context); let originalFunc = context[funcName]; - let proxyFuncName = callbackName || funcName; + let beforeFuncName = callbackName[0] || funcName; + let afterFuncName = callbackName[1] || callbackName[0] || funcName; context[funcName] = function(...glArgs) { - if (timing == 0 && !observer.suppressHandlers) { - let glBreak = observer[proxyFuncName](glArgs, cache, proxy); + if (timing <= 0 && !observer.suppressHandlers) { + let glBreak = observer[beforeFuncName](glArgs, cache, proxy); if (glBreak) return undefined; } let glResult = originalFunc.apply(this, glArgs); - if (timing == 1 && !observer.suppressHandlers) { - let glBreak = observer[proxyFuncName](glArgs, glResult, cache, proxy); + if (timing >= 0 && !observer.suppressHandlers) { + let glBreak = observer[afterFuncName](glArgs, glResult, cache, proxy); if (glBreak) return undefined; } @@ -516,19 +507,28 @@ let WebGLInstrumenter = { "linkProgram", "getAttribLocation", "getUniformLocation" ] }, { - callback: "toggleVertexAttribArray", + timing: -1, // before + callback: [ + "toggleVertexAttribArray" + ], functions: [ "enableVertexAttribArray", "disableVertexAttribArray" ] }, { - callback: "attribute_", + timing: -1, // before + callback: [ + "attribute_" + ], functions: [ "vertexAttrib1f", "vertexAttrib2f", "vertexAttrib3f", "vertexAttrib4f", "vertexAttrib1fv", "vertexAttrib2fv", "vertexAttrib3fv", "vertexAttrib4fv", "vertexAttribPointer" ] }, { - callback: "uniform_", + timing: -1, // before + callback: [ + "uniform_" + ], functions: [ "uniform1i", "uniform2i", "uniform3i", "uniform4i", "uniform1f", "uniform2f", "uniform3f", "uniform4f", @@ -537,10 +537,17 @@ let WebGLInstrumenter = { "uniformMatrix2fv", "uniformMatrix3fv", "uniformMatrix4fv" ] }, { - timing: 1, // after - functions: ["useProgram"] + timing: -1, // before + functions: [ + "useProgram", "enable", "disable", "blendColor", + "blendEquation", "blendEquationSeparate", + "blendFunc", "blendFuncSeparate" + ] }, { - callback: "draw_", + timing: 0, // before and after + callback: [ + "beforeDraw_", "afterDraw_" + ], functions: [ "drawArrays", "drawElements" ] @@ -573,6 +580,7 @@ WebGLObserver.prototype = { registerContextForWindow: function(id, context) { let cache = new WebGLCache(id, context); let proxy = new WebGLProxy(id, context, cache, this); + cache.refreshState(proxy); this._contexts.set(context, { ownerWindow: id, @@ -706,21 +714,116 @@ WebGLObserver.prototype = { }, /** - * Called immediately *after* 'useProgram' is requested in the context. + * Called immediately *before* 'useProgram' is requested in the context. * * @param array glArgs * Overridable arguments with which the function is called. - * @param void glResult - * The returned value of the original function call. * @param WebGLCache cache * The state storage for the WebGL context initiating this call. */ - useProgram: function(glArgs, glResult, cache) { + useProgram: function(glArgs, cache) { // Manually keeping a cache and not using gl.getParameter(CURRENT_PROGRAM) // because gl.get* functions are slow as potatoes. cache.currentProgram = glArgs[0]; }, + /** + * Called immediately *before* 'enable' is requested in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + */ + enable: function(glArgs, cache) { + cache.currentState[glArgs[0]] = true; + }, + + /** + * Called immediately *before* 'disable' is requested in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + */ + disable: function(glArgs, cache) { + cache.currentState[glArgs[0]] = false; + }, + + /** + * Called immediately *before* 'blendColor' is requested in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + */ + blendColor: function(glArgs, cache) { + let blendColor = cache.currentState.blendColor; + blendColor[0] = glArgs[0]; + blendColor[1] = glArgs[1]; + blendColor[2] = glArgs[2]; + blendColor[3] = glArgs[3]; + }, + + /** + * Called immediately *before* 'blendEquation' is requested in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + */ + blendEquation: function(glArgs, cache) { + let state = cache.currentState; + state.blendEquationRgb = state.blendEquationAlpha = glArgs[0]; + }, + + /** + * Called immediately *before* 'blendEquationSeparate' is requested in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + */ + blendEquationSeparate: function(glArgs, cache) { + let state = cache.currentState; + state.blendEquationRgb = glArgs[0]; + state.blendEquationAlpha = glArgs[1]; + }, + + /** + * Called immediately *before* 'blendFunc' is requested in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + */ + blendFunc: function(glArgs, cache) { + let state = cache.currentState; + state.blendSrcRgb = state.blendSrcAlpha = glArgs[0]; + state.blendDstRgb = state.blendDstAlpha = glArgs[1]; + }, + + /** + * Called immediately *before* 'blendFuncSeparate' is requested in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + */ + blendFuncSeparate: function(glArgs, cache) { + let state = cache.currentState; + state.blendSrcRgb = glArgs[0]; + state.blendDstRgb = glArgs[1]; + state.blendSrcAlpha = glArgs[2]; + state.blendDstAlpha = glArgs[3]; + }, + /** * Called immediately *before* 'drawArrays' or 'drawElements' is requested * in the context. @@ -729,10 +832,44 @@ WebGLObserver.prototype = { * Overridable arguments with which the function is called. * @param WebGLCache cache * The state storage for the WebGL context initiating this call. + * @param WebGLProxy proxy + * The proxy methods for the WebGL context initiating this call. */ - draw_: function(glArgs, cache) { - // Return true to break original function call. - return cache.currentProgramTraits & PROGRAM_BLACKBOX_TRAIT; + beforeDraw_: function(glArgs, cache, proxy) { + let traits = cache.currentProgramTraits; + + // Handle program blackboxing. + if (traits & PROGRAM_BLACKBOX_TRAIT) { + return true; // Return true to break original function call. + } + // Handle program highlighting. + if (traits & PROGRAM_HIGHLIGHT_TRAIT) { + proxy.enableHighlighting(); + } + + return false; + }, + + /** + * Called immediately *after* 'drawArrays' or 'drawElements' is requested + * in the context. + * + * @param array glArgs + * Overridable arguments with which the function is called. + * @param void glResult + * The returned value of the original function call. + * @param WebGLCache cache + * The state storage for the WebGL context initiating this call. + * @param WebGLProxy proxy + * The proxy methods for the WebGL context initiating this call. + */ + afterDraw_: function(glArgs, glResult, cache, proxy) { + let traits = cache.currentProgramTraits; + + // Handle program highlighting. + if (traits & PROGRAM_HIGHLIGHT_TRAIT) { + proxy.disableHighlighting(); + } } }; @@ -749,6 +886,7 @@ function WebGLCache(id, context) { this._id = id; this._gl = context; this._programs = new Map(); + this.currentState = {}; } WebGLCache.prototype = { @@ -762,6 +900,35 @@ WebGLCache.prototype = { get ownerWindow() this._id, get ownerContext() this._gl, + /** + * A collection of flags or properties representing the context's state. + * Implemented as an object hash and not a Map instance because keys are + * always either strings or numbers. + */ + currentState: null, + + /** + * Populates the current state with values retrieved from the context. + * + * @param WebGLProxy proxy + * The proxy methods for the WebGL context owning the state. + */ + refreshState: function(proxy) { + let gl = this._gl; + let s = this.currentState; + + // Populate only with the necessary parameters. Not all default WebGL + // state values are required. + s[gl.BLEND] = proxy.isEnabled("BLEND"); + s.blendColor = proxy.getParameter("BLEND_COLOR"); + s.blendEquationRgb = proxy.getParameter("BLEND_EQUATION_RGB"); + s.blendEquationAlpha = proxy.getParameter("BLEND_EQUATION_ALPHA"); + s.blendSrcRgb = proxy.getParameter("BLEND_SRC_RGB"); + s.blendSrcAlpha = proxy.getParameter("BLEND_SRC_ALPHA"); + s.blendDstRgb = proxy.getParameter("BLEND_DST_RGB"); + s.blendDstAlpha = proxy.getParameter("BLEND_DST_ALPHA"); + }, + /** * Adds a program to the cache. * @@ -947,10 +1114,14 @@ function WebGLProxy(id, context, cache, observer) { this._observer = observer; let exports = [ + "isEnabled", + "getParameter", "getAttachedShaders", "getShaderSource", "getShaderOfType", - "compileShader" + "compileShader", + "enableHighlighting", + "disableHighlighting" ]; exports.forEach(e => this[e] = (...args) => this._call(e, args)); } @@ -964,6 +1135,64 @@ WebGLProxy.prototype = { get ownerWindow() this._id, get ownerContext() this._gl, + /** + * Test whether a WebGL capability is enabled. + * + * @param string name + * The WebGL capability name, for example "BLEND". + * @return boolean + * True if enabled, false otherwise. + */ + _isEnabled: function(name) { + return this._gl.isEnabled(this._gl[name]); + }, + + /** + * Returns the value for the specified WebGL parameter name. + * + * @param string name + * The WebGL parameter name, for example "BLEND_COLOR". + * @return any + * The corresponding parameter's value. + */ + _getParameter: function(name) { + return this._gl.getParameter(this._gl[name]); + }, + + /** + * Returns the renderbuffer property value for the specified WebGL parameter. + * If no renderbuffer binding is available, null is returned. + * + * @param string name + * The WebGL parameter name, for example "BLEND_COLOR". + * @return any + * The corresponding parameter's value. + */ + _getRenderbufferParameter: function(name) { + if (!this._getParameter("RENDERBUFFER_BINDING")) { + return null; + } + let gl = this._gl; + return gl.getRenderbufferParameter(gl.RENDERBUFFER, gl[name]); + }, + + /** + * Returns the framebuffer property value for the specified WebGL parameter. + * If no framebuffer binding is available, null is returned. + * + * @param string name + * The WebGL parameter name, for example "BLEND_COLOR". + * @return any + * The corresponding parameter's value. + */ + _getFramebufferAttachmentParameter: function(type, name) { + if (!this._getParameter("FRAMEBUFFER_BINDING")) { + return null; + } + let gl = this._gl; + return gl.getFramebufferAttachmentParameter(gl.RENDERBUFFER, gl[type], gl[name]); + }, + /** * Returns the shader objects attached to a program object. * @@ -1046,6 +1275,48 @@ WebGLProxy.prototype = { return error; }, + /** + * Enables color blending based on the geometry highlight tint. + */ + _enableHighlighting: function() { + let gl = this._gl; + + // Avoid changing the blending params when rendering to a depth texture. + let format = this._getRenderbufferParameter("RENDERBUFFER_INTERNAL_FORMAT"); + if (format == gl.DEPTH_COMPONENT16) { + return; + } + + // Non-premultiplied alpha blending based on a predefined constant color. + // Simply using gl.colorMask won't work, because we want non-tinted colors + // to be drawn as black, not ignored. + gl.enable(gl.BLEND); + gl.blendColor.apply(gl, this.highlightTint); + gl.blendEquation(gl.FUNC_ADD); + gl.blendFunc(gl.CONSTANT_COLOR, gl.ONE_MINUS_SRC_ALPHA, gl.CONSTANT_COLOR, gl.ZERO); + this.wasHighlighting = true; + }, + + /** + * Disables color blending based on the geometry highlight tint, by + * reverting the corresponding params back to their original values. + */ + _disableHighlighting: function() { + let gl = this._gl; + let s = this._cache.currentState; + + gl[s[gl.BLEND] ? "enable" : "disable"](gl.BLEND); + gl.blendColor.apply(gl, s.blendColor); + gl.blendEquationSeparate(s.blendEquationRgb, s.blendEquationAlpha); + gl.blendFuncSeparate(s.blendSrcRgb, s.blendDstRgb, s.blendSrcAlpha, s.blendDstAlpha); + }, + + /** + * The color tint used for highlighting geometry. + * @see _enableHighlighting and _disableHighlighting. + */ + highlightTint: [0, 0, 0, 0], + /** * Executes a function in this object. *