Web Inspector: Canvas: modifications to shader modules can be shared between vertex...
authordrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 8 Oct 2019 23:15:59 +0000 (23:15 +0000)
committerdrousso@apple.com <drousso@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 8 Oct 2019 23:15:59 +0000 (23:15 +0000)
https://bugs.webkit.org/show_bug.cgi?id=202031

Reviewed by Dean Jackson.

Source/JavaScriptCore:

* inspector/protocol/Canvas.json:
Create a distinct `ShaderProgram` type so that additional data can be bundled and sent to
the frontend as part of the `programCreated` event without having to worry about having too
many arguments.

Source/WebCore:

Test: inspector/canvas/updateShader-webgpu-sharedVertexFragment.html

* Modules/webgpu/WebGPUPipeline.h:
* Modules/webgpu/WebGPUComputePipeline.cpp:
(WebCore::WebGPUComputePipeline::cloneShaderModules): Added.
(WebCore::WebGPUComputePipeline::recompile):
* Modules/webgpu/WebGPURenderPipeline.cpp:
(WebCore::WebGPURenderPipeline::cloneShaderModules): Added.
(WebCore::WebGPURenderPipeline::recompile):
Recreate the vertex/fragment/compute shader module(s) when recompiling so that modifications
to it via this pipeline don't affect other pipelines that also use the same shader module.

* inspector/InspectorShaderProgram.h:
* inspector/InspectorShaderProgram.cpp:
(WebCore::InspectorShaderProgram::updateShader):
(WebCore::InspectorShaderProgram::buildObjectForShaderProgram): Added.
* inspector/agents/InspectorCanvasAgent.cpp:
(WebCore::InspectorCanvasAgent::didCreateWebGLProgram):
(WebCore::InspectorCanvasAgent::didCreateWebGPUPipeline):
Include as part of the `Canvas.event.programCreated` payload a flag indicating whether the
vertex shader module and fragment shader module are the same for `WebGPURenderPipeline`s.

Source/WebInspectorUI:

If the vertex and fragment shaders share the same source module for a WebGPU shader pipeline,
only display a single editable content view for that shader pipeline in the Canvas Tab.

* UserInterface/Models/ShaderProgram.js:
(WI.ShaderProgram):
(WI.ShaderProgram.prototype.get sharesVertexFragmentShader): Added.
* UserInterface/Controllers/CanvasManager.js:
(WI.CanvasManager.prototype.programCreated):
* UserInterface/Protocol/CanvasObserver.js:
(WI.CanvasObserver.prototype.programCreated):

* UserInterface/Views/ShaderProgramContentView.js:
(WI.ShaderProgramContentView):
(WI.ShaderProgramContentView.prototype.shown):
(WI.ShaderProgramContentView.prototype.hidden):
(WI.ShaderProgramContentView.prototype._refreshContent):
* UserInterface/Views/ShaderProgramContentView.css:
(.content-view.shader-program > .shader.compute, .content-view.shader-program > .shader.vertex.shares-vertex-fragment-shader): Added.
(body[dir=ltr] .content-view.shader-program > .shader.vertex:not(.shares-vertex-fragment-shader), body[dir=rtl] .content-view.shader-program > .shader.fragment): Added.
(body[dir=ltr] .content-view.shader-program > .shader.fragment, body[dir=rtl] .content-view.shader-program > .shader.vertex:not(.shares-vertex-fragment-shader)): Added.
(.content-view.shader-program > .shader.compute): Deleted.
(body[dir=ltr] .content-view.shader-program > .shader.vertex, body[dir=rtl] .content-view.shader-program > .shader.fragment): Deleted.
(body[dir=ltr] .content-view.shader-program > .shader.fragment, body[dir=rtl] .content-view.shader-program > .shader.vertex): Deleted.

* UserInterface/Views/CodeMirrorAdditions.js:
Replace the vertex/fragment specific MIME types with a more general "render" MIME type.

* Localizations/en.lproj/localizedStrings.js:

LayoutTests:

* inspector/canvas/updateShader-webgpu-sharedVertexFragment.html: Added.
* inspector/canvas/updateShader-webgpu-sharedVertexFragment-expected.txt: Added.

* platform/gtk/TestExpectations:
* platform/ios/TestExpectations:
* platform/mac-wk1/TestExpectations:
* platform/mac/TestExpectations:
* platform/win/TestExpectations:
* platform/wincairo/TestExpectations:
* platform/wpe/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@250874 268f45cc-cd09-0410-ab3c-d52691b4dbfc

29 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/canvas/updateShader-webgpu-sharedVertexFragment-expected.txt [new file with mode: 0644]
LayoutTests/inspector/canvas/updateShader-webgpu-sharedVertexFragment.html [new file with mode: 0644]
LayoutTests/platform/gtk/TestExpectations
LayoutTests/platform/ios/TestExpectations
LayoutTests/platform/mac-wk1/TestExpectations
LayoutTests/platform/mac/TestExpectations
LayoutTests/platform/win/TestExpectations
LayoutTests/platform/wincairo/TestExpectations
LayoutTests/platform/wpe/TestExpectations
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/protocol/Canvas.json
Source/WebCore/ChangeLog
Source/WebCore/Modules/webgpu/WebGPUComputePipeline.cpp
Source/WebCore/Modules/webgpu/WebGPUComputePipeline.h
Source/WebCore/Modules/webgpu/WebGPUPipeline.h
Source/WebCore/Modules/webgpu/WebGPURenderPipeline.cpp
Source/WebCore/Modules/webgpu/WebGPURenderPipeline.h
Source/WebCore/inspector/InspectorShaderProgram.cpp
Source/WebCore/inspector/InspectorShaderProgram.h
Source/WebCore/inspector/agents/InspectorCanvasAgent.cpp
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Controllers/CanvasManager.js
Source/WebInspectorUI/UserInterface/Models/ShaderProgram.js
Source/WebInspectorUI/UserInterface/Protocol/CanvasObserver.js
Source/WebInspectorUI/UserInterface/Views/CodeMirrorAdditions.js
Source/WebInspectorUI/UserInterface/Views/ShaderProgramContentView.css
Source/WebInspectorUI/UserInterface/Views/ShaderProgramContentView.js

index c0cf2b7..dd38415 100644 (file)
@@ -1,3 +1,21 @@
+2019-10-08  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Canvas: modifications to shader modules can be shared between vertex/fragment shaders
+        https://bugs.webkit.org/show_bug.cgi?id=202031
+
+        Reviewed by Dean Jackson.
+
+        * inspector/canvas/updateShader-webgpu-sharedVertexFragment.html: Added.
+        * inspector/canvas/updateShader-webgpu-sharedVertexFragment-expected.txt: Added.
+
+        * platform/gtk/TestExpectations:
+        * platform/ios/TestExpectations:
+        * platform/mac-wk1/TestExpectations:
+        * platform/mac/TestExpectations:
+        * platform/win/TestExpectations:
+        * platform/wincairo/TestExpectations:
+        * platform/wpe/TestExpectations:
+
 2019-10-08  Yury Semikhatsky  <yurys@chromium.org>
 
         Web Inspector: inspector/layers/layers-for-node.html and  inspector/timeline/line-column.html are flaky
diff --git a/LayoutTests/inspector/canvas/updateShader-webgpu-sharedVertexFragment-expected.txt b/LayoutTests/inspector/canvas/updateShader-webgpu-sharedVertexFragment-expected.txt
new file mode 100644 (file)
index 0000000..f6abcd0
--- /dev/null
@@ -0,0 +1,22 @@
+WebGPU tests for Canvas.updateShader command when the vertex and fragment shaders of a render pipeline share the same module.
+
+
+== Running test suite: Canvas.updateShader.WebGPU.SharedVertexFragment
+-- Running test case: Canvas.updateShader.WebGPU.SharedVertexFragment.SinglePipeline
+Creating render pipeline...
+PASS: Vertex and Fragment shader sources should be the same.
+Updating vertex shader source...
+PASS: Vertex and Fragment shader sources should be the same.
+PASS: Vertex source should have changed.
+Updating fragment shader source...
+PASS: Vertex and Fragment shader sources should be the same.
+PASS: Fragment source should have changed.
+
+-- Running test case: Canvas.updateShader.WebGPU.SharedVertexFragment.MultiplePipelines
+Creating render pipeline...
+Creating render pipeline...
+PASS: Both pipelines should have the same source.
+Updating pipeline 1 vertex source...
+PASS: Source of pipeline 1 should have changed.
+PASS: Both pipelines should have different sources.
+
diff --git a/LayoutTests/inspector/canvas/updateShader-webgpu-sharedVertexFragment.html b/LayoutTests/inspector/canvas/updateShader-webgpu-sharedVertexFragment.html
new file mode 100644 (file)
index 0000000..34d02ab
--- /dev/null
@@ -0,0 +1,141 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+if (window.internals)
+    window.internals.settings.setWebGPUEnabled(true);
+
+const renderPipelineSource = `
+vertex float4 vertexShader(float4 position : attribute(0), float i : attribute(1)) : SV_Position {
+    return position;
+}
+
+fragment float4 fragmentShader(float4 position : SV_Position) : SV_Target 0 {
+    return position;
+}
+`;
+
+let device = null;
+let shaderModule = null;
+
+async function createRenderPipeline() {
+    if (!device) {
+        let adapter = await navigator.gpu.requestAdapter();
+        device = await adapter.requestDevice();
+    }
+
+    if (!shaderModule)
+        shaderModule = device.createShaderModule({code: renderPipelineSource});
+
+    // Copied from webgpu/whlsl/whlsl.html.
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWrite.ALL
+    const depthStencilState = null;
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const attribute1 = {shaderLocation: 1, format: "float"};
+    const input0 = {stride: 16, attributeSet: [attribute0]};
+    const input1 = {stride: 4, attributeSet: [attribute1]};
+    const inputs = [input0, input1];
+    const vertexInput = {vertexBuffers: inputs};
+    const bindGroupLayoutDescriptor = {bindings: [{binding: 0, visibility: 7, type: "uniform-buffer"}]};
+    const bindGroupLayout = device.createBindGroupLayout(bindGroupLayoutDescriptor);
+    const pipelineLayoutDescriptor = {bindGroupLayouts: [bindGroupLayout]};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+    device.createRenderPipeline({vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout});
+}
+
+function test() {
+    let suite = InspectorTest.createAsyncSuite("Canvas.updateShader.WebGPU.SharedVertexFragment");
+
+    async function awaitProgramCreated() {
+        InspectorTest.log("Creating render pipeline...");
+        let evalutePromise = InspectorTest.evaluateInPage(`createRenderPipeline()`);
+
+        if (!WI.canvasManager.canvases.length)
+            await WI.canvasManager.awaitEvent(WI.CanvasManager.Event.CanvasAdded);
+
+        let canvas = WI.canvasManager.canvases[0];
+        InspectorTest.assert(canvas.contextType === WI.Canvas.ContextType.WebGPU, "Canvas should be WebGPU.");
+
+        let itemAddedEvent = await canvas.shaderProgramCollection.awaitEvent(WI.Collection.Event.ItemAdded);
+        let shaderProgram = itemAddedEvent.data.item;
+        InspectorTest.assert(shaderProgram.programType === WI.ShaderProgram.ProgramType.Render, "Shader program should be a render pipeline.");
+        InspectorTest.assert(shaderProgram.sharesVertexFragmentShader, "Shader program should have a shared vertex and fragment module.");
+
+        await evalutePromise;
+        return shaderProgram;
+    }
+
+    suite.addTestCase({
+        name: "Canvas.updateShader.WebGPU.SharedVertexFragment.SinglePipeline",
+        description: "Check that updating the vertex/fragment shader will also affect the fragment/vertex shader if they both use the same module.",
+        async test() {
+            let shaderProgram = await awaitProgramCreated();
+
+            let [originalVertexSource, originalFragmentSource] = await Promise.all([
+                CanvasAgent.requestShaderSource(shaderProgram.identifier, WI.ShaderProgram.ShaderType.Vertex),
+                CanvasAgent.requestShaderSource(shaderProgram.identifier, WI.ShaderProgram.ShaderType.Fragment),
+            ]);
+            InspectorTest.expectShallowEqual(originalVertexSource, originalFragmentSource, "Vertex and Fragment shader sources should be the same.");
+
+            InspectorTest.log("Updating vertex shader source...");
+            await CanvasAgent.updateShader(shaderProgram.identifier, WI.ShaderProgram.ShaderType.Vertex, "// MODIFIED VERTEX\n\n" + originalVertexSource.source);
+
+            let [vertexSourceAfterUpdatingVertex, fragmentSourceAfterUpdatingVertex] = await Promise.all([
+                CanvasAgent.requestShaderSource(shaderProgram.identifier, WI.ShaderProgram.ShaderType.Vertex),
+                CanvasAgent.requestShaderSource(shaderProgram.identifier, WI.ShaderProgram.ShaderType.Fragment),
+            ]);
+            InspectorTest.expectShallowEqual(vertexSourceAfterUpdatingVertex, fragmentSourceAfterUpdatingVertex, "Vertex and Fragment shader sources should be the same.");
+            InspectorTest.expectNotShallowEqual(vertexSourceAfterUpdatingVertex, originalVertexSource, "Vertex source should have changed.");
+
+            InspectorTest.log("Updating fragment shader source...");
+            await CanvasAgent.updateShader(shaderProgram.identifier, WI.ShaderProgram.ShaderType.Fragment, "// MODIFIED FRAGMENT\n\n" + originalFragmentSource.source);
+
+            let [vertexSourceAfterUpdatingFragment, fragmentSourceAfterUpdatingFragment] = await Promise.all([
+                CanvasAgent.requestShaderSource(shaderProgram.identifier, WI.ShaderProgram.ShaderType.Vertex),
+                CanvasAgent.requestShaderSource(shaderProgram.identifier, WI.ShaderProgram.ShaderType.Fragment),
+            ]);
+            InspectorTest.expectShallowEqual(vertexSourceAfterUpdatingFragment, fragmentSourceAfterUpdatingFragment, "Vertex and Fragment shader sources should be the same.");
+            InspectorTest.expectNotShallowEqual(fragmentSourceAfterUpdatingFragment, originalFragmentSource, "Fragment source should have changed.");
+        }
+    });
+
+    suite.addTestCase({
+        name: "Canvas.updateShader.WebGPU.SharedVertexFragment.MultiplePipelines",
+        description: "Check that updating one pipeline won't affect any other pipelines if they share the same module.",
+        async test() {
+            let shaderProgram1 = await awaitProgramCreated();
+            let shaderProgram2 = await awaitProgramCreated();
+
+            let [originalVertexSource1, originalVertexSource2] = await Promise.all([
+                CanvasAgent.requestShaderSource(shaderProgram1.identifier, WI.ShaderProgram.ShaderType.Vertex),
+                CanvasAgent.requestShaderSource(shaderProgram2.identifier, WI.ShaderProgram.ShaderType.Vertex),
+            ]);
+            InspectorTest.expectShallowEqual(originalVertexSource1, originalVertexSource2, "Both pipelines should have the same source.");
+
+            InspectorTest.log("Updating pipeline 1 vertex source...");
+            await CanvasAgent.updateShader(shaderProgram1.identifier, WI.ShaderProgram.ShaderType.Vertex, "// MODIFIED VERTEX\n\n" + originalVertexSource1.source);
+
+            let [newVertexSource1, newVertexSource2] = await Promise.all([
+                CanvasAgent.requestShaderSource(shaderProgram1.identifier, WI.ShaderProgram.ShaderType.Vertex),
+                CanvasAgent.requestShaderSource(shaderProgram2.identifier, WI.ShaderProgram.ShaderType.Vertex),
+            ]);
+            InspectorTest.expectNotShallowEqual(originalVertexSource1, newVertexSource1, "Source of pipeline 1 should have changed.");
+            InspectorTest.expectNotShallowEqual(newVertexSource1, newVertexSource2, "Both pipelines should have different sources.");
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+<p>WebGPU tests for Canvas.updateShader command when the vertex and fragment shaders of a render pipeline share the same module.</p>
+</body>
+</html>
index c7ed8b3..2982099 100644 (file)
@@ -1153,6 +1153,7 @@ webkit.org/b/191005 inspector/canvas/requestShaderSource-webgpu.html [ Skip ]
 webkit.org/b/191005 inspector/canvas/resolveContext-webgpu.html [ Skip ]
 webkit.org/b/191005 inspector/canvas/shaderProgram-add-remove-webgpu.html [ Skip ]
 webkit.org/b/191005 inspector/canvas/updateShader-webgpu.html [ Skip ]
+webkit.org/b/191005 inspector/canvas/updateShader-webgpu-sharedVertexFragment.html [ Skip ]
 
 # No support for resource load statistics yet
 http/tests/resourceLoadStatistics/ [ Skip ]
index 3af31c8..64031d2 100644 (file)
@@ -49,6 +49,7 @@ inspector/canvas/requestShaderSource-webgpu.html [ Skip ]
 inspector/canvas/resolveContext-webgpu.html [ Skip ]
 inspector/canvas/shaderProgram-add-remove-webgpu.html [ Skip ]
 inspector/canvas/updateShader-webgpu.html [ Skip ]
+inspector/canvas/updateShader-webgpu-sharedVertexFragment.html [ Skip ]
 
 # Encrypted Media Extensions are not enabled
 media/encrypted-media/
index 2d79b1d..c7b1318 100644 (file)
@@ -54,6 +54,7 @@ inspector/canvas/requestShaderSource-webgpu.html [ Skip ]
 inspector/canvas/resolveContext-webgpu.html [ Skip ]
 inspector/canvas/shaderProgram-add-remove-webgpu.html [ Skip ]
 inspector/canvas/updateShader-webgpu.html [ Skip ]
+inspector/canvas/updateShader-webgpu-sharedVertexFragment.html [ Skip ]
 
 # Media Stream API testing is not supported for WK1 yet.
 fast/mediastream
index fd49006..33a9936 100644 (file)
@@ -1783,6 +1783,7 @@ webkit.org/b/199275 [ HighSierra ] inspector/canvas/requestShaderSource-webgpu.h
 webkit.org/b/199275 [ HighSierra ] inspector/canvas/resolveContext-webgpu.html [ Skip ]
 webkit.org/b/199275 [ HighSierra ] inspector/canvas/shaderProgram-add-remove-webgpu.html [ Skip ]
 webkit.org/b/199275 [ HighSierra ] inspector/canvas/updateShader-webgpu.html [ Skip ]
+webkit.org/b/199275 [ HighSierra ] inspector/canvas/updateShader-webgpu-sharedVertexFragment.html [ Skip ]
 
 webkit.org/b/189680 platform/mac/media/audio-session-category-video-paused.html [ Pass Timeout ]
 
index 3d51f3f..2e81641 100644 (file)
@@ -4268,6 +4268,7 @@ inspector/canvas/requestShaderSource-webgpu.html [ Skip ]
 inspector/canvas/resolveContext-webgpu.html [ Skip ]
 inspector/canvas/shaderProgram-add-remove-webgpu.html [ Skip ]
 inspector/canvas/updateShader-webgpu.html [ Skip ]
+inspector/canvas/updateShader-webgpu-sharedVertexFragment.html [ Skip ]
 
 webkit.org/b/191194 fast/block/basic/inline-content-with-floating-image.html [ Failure ]
 webkit.org/b/191194 fast/block/basic/inline-content-with-floating-images2.html [ Failure ]
index a7e5c72..3201448 100644 (file)
@@ -300,6 +300,7 @@ inspector/canvas/requestShaderSource-webgpu.html [ Skip ]
 inspector/canvas/resolveContext-webgpu.html [ Skip ]
 inspector/canvas/shaderProgram-add-remove-webgpu.html [ Skip ]
 inspector/canvas/updateShader-webgpu.html [ Skip ]
+inspector/canvas/updateShader-webgpu-sharedVertexFragment.html [ Skip ]
 
 # WIRELESS_PLAYBACK_TARGET is disabled
 media/airplay-target-availability.html [ Skip ]
index c39d113..5c9985b 100644 (file)
@@ -306,6 +306,7 @@ inspector/canvas/requestShaderSource-webgpu.html [ Skip ]
 inspector/canvas/resolveContext-webgpu.html [ Skip ]
 inspector/canvas/shaderProgram-add-remove-webgpu.html [ Skip ]
 inspector/canvas/updateShader-webgpu.html [ Skip ]
+inspector/canvas/updateShader-webgpu-sharedVertexFragment.html [ Skip ]
 
 # Skipped due to untestable DRM key system. ClearKey counterparts are tested instead.
 imported/w3c/web-platform-tests/encrypted-media/drm-check-initdata-type.https.html [ Skip ]
index debe513..bccb200 100644 (file)
@@ -1,3 +1,15 @@
+2019-10-08  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Canvas: modifications to shader modules can be shared between vertex/fragment shaders
+        https://bugs.webkit.org/show_bug.cgi?id=202031
+
+        Reviewed by Dean Jackson.
+
+        * inspector/protocol/Canvas.json:
+        Create a distinct `ShaderProgram` type so that additional data can be bundled and sent to
+        the frontend as part of the `programCreated` event without having to worry about having too
+        many arguments.
+
 2019-10-08  Alexey Shvayka  <shvaikalesh@gmail.com>
 
         JSON.parse incorrectly handles array proxies
index 8aee55e..175ff40 100644 (file)
                 { "name": "memoryCost", "type": "number", "optional": true, "description": "Memory usage of the canvas in bytes." },
                 { "name": "backtrace", "type": "array", "items": { "$ref": "Console.CallFrame" }, "optional": true, "description": "Backtrace that was captured when this canvas context was created." }
             ]
+        },
+        {
+            "id": "ShaderProgram",
+            "type": "object",
+            "description": "Information about a WebGL/WebGL2 shader program or WebGPU shader pipeline.",
+            "properties": [
+                { "name": "programId", "$ref": "ProgramId" },
+                { "name": "programType", "$ref": "ProgramType" },
+                { "name": "canvasId", "$ref": "CanvasId"} ,
+                { "name": "sharesVertexFragmentShader", "type": "boolean", "optional": true, "description": "Indicates whether the vertex and fragment shader modules are the same object for a render shader pipleine for a WebGPU device." }
+            ]
         }
     ],
     "commands": [
         {
             "name": "programCreated",
             "parameters": [
-                { "name": "canvasId", "$ref": "CanvasId"} ,
-                { "name": "programId", "$ref": "ProgramId" },
-                { "name": "programType", "$ref": "ProgramType" }
+                { "name": "shaderProgram", "$ref": "ShaderProgram" }
             ]
         },
         {
index a6166e9..d7b50e9 100644 (file)
@@ -1,3 +1,32 @@
+2019-10-08  Devin Rousso  <drousso@apple.com>
+
+        Web Inspector: Canvas: modifications to shader modules can be shared between vertex/fragment shaders
+        https://bugs.webkit.org/show_bug.cgi?id=202031
+
+        Reviewed by Dean Jackson.
+
+        Test: inspector/canvas/updateShader-webgpu-sharedVertexFragment.html
+
+        * Modules/webgpu/WebGPUPipeline.h:
+        * Modules/webgpu/WebGPUComputePipeline.cpp:
+        (WebCore::WebGPUComputePipeline::cloneShaderModules): Added.
+        (WebCore::WebGPUComputePipeline::recompile):
+        * Modules/webgpu/WebGPURenderPipeline.cpp:
+        (WebCore::WebGPURenderPipeline::cloneShaderModules): Added.
+        (WebCore::WebGPURenderPipeline::recompile):
+        Recreate the vertex/fragment/compute shader module(s) when recompiling so that modifications
+        to it via this pipeline don't affect other pipelines that also use the same shader module.
+
+        * inspector/InspectorShaderProgram.h:
+        * inspector/InspectorShaderProgram.cpp:
+        (WebCore::InspectorShaderProgram::updateShader):
+        (WebCore::InspectorShaderProgram::buildObjectForShaderProgram): Added.
+        * inspector/agents/InspectorCanvasAgent.cpp:
+        (WebCore::InspectorCanvasAgent::didCreateWebGLProgram):
+        (WebCore::InspectorCanvasAgent::didCreateWebGPUPipeline):
+        Include as part of the `Canvas.event.programCreated` payload a flag indicating whether the
+        vertex shader module and fragment shader module are the same for `WebGPURenderPipeline`s.
+
 2019-10-08  Timothy Hatcher  <timothy@apple.com>
 
         Copying white text from dark mode WebKit apps and pasting in a light mode app results in white (invisible) text.
index 3535392..8a8cf0d 100644 (file)
 #include "GPUErrorScopes.h"
 #include "GPUPipeline.h"
 #include "GPUProgrammableStageDescriptor.h"
+#include "GPUShaderModule.h"
+#include "GPUShaderModuleDescriptor.h"
 #include "WebGPUDevice.h"
+#include "WebGPUShaderModule.h"
 #include <wtf/Optional.h>
 #include <wtf/Ref.h>
 
@@ -52,10 +55,25 @@ WebGPUComputePipeline::WebGPUComputePipeline(WebGPUDevice& device, RefPtr<GPUCom
 
 WebGPUComputePipeline::~WebGPUComputePipeline() = default;
 
+bool WebGPUComputePipeline::cloneShaderModules(const WebGPUDevice& device)
+{
+    if (m_computeShader) {
+        if (auto& webGPUComputeShaderModule = m_computeShader.value().module) {
+            const auto& computeSource = webGPUComputeShaderModule->source();
+            webGPUComputeShaderModule = WebGPUShaderModule::create(GPUShaderModule::tryCreate(device.device(), { computeSource }), computeSource);
+            return true;
+        }
+    }
+    return false;
+}
+
 bool WebGPUComputePipeline::recompile(const WebGPUDevice& device)
 {
     if (m_computePipeline && m_computeShader) {
         if (auto& webGPUComputeShaderModule = m_computeShader.value().module) {
+            // Recreate the shader module so that modifications to it via this pipeline don't affect
+            // other pipelines that also use the same shader module.
+
             if (auto* gpuComputeShaderModule = webGPUComputeShaderModule->module()) {
                 GPUProgrammableStageDescriptor computeStage(makeRef(*gpuComputeShaderModule), { m_computeShader.value().entryPoint });
                 return m_computePipeline->recompile(device.device(), WTFMove(computeStage));
index c1a7180..65ae411 100644 (file)
@@ -49,6 +49,7 @@ public:
     const GPUComputePipeline* computePipeline() const { return m_computePipeline.get(); }
     Optional<WebGPUPipeline::ShaderData> computeShader() const { return m_computeShader; }
 
+    bool cloneShaderModules(const WebGPUDevice&);
     bool recompile(const WebGPUDevice&);
 
 private:
index 3b34a4e..c41a1a9 100644 (file)
@@ -56,6 +56,7 @@ public:
         String entryPoint;
     };
 
+    virtual bool cloneShaderModules(const WebGPUDevice&) = 0;
     virtual bool recompile(const WebGPUDevice&) = 0;
 
 protected:
index fe957f5..02aabdb 100644 (file)
 #include "GPUPipeline.h"
 #include "GPUProgrammableStageDescriptor.h"
 #include "GPURenderPipeline.h"
+#include "GPUShaderModule.h"
+#include "GPUShaderModuleDescriptor.h"
 #include "WebGPUDevice.h"
+#include "WebGPUShaderModule.h"
 #include <wtf/Optional.h>
 #include <wtf/Ref.h>
 
@@ -53,6 +56,32 @@ WebGPURenderPipeline::WebGPURenderPipeline(WebGPUDevice& device, RefPtr<GPURende
 
 WebGPURenderPipeline::~WebGPURenderPipeline() = default;
 
+bool WebGPURenderPipeline::cloneShaderModules(const WebGPUDevice& device)
+{
+    if (m_vertexShader) {
+        if (auto& webGPUVertexShaderModule = m_vertexShader.value().module) {
+            bool sharesVertexFragmentShaderModule = m_fragmentShader && m_fragmentShader.value().module == webGPUVertexShaderModule;
+
+            const auto& vertexSource = webGPUVertexShaderModule->source();
+            webGPUVertexShaderModule = WebGPUShaderModule::create(GPUShaderModule::tryCreate(device.device(), { vertexSource }), vertexSource);
+
+            if (!m_fragmentShader)
+                return true;
+
+            if (auto& webGPUFragmentShaderModule = m_fragmentShader.value().module) {
+                if (sharesVertexFragmentShaderModule)
+                    webGPUFragmentShaderModule = webGPUVertexShaderModule;
+                else {
+                    const auto& fragmentSource = webGPUFragmentShaderModule->source();
+                    webGPUFragmentShaderModule = WebGPUShaderModule::create(GPUShaderModule::tryCreate(device.device(), { fragmentSource }), fragmentSource);
+                }
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 bool WebGPURenderPipeline::recompile(const WebGPUDevice& device)
 {
     if (m_renderPipeline && m_vertexShader) {
index b2b8094..62a70d1 100644 (file)
@@ -50,6 +50,7 @@ public:
     Optional<WebGPUPipeline::ShaderData> vertexShader() const { return m_vertexShader; }
     Optional<WebGPUPipeline::ShaderData> fragmentShader() const { return m_fragmentShader; }
 
+    bool cloneShaderModules(const WebGPUDevice&);
     bool recompile(const WebGPUDevice&);
 
 private:
index 7af7196..1151bf2 100644 (file)
@@ -215,11 +215,13 @@ bool InspectorShaderProgram::updateShader(Inspector::Protocol::Canvas::ShaderTyp
         [&] (std::reference_wrapper<WebGPUPipeline> pipelineWrapper) {
             auto& pipeline = pipelineWrapper.get();
             if (auto* device = m_canvas.deviceContext()) {
-                if (auto shaderData = shaderForType(pipeline, shaderType)) {
-                    if (auto module = shaderData.value().module) {
-                        module->update(*device, source);
-                        if (pipeline.recompile(*device))
-                            return true;
+                if (pipeline.cloneShaderModules(*device)) {
+                    if (auto shaderData = shaderForType(pipeline, shaderType)) {
+                        if (auto module = shaderData.value().module) {
+                            module->update(*device, source);
+                            if (pipeline.recompile(*device))
+                                return true;
+                        }
                     }
                 }
             }
@@ -235,4 +237,53 @@ bool InspectorShaderProgram::updateShader(Inspector::Protocol::Canvas::ShaderTyp
     );
 }
 
+Ref<Inspector::Protocol::Canvas::ShaderProgram> InspectorShaderProgram::buildObjectForShaderProgram()
+{
+    bool sharesVertexFragmentShader = false;
+
+    using ProgramTypeType = Optional<Inspector::Protocol::Canvas::ProgramType>;
+    auto programType = WTF::switchOn(m_program,
+#if ENABLE(WEBGL)
+        [&] (std::reference_wrapper<WebGLProgram>) -> ProgramTypeType {
+            return Inspector::Protocol::Canvas::ProgramType::Render;
+        },
+#endif
+#if ENABLE(WEBGPU)
+        [&] (std::reference_wrapper<WebGPUPipeline> pipelineWrapper) -> ProgramTypeType {
+            auto& pipeline = pipelineWrapper.get();
+            if (is<WebGPUComputePipeline>(pipeline))
+                return Inspector::Protocol::Canvas::ProgramType::Compute;
+            if (is<WebGPURenderPipeline>(pipeline)) {
+                auto& renderPipeline = downcast<WebGPURenderPipeline>(pipeline);
+                auto vertexShader = renderPipeline.vertexShader();
+                auto fragmentShader = renderPipeline.fragmentShader();
+                if (vertexShader && fragmentShader && vertexShader.value().module == fragmentShader.value().module)
+                    sharesVertexFragmentShader = true;
+                return Inspector::Protocol::Canvas::ProgramType::Render;
+            }
+            return WTF::nullopt;
+        },
+#endif
+        [&] (Monostate) -> ProgramTypeType {
+#if ENABLE(WEBGL) || ENABLE(WEBGPU)
+            ASSERT_NOT_REACHED();
+#endif
+            return WTF::nullopt;
+        }
+    );
+    if (!programType) {
+        ASSERT_NOT_REACHED();
+        programType = Inspector::Protocol::Canvas::ProgramType::Render;
+    }
+
+    auto payload = Inspector::Protocol::Canvas::ShaderProgram::create()
+        .setProgramId(m_identifier)
+        .setProgramType(programType.value())
+        .setCanvasId(m_canvas.identifier())
+        .release();
+    if (sharesVertexFragmentShader)
+        payload->setSharesVertexFragmentShader(true);
+    return payload;
+}
+
 } // namespace WebCore
index dcd9eb1..6df13df 100644 (file)
@@ -71,6 +71,8 @@ public:
     bool highlighted() const { return m_highlighted; }
     void setHighlighted(bool value) { m_highlighted = value; }
 
+    Ref<Inspector::Protocol::Canvas::ShaderProgram> buildObjectForShaderProgram();
+
 private:
 #if ENABLE(WEBGL)
     InspectorShaderProgram(WebGLProgram&, InspectorCanvas&);
index 21b3b6c..f91a373 100644 (file)
@@ -568,10 +568,10 @@ void InspectorCanvasAgent::didCreateWebGLProgram(WebGLRenderingContextBase& cont
     if (!inspectorCanvas)
         return;
 
-    auto inspectorProgram = InspectorShaderProgram::create(program, *inspectorCanvas);
-    String programIdentifier = inspectorProgram->identifier();
-    m_identifierToInspectorProgram.set(programIdentifier, WTFMove(inspectorProgram));
-    m_frontendDispatcher->programCreated(inspectorCanvas->identifier(), programIdentifier, Inspector::Protocol::Canvas::ProgramType::Render);
+    auto inspectorProgramRef = InspectorShaderProgram::create(program, *inspectorCanvas);
+    auto& inspectorProgram = inspectorProgramRef.get();
+    m_identifierToInspectorProgram.set(inspectorProgram.identifier(), WTFMove(inspectorProgramRef));
+    m_frontendDispatcher->programCreated(inspectorProgram.buildObjectForShaderProgram());
 }
 
 void InspectorCanvasAgent::willDestroyWebGLProgram(WebGLProgram& program)
@@ -651,18 +651,10 @@ void InspectorCanvasAgent::didCreateWebGPUPipeline(WebGPUDevice& device, WebGPUP
 
     ASSERT(pipeline.isValid());
 
-    auto inspectorProgram = InspectorShaderProgram::create(pipeline, *inspectorCanvas);
-    String programIdentifier = inspectorProgram->identifier();
-    m_identifierToInspectorProgram.set(programIdentifier, WTFMove(inspectorProgram));
-
-    Optional<Inspector::Protocol::Canvas::ProgramType> programType;
-    if (is<WebGPUComputePipeline>(pipeline))
-        programType = Inspector::Protocol::Canvas::ProgramType::Compute;
-    else if (is<WebGPURenderPipeline>(pipeline))
-        programType = Inspector::Protocol::Canvas::ProgramType::Render;
-    ASSERT(programType);
-
-    m_frontendDispatcher->programCreated(inspectorCanvas->identifier(), programIdentifier, programType.value());
+    auto inspectorProgramRef = InspectorShaderProgram::create(pipeline, *inspectorCanvas);
+    auto& inspectorProgram = inspectorProgramRef.get();
+    m_identifierToInspectorProgram.set(inspectorProgram.identifier(), WTFMove(inspectorProgramRef));
+    m_frontendDispatcher->programCreated(inspectorProgram.buildObjectForShaderProgram());
 }
 
 void InspectorCanvasAgent::willDestroyWebGPUPipeline(WebGPUPipeline& pipeline)
index 3a6c5f7..486bdc8 100644 (file)
@@ -1,5 +1,41 @@
 2019-10-08  Devin Rousso  <drousso@apple.com>
 
+        Web Inspector: Canvas: modifications to shader modules can be shared between vertex/fragment shaders
+        https://bugs.webkit.org/show_bug.cgi?id=202031
+
+        Reviewed by Dean Jackson.
+
+        If the vertex and fragment shaders share the same source module for a WebGPU shader pipeline,
+        only display a single editable content view for that shader pipeline in the Canvas Tab.
+
+        * UserInterface/Models/ShaderProgram.js:
+        (WI.ShaderProgram):
+        (WI.ShaderProgram.prototype.get sharesVertexFragmentShader): Added.
+        * UserInterface/Controllers/CanvasManager.js:
+        (WI.CanvasManager.prototype.programCreated):
+        * UserInterface/Protocol/CanvasObserver.js:
+        (WI.CanvasObserver.prototype.programCreated):
+
+        * UserInterface/Views/ShaderProgramContentView.js:
+        (WI.ShaderProgramContentView):
+        (WI.ShaderProgramContentView.prototype.shown):
+        (WI.ShaderProgramContentView.prototype.hidden):
+        (WI.ShaderProgramContentView.prototype._refreshContent):
+        * UserInterface/Views/ShaderProgramContentView.css:
+        (.content-view.shader-program > .shader.compute, .content-view.shader-program > .shader.vertex.shares-vertex-fragment-shader): Added.
+        (body[dir=ltr] .content-view.shader-program > .shader.vertex:not(.shares-vertex-fragment-shader), body[dir=rtl] .content-view.shader-program > .shader.fragment): Added.
+        (body[dir=ltr] .content-view.shader-program > .shader.fragment, body[dir=rtl] .content-view.shader-program > .shader.vertex:not(.shares-vertex-fragment-shader)): Added.
+        (.content-view.shader-program > .shader.compute): Deleted.
+        (body[dir=ltr] .content-view.shader-program > .shader.vertex, body[dir=rtl] .content-view.shader-program > .shader.fragment): Deleted.
+        (body[dir=ltr] .content-view.shader-program > .shader.fragment, body[dir=rtl] .content-view.shader-program > .shader.vertex): Deleted.
+
+        * UserInterface/Views/CodeMirrorAdditions.js:
+        Replace the vertex/fragment specific MIME types with a more general "render" MIME type.
+
+        * Localizations/en.lproj/localizedStrings.js:
+
+2019-10-08  Devin Rousso  <drousso@apple.com>
+
         Web Inspector: Canvas: make it more obvious that the cards in the overview are clickable
         https://bugs.webkit.org/show_bug.cgi?id=202680
 
index 7a1f411..9cbcc2f 100644 (file)
@@ -1231,6 +1231,7 @@ localizedStrings["Verbose"] = "Verbose";
 localizedStrings["Version"] = "Version";
 localizedStrings["Vertex"] = "Vertex";
 localizedStrings["Vertex Shader"] = "Vertex Shader";
+localizedStrings["Vertex/Fragment Shader"] = "Vertex/Fragment Shader";
 localizedStrings["Very High"] = "Very High";
 localizedStrings["View Image"] = "View Image";
 localizedStrings["View Recording"] = "View Recording";
index 38f01bb..733c100 100644 (file)
@@ -234,20 +234,28 @@ WI.CanvasManager = class CanvasManager extends WI.Object
         canvas.enableExtension(extension);
     }
 
-    programCreated(canvasIdentifier, programIdentifier, programType)
+    programCreated(shaderProgramPayload)
     {
-        let canvas = this._canvasIdentifierMap.get(canvasIdentifier);
+        let canvas = this._canvasIdentifierMap.get(shaderProgramPayload.canvasId);
         console.assert(canvas);
         if (!canvas)
             return;
 
-        console.assert(!this._shaderProgramIdentifierMap.has(programIdentifier), `ShaderProgram already exists with id ${programIdentifier}.`);
+        let programId = shaderProgramPayload.programId;
+        console.assert(!this._shaderProgramIdentifierMap.has(programId), `ShaderProgram already exists with id ${programId}.`);
 
-        // COMPATIBILITY (iOS 13): `programType` did not exist yet.
+        // COMPATIBILITY (iOS 13.0): `Canvas.ShaderProgram.programType` did not exist yet.
+        let programType = shaderProgramPayload.programType;
         if (!programType)
             programType = WI.ShaderProgram.ProgramType.Render;
 
-        let program = new WI.ShaderProgram(programIdentifier, programType, canvas);
+        let options = {};
+
+        // COMPATIBILITY (iOS 13.0): `Canvas.ShaderProgram.sharesVertexFragmentShader` did not exist yet.
+        if (shaderProgramPayload.sharesVertexFragmentShader)
+            options.sharesVertexFragmentShader = true;
+
+        let program = new WI.ShaderProgram(programId, programType, canvas, options);
         this._shaderProgramIdentifierMap.set(program.identifier, program);
 
         canvas.shaderProgramCollection.add(program);
index c9b6e03..016d8b1 100644 (file)
@@ -25,7 +25,7 @@
 
 WI.ShaderProgram = class ShaderProgram extends WI.Object
 {
-    constructor(identifier, programType, canvas)
+    constructor(identifier, programType, canvas, {sharesVertexFragmentShader} = {})
     {
         console.assert(identifier);
         console.assert(Object.values(ShaderProgram.ProgramType).includes(programType));
@@ -37,6 +37,10 @@ WI.ShaderProgram = class ShaderProgram extends WI.Object
         this._identifier = identifier;
         this._programType = programType;
         this._canvas = canvas;
+
+        this._sharesVertexFragmentShader = !!sharesVertexFragmentShader;
+        console.assert(!this._sharesVertexFragmentShader || (this._canvas.contextType === WI.Canvas.ContextType.WebGPU && this._programType === ShaderProgram.ProgramType.Render));
+
         this._disabled = false;
     }
 
@@ -78,6 +82,7 @@ WI.ShaderProgram = class ShaderProgram extends WI.Object
     get identifier() { return this._identifier; }
     get programType() { return this._programType; }
     get canvas() { return this._canvas; }
+    get sharesVertexFragmentShader() { return this._sharesVertexFragmentShader; }
 
     get displayName()
     {
index 6f53574..8ff3fe7 100644 (file)
@@ -67,9 +67,16 @@ WI.CanvasObserver = class CanvasObserver
         WI.canvasManager.extensionEnabled(canvasId, extension);
     }
 
-    programCreated(canvasId, programId, programType)
+    programCreated(shaderProgram)
     {
-        WI.canvasManager.programCreated(canvasId, programId, programType);
+        // COMPATIBILITY (iOS 13.0): `shaderProgram` replaced `canvasId` and `programId`.
+        if (arguments.length === 2) {
+            shaderProgram = {
+                canvasId: arguments[0],
+                programId: arguments[1],
+            };
+        }
+        WI.canvasManager.programCreated(shaderProgram);
     }
 
     programDeleted(programId)
index 8ce2815..e1390cd 100644 (file)
 
     // FIXME: Add WHLSL specific modes.
     CodeMirror.defineMIME("x-pipeline/x-compute", CodeMirror.resolveMode("x-shader/x-vertex"));
-    CodeMirror.defineMIME("x-pipeline/x-fragment", CodeMirror.resolveMode("x-shader/x-fragment"));
-    CodeMirror.defineMIME("x-pipeline/x-vertex", CodeMirror.resolveMode("x-shader/x-vertex"));
+    CodeMirror.defineMIME("x-pipeline/x-render", CodeMirror.resolveMode("x-shader/x-vertex"));
 })();
 
 WI.compareCodeMirrorPositions = function(a, b)
index 496a5e5..2f6c066 100644 (file)
     bottom: 0;
 }
 
-.content-view.shader-program > .shader.compute {
+.content-view.shader-program > .shader.compute,
+.content-view.shader-program > .shader.vertex.shares-vertex-fragment-shader {
     right: 0;
     left: 0;
 }
 
-body[dir=ltr] .content-view.shader-program > .shader.vertex,
+body[dir=ltr] .content-view.shader-program > .shader.vertex:not(.shares-vertex-fragment-shader),
 body[dir=rtl] .content-view.shader-program > .shader.fragment {
     width: calc(50% - 1px);
     left: 0;
 }
 
 body[dir=ltr] .content-view.shader-program > .shader.fragment,
-body[dir=rtl] .content-view.shader-program > .shader.vertex {
+body[dir=rtl] .content-view.shader-program > .shader.vertex:not(.shares-vertex-fragment-shader) {
     width: calc(50% + 1px);
     right: 0;
 }
index e987fe5..17751fa 100644 (file)
@@ -32,6 +32,7 @@ WI.ShaderProgramContentView = class ShaderProgramContentView extends WI.ContentV
         super(shaderProgram);
 
         let isWebGPU = this.representedObject.canvas.contextType === WI.Canvas.ContextType.WebGPU;
+        let sharesVertexFragmentShader = isWebGPU && this.representedObject.sharesVertexFragmentShader;
 
         this._refreshButtonNavigationItem = new WI.ButtonNavigationItem("refresh", WI.UIString("Refresh"), "Images/ReloadFull.svg", 13, 13);
         this._refreshButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
@@ -67,18 +68,22 @@ WI.ShaderProgramContentView = class ShaderProgramContentView extends WI.ContentV
 
             case WI.ShaderProgram.ShaderType.Fragment:
                 shaderTypeContainer.textContent = WI.UIString("Fragment Shader");
-                textEditor.mimeType = isWebGPU ? "x-pipeline/x-fragment" : "x-shader/x-fragment";
+                textEditor.mimeType = isWebGPU ? "x-pipeline/x-render" : "x-shader/x-fragment";
                 break;
 
             case WI.ShaderProgram.ShaderType.Vertex:
-                shaderTypeContainer.textContent = WI.UIString("Vertex Shader");
-                textEditor.mimeType = isWebGPU ? "x-pipeline/x-vertex" : "x-shader/x-vertex";
+                if (sharesVertexFragmentShader)
+                    shaderTypeContainer.textContent = WI.UIString("Vertex/Fragment Shader");
+                else
+                    shaderTypeContainer.textContent = WI.UIString("Vertex Shader");
+                textEditor.mimeType = isWebGPU ? "x-pipeline/x-render" : "x-shader/x-vertex";
                 break;
             }
 
             this.addSubview(textEditor);
             container.appendChild(textEditor.element);
             container.classList.add("shader", shaderType);
+            container.classList.toggle("shares-vertex-fragment-shader", sharesVertexFragmentShader);
 
             return {container, textEditor};
         };
@@ -98,9 +103,11 @@ WI.ShaderProgramContentView = class ShaderProgramContentView extends WI.ContentV
             this._vertexContainer = vertexEditor.container;
             this._vertexEditor = vertexEditor.textEditor;
 
-            let fragmentEditor = createEditor(WI.ShaderProgram.ShaderType.Fragment);
-            this._fragmentContainer = fragmentEditor.container;
-            this._fragmentEditor = fragmentEditor.textEditor;
+            if (!sharesVertexFragmentShader) {
+                let fragmentEditor = createEditor(WI.ShaderProgram.ShaderType.Fragment);
+                this._fragmentContainer = fragmentEditor.container;
+                this._fragmentEditor = fragmentEditor.textEditor;
+            }
 
             this._lastActiveEditor = this._vertexEditor;
             break;
@@ -128,7 +135,8 @@ WI.ShaderProgramContentView = class ShaderProgramContentView extends WI.ContentV
 
         case WI.ShaderProgram.ProgramType.Render:
             this._vertexEditor.shown();
-            this._fragmentEditor.shown();
+            if (!this.representedObject.sharesVertexFragmentShader)
+                this._fragmentEditor.shown();
             break;
         }
 
@@ -144,7 +152,8 @@ WI.ShaderProgramContentView = class ShaderProgramContentView extends WI.ContentV
 
         case WI.ShaderProgram.ProgramType.Render:
             this._vertexEditor.hidden();
-            this._fragmentEditor.hidden();
+            if (!this.representedObject.sharesVertexFragmentShader)
+                this._fragmentEditor.hidden();
             break;
         }
 
@@ -276,7 +285,8 @@ WI.ShaderProgramContentView = class ShaderProgramContentView extends WI.ContentV
 
         case WI.ShaderProgram.ProgramType.Render:
             this.representedObject.requestShaderSource(WI.ShaderProgram.ShaderType.Vertex, createCallback(this._vertexContainer, this._vertexEditor));
-            this.representedObject.requestShaderSource(WI.ShaderProgram.ShaderType.Fragment, createCallback(this._fragmentContainer, this._fragmentEditor));
+            if (!this.representedObject.sharesVertexFragmentShader)
+                this.representedObject.requestShaderSource(WI.ShaderProgram.ShaderType.Fragment, createCallback(this._fragmentContainer, this._fragmentEditor));
             return;
         }