GLSL editing in Viewer
When in GL backend, adds a "Shaders" section to the debug menu.
"Load" scrapes all of the vertex and fragment shaders being used,
then displays them. They can be edited, and "Save" pushes the
results.
Note: It is trivial to trigger an assert by saving a shader that
doesn't compile. I'd like to make the program builder more robust
in a follow-up CL, to fall back to the "real" SkSL, not draw, or
something along those lines.
Change-Id: I841fe2ee76a3c2eae58b64ef587fcbe25b95cc7e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/206905
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index b6d6609..9cfd338 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -149,7 +149,7 @@
const GrGLContext* glContextForTesting() const override { return &this->glContext(); }
- void resetShaderCacheForTesting() const override { fProgramCache->abandon(); }
+ void resetShaderCacheForTesting() const override { fProgramCache->reset(); }
void testingOnly_flushGpuAndSync() override;
#endif
@@ -314,6 +314,7 @@
~ProgramCache();
void abandon();
+ void reset();
GrGLProgram* refProgram(GrGLGpu*, GrRenderTarget*, GrSurfaceOrigin,
const GrPrimitiveProcessor&,
const GrTextureProxy* const primProcProxies[],
diff --git a/src/gpu/gl/GrGLGpuProgramCache.cpp b/src/gpu/gl/GrGLGpuProgramCache.cpp
index 2cb5726..4a8f150 100644
--- a/src/gpu/gl/GrGLGpuProgramCache.cpp
+++ b/src/gpu/gl/GrGLGpuProgramCache.cpp
@@ -56,15 +56,20 @@
}
void GrGLGpu::ProgramCache::abandon() {
+ fMap.foreach([](std::unique_ptr<Entry>* e) {
+ (*e)->fProgram->abandon();
+ });
+
+ this->reset();
+}
+
+void GrGLGpu::ProgramCache::reset() {
#ifdef PROGRAM_CACHE_STATS
fTotalRequests = 0;
fCacheMisses = 0;
fHashMisses = 0;
#endif
- fMap.foreach([](std::unique_ptr<Entry>* e) {
- (*e)->fProgram->abandon();
- });
fMap.reset();
}
diff --git a/third_party/imgui/BUILD.gn b/third_party/imgui/BUILD.gn
index 8dd3a43..94ce3bf 100644
--- a/third_party/imgui/BUILD.gn
+++ b/third_party/imgui/BUILD.gn
@@ -16,5 +16,6 @@
"../externals/imgui/imgui_demo.cpp",
"../externals/imgui/imgui_draw.cpp",
"../externals/imgui/imgui_widgets.cpp",
+ "../externals/imgui/misc/cpp/imgui_stdlib.cpp",
]
}
diff --git a/tools/gpu/MemoryCache.h b/tools/gpu/MemoryCache.h
index 119ea8d..1e61994 100644
--- a/tools/gpu/MemoryCache.h
+++ b/tools/gpu/MemoryCache.h
@@ -27,6 +27,10 @@
MemoryCache() = default;
MemoryCache(const MemoryCache&) = delete;
MemoryCache& operator=(const MemoryCache&) = delete;
+ void reset() {
+ fCacheMissCnt = 0;
+ fMap.clear();
+ }
sk_sp<SkData> load(const SkData& key) override;
void store(const SkData& key, const SkData& data) override;
@@ -35,6 +39,13 @@
void writeShadersToDisk(const char* path, GrBackendApi backend);
+ template <typename Fn>
+ void foreach(Fn&& fn) {
+ for (auto it = fMap.begin(); it != fMap.end(); ++it) {
+ fn(it->first.fKey, it->second.fData, it->second.fHitCount);
+ }
+ }
+
private:
struct Key {
Key() = default;
diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp
index 8c5c7d1..3e20c0f 100644
--- a/tools/viewer/Viewer.cpp
+++ b/tools/viewer/Viewer.cpp
@@ -13,6 +13,8 @@
#include "GMSlide.h"
#include "GrContext.h"
#include "GrContextPriv.h"
+#include "GrGpu.h"
+#include "GrPersistentCacheUtils.h"
#include "ImageSlide.h"
#include "ParticlesSlide.h"
#include "Resources.h"
@@ -20,10 +22,12 @@
#include "SampleSlide.h"
#include "SkCanvas.h"
#include "SkColorSpacePriv.h"
+#include "SkData.h"
#include "SkGraphics.h"
#include "SkImagePriv.h"
#include "SkJSONWriter.h"
#include "SkMakeUnique.h"
+#include "SkMD5.h"
#include "SkOSFile.h"
#include "SkOSPath.h"
#include "SkPaintFilterCanvas.h"
@@ -42,6 +46,7 @@
#include <map>
#include "imgui.h"
+#include "misc/cpp/imgui_stdlib.h" // For ImGui support of std::string
#if defined(SK_ENABLE_SKOTTIE)
#include "SkottieSlide.h"
@@ -274,6 +279,8 @@
DisplayParams displayParams;
displayParams.fMSAASampleCount = FLAGS_msaa;
SetCtxOptionsFromCommonFlags(&displayParams.fGrContextOptions);
+ displayParams.fGrContextOptions.fPersistentCache = &fPersistentCache;
+ displayParams.fGrContextOptions.fDisallowGLSLBinaryCaching = true;
fWindow->setRequestedDisplayParams(displayParams);
// Configure timers
@@ -1482,6 +1489,8 @@
ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);
DisplayParams params = fWindow->getRequestedDisplayParams();
bool paramsChanged = false;
+ const GrContext* ctx = fWindow->getGrContext();
+
if (ImGui::Begin("Tools", &fShowImGuiDebugWindow,
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
if (ImGui::CollapsingHeader("Backend")) {
@@ -1507,7 +1516,6 @@
});
}
- const GrContext* ctx = fWindow->getGrContext();
bool* wire = ¶ms.fGrContextOptions.fWireframeMode;
if (ctx && ImGui::Checkbox("Wireframe Mode", wire)) {
paramsChanged = true;
@@ -1940,6 +1948,85 @@
fAnimTimer.setSpeed(speed);
}
}
+
+ if (Window::kNativeGL_BackendType == fBackendType &&
+ ImGui::CollapsingHeader("Shaders")) {
+ // To re-load shaders from the currently active programs, we flush all caches on one
+ // frame, then set a flag to poll the cache on the next frame.
+ static bool gLoadPending = false;
+ if (gLoadPending) {
+ auto collectShaders = [this](sk_sp<const SkData> key, sk_sp<SkData> data,
+ int hitCount) {
+ CachedGLSL& entry(fCachedGLSL.push_back());
+ entry.fKey = key;
+ SkMD5 hash;
+ hash.write(key->bytes(), key->size());
+ SkMD5::Digest digest = hash.finish();
+ for (int i = 0; i < 16; ++i) {
+ entry.fKeyString.appendf("%02x", digest.data[i]);
+ }
+
+ GrPersistentCacheUtils::UnpackCachedGLSL(data.get(), &entry.fInputs,
+ entry.fShader);
+ };
+ fCachedGLSL.reset();
+ fPersistentCache.foreach(collectShaders);
+ gLoadPending = false;
+ }
+
+ // Defer actually doing the load/save logic so that we can trigger a save when we
+ // start or finish hovering on a tree node in the list below:
+ bool doLoad = ImGui::Button("Load"); ImGui::SameLine();
+ bool doSave = ImGui::Button("Save");
+
+ ImGui::BeginChild("##ScrollingRegion");
+ for (auto& entry : fCachedGLSL) {
+ bool inTreeNode = ImGui::TreeNode(entry.fKeyString.c_str());
+ bool hovered = ImGui::IsItemHovered();
+ if (hovered != entry.fHovered) {
+ // Force a save to patch the highlight shader in/out
+ entry.fHovered = hovered;
+ doSave = true;
+ }
+ if (inTreeNode) {
+ // Full width, and a reasonable amount of space for each shader.
+ ImVec2 boxSize(-1.0f, ImGui::GetTextLineHeight() * 20.0f);
+ ImGui::InputTextMultiline("##VP", &entry.fShader[kVertex_GrShaderType],
+ boxSize);
+ ImGui::InputTextMultiline("##FP", &entry.fShader[kFragment_GrShaderType],
+ boxSize);
+ ImGui::TreePop();
+ }
+ }
+ ImGui::EndChild();
+
+ if (doLoad) {
+ fPersistentCache.reset();
+ fWindow->getGrContext()->priv().getGpu()->resetShaderCacheForTesting();
+ gLoadPending = true;
+ }
+ if (doSave) {
+ // The hovered item (if any) gets a special shader to make it identifiable
+ SkSL::String highlight = ctx->priv().caps()->shaderCaps()->versionDeclString();
+ highlight.append("out vec4 sk_FragColor;\n"
+ "void main() { sk_FragColor = vec4(1, 0, 1, 0.5); }");
+
+ fPersistentCache.reset();
+ fWindow->getGrContext()->priv().getGpu()->resetShaderCacheForTesting();
+ for (auto& entry : fCachedGLSL) {
+ SkSL::String backup = entry.fShader[kFragment_GrShaderType];
+ if (entry.fHovered) {
+ entry.fShader[kFragment_GrShaderType] = highlight;
+ }
+
+ auto data = GrPersistentCacheUtils::PackCachedGLSL(entry.fInputs,
+ entry.fShader);
+ fPersistentCache.store(*entry.fKey, *data);
+
+ entry.fShader[kFragment_GrShaderType] = backup;
+ }
+ }
+ }
}
if (paramsChanged) {
fDeferredActions.push_back([=]() {
diff --git a/tools/viewer/Viewer.h b/tools/viewer/Viewer.h
index 92c5696..061d5ac 100644
--- a/tools/viewer/Viewer.h
+++ b/tools/viewer/Viewer.h
@@ -10,9 +10,12 @@
#include "AnimTimer.h"
#include "ImGuiLayer.h"
+#include "MemoryCache.h"
#include "SkExecutor.h"
#include "SkFont.h"
#include "SkScan.h"
+#include "ir/SkSLProgram.h"
+#include "SkSLString.h"
#include "Slide.h"
#include "StatsLayer.h"
#include "TouchGesture.h"
@@ -22,6 +25,7 @@
#include "sk_app/Window.h"
class SkCanvas;
+class SkData;
class Viewer : public sk_app::Application, sk_app::Window::Layer {
public:
@@ -183,7 +187,19 @@
SkFont fFont;
SkFontFields fFontOverrides;
bool fPixelGeometryOverrides = false;
-};
+ struct CachedGLSL {
+ bool fHovered = false;
+
+ sk_sp<const SkData> fKey;
+ SkString fKeyString;
+
+ SkSL::Program::Inputs fInputs;
+ SkSL::String fShader[kGrShaderTypeCount];
+ };
+
+ sk_gpu_test::MemoryCache fPersistentCache;
+ SkTArray<CachedGLSL> fCachedGLSL;
+};
#endif