Create Debugger slide for Viewer.

This is a very rough first draft which provides the bare minimum. Shader
source is dumped to the window as static text, along with an arrow
tracking the current trace line. Two buttons allow for single-step and
step-over. There aren't yet any GUI affordances for the stack or
variables.

http://screen/44WAnxchjy8MPjM

Change-Id: Ifad4a146b54d334113b02132eec2238af4fd8580
Bug: skia:12666
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/481681
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 7e75cef..a8e8e29 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -2810,6 +2810,8 @@
         "tools/viewer/SampleSlide.h",
         "tools/viewer/SkRiveSlide.cpp",
         "tools/viewer/SkRiveSlide.h",
+        "tools/viewer/SkSLDebuggerSlide.cpp",
+        "tools/viewer/SkSLDebuggerSlide.h",
         "tools/viewer/SkSLSlide.cpp",
         "tools/viewer/SkSLSlide.h",
         "tools/viewer/SkottieSlide.cpp",
diff --git a/tools/viewer/SkSLDebuggerSlide.cpp b/tools/viewer/SkSLDebuggerSlide.cpp
new file mode 100644
index 0000000..ad05961
--- /dev/null
+++ b/tools/viewer/SkSLDebuggerSlide.cpp
@@ -0,0 +1,106 @@
+/*
+* Copyright 2021 Google LLC
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#include "tools/viewer/SkSLDebuggerSlide.h"
+
+#include "include/core/SkCanvas.h"
+#include "tools/viewer/Viewer.h"
+
+#include <algorithm>
+#include <cstdio>
+#include "imgui.h"
+
+using namespace sk_app;
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkSLDebuggerSlide::SkSLDebuggerSlide() {
+    fName = "Debugger";
+    fTrace = sk_make_sp<SkSL::SkVMDebugTrace>();
+}
+
+void SkSLDebuggerSlide::load(SkScalar winWidth, SkScalar winHeight) {}
+
+void SkSLDebuggerSlide::unload() {
+    fTrace = sk_make_sp<SkSL::SkVMDebugTrace>();
+    fPlayer.reset(nullptr);
+}
+
+void SkSLDebuggerSlide::showLoadTraceGUI() {
+    ImGui::InputText("Trace Path", fTraceFile, SK_ARRAY_COUNT(fTraceFile));
+    bool load = ImGui::Button("Load Debug Trace");
+
+    if (load) {
+        SkFILEStream file(fTraceFile);
+        if (!file.isValid()) {
+            ImGui::OpenPopup("Can't Open Trace");
+        } else if (!fTrace->readTrace(&file)) {
+            ImGui::OpenPopup("Invalid Trace");
+        } else {
+            // Trace loaded successfully. On the next refresh, the user will see the debug UI.
+            fPlayer.reset(fTrace);
+            fPlayer.step();
+            return;
+        }
+    }
+
+    if (ImGui::BeginPopupModal("Can't Open Trace", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
+        ImGui::Text("The trace file doesn't exist.");
+        ImGui::Separator();
+        if (ImGui::Button("OK", ImVec2(120, 0))) {
+            ImGui::CloseCurrentPopup();
+        }
+        ImGui::EndPopup();
+    }
+
+    if (ImGui::BeginPopupModal("Invalid Trace", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
+        ImGui::Text("The trace data could not be parsed.");
+        ImGui::Separator();
+        if (ImGui::Button("OK", ImVec2(120, 0))) {
+            ImGui::CloseCurrentPopup();
+        }
+        ImGui::EndPopup();
+    }
+}
+
+void SkSLDebuggerSlide::showDebuggerGUI() {
+    if (ImGui::Button("Step")) {
+        fPlayer.step();
+    }
+    ImGui::SameLine();
+    if (ImGui::Button("Step Over")) {
+        fPlayer.stepOver();
+    }
+    for (size_t line = 0; line < fTrace->fSource.size(); ++line) {
+        size_t humanReadableLine = line + 1;
+        bool isCurrentLine = (fPlayer.getCurrentLine() == (int)humanReadableLine);
+        ImGui::Text("%s%03zu %s",
+                    isCurrentLine ? "-> " : "   ",
+                    humanReadableLine,
+                    fTrace->fSource[line].c_str());
+    }
+}
+
+void SkSLDebuggerSlide::showRootGUI() {
+    if (fTrace->fSource.empty()) {
+        this->showLoadTraceGUI();
+        return;
+    }
+
+    this->showDebuggerGUI();
+}
+
+void SkSLDebuggerSlide::draw(SkCanvas* canvas) {
+    canvas->clear(SK_ColorWHITE);
+    ImGui::Begin("Debugger", nullptr, ImGuiWindowFlags_AlwaysVerticalScrollbar);
+    this->showRootGUI();
+    ImGui::End();
+}
+
+bool SkSLDebuggerSlide::animate(double nanos) {
+    return true;
+}
diff --git a/tools/viewer/SkSLDebuggerSlide.h b/tools/viewer/SkSLDebuggerSlide.h
new file mode 100644
index 0000000..6eecc5b
--- /dev/null
+++ b/tools/viewer/SkSLDebuggerSlide.h
@@ -0,0 +1,42 @@
+/*
+* Copyright 2021 Google LLC
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#ifndef SkSLDebuggerSlide_DEFINED
+#define SkSLDebuggerSlide_DEFINED
+
+#include "src/sksl/tracing/SkVMDebugTrace.h"
+#include "src/sksl/tracing/SkVMDebugTracePlayer.h"
+#include "tools/viewer/Slide.h"
+
+class SkSLDebuggerSlide : public Slide {
+public:
+    SkSLDebuggerSlide();
+
+    SkISize getDimensions() const override { return SkISize::MakeEmpty(); }
+
+    void draw(SkCanvas* canvas) override;
+    bool animate(double nanos) override;
+
+    void resize(SkScalar winWidth, SkScalar winHeight) override {}
+    void load(SkScalar winWidth, SkScalar winHeight) override;
+    void unload() override;
+
+    bool onMouse(SkScalar x, SkScalar y, skui::InputState state,
+                 skui::ModifierKey modifiers) override { return true; }
+
+private:
+    void showRootGUI();
+    void showLoadTraceGUI();
+    void showDebuggerGUI();
+
+    sk_sp<SkSL::SkVMDebugTrace> fTrace;
+    SkSL::SkVMDebugTracePlayer fPlayer;
+
+    char fTraceFile[256] = "SkVMDebugTrace.json";
+};
+
+#endif
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index 4f42109..777f9a0 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -51,6 +51,7 @@
 #include "tools/viewer/ParticlesSlide.h"
 #include "tools/viewer/SKPSlide.h"
 #include "tools/viewer/SampleSlide.h"
+#include "tools/viewer/SkSLDebuggerSlide.h"
 #include "tools/viewer/SkSLSlide.h"
 #include "tools/viewer/SlideDir.h"
 #include "tools/viewer/SvgSlide.h"
@@ -878,6 +879,14 @@
         }
     }
 
+    // Runtime shader debugger
+    {
+        auto slide = sk_make_sp<SkSLDebuggerSlide>();
+        if (!CommandLineFlags::ShouldSkip(FLAGS_match, slide->getName().c_str())) {
+            fSlides.push_back(std::move(slide));
+        }
+    }
+
     for (const auto& info : gExternalSlidesInfo) {
         for (const auto& flag : info.fFlags) {
             if (SkStrEndsWith(flag.c_str(), info.fExtension)) {