Revert "GrContext::dump that produces JSON formatted output"

This reverts commit 175af0d01177fc6e5a81e979cd2ae3009c375940.

Reason for revert: Chrome doesn't know about portable format specifiers. Sigh.

Original change's description:
> GrContext::dump that produces JSON formatted output
> 
> Includes caps, GL strings, and extensions
> 
> Bug: skia:
> Change-Id: I1e8b3dd50fb68357f9de8ca6149cf65443d027ef
> Reviewed-on: https://skia-review.googlesource.com/32340
> Commit-Queue: Brian Osman <brianosman@google.com>
> Reviewed-by: Brian Salomon <bsalomon@google.com>

TBR=bsalomon@google.com,brianosman@google.com

Change-Id: Ie280b25275725f0661da7541f54ed62897abb82f
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: skia:
Reviewed-on: https://skia-review.googlesource.com/32861
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/gn/tests.gni b/gn/tests.gni
index 26bdc94..3edb3e3 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -89,6 +89,7 @@
   "$_tests/GrAllocatorTest.cpp",
   "$_tests/GrContextAbandonTest.cpp",
   "$_tests/GrContextFactoryTest.cpp",
+  "$_tests/GrDrawTargetTest.cpp",
   "$_tests/GrMemoryPoolTest.cpp",
   "$_tests/GrMeshTest.cpp",
   "$_tests/GrPipelineDynamicStateTest.cpp",
diff --git a/include/gpu/GrCaps.h b/include/gpu/GrCaps.h
index 65df092..0990ecb 100644
--- a/include/gpu/GrCaps.h
+++ b/include/gpu/GrCaps.h
@@ -16,7 +16,6 @@
 
 struct GrContextOptions;
 class GrRenderTargetProxy;
-class SkJSONWriter;
 
 /**
  * Represents the capabilities of a GrContext.
@@ -25,8 +24,7 @@
 public:
     GrCaps(const GrContextOptions&);
 
-    void dumpJSON(SkJSONWriter*) const;
-
+    virtual SkString dump() const;
     const GrShaderCaps* shaderCaps() const { return fShaderCaps.get(); }
 
     bool npotTextureTileSupport() const { return fNPOTTextureTileSupport; }
@@ -240,7 +238,6 @@
 
 private:
     virtual void onApplyOptionsOverrides(const GrContextOptions&) {}
-    virtual void onDumpJSON(SkJSONWriter*) const {}
 
     bool fSuppressPrints : 1;
     bool fWireframeMode  : 1;
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 233a54f..7a14a7d 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -310,9 +310,6 @@
     void dumpGpuStatsKeyValuePairs(SkTArray<SkString>* keys, SkTArray<double>* values) const;
     void printGpuStats() const;
 
-    /** Returns a string with detailed information about the context & GPU, in JSON format. */
-    SkString dump() const;
-
     /** Specify the TextBlob cache limit. If the current cache exceeds this limit it will purge.
         this is for testing only */
     void setTextBlobCacheLimit_ForTesting(size_t bytes);
diff --git a/include/gpu/GrShaderCaps.h b/include/gpu/GrShaderCaps.h
index f2c6aae..214d45e 100644
--- a/include/gpu/GrShaderCaps.h
+++ b/include/gpu/GrShaderCaps.h
@@ -16,7 +16,6 @@
     class ShaderCapsFactory;
 }
 struct GrContextOptions;
-class SkJSONWriter;
 
 class GrShaderCaps : public SkRefCnt {
 public:
@@ -69,7 +68,7 @@
 
     GrShaderCaps(const GrContextOptions&);
 
-    void dumpJSON(SkJSONWriter*) const;
+    SkString dump() const;
 
     bool shaderDerivativeSupport() const { return fShaderDerivativeSupport; }
     bool geometryShaderSupport() const { return fGeometryShaderSupport; }
diff --git a/include/gpu/gl/GrGLExtensions.h b/include/gpu/gl/GrGLExtensions.h
index 0355ad2..faf73fd 100644
--- a/include/gpu/gl/GrGLExtensions.h
+++ b/include/gpu/gl/GrGLExtensions.h
@@ -13,7 +13,6 @@
 #include "SkString.h"
 
 struct GrGLInterface;
-class SkJSONWriter;
 
 /**
  * This helper queries the current GL context for its extensions, remembers them, and can be
@@ -65,7 +64,7 @@
 
     void reset() { fStrings->reset(); }
 
-    void dumpJSON(SkJSONWriter*) const;
+    void print(const char* sep = "\n") const;
 
 private:
     bool                                fInitialized;
diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp
index 00475e6..67a8e68 100644
--- a/src/gpu/GrCaps.cpp
+++ b/src/gpu/GrCaps.cpp
@@ -8,7 +8,6 @@
 #include "GrCaps.h"
 #include "GrContextOptions.h"
 #include "GrWindowRectangles.h"
-#include "SkJSONWriter.h"
 
 static const char* pixel_config_name(GrPixelConfig config) {
     switch (config) {
@@ -117,45 +116,44 @@
     return str;
 }
 
-void GrCaps::dumpJSON(SkJSONWriter* writer) const {
-    writer->beginObject();
+SkString GrCaps::dump() const {
+    SkString r;
+    static const char* gNY[] = {"NO", "YES"};
+    r.appendf("MIP Map Support                    : %s\n", gNY[fMipMapSupport]);
+    r.appendf("NPOT Texture Tile Support          : %s\n", gNY[fNPOTTextureTileSupport]);
+    r.appendf("sRGB Support                       : %s\n", gNY[fSRGBSupport]);
+    r.appendf("sRGB Write Control                 : %s\n", gNY[fSRGBWriteControl]);
+    r.appendf("Discard Render Target Support      : %s\n", gNY[fDiscardRenderTargetSupport]);
+    r.appendf("Reuse Scratch Textures             : %s\n", gNY[fReuseScratchTextures]);
+    r.appendf("Reuse Scratch Buffers              : %s\n", gNY[fReuseScratchBuffers]);
+    r.appendf("Gpu Tracing Support                : %s\n", gNY[fGpuTracingSupport]);
+    r.appendf("Oversized Stencil Support          : %s\n", gNY[fOversizedStencilSupport]);
+    r.appendf("Texture Barrier Support            : %s\n", gNY[fTextureBarrierSupport]);
+    r.appendf("Sample Locations Support           : %s\n", gNY[fSampleLocationsSupport]);
+    r.appendf("Multisample disable support        : %s\n", gNY[fMultisampleDisableSupport]);
+    r.appendf("Instance Attrib Support            : %s\n", gNY[fInstanceAttribSupport]);
+    r.appendf("Uses Mixed Samples                 : %s\n", gNY[fUsesMixedSamples]);
+    r.appendf("Prefer client-side dynamic buffers : %s\n", gNY[fPreferClientSideDynamicBuffers]);
+    r.appendf("Full screen clear is free          : %s\n", gNY[fFullClearIsFree]);
+    r.appendf("Must clear buffer memory           : %s\n", gNY[fMustClearUploadedBufferData]);
+    r.appendf("Sample shading support             : %s\n", gNY[fSampleShadingSupport]);
+    r.appendf("Fence sync support                 : %s\n", gNY[fFenceSyncSupport]);
+    r.appendf("Cross context texture support      : %s\n", gNY[fCrossContextTextureSupport]);
 
-    writer->appendBool("MIP Map Support", fMipMapSupport);
-    writer->appendBool("NPOT Texture Tile Support", fNPOTTextureTileSupport);
-    writer->appendBool("sRGB Support", fSRGBSupport);
-    writer->appendBool("sRGB Write Control", fSRGBWriteControl);
-    writer->appendBool("sRGB Decode Disable", fSRGBDecodeDisableSupport);
-    writer->appendBool("Discard Render Target Support", fDiscardRenderTargetSupport);
-    writer->appendBool("Reuse Scratch Textures", fReuseScratchTextures);
-    writer->appendBool("Reuse Scratch Buffers", fReuseScratchBuffers);
-    writer->appendBool("Gpu Tracing Support", fGpuTracingSupport);
-    writer->appendBool("Oversized Stencil Support", fOversizedStencilSupport);
-    writer->appendBool("Texture Barrier Support", fTextureBarrierSupport);
-    writer->appendBool("Sample Locations Support", fSampleLocationsSupport);
-    writer->appendBool("Multisample disable support", fMultisampleDisableSupport);
-    writer->appendBool("Instance Attrib Support", fInstanceAttribSupport);
-    writer->appendBool("Uses Mixed Samples", fUsesMixedSamples);
-    writer->appendBool("Prefer client-side dynamic buffers", fPreferClientSideDynamicBuffers);
-    writer->appendBool("Full screen clear is free", fFullClearIsFree);
-    writer->appendBool("Must clear buffer memory", fMustClearUploadedBufferData);
-    writer->appendBool("Sample shading support", fSampleShadingSupport);
-    writer->appendBool("Fence sync support", fFenceSyncSupport);
-    writer->appendBool("Cross context texture support", fCrossContextTextureSupport);
-
-    writer->appendBool("Draw Instead of Clear [workaround]", fUseDrawInsteadOfClear);
-    writer->appendBool("Prefer VRAM Use over flushes [workaround]", fPreferVRAMUseOverFlushes);
+    r.appendf("Draw Instead of Clear [workaround] : %s\n", gNY[fUseDrawInsteadOfClear]);
+    r.appendf("Prefer VRAM Use over flushes [workaround] : %s\n", gNY[fPreferVRAMUseOverFlushes]);
 
     if (this->advancedBlendEquationSupport()) {
-        writer->appendHexU32("Advanced Blend Equation Blacklist", fAdvBlendEqBlacklist);
+        r.appendf("Advanced Blend Equation Blacklist  : 0x%x\n", fAdvBlendEqBlacklist);
     }
 
-    writer->appendS32("Max Vertex Attributes", fMaxVertexAttributes);
-    writer->appendS32("Max Texture Size", fMaxTextureSize);
-    writer->appendS32("Max Render Target Size", fMaxRenderTargetSize);
-    writer->appendS32("Max Color Sample Count", fMaxColorSampleCount);
-    writer->appendS32("Max Stencil Sample Count", fMaxStencilSampleCount);
-    writer->appendS32("Max Raster Samples", fMaxRasterSamples);
-    writer->appendS32("Max Window Rectangles", fMaxWindowRectangles);
+    r.appendf("Max Vertex Attributes              : %d\n", fMaxVertexAttributes);
+    r.appendf("Max Texture Size                   : %d\n", fMaxTextureSize);
+    r.appendf("Max Render Target Size             : %d\n", fMaxRenderTargetSize);
+    r.appendf("Max Color Sample Count             : %d\n", fMaxColorSampleCount);
+    r.appendf("Max Stencil Sample Count           : %d\n", fMaxStencilSampleCount);
+    r.appendf("Max Raster Samples                 : %d\n", fMaxRasterSamples);
+    r.appendf("Max Window Rectangles              : %d\n", fMaxWindowRectangles);
 
     static const char* kInstancedSupportNames[] = {
         "None",
@@ -169,7 +167,8 @@
     GR_STATIC_ASSERT(3 == (int)InstancedSupport::kMixedSampled);
     GR_STATIC_ASSERT(4 == SK_ARRAY_COUNT(kInstancedSupportNames));
 
-    writer->appendString("Instanced Support", kInstancedSupportNames[(int)fInstancedSupport]);
+    r.appendf("Instanced Support                  : %s\n",
+              kInstancedSupportNames[(int)fInstancedSupport]);
 
     static const char* kBlendEquationSupportNames[] = {
         "Basic",
@@ -181,32 +180,30 @@
     GR_STATIC_ASSERT(2 == kAdvancedCoherent_BlendEquationSupport);
     GR_STATIC_ASSERT(SK_ARRAY_COUNT(kBlendEquationSupportNames) == kLast_BlendEquationSupport + 1);
 
-    writer->appendString("Blend Equation Support",
-                         kBlendEquationSupportNames[fBlendEquationSupport]);
-    writer->appendString("Map Buffer Support", map_flags_to_string(fMapBufferFlags).c_str());
+    r.appendf("Blend Equation Support             : %s\n",
+              kBlendEquationSupportNames[fBlendEquationSupport]);
+    r.appendf("Map Buffer Support                 : %s\n",
+              map_flags_to_string(fMapBufferFlags).c_str());
 
     SkASSERT(!this->isConfigRenderable(kUnknown_GrPixelConfig, false));
     SkASSERT(!this->isConfigRenderable(kUnknown_GrPixelConfig, true));
-    SkASSERT(!this->isConfigTexturable(kUnknown_GrPixelConfig));
 
-    writer->beginArray("configs");
-
-    for (size_t i = 1; i < kGrPixelConfigCnt; ++i) {
+    for (size_t i = 1; i < kGrPixelConfigCnt; ++i)  {
         GrPixelConfig config = static_cast<GrPixelConfig>(i);
-        writer->beginObject();
-        writer->appendString("name", pixel_config_name(config));
-        writer->appendBool("renderable", this->isConfigRenderable(config, false));
-        writer->appendBool("renderableMSAA", this->isConfigRenderable(config, true));
-        writer->appendBool("texturable", this->isConfigTexturable(config));
-        writer->endObject();
+        r.appendf("%s is renderable: %s, with MSAA: %s\n",
+                  pixel_config_name(config),
+                  gNY[this->isConfigRenderable(config, false)],
+                  gNY[this->isConfigRenderable(config, true)]);
     }
 
-    writer->endArray();
+    SkASSERT(!this->isConfigTexturable(kUnknown_GrPixelConfig));
 
-    this->onDumpJSON(writer);
+    for (size_t i = 1; i < kGrPixelConfigCnt; ++i)  {
+        GrPixelConfig config = static_cast<GrPixelConfig>(i);
+        r.appendf("%s is uploadable to a texture: %s\n",
+                  pixel_config_name(config),
+                  gNY[this->isConfigTexturable(config)]);
+    }
 
-    writer->appendName("shaderCaps");
-    this->shaderCaps()->dumpJSON(writer);
-
-    writer->endObject();
+    return r;
 }
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index efa8e8c..a0cf5f5 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -26,7 +26,6 @@
 #include "GrTracing.h"
 #include "SkConvertPixels.h"
 #include "SkGr.h"
-#include "SkJSONWriter.h"
 #include "SkUnPreMultiplyPriv.h"
 #include "effects/GrConfigConversionEffect.h"
 #include "text/GrTextBlobCache.h"
@@ -994,41 +993,3 @@
     ASSERT_SINGLE_OWNER
     fResourceCache->dumpMemoryStatistics(traceMemoryDump);
 }
-
-//////////////////////////////////////////////////////////////////////////////
-
-SkString GrContext::dump() const {
-    SkDynamicMemoryWStream stream;
-    SkJSONWriter writer(&stream, SkJSONWriter::Mode::kPretty);
-    writer.beginObject();
-
-    static const char* kBackendStr[] = {
-        "Metal",
-        "OpenGL",
-        "Vulkan",
-        "Mock",
-    };
-    GR_STATIC_ASSERT(0 == kMetal_GrBackend);
-    GR_STATIC_ASSERT(1 == kOpenGL_GrBackend);
-    GR_STATIC_ASSERT(2 == kVulkan_GrBackend);
-    GR_STATIC_ASSERT(3 == kMock_GrBackend);
-    writer.appendString("backend", kBackendStr[fBackend]);
-
-    writer.appendName("caps");
-    fCaps->dumpJSON(&writer);
-
-    writer.appendName("gpu");
-    fGpu->dumpJSON(&writer);
-
-    // Flush JSON to the memory stream
-    writer.endObject();
-    writer.flush();
-
-    // Null terminate the JSON data in the memory stream
-    stream.write8(0);
-
-    // Allocate a string big enough to hold all the data, then copy out of the stream
-    SkString result(stream.bytesWritten());
-    stream.copyToAndReset(result.writable_str());
-    return result;
-}
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 50a05c8..f237cce 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -26,7 +26,6 @@
 #include "GrSurfacePriv.h"
 #include "GrTexturePriv.h"
 #include "GrTracing.h"
-#include "SkJSONWriter.h"
 #include "SkMathPriv.h"
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -539,13 +538,3 @@
     return this->caps()->fenceSyncSupport() ? GrSemaphoresSubmitted::kYes
                                             : GrSemaphoresSubmitted::kNo;
 }
-
-void GrGpu::dumpJSON(SkJSONWriter* writer) const {
-    writer->beginObject();
-
-    // TODO: Is there anything useful in the base class to dump here?
-
-    this->onDumpJSON(writer);
-
-    writer->endObject();
-}
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index cc55a05..fa5f372 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -40,7 +40,6 @@
 class GrStencilSettings;
 class GrSurface;
 class GrTexture;
-class SkJSONWriter;
 
 namespace gr_instanced {
     class InstancedOp;
@@ -459,7 +458,6 @@
     };
 
     Stats* stats() { return &fStats; }
-    void dumpJSON(SkJSONWriter*) const;
 
     /** Creates a texture directly in the backend API without wrapping it in a GrTexture. This is
         only to be used for testing (particularly for testing the methods that import an externally
@@ -621,8 +619,6 @@
 
     virtual void onFinishFlush(bool insertedSemaphores) = 0;
 
-    virtual void onDumpJSON(SkJSONWriter*) const {}
-
     void resetContext() {
         this->onResetContext(fResetBits);
         fResetBits = 0;
diff --git a/src/gpu/GrShaderCaps.cpp b/src/gpu/GrShaderCaps.cpp
index 03495e9..5055b24 100644
--- a/src/gpu/GrShaderCaps.cpp
+++ b/src/gpu/GrShaderCaps.cpp
@@ -9,7 +9,6 @@
 #include "GrShaderCaps.h"
 
 #include "GrContextOptions.h"
-#include "SkJSONWriter.h"
 
 ////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -96,36 +95,33 @@
     fAdvBlendEqInteraction = kNotSupported_AdvBlendEqInteraction;
 }
 
-void GrShaderCaps::dumpJSON(SkJSONWriter* writer) const {
-    writer->beginObject();
+SkString GrShaderCaps::dump() const {
+    SkString r;
+    static const char* gNY[] = { "NO", "YES" };
+    r.appendf("Shader Derivative Support          : %s\n", gNY[fShaderDerivativeSupport]);
+    r.appendf("Geometry Shader Support            : %s\n", gNY[fGeometryShaderSupport]);
+    r.appendf("Path Rendering Support             : %s\n", gNY[fPathRenderingSupport]);
+    r.appendf("Dst Read In Shader Support         : %s\n", gNY[fDstReadInShaderSupport]);
+    r.appendf("Dual Source Blending Support       : %s\n", gNY[fDualSourceBlendingSupport]);
+    r.appendf("Integer Support                    : %s\n", gNY[fIntegerSupport]);
+    r.appendf("Texel Buffer Support               : %s\n", gNY[fTexelBufferSupport]);
+    r.appendf("Image Load Store Support           : %s\n", gNY[fImageLoadStoreSupport]);
 
-    writer->appendBool("Shader Derivative Support", fShaderDerivativeSupport);
-    writer->appendBool("Geometry Shader Support", fGeometryShaderSupport);
-    writer->appendBool("Path Rendering Support", fPathRenderingSupport);
-    writer->appendBool("Dst Read In Shader Support", fDstReadInShaderSupport);
-    writer->appendBool("Dual Source Blending Support", fDualSourceBlendingSupport);
-    writer->appendBool("Integer Support", fIntegerSupport);
-    writer->appendBool("Texel Buffer Support", fTexelBufferSupport);
-    writer->appendBool("Image Load Store Support", fImageLoadStoreSupport);
-
-    writer->appendBool("Variable Precision", fShaderPrecisionVaries);
+    r.appendf("Shader Float Precisions (varies: %s):\n", gNY[fShaderPrecisionVaries]);
 
     for (int s = 0; s < kGrShaderTypeCount; ++s) {
         GrShaderType shaderType = static_cast<GrShaderType>(s);
-        writer->beginArray(SkStringPrintf("%s precisions",
-                                          shader_type_to_string(shaderType)).c_str());
+        r.appendf("\t%s:\n", shader_type_to_string(shaderType));
         for (int p = 0; p < kGrSLPrecisionCount; ++p) {
             if (fFloatPrecisions[s][p].supported()) {
                 GrSLPrecision precision = static_cast<GrSLPrecision>(p);
-                writer->beginObject();
-                writer->appendString("precision", precision_to_string(precision));
-                writer->appendS32("log_low", fFloatPrecisions[s][p].fLogRangeLow);
-                writer->appendS32("log_high", fFloatPrecisions[s][p].fLogRangeHigh);
-                writer->appendS32("bits", fFloatPrecisions[s][p].fBits);
-                writer->endObject();
+                r.appendf("\t\t%s: log_low: %d log_high: %d bits: %d\n",
+                    precision_to_string(precision),
+                    fFloatPrecisions[s][p].fLogRangeLow,
+                    fFloatPrecisions[s][p].fLogRangeHigh,
+                    fFloatPrecisions[s][p].fBits);
             }
         }
-        writer->endArray();
     }
 
     static const char* kAdvBlendEqInteractionStr[] = {
@@ -140,41 +136,47 @@
     GR_STATIC_ASSERT(3 == kSpecificEnables_AdvBlendEqInteraction);
     GR_STATIC_ASSERT(SK_ARRAY_COUNT(kAdvBlendEqInteractionStr) == kLast_AdvBlendEqInteraction + 1);
 
-    writer->appendBool("FB Fetch Support", fFBFetchSupport);
-    writer->appendBool("Drops tile on zero divide", fDropsTileOnZeroDivide);
-    writer->appendBool("Bindless texture support", fBindlessTextureSupport);
-    writer->appendBool("Uses precision modifiers", fUsesPrecisionModifiers);
-    writer->appendBool("Can use any() function", fCanUseAnyFunctionInShader);
-    writer->appendBool("Can use min() and abs() together", fCanUseMinAndAbsTogether);
-    writer->appendBool("Can use fract() for negative values", fCanUseFractForNegativeValues);
-    writer->appendBool("Must force negated atan param to float", fMustForceNegatedAtanParamToFloat);
-    writer->appendBool("Must use local out color for FBFetch", fRequiresLocalOutputColorForFBFetch);
-    writer->appendBool("Must implement geo shader invocations with loop",
-                       fMustImplementGSInvocationsWithLoop);
-    writer->appendBool("Must obfuscate uniform color", fMustObfuscateUniformColor);
-    writer->appendBool("Must guard division even after explicit zero check",
-                       fMustGuardDivisionEvenAfterExplicitZeroCheck);
-    writer->appendBool("Flat interpolation support", fFlatInterpolationSupport);
-    writer->appendBool("No perspective interpolation support", fNoPerspectiveInterpolationSupport);
-    writer->appendBool("Multisample interpolation support", fMultisampleInterpolationSupport);
-    writer->appendBool("Sample variables support", fSampleVariablesSupport);
-    writer->appendBool("Sample mask override coverage support", fSampleMaskOverrideCoverageSupport);
-    writer->appendBool("External texture support", fExternalTextureSupport);
-    writer->appendBool("texelFetch support", fTexelFetchSupport);
-    writer->appendBool("sk_VertexID support", fVertexIDSupport);
+    r.appendf("--- GLSL-Specific ---\n");
 
-    writer->appendS32("Max VS Samplers", fMaxVertexSamplers);
-    writer->appendS32("Max GS Samplers", fMaxGeometrySamplers);
-    writer->appendS32("Max FS Samplers", fMaxFragmentSamplers);
-    writer->appendS32("Max Combined Samplers", fMaxFragmentSamplers);
-    writer->appendS32("Max VS Image Storages", fMaxVertexImageStorages);
-    writer->appendS32("Max GS Image Storages", fMaxGeometryImageStorages);
-    writer->appendS32("Max FS Image Storages", fMaxFragmentImageStorages);
-    writer->appendS32("Max Combined Image Storages", fMaxFragmentImageStorages);
-    writer->appendString("Advanced blend equation interaction",
-                         kAdvBlendEqInteractionStr[fAdvBlendEqInteraction]);
-
-    writer->endObject();
+    r.appendf("FB Fetch Support: %s\n", (fFBFetchSupport ? "YES" : "NO"));
+    r.appendf("Drops tile on zero divide: %s\n", (fDropsTileOnZeroDivide ? "YES" : "NO"));
+    r.appendf("Bindless texture support: %s\n", (fBindlessTextureSupport ? "YES" : "NO"));
+    r.appendf("Uses precision modifiers: %s\n", (fUsesPrecisionModifiers ? "YES" : "NO"));
+    r.appendf("Can use any() function: %s\n", (fCanUseAnyFunctionInShader ? "YES" : "NO"));
+    r.appendf("Can use min() and abs() together: %s\n", (fCanUseMinAndAbsTogether ? "YES" : "NO"));
+    r.appendf("Can use fract() for negative values: %s\n", (fCanUseFractForNegativeValues ?
+                                                            "YES" : "NO"));
+    r.appendf("Must force negated atan param to float: %s\n", (fMustForceNegatedAtanParamToFloat ?
+                                                               "YES" : "NO"));
+    r.appendf("Must use local out color for FBFetch: %s\n", (fRequiresLocalOutputColorForFBFetch ?
+                                                             "YES" : "NO"));
+    r.appendf("Must implement geo shader invocations with loop : %s\n",
+              (fMustImplementGSInvocationsWithLoop ? "YES" : "NO"));
+    r.appendf("Must obfuscate uniform color: %s\n", (fMustObfuscateUniformColor ? "YES" : "NO"));
+    r.appendf("Must guard division even after explicit zero check: %s\n",
+              (fMustGuardDivisionEvenAfterExplicitZeroCheck ? "YES" : "NO"));
+    r.appendf("Flat interpolation support: %s\n", (fFlatInterpolationSupport ?  "YES" : "NO"));
+    r.appendf("No perspective interpolation support: %s\n", (fNoPerspectiveInterpolationSupport ?
+                                                             "YES" : "NO"));
+    r.appendf("Multisample interpolation support: %s\n", (fMultisampleInterpolationSupport ?
+                                                          "YES" : "NO"));
+    r.appendf("Sample variables support: %s\n", (fSampleVariablesSupport ? "YES" : "NO"));
+    r.appendf("Sample mask override coverage support: %s\n", (fSampleMaskOverrideCoverageSupport ?
+                                                              "YES" : "NO"));
+    r.appendf("External texture support: %s\n", (fExternalTextureSupport ? "YES" : "NO"));
+    r.appendf("texelFetch support: %s\n", (fTexelFetchSupport ? "YES" : "NO"));
+    r.appendf("sk_VertexID support: %s\n", (fVertexIDSupport ? "YES" : "NO"));
+    r.appendf("Max VS Samplers: %d\n", fMaxVertexSamplers);
+    r.appendf("Max GS Samplers: %d\n", fMaxGeometrySamplers);
+    r.appendf("Max FS Samplers: %d\n", fMaxFragmentSamplers);
+    r.appendf("Max Combined Samplers: %d\n", fMaxFragmentSamplers);
+    r.appendf("Max VS Image Storages: %d\n", fMaxVertexImageStorages);
+    r.appendf("Max GS Image Storages: %d\n", fMaxGeometryImageStorages);
+    r.appendf("Max FS Image Storages: %d\n", fMaxFragmentImageStorages);
+    r.appendf("Max Combined Image Storages: %d\n", fMaxFragmentImageStorages);
+    r.appendf("Advanced blend equation interaction: %s\n",
+              kAdvBlendEqInteractionStr[fAdvBlendEqInteraction]);
+    return r;
 }
 
 void GrShaderCaps::initSamplerPrecisionTable() {
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index b90eefb..04559c3 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -12,7 +12,6 @@
 #include "GrGLTexture.h"
 #include "GrShaderCaps.h"
 #include "GrSurfaceProxyPriv.h"
-#include "SkJSONWriter.h"
 #include "SkTSearch.h"
 #include "SkTSort.h"
 #include "instanced/GLInstancedRendering.h"
@@ -1249,23 +1248,18 @@
     }
 }
 
-void GrGLCaps::onDumpJSON(SkJSONWriter* writer) const {
+SkString GrGLCaps::dump() const {
 
-    // We are called by the base class, which has already called beginObject(). We choose to nest
-    // all of our caps information in a named sub-object.
-    writer->beginObject("GL caps");
+    SkString r = INHERITED::dump();
 
-    writer->beginArray("Stencil Formats");
-
+    r.appendf("--- GL-Specific ---\n");
     for (int i = 0; i < fStencilFormats.count(); ++i) {
-        writer->beginObject();
-        writer->appendS32("stencil bits", fStencilFormats[i].fStencilBits);
-        writer->appendS32("total bits", fStencilFormats[i].fTotalBits);
-        writer->endObject();
+        r.appendf("Stencil Format %d, stencil bits: %02d, total bits: %02d\n",
+                 i,
+                 fStencilFormats[i].fStencilBits,
+                 fStencilFormats[i].fTotalBits);
     }
 
-    writer->endArray();
-
     static const char* kMSFBOExtStr[] = {
         "None",
         "Standard",
@@ -1304,59 +1298,55 @@
     GR_STATIC_ASSERT(3 == kChromium_MapBufferType);
     GR_STATIC_ASSERT(SK_ARRAY_COUNT(kMapBufferTypeStr) == kLast_MapBufferType + 1);
 
-    writer->appendBool("Core Profile", fIsCoreProfile);
-    writer->appendString("MSAA Type", kMSFBOExtStr[fMSFBOType]);
-    writer->appendString("Invalidate FB Type", kInvalidateFBTypeStr[fInvalidateFBType]);
-    writer->appendString("Map Buffer Type", kMapBufferTypeStr[fMapBufferType]);
-    writer->appendS32("Max FS Uniform Vectors", fMaxFragmentUniformVectors);
-    writer->appendBool("Unpack Row length support", fUnpackRowLengthSupport);
-    writer->appendBool("Unpack Flip Y support", fUnpackFlipYSupport);
-    writer->appendBool("Pack Row length support", fPackRowLengthSupport);
-    writer->appendBool("Pack Flip Y support", fPackFlipYSupport);
+    r.appendf("Core Profile: %s\n", (fIsCoreProfile ? "YES" : "NO"));
+    r.appendf("MSAA Type: %s\n", kMSFBOExtStr[fMSFBOType]);
+    r.appendf("Invalidate FB Type: %s\n", kInvalidateFBTypeStr[fInvalidateFBType]);
+    r.appendf("Map Buffer Type: %s\n", kMapBufferTypeStr[fMapBufferType]);
+    r.appendf("Max FS Uniform Vectors: %d\n", fMaxFragmentUniformVectors);
+    r.appendf("Unpack Row length support: %s\n", (fUnpackRowLengthSupport ? "YES": "NO"));
+    r.appendf("Unpack Flip Y support: %s\n", (fUnpackFlipYSupport ? "YES": "NO"));
+    r.appendf("Pack Row length support: %s\n", (fPackRowLengthSupport ? "YES": "NO"));
+    r.appendf("Pack Flip Y support: %s\n", (fPackFlipYSupport ? "YES": "NO"));
 
-    writer->appendBool("Texture Usage support", fTextureUsageSupport);
-    writer->appendBool("GL_R support", fTextureRedSupport);
-    writer->appendBool("Alpha8 is renderable", fAlpha8IsRenderable);
-    writer->appendBool("GL_ARB_imaging support", fImagingSupport);
-    writer->appendBool("Vertex array object support", fVertexArrayObjectSupport);
-    writer->appendBool("Direct state access support", fDirectStateAccessSupport);
-    writer->appendBool("Debug support", fDebugSupport);
-    writer->appendBool("Draw indirect support", fDrawIndirectSupport);
-    writer->appendBool("Multi draw indirect support", fMultiDrawIndirectSupport);
-    writer->appendBool("Base instance support", fBaseInstanceSupport);
-    writer->appendBool("RGBA 8888 pixel ops are slow", fRGBA8888PixelsOpsAreSlow);
-    writer->appendBool("Partial FBO read is slow", fPartialFBOReadIsSlow);
-    writer->appendBool("Bind uniform location support", fBindUniformLocationSupport);
-    writer->appendBool("Rectangle texture support", fRectangleTextureSupport);
-    writer->appendBool("Texture swizzle support", fTextureSwizzleSupport);
-    writer->appendBool("BGRA to RGBA readback conversions are slow",
-                       fRGBAToBGRAReadbackConversionsAreSlow);
-    writer->appendBool("Intermediate texture for partial updates of unorm textures ever bound to FBOs",
-                       fDisallowTexSubImageForUnormConfigTexturesEverBoundToFBO);
-    writer->appendBool("Intermediate texture for all updates of textures bound to FBOs",
-                       fUseDrawInsteadOfAllRenderTargetWrites);
+    r.appendf("Texture Usage support: %s\n", (fTextureUsageSupport ? "YES": "NO"));
+    r.appendf("GL_R support: %s\n", (fTextureRedSupport ? "YES": "NO"));
+    r.appendf("Alpha8 is renderable: %s\n", (fAlpha8IsRenderable ? "YES" : "NO"));
+    r.appendf("GL_ARB_imaging support: %s\n", (fImagingSupport ? "YES": "NO"));
+    r.appendf("Vertex array object support: %s\n", (fVertexArrayObjectSupport ? "YES": "NO"));
+    r.appendf("Direct state access support: %s\n", (fDirectStateAccessSupport ? "YES": "NO"));
+    r.appendf("Debug support: %s\n", (fDebugSupport ? "YES": "NO"));
+    r.appendf("Draw indirect support: %s\n", (fDrawIndirectSupport ? "YES" : "NO"));
+    r.appendf("Multi draw indirect support: %s\n", (fMultiDrawIndirectSupport ? "YES" : "NO"));
+    r.appendf("Base instance support: %s\n", (fBaseInstanceSupport ? "YES" : "NO"));
+    r.appendf("RGBA 8888 pixel ops are slow: %s\n", (fRGBA8888PixelsOpsAreSlow ? "YES" : "NO"));
+    r.appendf("Partial FBO read is slow: %s\n", (fPartialFBOReadIsSlow ? "YES" : "NO"));
+    r.appendf("Bind uniform location support: %s\n", (fBindUniformLocationSupport ? "YES" : "NO"));
+    r.appendf("Rectangle texture support: %s\n", (fRectangleTextureSupport? "YES" : "NO"));
+    r.appendf("Texture swizzle support: %s\n", (fTextureSwizzleSupport ? "YES" : "NO"));
+    r.appendf("BGRA to RGBA readback conversions are slow: %s\n",
+              (fRGBAToBGRAReadbackConversionsAreSlow ? "YES" : "NO"));
+    r.appendf("Intermediate texture for partial updates of unorm textures ever bound to FBOs: %s\n",
+              fDisallowTexSubImageForUnormConfigTexturesEverBoundToFBO ? "YES" : "NO");
+    r.appendf("Intermediate texture for all updates of textures bound to FBOs: %s\n",
+              fUseDrawInsteadOfAllRenderTargetWrites ? "YES" : "NO");
 
-    writer->beginArray("configs");
-
+    r.append("Configs\n-------\n");
     for (int i = 0; i < kGrPixelConfigCnt; ++i) {
-        writer->beginObject();
-        writer->appendHexU32("flags", fConfigTable[i].fFlags);
-        writer->appendHexU32("b_internal", fConfigTable[i].fFormats.fBaseInternalFormat);
-        writer->appendHexU32("s_internal", fConfigTable[i].fFormats.fSizedInternalFormat);
-        writer->appendHexU32("e_format",
-                             fConfigTable[i].fFormats.fExternalFormat[kOther_ExternalFormatUsage]);
-        writer->appendHexU32(
-                "e_format_teximage",
-                fConfigTable[i].fFormats.fExternalFormat[kTexImage_ExternalFormatUsage]);
-        writer->appendHexU32("e_type", fConfigTable[i].fFormats.fExternalType);
-        writer->appendHexU32("i_for_teximage", fConfigTable[i].fFormats.fInternalFormatTexImage);
-        writer->appendHexU32("i_for_renderbuffer",
-                             fConfigTable[i].fFormats.fInternalFormatRenderbuffer);
-        writer->endObject();
+        r.appendf("  cfg: %d flags: 0x%04x, b_internal: 0x%08x s_internal: 0x%08x, e_format: "
+                  "0x%08x, e_format_teximage: 0x%08x, e_type: 0x%08x, i_for_teximage: 0x%08x, "
+                  "i_for_renderbuffer: 0x%08x\n",
+                  i,
+                  fConfigTable[i].fFlags,
+                  fConfigTable[i].fFormats.fBaseInternalFormat,
+                  fConfigTable[i].fFormats.fSizedInternalFormat,
+                  fConfigTable[i].fFormats.fExternalFormat[kOther_ExternalFormatUsage],
+                  fConfigTable[i].fFormats.fExternalFormat[kTexImage_ExternalFormatUsage],
+                  fConfigTable[i].fFormats.fExternalType,
+                  fConfigTable[i].fFormats.fInternalFormatTexImage,
+                  fConfigTable[i].fFormats.fInternalFormatRenderbuffer);
     }
 
-    writer->endArray();
-    writer->endObject();
+    return r;
 }
 
 static GrGLenum precision_to_gl_float_type(GrSLPrecision p) {
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index 1ce6094..104308a 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -348,7 +348,10 @@
 
     bool srgbDecodeDisableAffectsMipmaps() const { return fSRGBDecodeDisableAffectsMipmaps; }
 
-    void onDumpJSON(SkJSONWriter*) const override;
+    /**
+     * Returns a string containing the caps info.
+     */
+    SkString dump() const override;
 
     bool rgba8888PixelsOpsAreSlow() const { return fRGBA8888PixelsOpsAreSlow; }
     bool partialFBOReadIsSlow() const { return fPartialFBOReadIsSlow; }
diff --git a/src/gpu/gl/GrGLExtensions.cpp b/src/gpu/gl/GrGLExtensions.cpp
index c4f6475..29f7799 100644
--- a/src/gpu/gl/GrGLExtensions.cpp
+++ b/src/gpu/gl/GrGLExtensions.cpp
@@ -9,7 +9,6 @@
 #include "gl/GrGLDefines.h"
 #include "gl/GrGLUtil.h"
 
-#include "SkJSONWriter.h"
 #include "SkMakeUnique.h"
 #include "SkTSearch.h"
 #include "SkTSort.h"
@@ -147,10 +146,12 @@
     }
 }
 
-void GrGLExtensions::dumpJSON(SkJSONWriter* writer) const {
-    writer->beginArray();
-    for (int i = 0; i < fStrings->count(); ++i) {
-        writer->appendString((*fStrings)[i].c_str());
+void GrGLExtensions::print(const char* sep) const {
+    if (nullptr == sep) {
+        sep = " ";
     }
-    writer->endArray();
+    int cnt = fStrings->count();
+    for (int i = 0; i < cnt; ++i) {
+        SkDebugf("%s%s", (*fStrings)[i].c_str(), (i < cnt - 1) ? sep : "");
+    }
 }
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 2ce2995..c20cdf0 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -27,7 +27,6 @@
 #include "GrTexturePriv.h"
 #include "GrTypes.h"
 #include "SkAutoMalloc.h"
-#include "SkJSONWriter.h"
 #include "SkMakeUnique.h"
 #include "SkMipMap.h"
 #include "SkPixmap.h"
@@ -209,6 +208,8 @@
     return nullptr;
 }
 
+static bool gPrintStartupSpew;
+
 GrGLGpu::GrGLGpu(GrGLContext* ctx, GrContext* context)
     : GrGpu(context)
     , fGLContext(ctx)
@@ -257,6 +258,26 @@
     }
 
     GrGLClearErr(this->glInterface());
+    if (gPrintStartupSpew) {
+        const GrGLubyte* vendor;
+        const GrGLubyte* renderer;
+        const GrGLubyte* version;
+        const GrGLubyte* glslVersion;
+        GL_CALL_RET(vendor, GetString(GR_GL_VENDOR));
+        GL_CALL_RET(renderer, GetString(GR_GL_RENDERER));
+        GL_CALL_RET(version, GetString(GR_GL_VERSION));
+        GL_CALL_RET(glslVersion, GetString(GR_GL_SHADING_LANGUAGE_VERSION));
+        SkDebugf("------------------------- create GrGLGpu %p --------------\n",
+                 this);
+        SkDebugf("------ VENDOR %s\n", vendor);
+        SkDebugf("------ RENDERER %s\n", renderer);
+        SkDebugf("------ VERSION %s\n",  version);
+        SkDebugf("------ SHADING LANGUAGE VERSION %s\n", glslVersion);
+        SkDebugf("------ EXTENSIONS\n");
+        this->glContext().extensions().print();
+        SkDebugf("\n");
+        SkDebugf("%s", this->glCaps().dump().c_str());
+    }
 }
 
 GrGLGpu::~GrGLGpu() {
@@ -4403,24 +4424,3 @@
             return 0;
     }
 }
-
-void GrGLGpu::onDumpJSON(SkJSONWriter* writer) const {
-    // We are called by the base class, which has already called beginObject(). We choose to nest
-    // all of our caps information in a named sub-object.
-    writer->beginObject("GL GPU");
-
-    const GrGLubyte* str;
-    GL_CALL_RET(str, GetString(GR_GL_VERSION));
-    writer->appendString("GL_VERSION", (const char*)(str));
-    GL_CALL_RET(str, GetString(GR_GL_RENDERER));
-    writer->appendString("GL_RENDERER", (const char*)(str));
-    GL_CALL_RET(str, GetString(GR_GL_VENDOR));
-    writer->appendString("GL_VENDOR", (const char*)(str));
-    GL_CALL_RET(str, GetString(GR_GL_SHADING_LANGUAGE_VERSION));
-    writer->appendString("GL_SHADING_LANGUAGE_VERSION", (const char*)(str));
-
-    writer->appendName("extensions");
-    glInterface()->fExtensions.dumpJSON(writer);
-
-    writer->endObject();
-}
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index aafcc37..7ff822f 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -409,8 +409,6 @@
     // Must be called if bindSurfaceFBOForPixelOps was used to bind a surface for copying.
     void unbindTextureFBOForPixelOps(GrGLenum fboTarget, GrSurface* surface);
 
-    void onDumpJSON(SkJSONWriter*) const override;
-
     sk_sp<GrGLContext>          fGLContext;
 
     bool createCopyProgram(GrTexture* srcTexture);
diff --git a/tests/GrContextFactoryTest.cpp b/tests/GrContextFactoryTest.cpp
index 098b2d6..d4d5667 100644
--- a/tests/GrContextFactoryTest.cpp
+++ b/tests/GrContextFactoryTest.cpp
@@ -139,10 +139,4 @@
     }
 }
 
-DEF_GPUTEST_FOR_ALL_CONTEXTS(GrContextDump, reporter, ctxInfo) {
-    // Ensure that GrContext::dump doesn't assert (which is possible, if the JSON code is wrong)
-    SkString result = ctxInfo.grContext()->dump();
-    REPORTER_ASSERT(reporter, !result.isEmpty());
-}
-
 #endif
diff --git a/tests/GrDrawTargetTest.cpp b/tests/GrDrawTargetTest.cpp
new file mode 100644
index 0000000..c8492bc
--- /dev/null
+++ b/tests/GrDrawTargetTest.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#if SK_SUPPORT_GPU
+
+#include "GrCaps.h"
+#include "GrContext.h"
+#include "GrGpu.h"
+
+DEF_GPUTEST_FOR_ALL_CONTEXTS(GrDrawTargetPrint, reporter, ctxInfo) {
+    // This used to assert.
+    SkString result = ctxInfo.grContext()->caps()->dump();
+    SkASSERT(!result.isEmpty());
+    SkString shaderResult = ctxInfo.grContext()->caps()->shaderCaps()->dump();
+    SkASSERT(!shaderResult.isEmpty());
+}
+
+#endif