Add software backend to gpu build

Bug: skia:8548
Change-Id: If3853711f4b183f3b0437ae2b31525b0e6c88213
Reviewed-on: https://skia-review.googlesource.com/c/172120
Reviewed-by: Kevin Lubick <kjlubick@google.com>
diff --git a/experimental/canvaskit/Makefile b/experimental/canvaskit/Makefile
index cc9f332..3673fbf 100644
--- a/experimental/canvaskit/Makefile
+++ b/experimental/canvaskit/Makefile
@@ -12,7 +12,7 @@
 
 release_cpu:
 	# Does an incremental build where possible.
-	./compile.sh cpu
+	./compile.sh cpu_only
 	mkdir -p ./canvaskit/bin
 	cp ../../out/canvaskit_wasm/canvaskit.js   ./canvaskit/bin
 	cp ../../out/canvaskit_wasm/canvaskit.wasm ./canvaskit/bin
@@ -26,7 +26,7 @@
 
 debug_cpu:
 	# Does an incremental build where possible.
-	./compile.sh debug cpu
+	./compile.sh debug cpu_only
 	mkdir -p ./canvaskit/bin
 	cp ../../out/canvaskit_wasm_debug/canvaskit.js   ./canvaskit/bin
 	cp ../../out/canvaskit_wasm_debug/canvaskit.wasm ./canvaskit/bin
diff --git a/experimental/canvaskit/canvaskit/example.html b/experimental/canvaskit/canvaskit/example.html
index fc387ad..8b22bf4 100644
--- a/experimental/canvaskit/canvaskit/example.html
+++ b/experimental/canvaskit/canvaskit/example.html
@@ -9,7 +9,7 @@
     border: 1px dashed #AAA;
   }
 
-  #patheffect,#paths,#sk_drinks,#sk_party, #sk_legos, #sk_onboarding
+  #patheffect,#paths,#sk_drinks,#sk_party, #sk_legos, #sk_onboarding,
   #api1_c, #api2_c {
     width: 300px;
     height: 300px;
@@ -428,10 +428,6 @@
   }
 
   function CanvasAPI1(CanvasKit) {
-    if (CanvasKit.gpu) {
-      console.warn("Skipping canvas example because its GPU build");
-      return;
-    }
     let skcanvas = CanvasKit.MakeCanvas(300, 300);
     let realCanvas = document.getElementById('api1_c');
 
@@ -457,10 +453,6 @@
   }
 
   function CanvasAPI2(CanvasKit) {
-    if (CanvasKit.gpu) {
-      console.warn("Skipping canvas example because its GPU build");
-      return;
-    }
     let skcanvas = CanvasKit.MakeCanvas(200, 200);
     let realCanvas = document.getElementById('api2_c');
     realCanvas.width = 200;
diff --git a/experimental/canvaskit/canvaskit_bindings.cpp b/experimental/canvaskit/canvaskit_bindings.cpp
index a7fb875..4d9b73f 100644
--- a/experimental/canvaskit/canvaskit_bindings.cpp
+++ b/experimental/canvaskit/canvaskit_bindings.cpp
@@ -341,11 +341,11 @@
     function("currentContext", &emscripten_webgl_get_current_context);
     function("setCurrentContext", &emscripten_webgl_make_context_current);
     constant("gpu", true);
-#else
+#endif
     function("_getRasterN32PremulSurface", optional_override([](int width, int height)->sk_sp<SkSurface> {
         return SkSurface::MakeRasterN32Premul(width, height, nullptr);
     }), allow_raw_pointers());
-#endif
+
     function("getSkDataBytes", &getSkDataBytes, allow_raw_pointers());
     function("MakeSkCornerPathEffect", &SkCornerPathEffect::Make, allow_raw_pointers());
     function("MakeSkDiscretePathEffect", &SkDiscretePathEffect::Make, allow_raw_pointers());
diff --git a/experimental/canvaskit/compile.sh b/experimental/canvaskit/compile.sh
index 5979f98..881f7f4 100755
--- a/experimental/canvaskit/compile.sh
+++ b/experimental/canvaskit/compile.sh
@@ -36,7 +36,7 @@
 GN_GPU="skia_enable_gpu=true"
 GN_GPU_FLAGS="\"-DIS_WEBGL=1\", \"-DSK_DISABLE_LEGACY_SHADERCONTEXT\","
 WASM_GPU="-lEGL -lGLESv2 -DSK_SUPPORT_GPU=1 \
-          -DSK_DISABLE_LEGACY_SHADERCONTEXT --pre-js $BASE_DIR/gpu.js"
+          -DSK_DISABLE_LEGACY_SHADERCONTEXT --pre-js $BASE_DIR/cpu.js --pre-js $BASE_DIR/gpu.js"
 if [[ $@ == *cpu* ]]; then
   echo "Using the CPU backend instead of the GPU backend"
   GN_GPU="skia_enable_gpu=false"
diff --git a/experimental/canvaskit/cpu.js b/experimental/canvaskit/cpu.js
index d144664..a0282d5 100644
--- a/experimental/canvaskit/cpu.js
+++ b/experimental/canvaskit/cpu.js
@@ -3,7 +3,7 @@
 (function(CanvasKit){
   CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
   CanvasKit._extraInitializations.push(function() {
-    CanvasKit.MakeCanvasSurface = function(htmlID) {
+    CanvasKit.MakeSWCanvasSurface = function(htmlID) {
       var canvas = document.getElementById(htmlID);
       if (!canvas) {
         throw 'Canvas with id ' + htmlID + ' was not found';
@@ -22,6 +22,11 @@
       return surface;
     };
 
+    // Don't over-write the MakeCanvasSurface set by gpu.js if it exists.
+    if (!CanvasKit.MakeCanvasSurface) {
+      CanvasKit.MakeCanvasSurface = CanvasKit.MakeSWCanvasSurface;
+    }
+
     CanvasKit.MakeSurface = function(width, height) {
       var surface = this._getRasterN32PremulSurface(width, height);
       if (surface) {
@@ -38,6 +43,7 @@
     CanvasKit.SkSurface.prototype.flush = function() {
       this._flush();
       // Do we have an HTML canvas to write the pixels to?
+      // We will not if this a GPU build or a raster surface, for example.
       if (this._canvas) {
         var success = this._readPixels(this._width, this._height, this._pixelPtr);
         if (!success) {
@@ -60,12 +66,12 @@
       this.delete();
     }
 
-    CanvasKit.currentContext = function() {
-      // no op. aka return undefined.
+    CanvasKit.currentContext = CanvasKit.currentContext || function() {
+      // no op if this is a cpu-only build.
     };
 
-    CanvasKit.setCurrentContext = function() {
-      // no op. aka return undefined.
+    CanvasKit.setCurrentContext = CanvasKit.setCurrentContext || function() {
+       // no op if this is a cpu-only build.
     };
   });
 }(Module)); // When this file is loaded in, the high level object is "Module";
diff --git a/experimental/canvaskit/externs.js b/experimental/canvaskit/externs.js
index 7c5159f..1f1be54 100644
--- a/experimental/canvaskit/externs.js
+++ b/experimental/canvaskit/externs.js
@@ -29,6 +29,8 @@
 	LTRBRect: function() {},
 	MakeCanvas: function() {},
 	MakeCanvasSurface: function() {},
+	MakeSWCanvasSurface: function() {},
+	MakeWebGLCanvasSurface: function() {},
 	MakeImageShader: function() {},
 	MakeLinearGradientShader: function() {},
 	MakeRadialGradientShader: function() {},
diff --git a/experimental/canvaskit/gpu.js b/experimental/canvaskit/gpu.js
index 3a26842..348ea29 100644
--- a/experimental/canvaskit/gpu.js
+++ b/experimental/canvaskit/gpu.js
@@ -3,22 +3,21 @@
 (function(CanvasKit){
     CanvasKit._extraInitializations = CanvasKit._extraInitializations || [];
     CanvasKit._extraInitializations.push(function() {
-      CanvasKit.MakeCanvasSurface = function(htmlID) {
+      CanvasKit.MakeWebGLCanvasSurface = function(htmlID) {
         var canvas = document.getElementById(htmlID);
         if (!canvas) {
           throw 'Canvas with id ' + htmlID + ' was not found';
         }
         // Maybe better to use clientWidth/height.  See:
         // https://webglfundamentals.org/webgl/lessons/webgl-anti-patterns.html
-        return this._getWebGLSurface(htmlID, canvas.width, canvas.height);
+        var surface = this._getWebGLSurface(htmlID, canvas.width, canvas.height);
+        if (!surface) {
+          console.log('falling back from GPU implementation to a SW based one');
+          return CanvasKit.MakeSWCanvasSurface(htmlID);
+        }
+        return surface;
       };
-
-      CanvasKit.SkSurface.prototype.flush = function() {
-        this._flush();
-      }
-
-      CanvasKit.SkSurface.prototype.dispose = function() {
-        this.delete();
-      }
+      // Default to trying WebGL first.
+      CanvasKit.MakeCanvasSurface = CanvasKit.MakeWebGLCanvasSurface;
     });
 }(Module)); // When this file is loaded in, the high level object is "Module";
diff --git a/experimental/canvaskit/tests/canvas2d.spec.js b/experimental/canvaskit/tests/canvas2d.spec.js
index c763362..d2b3f66 100644
--- a/experimental/canvaskit/tests/canvas2d.spec.js
+++ b/experimental/canvaskit/tests/canvas2d.spec.js
@@ -81,12 +81,6 @@
     }); // end describe('color string parsing')
 
     function multipleCanvasTest(testname, done, test) {
-        if (CanvasKit.gpu) {
-            // TODO(kjlubick): add Software backend to GPU build skia:8548
-            console.log(`SKIPPING ${testname} on GPU`);
-            done();
-            return;
-        }
         const skcanvas = CanvasKit.MakeCanvas(CANVAS_WIDTH, CANVAS_HEIGHT);
         skcanvas._config = 'software_canvas';
         const realCanvas = document.getElementById('test');