Add threaded backend to viewer

Bug: skia:
Change-Id: Ibf672921f8a05705e7262aad0b1f3f1e6fc0ef9c
Reviewed-on: https://skia-review.googlesource.com/75382
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Yuqian Li <liyuqian@google.com>
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index 0839161..8c32093 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -36,6 +36,7 @@
 #include "SkSurface.h"
 #include "SkSwizzle.h"
 #include "SkTaskGroup.h"
+#include "SkThreadedBMPDevice.h"
 #include "SkTime.h"
 #include "SkVertices.h"
 
@@ -276,6 +277,8 @@
     , fColorSpaceTransferFn(g2Dot2_TransferFn)
     , fZoomLevel(0.0f)
     , fGestureDevice(GestureDevice::kNone)
+    , fTileCnt(0)
+    , fThreadCnt(0)
 {
     SkGraphics::Init();
 
@@ -421,7 +424,7 @@
         this->setBackend(newBackend);
     });
 
-    fCommands.addCommand('A', "AAA", "Toggle analytic AA", [this]() {
+    fCommands.addCommand('A', "AA", "Toggle analytic AA", [this]() {
         if (!gSkUseAnalyticAA) {
             gSkUseAnalyticAA = true;
         } else if (!gSkForceAnalyticAA) {
@@ -432,7 +435,7 @@
         this->updateTitle();
         fWindow->inval();
     });
-    fCommands.addCommand('D', "DAA", "Toggle delta AA", [this]() {
+    fCommands.addCommand('D', "AA", "Toggle delta AA", [this]() {
         if (!gSkUseDeltaAA) {
             gSkUseDeltaAA = true;
         } else if (!gSkForceDeltaAA) {
@@ -444,6 +447,41 @@
         fWindow->inval();
     });
 
+    fCommands.addCommand('+', "Threaded Backend", "Increase tile count", [this]() {
+        fTileCnt++;
+        if (fThreadCnt == 0) {
+            this->resetExecutor();
+        }
+        this->updateTitle();
+        fWindow->inval();
+    });
+    fCommands.addCommand('-', "Threaded Backend", "Decrease tile count", [this]() {
+        fTileCnt = SkTMax(0, fTileCnt - 1);
+        if (fThreadCnt == 0) {
+            this->resetExecutor();
+        }
+        this->updateTitle();
+        fWindow->inval();
+    });
+    fCommands.addCommand('>', "Threaded Backend", "Increase thread count", [this]() {
+        if (fTileCnt == 0) {
+            return;
+        }
+        fThreadCnt = (fThreadCnt + 1) % fTileCnt;
+        this->resetExecutor();
+        this->updateTitle();
+        fWindow->inval();
+    });
+    fCommands.addCommand('<', "Threaded Backend", "Decrease thread count", [this]() {
+        if (fTileCnt == 0) {
+            return;
+        }
+        fThreadCnt = (fThreadCnt + fTileCnt - 1) % fTileCnt;
+        this->resetExecutor();
+        this->updateTitle();
+        fWindow->inval();
+    });
+
     // set up slides
     this->initSlides();
     this->setStartupSlide();
@@ -612,6 +650,13 @@
         }
     }
 
+    if (fTileCnt > 0) {
+        title.appendf(" T%d", fTileCnt);
+        if (fThreadCnt > 0) {
+            title.appendf("/%d", fThreadCnt);
+        }
+    }
+
     switch (fColorMode) {
         case ColorMode::kLegacy:
             title.append(" Legacy 8888");
@@ -825,6 +870,8 @@
     // we need to render offscreen. We also need to render offscreen if we're in any raster mode,
     // because the window surface is actually GL.
     sk_sp<SkSurface> offscreenSurface = nullptr;
+    std::unique_ptr<SkThreadedBMPDevice> threadedDevice;
+    std::unique_ptr<SkCanvas> threadedCanvas;
     if (Window::kRaster_BackendType == fBackendType ||
         ColorMode::kColorManagedLinearF16 == fColorMode ||
         fShowZoomWindow ||
@@ -841,7 +888,17 @@
                                              kPremul_SkAlphaType, std::move(offscreenColorSpace));
         offscreenSurface = Window::kRaster_BackendType == fBackendType ? SkSurface::MakeRaster(info)
                                                                        : canvas->makeSurface(info);
-        slideCanvas = offscreenSurface->getCanvas();
+        SkPixmap offscreenPixmap;
+        if (fTileCnt > 0 && offscreenSurface->peekPixels(&offscreenPixmap)) {
+            SkBitmap offscreenBitmap;
+            offscreenBitmap.installPixels(offscreenPixmap);
+            threadedDevice.reset(new SkThreadedBMPDevice(offscreenBitmap, fTileCnt,
+                                                         fThreadCnt, fExecutor.get()));
+            threadedCanvas.reset(new SkCanvas(threadedDevice.get()));
+            slideCanvas = threadedCanvas.get();
+        } else {
+            slideCanvas = offscreenSurface->getCanvas();
+        }
     }
 
     std::unique_ptr<SkCanvas> xformCanvas = nullptr;
diff --git a/tools/viewer/Viewer.h b/tools/viewer/Viewer.h
index 24f68b4..53b75b2 100644
--- a/tools/viewer/Viewer.h
+++ b/tools/viewer/Viewer.h
@@ -13,6 +13,7 @@
 #include "sk_app/Window.h"
 #include "gm.h"
 #include "SkAnimTimer.h"
+#include "SkExecutor.h"
 #include "SkJSONCPP.h"
 #include "SkTouchGesture.h"
 #include "Slide.h"
@@ -59,6 +60,10 @@
     void changeZoomLevel(float delta);
     SkMatrix computeMatrix();
 
+    void resetExecutor() {
+        fExecutor = SkExecutor::MakeFIFOThreadPool(fThreadCnt == 0 ? fTileCnt : fThreadCnt);
+    }
+
     sk_app::Window*        fWindow;
 
     static const int kMeasurementCount = 1 << 6;  // should be power of 2 for fast mod
@@ -112,6 +117,10 @@
     SkTArray<std::function<void(void)>> fDeferredActions;
 
     Json::Value            fAllSlideNames; // cache all slide names for fast updateUIState
+
+    int fTileCnt;
+    int fThreadCnt;
+    std::unique_ptr<SkExecutor> fExecutor;
 };