Rebase gpu_dev up to r5182



git-svn-id: http://skia.googlecode.com/svn/branches/gpu_dev@6187 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/experimental/Intersection/Simplify.cpp b/experimental/Intersection/Simplify.cpp
index 3ecb164..d5b4d9e 100644
--- a/experimental/Intersection/Simplify.cpp
+++ b/experimental/Intersection/Simplify.cpp
@@ -897,14 +897,14 @@
         fEmpty = true;
         fDefer[0] = fDefer[1] = pt;
     }
-    
+
     void deferredMoveLine(const SkPoint& pt) {
         if (!fHasMove) {
             deferredMove(pt);
         }
         deferredLine(pt);
     }
-    
+
     bool hasMove() const {
         return fHasMove;
     }
@@ -918,7 +918,7 @@
     bool isClosed() const {
         return !fEmpty && fFirstPt == fDefer[1];
     }
-    
+
     void lineTo() {
         if (fDefer[0] == fDefer[1]) {
             return;
@@ -952,7 +952,7 @@
     bool changedSlopes(const SkPoint& pt) const {
         if (fDefer[0] == fDefer[1]) {
             return false;
-        } 
+        }
         SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
         SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
         SkScalar lineDx = pt.fX - fDefer[1].fX;
@@ -1205,7 +1205,7 @@
         init(pts, SkPath::kCubic_Verb, operand);
         fBounds.setCubicBounds(pts);
     }
-    
+
     /* SkPoint */ void addCurveTo(int start, int end, PathWrapper& path, bool active) const {
         SkPoint edge[4];
         const SkPoint* ePtr;
@@ -1876,7 +1876,7 @@
         SkASSERT(fDoneSpans <= fTs.count());
         return fDoneSpans == fTs.count();
     }
-    
+
     bool done(int min) const {
         return fTs[min].fDone;
     }
@@ -2250,7 +2250,7 @@
                 }
                 continue;
             }
-            
+
             if (!maxWinding && (!foundAngle || foundDone2)) {
         #if DEBUG_WINDING
                 if (foundAngle && foundDone2) {
@@ -2809,7 +2809,7 @@
         } while (++index < fTs.count() && approximately_negative(fTs[index].fT - referenceT));
     #endif
     }
-    
+
     void markOneDone(const char* funName, int tIndex, int winding) {
         Span* span = markOneWinding(funName, tIndex, winding);
         if (!span) {
@@ -3511,7 +3511,7 @@
         }
         return false;
     }
-    
+
     const SkPoint& end() const {
         const Segment& segment = fSegments.back();
         return segment.pts()[segment.verb()];
@@ -3635,7 +3635,7 @@
         }
         path.close();
     }
-    
+
     void toPartialBackward(PathWrapper& path) const {
         int segmentCount = fSegments.count();
         for (int test = segmentCount - 1; test >= 0; --test) {
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
index bf03214..54bbd8b 100644
--- a/gm/gmmain.cpp
+++ b/gm/gmmain.cpp
@@ -16,6 +16,7 @@
 #include "SkGraphics.h"
 #include "SkImageDecoder.h"
 #include "SkImageEncoder.h"
+#include "SkOSFile.h"
 #include "SkPicture.h"
 #include "SkRefCnt.h"
 #include "SkStream.h"
@@ -64,13 +65,6 @@
 const static ErrorBitfield ERROR_READING_REFERENCE_IMAGE = 0x08;
 const static ErrorBitfield ERROR_WRITING_REFERENCE_IMAGE = 0x10;
 
-// TODO: This should be defined as "\\" on Windows, but this is the way this
-// file has been working for a long time. We can fix it later.
-const static char* PATH_SEPARATOR = "/";
-
-// If true, emit a messange when we can't find a reference image to compare
-static bool gNotifyMissingReadReference;
-
 using namespace skiagm;
 
 class Iter {
@@ -156,9 +150,20 @@
 
 class GMMain {
 public:
-    static SkString make_name(const char shortName[], const char configName[]) {
-        SkString name(shortName);
-        name.appendf("_%s", configName);
+    GMMain() {
+        // Set default values of member variables, which tool_main()
+        // may override.
+        fNotifyMissingReadReference = true;
+        fUseFileHierarchy = false;
+    }
+
+    SkString make_name(const char shortName[], const char configName[]) {
+        SkString name;
+        if (fUseFileHierarchy) {
+            name.appendf("%s%c%s", configName, SkPATH_SEPARATOR, shortName);
+        } else {
+            name.appendf("%s_%s", shortName, configName);
+        }
         return name;
     }
 
@@ -167,12 +172,11 @@
                                   const SkString& name,
                                   const char suffix[]) {
         SkString filename(path);
-        if (filename.endsWith(PATH_SEPARATOR)) {
+        if (filename.endsWith(SkPATH_SEPARATOR)) {
             filename.remove(filename.size() - 1, 1);
         }
-        filename.append(pathSuffix);
-        filename.append(PATH_SEPARATOR);
-        filename.appendf("%s.%s", name.c_str(), suffix);
+        filename.appendf("%s%c%s.%s", pathSuffix, SkPATH_SEPARATOR,
+                         name.c_str(), suffix);
         return filename;
     }
 
@@ -489,7 +493,7 @@
     // Returns a description of the difference between "bitmap" and
     // the reference bitmap, or ERROR_READING_REFERENCE_IMAGE if
     // unable to read the reference bitmap from disk.
-    static ErrorBitfield compare_to_reference_image_on_disk(
+    ErrorBitfield compare_to_reference_image_on_disk(
       const char readPath [], const SkString& name, SkBitmap &bitmap,
       const char diffPath [], const char renderModeDescriptor []) {
         SkString path = make_filename(readPath, "", name, "png");
@@ -503,7 +507,7 @@
                                                         diffPath,
                                                         renderModeDescriptor);
         } else {
-            if (gNotifyMissingReadReference) {
+            if (fNotifyMissingReadReference) {
                 fprintf(stderr, "FAILED to read %s\n", path.c_str());
             }
             return ERROR_READING_REFERENCE_IMAGE;
@@ -515,15 +519,15 @@
     // both NULL (and thus no images are read from or written to disk).
     // So I don't trust that the renderModeDescriptor is being used for
     // anything other than debug output these days.
-    static ErrorBitfield handle_test_results(GM* gm,
-                                             const ConfigData& gRec,
-                                             const char writePath [],
-                                             const char readPath [],
-                                             const char diffPath [],
-                                             const char renderModeDescriptor [],
-                                             SkBitmap& bitmap,
-                                             SkDynamicMemoryWStream* pdf,
-                                             const SkBitmap* referenceBitmap) {
+    ErrorBitfield handle_test_results(GM* gm,
+                                      const ConfigData& gRec,
+                                      const char writePath [],
+                                      const char readPath [],
+                                      const char diffPath [],
+                                      const char renderModeDescriptor [],
+                                      SkBitmap& bitmap,
+                                      SkDynamicMemoryWStream* pdf,
+                                      const SkBitmap* referenceBitmap) {
         SkString name = make_name(gm->shortName(), gRec.fName);
         ErrorBitfield retval = ERROR_NONE;
 
@@ -580,14 +584,14 @@
     // Test: draw into a bitmap or pdf.
     // Depending on flags, possibly compare to an expected image
     // and possibly output a diff image if it fails to match.
-    static ErrorBitfield test_drawing(GM* gm,
-                                      const ConfigData& gRec,
-                                      const char writePath [],
-                                      const char readPath [],
-                                      const char diffPath [],
-                                      GrContext* context,
-                                      GrRenderTarget* rt,
-                                      SkBitmap* bitmap) {
+    ErrorBitfield test_drawing(GM* gm,
+                               const ConfigData& gRec,
+                               const char writePath [],
+                               const char readPath [],
+                               const char diffPath [],
+                               GrContext* context,
+                               GrRenderTarget* rt,
+                               SkBitmap* bitmap) {
         SkDynamicMemoryWStream document;
 
         if (gRec.fBackend == kRaster_Backend ||
@@ -612,12 +616,12 @@
                                    "", *bitmap, &document, NULL);
     }
 
-    static ErrorBitfield test_deferred_drawing(GM* gm,
-                                               const ConfigData& gRec,
-                                               const SkBitmap& referenceBitmap,
-                                               const char diffPath [],
-                                               GrContext* context,
-                                               GrRenderTarget* rt) {
+    ErrorBitfield test_deferred_drawing(GM* gm,
+                                        const ConfigData& gRec,
+                                        const SkBitmap& referenceBitmap,
+                                        const char diffPath [],
+                                        GrContext* context,
+                                        GrRenderTarget* rt) {
         SkDynamicMemoryWStream document;
 
         if (gRec.fBackend == kRaster_Backend ||
@@ -635,11 +639,11 @@
         return ERROR_NONE;
     }
 
-    static ErrorBitfield test_pipe_playback(GM* gm,
-                                            const ConfigData& gRec,
-                                            const SkBitmap& referenceBitmap,
-                                            const char readPath [],
-                                            const char diffPath []) {
+    ErrorBitfield test_pipe_playback(GM* gm,
+                                     const ConfigData& gRec,
+                                     const SkBitmap& referenceBitmap,
+                                     const char readPath [],
+                                     const char diffPath []) {
         ErrorBitfield errors = ERROR_NONE;
         for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) {
             SkBitmap bitmap;
@@ -664,7 +668,7 @@
         return errors;
     }
 
-    static ErrorBitfield test_tiled_pipe_playback(
+    ErrorBitfield test_tiled_pipe_playback(
       GM* gm, const ConfigData& gRec, const SkBitmap& referenceBitmap,
       const char readPath [], const char diffPath []) {
         ErrorBitfield errors = ERROR_NONE;
@@ -690,6 +694,17 @@
         }
         return errors;
     }
+
+    //
+    // member variables.
+    // They are public for now, to allow easier setting by tool_main().
+    //
+
+    // if true, emit a message when we can't find a reference image to compare
+    bool fNotifyMissingReadReference;
+
+    bool fUseFileHierarchy;
+
 }; // end of GMMain class definition
 
 #if SK_SUPPORT_GPU
@@ -734,8 +749,6 @@
 
 static void usage(const char * argv0) {
     SkDebugf("%s\n", argv0);
-    SkDebugf("    [-w writePath] [-r readPath] [-d diffPath] [-i resourcePath]\n");
-    SkDebugf("    [-wp writePicturePath]\n");
     SkDebugf("    [--config ");
     for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
         if (i > 0) {
@@ -743,30 +756,37 @@
         }
         SkDebugf(gRec[i].fName);
     }
-    SkDebugf(" ]\n");
-    SkDebugf("    [--noreplay] [--nopipe] [--noserialize] [--forceBWtext] [--nopdf] \n"
-             "    [--tiledPipe] \n"
-             "    [--nodeferred] [--match substring] [--notexturecache]\n"
-             "    [-h|--help]\n"
+    SkDebugf("]:\n        run these configurations\n");
+    SkDebugf(
+// Alphabetized ignoring "no" prefix ("readPath", "noreplay", "resourcePath").
+// It would probably be better if we allowed both yes-and-no settings for each
+// one, e.g.:
+// [--replay|--noreplay]: whether to exercise SkPicture replay; default is yes
+"    [--nodeferred]: skip the deferred rendering test pass\n"
+"    [--diffPath|-d <path>]: write difference images into this directory\n"
+"    [--disable-missing-warning]: don't print a message to stderr if\n"
+"        unable to read a reference image for any tests (NOT default behavior)\n"
+"    [--enable-missing-warning]: print message to stderr (but don't fail) if\n"
+"        unable to read a reference image for any tests (default behavior)\n"
+"    [--forceBWtext]: disable text anti-aliasing\n"
+"    [--help|-h]: show this help message\n"
+"    [--hierarchy|--nohierarchy]: whether to use multilevel directory structure\n"
+"        when reading/writing files; default is no\n"
+"    [--match <substring>]: only run tests whose name includes this substring\n"
+"    [--modulo <remainder> <divisor>]: only run tests for which \n"
+"        testIndex %% divisor == remainder\n"
+"    [--nopdf]: skip the pdf rendering test pass\n"
+"    [--nopipe]: Skip SkGPipe replay\n"
+"    [--readPath|-r <path>]: read reference images from this dir, and report\n"
+"        any differences between those and the newly generated ones\n"
+"    [--noreplay]: do not exercise SkPicture replay\n"
+"    [--resourcePath|-i <path>]: directory that stores image resources\n"
+"    [--noserialize]: do not exercise SkPicture serialization & deserialization\n"
+"    [--notexturecache]: disable the gpu texture cache\n"
+"    [--tiledPipe]: Exercise tiled SkGPipe replay\n"
+"    [--writePath|-w <path>]: write rendered images into this directory\n"
+"    [--writePicturePath|-wp <path>]: write .skp files into this directory\n"
              );
-    SkDebugf("    writePath: directory to write rendered images in.\n");
-    SkDebugf("    writePicturePath: directory to write images to in .skp format.\n");
-    SkDebugf(
-             "    readPath: directory to read reference images from;\n"
-             "        reports if any pixels mismatch between reference and new images\n");
-    SkDebugf("    diffPath: directory to write difference images in.\n");
-    SkDebugf("    resourcePath: directory that stores image resources.\n");
-    SkDebugf("    --noreplay: do not exercise SkPicture replay.\n");
-    SkDebugf("    --nopipe: Skip SkGPipe replay.\n");
-    SkDebugf("    --tiledPipe: Exercise tiled SkGPipe replay.\n");
-    SkDebugf(
-             "    --noserialize: do not exercise SkPicture serialization & deserialization.\n");
-    SkDebugf("    --forceBWtext: disable text anti-aliasing.\n");
-    SkDebugf("    --nopdf: skip the pdf rendering test pass.\n");
-    SkDebugf("    --nodeferred: skip the deferred rendering test pass.\n");
-    SkDebugf("    --match foo: will only run tests that substring match foo.\n");
-    SkDebugf("    --notexturecache: disable the gpu texture cache.\n");
-    SkDebugf("    -h|--help : Show this help message. \n");
 }
 
 static int findConfig(const char config[]) {
@@ -869,82 +889,13 @@
     SkTDArray<size_t> configs;
     bool userConfig = false;
 
-    int moduloIndex = -1;
-    int moduloCount = -1;
-
-    gNotifyMissingReadReference = true;
+    int moduloRemainder = -1;
+    int moduloDivisor = -1;
 
     const char* const commandName = argv[0];
     char* const* stop = argv + argc;
     for (++argv; argv < stop; ++argv) {
-        if (strcmp(*argv, "-w") == 0) {
-            argv++;
-            if (argv < stop && **argv) {
-                writePath = *argv;
-            }
-        } else if (strcmp(*argv, "-wp") == 0) {
-            argv++;
-            if (argv < stop && **argv) {
-                writePicturePath = *argv;
-            }
-        } else if (strcmp(*argv, "-r") == 0) {
-            argv++;
-            if (argv < stop && **argv) {
-                readPath = *argv;
-            }
-        } else if (strcmp(*argv, "-d") == 0) {
-            argv++;
-            if (argv < stop && **argv) {
-                diffPath = *argv;
-            }
-        } else if (strcmp(*argv, "-i") == 0) {
-            argv++;
-            if (argv < stop && **argv) {
-                resourcePath = *argv;
-            }
-        } else if (strcmp(*argv, "--forceBWtext") == 0) {
-            gForceBWtext = true;
-        } else if (strcmp(*argv, "--nopipe") == 0) {
-            doPipe = false;
-        } else if (strcmp(*argv, "--tiledPipe") == 0) {
-            doTiledPipe = true;
-        } else if (strcmp(*argv, "--noreplay") == 0) {
-            doReplay = false;
-        } else if (strcmp(*argv, "--nopdf") == 0) {
-            doPDF = false;
-        } else if (strcmp(*argv, "--nodeferred") == 0) {
-            doDeferred = false;
-        } else if (strcmp(*argv, "--modulo") == 0) {
-            ++argv;
-            if (argv >= stop) {
-                continue;
-            }
-            moduloIndex = atoi(*argv);
-
-            ++argv;
-            if (argv >= stop) {
-                continue;
-            }
-            moduloCount = atoi(*argv);
-        } else if (strcmp(*argv, "--disable-missing-warning") == 0) {
-            gNotifyMissingReadReference = false;
-        } else if (strcmp(*argv, "--enable-missing-warning") == 0) {
-            gNotifyMissingReadReference = true;
-        } else if (strcmp(*argv, "--serialize") == 0) {
-            // Leaving in this option so that a user need not modify
-            // their command line arguments to still run.
-            doSerialize = true;
-        } else if (strcmp(*argv, "--noserialize") == 0) {
-            doSerialize = false;
-        } else if (strcmp(*argv, "--match") == 0) {
-            ++argv;
-            if (argv < stop && **argv) {
-                // just record the ptr, no need for a deep copy
-                *fMatches.append() = *argv;
-            }
-        } else if (strcmp(*argv, "--notexturecache") == 0) {
-            disableTextureCache = true;
-        } else if (strcmp(*argv, "--config") == 0) {
+        if (strcmp(*argv, "--config") == 0) {
             argv++;
             if (argv < stop) {
                 int index = findConfig(*argv);
@@ -963,9 +914,83 @@
                 usage(commandName);
                 return -1;
             }
+        } else if (strcmp(*argv, "--nodeferred") == 0) {
+            doDeferred = false;
+        } else if ((0 == strcmp(*argv, "--diffPath")) ||
+                   (0 == strcmp(*argv, "-d"))) {
+            argv++;
+            if (argv < stop && **argv) {
+                diffPath = *argv;
+            }
+        } else if (strcmp(*argv, "--disable-missing-warning") == 0) {
+            gmmain.fNotifyMissingReadReference = false;
+        } else if (strcmp(*argv, "--enable-missing-warning") == 0) {
+            gmmain.fNotifyMissingReadReference = true;
+        } else if (strcmp(*argv, "--forceBWtext") == 0) {
+            gForceBWtext = true;
         } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) {
             usage(commandName);
             return -1;
+        } else if (strcmp(*argv, "--hierarchy") == 0) {
+            gmmain.fUseFileHierarchy = true;
+        } else if (strcmp(*argv, "--nohierarchy") == 0) {
+            gmmain.fUseFileHierarchy = false;
+        } else if (strcmp(*argv, "--match") == 0) {
+            ++argv;
+            if (argv < stop && **argv) {
+                // just record the ptr, no need for a deep copy
+                *fMatches.append() = *argv;
+            }
+        } else if (strcmp(*argv, "--modulo") == 0) {
+            ++argv;
+            if (argv >= stop) {
+                continue;
+            }
+            moduloRemainder = atoi(*argv);
+
+            ++argv;
+            if (argv >= stop) {
+                continue;
+            }
+            moduloDivisor = atoi(*argv);
+        } else if (strcmp(*argv, "--nopdf") == 0) {
+            doPDF = false;
+        } else if (strcmp(*argv, "--nopipe") == 0) {
+            doPipe = false;
+        } else if ((0 == strcmp(*argv, "--readPath")) ||
+                   (0 == strcmp(*argv, "-r"))) {
+            argv++;
+            if (argv < stop && **argv) {
+                readPath = *argv;
+            }
+        } else if (strcmp(*argv, "--noreplay") == 0) {
+            doReplay = false;
+        } else if ((0 == strcmp(*argv, "--resourcePath")) ||
+                   (0 == strcmp(*argv, "-i"))) {
+            argv++;
+            if (argv < stop && **argv) {
+                resourcePath = *argv;
+            }
+        } else if (strcmp(*argv, "--serialize") == 0) {
+            doSerialize = true;
+        } else if (strcmp(*argv, "--noserialize") == 0) {
+            doSerialize = false;
+        } else if (strcmp(*argv, "--notexturecache") == 0) {
+            disableTextureCache = true;
+        } else if (strcmp(*argv, "--tiledPipe") == 0) {
+            doTiledPipe = true;
+        } else if ((0 == strcmp(*argv, "--writePath")) ||
+            (0 == strcmp(*argv, "-w"))) {
+            argv++;
+            if (argv < stop && **argv) {
+                writePath = *argv;
+            }
+        } else if ((0 == strcmp(*argv, "--writePicturePath")) ||
+                   (0 == strcmp(*argv, "-wp"))) {
+            argv++;
+            if (argv < stop && **argv) {
+                writePicturePath = *argv;
+            }
         } else {
             usage(commandName);
             return -1;
@@ -998,11 +1023,11 @@
         fprintf(stderr, "reading resources from %s\n", resourcePath);
     }
 
-    if (moduloCount <= 0) {
-        moduloIndex = -1;
+    if (moduloDivisor <= 0) {
+        moduloRemainder = -1;
     }
-    if (moduloIndex < 0 || moduloIndex >= moduloCount) {
-        moduloIndex = -1;
+    if (moduloRemainder < 0 || moduloRemainder >= moduloDivisor) {
+        moduloRemainder = -1;
     }
 
     // Accumulate success of all tests.
@@ -1023,16 +1048,34 @@
     int gmIndex = -1;
     SkString moduloStr;
 
+    // If we will be writing out files, prepare subdirectories.
+    if (writePath) {
+        if (!sk_mkdir(writePath)) {
+            return -1;
+        }
+        if (gmmain.fUseFileHierarchy) {
+            for (int i = 0; i < configs.count(); i++) {
+                ConfigData config = gRec[configs[i]];
+                SkString subdir;
+                subdir.appendf("%s%c%s", writePath, SkPATH_SEPARATOR,
+                               config.fName);
+                if (!sk_mkdir(subdir.c_str())) {
+                    return -1;
+                }
+            }
+        }
+    }
+
     Iter iter;
     GM* gm;
     while ((gm = iter.next()) != NULL) {
 
         ++gmIndex;
-        if (moduloIndex >= 0) {
-            if ((gmIndex % moduloCount) != moduloIndex) {
+        if (moduloRemainder >= 0) {
+            if ((gmIndex % moduloDivisor) != moduloRemainder) {
                 continue;
             }
-            moduloStr.printf("[%d.%d] ", gmIndex, moduloCount);
+            moduloStr.printf("[%d.%d] ", gmIndex, moduloDivisor);
         }
 
         const char* shortName = gm->shortName();
@@ -1050,6 +1093,7 @@
 
         for (int i = 0; i < configs.count(); i++) {
             ConfigData config = gRec[configs[i]];
+
             // Skip any tests that we don't even need to try.
             if ((kPDF_Backend == config.fBackend) &&
                 (!doPDF || (gmFlags & GM::kSkipPDF_Flag)))
diff --git a/gm/lighting.cpp b/gm/lighting.cpp
index 46474ab..9db34fd 100644
--- a/gm/lighting.cpp
+++ b/gm/lighting.cpp
@@ -47,6 +47,18 @@
             make_bitmap();
             fInitialized = true;
         }
+        canvas->clear(0xFF101010);
+        SkPaint checkPaint;
+        checkPaint.setColor(0xFF202020);
+        for (int y = 0; y < HEIGHT; y += 16) {
+          for (int x = 0; x < WIDTH; x += 16) {
+            canvas->save();
+            canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
+            canvas->drawRect(SkRect::MakeXYWH(8, 0, 8, 8), checkPaint);
+            canvas->drawRect(SkRect::MakeXYWH(0, 8, 8, 8), checkPaint);
+            canvas->restore();
+          }
+        }
         SkPoint3 pointLocation(0, 0, SkIntToScalar(10));
         SkScalar azimuthRad = SkDegreesToRadians(SkIntToScalar(225));
         SkScalar elevationRad = SkDegreesToRadians(SkIntToScalar(5));
diff --git a/gyp/common_variables.gypi b/gyp/common_variables.gypi
index 3d19e62..6acf5bc 100644
--- a/gyp/common_variables.gypi
+++ b/gyp/common_variables.gypi
@@ -81,6 +81,7 @@
       'skia_scalar%': 'float',
       'skia_mesa%': 0,
       'skia_nv_path_rendering%': 0,
+      'skia_texture_cache_mb_limit%': 0,
       'skia_angle%': 0,
       'skia_directwrite%': 0,
       'skia_nacl%': 0,
@@ -97,6 +98,7 @@
     'skia_scalar%': '<(skia_scalar)',
     'skia_mesa%': '<(skia_mesa)',
     'skia_nv_path_rendering%': '<(skia_nv_path_rendering)',
+    'skia_texture_cache_mb_limit%': '<(skia_texture_cache_mb_limit)',
     'skia_angle%': '<(skia_angle)',
     'skia_arch_width%': '<(skia_arch_width)',
     'skia_arch_type%': '<(skia_arch_type)',
diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp
index 0dc84ee..9cb0698 100644
--- a/gyp/gpu.gyp
+++ b/gyp/gpu.gyp
@@ -71,6 +71,11 @@
           ],
         },
       }],
+      [ 'skia_texture_cache_mb_limit != 0', {
+        'defines': [
+          'GR_DEFAULT_TEXTURE_CACHE_MB_LIMIT=<(skia_texture_cache_mb_limit)',
+        ],
+      }],
     ],
     'direct_dependent_settings': {
       'conditions': [
diff --git a/include/core/SkOSFile.h b/include/core/SkOSFile.h
index b5477cd..79551ae 100644
--- a/include/core/SkOSFile.h
+++ b/include/core/SkOSFile.h
@@ -7,7 +7,8 @@
  */
 
 
-//
+// TODO: add unittests for all these operations
+
 #ifndef SkOSFile_DEFINED
 #define SkOSFile_DEFINED
 
@@ -24,6 +25,12 @@
     kWrite_SkFILE_Flag  = 0x02
 };
 
+#ifdef _WIN32
+const static char SkPATH_SEPARATOR = '\\';
+#else
+const static char SkPATH_SEPARATOR = '/';
+#endif
+
 SkFILE* sk_fopen(const char path[], SkFILE_Flags);
 void    sk_fclose(SkFILE*);
 
@@ -39,6 +46,17 @@
 int     sk_fseek( SkFILE*, size_t, int );
 size_t  sk_ftell( SkFILE* );
 
+// Returns true if something (file, directory, ???) exists at this path.
+bool    sk_exists(const char *path);
+
+// Returns true if a directory exists at this path.
+bool    sk_isdir(const char *path);
+
+// Create a new directory at this path; returns true if successful.
+// If the directory already existed, this will return true.
+// Description of the error, if any, will be written to stderr.
+bool    sk_mkdir(const char* path);
+
 class SkOSFile {
 public:
     class Iter {
@@ -79,4 +97,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkString.h b/include/core/SkString.h
index 20f16c4..94dcf8b 100644
--- a/include/core/SkString.h
+++ b/include/core/SkString.h
@@ -15,18 +15,30 @@
 /*  Some helper functions for C strings
 */
 
-static bool SkStrStartsWith(const char string[], const char prefix[]) {
+static bool SkStrStartsWith(const char string[], const char prefixStr[]) {
     SkASSERT(string);
-    SkASSERT(prefix);
-    return !strncmp(string, prefix, strlen(prefix));
+    SkASSERT(prefixStr);
+    return !strncmp(string, prefixStr, strlen(prefixStr));
 }
-bool SkStrEndsWith(const char string[], const char suffix[]);
+static bool SkStrStartsWith(const char string[], const char prefixChar) {
+    SkASSERT(string);
+    return (prefixChar == *string);
+}
+
+bool SkStrEndsWith(const char string[], const char suffixStr[]);
+bool SkStrEndsWith(const char string[], const char suffixChar);
+
 int SkStrStartsWithOneOf(const char string[], const char prefixes[]);
+
 static bool SkStrContains(const char string[], const char substring[]) {
     SkASSERT(string);
     SkASSERT(substring);
     return (NULL != strstr(string, substring));
 }
+static bool SkStrContains(const char string[], const char subchar) {
+    SkASSERT(string);
+    return (NULL != strchr(string, subchar));
+}
 
 #define SkStrAppendS32_MaxSize  11
 char*   SkStrAppendS32(char buffer[], int32_t);
@@ -82,15 +94,24 @@
     bool equals(const char text[]) const;
     bool equals(const char text[], size_t len) const;
 
-    bool startsWith(const char prefix[]) const {
-        return SkStrStartsWith(fRec->data(), prefix);
+    bool startsWith(const char prefixStr[]) const {
+        return SkStrStartsWith(fRec->data(), prefixStr);
     }
-    bool endsWith(const char suffix[]) const {
-        return SkStrEndsWith(fRec->data(), suffix);
+    bool startsWith(const char prefixChar) const {
+        return SkStrStartsWith(fRec->data(), prefixChar);
+    }
+    bool endsWith(const char suffixStr[]) const {
+        return SkStrEndsWith(fRec->data(), suffixStr);
+    }
+    bool endsWith(const char suffixChar) const {
+        return SkStrEndsWith(fRec->data(), suffixChar);
     }
     bool contains(const char substring[]) const {
         return SkStrContains(fRec->data(), substring);
     }
+    bool contains(const char subchar) const {
+        return SkStrContains(fRec->data(), subchar);
+    }
 
     friend bool operator==(const SkString& a, const SkString& b) {
         return a.equals(b);
diff --git a/include/gpu/GrConfig.h b/include/gpu/GrConfig.h
index c61c34e..66d1c97 100644
--- a/include/gpu/GrConfig.h
+++ b/include/gpu/GrConfig.h
@@ -369,6 +369,15 @@
     #define GR_GEOM_BUFFER_LOCK_THRESHOLD (1 << 15)
 #endif
 
+/**
+ * GR_DEFAULT_TEXTURE_CACHE_MB_LIMIT gives a threshold (in megabytes) for the
+ * maximum size of the texture cache in vram. The value is only a default and
+ * can be overridden at runtime.
+ */
+#if !defined(GR_DEFAULT_TEXTURE_CACHE_MB_LIMIT)
+    #define GR_DEFAULT_TEXTURE_CACHE_MB_LIMIT 96
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 // tail section:
 //
diff --git a/include/gpu/GrUserConfig.h b/include/gpu/GrUserConfig.h
index d514486..7b4e4bb 100644
--- a/include/gpu/GrUserConfig.h
+++ b/include/gpu/GrUserConfig.h
@@ -54,6 +54,12 @@
  */
 //#define GR_GEOM_BUFFER_LOCK_THRESHOLD (1<<15)
 
+/**
+ * This gives a threshold in megabytes for the maximum size of the texture cache
+ * in vram. The value is only a default and can be overridden at runtime.
+ */
+//#define GR_DEFAULT_TEXTURE_CACHE_MB_LIMIT 96
+
 ///////////////////////////////////////////////////////////////////////////////
 // Decide Ganesh types
 
diff --git a/src/core/SkString.cpp b/src/core/SkString.cpp
index fee614c..5d1b8ce 100644
--- a/src/core/SkString.cpp
+++ b/src/core/SkString.cpp
@@ -36,13 +36,23 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool SkStrEndsWith(const char string[], const char suffix[]) {
+bool SkStrEndsWith(const char string[], const char suffixStr[]) {
     SkASSERT(string);
-    SkASSERT(suffix);
+    SkASSERT(suffixStr);
     size_t  strLen = strlen(string);
-    size_t  suffixLen = strlen(suffix);
+    size_t  suffixLen = strlen(suffixStr);
     return  strLen >= suffixLen &&
-            !strncmp(string + strLen - suffixLen, suffix, suffixLen);
+            !strncmp(string + strLen - suffixLen, suffixStr, suffixLen);
+}
+
+bool SkStrEndsWith(const char string[], const char suffixChar) {
+    SkASSERT(string);
+    size_t  strLen = strlen(string);
+    if (0 == strLen) {
+        return false;
+    } else {
+        return (suffixChar == string[strLen-1]);
+    }
 }
 
 int SkStrStartsWithOneOf(const char string[], const char prefixes[]) {
@@ -602,4 +612,3 @@
 
 #undef VSNPRINTF
 #undef SNPRINTF
-
diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp
index 2f403c1..82e1f22 100644
--- a/src/effects/SkLightingImageFilter.cpp
+++ b/src/effects/SkLightingImageFilter.cpp
@@ -1282,7 +1282,8 @@
     SkString lightBody;
     lightBody.appendf("\tvec3 halfDir = vec3(normalize(surfaceToLight + vec3(0, 0, 1)));\n");
     lightBody.appendf("\tfloat colorScale = %s * pow(dot(normal, halfDir), %s);\n", ks, shininess);
-    lightBody.appendf("\treturn vec4(lightColor * clamp(colorScale, 0.0, 1.0), 1.0);\n");
+    lightBody.appendf("\tvec3 color = lightColor * clamp(colorScale, 0.0, 1.0);\n");
+    lightBody.appendf("\treturn vec4(color, max(max(color.r, color.g), color.b));\n");
     builder->emitFunction(GrGLShaderBuilder::kFragment_ShaderType,
                           kVec4f_GrSLType,
                           "light",
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index b2733c2..900af8a 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -49,7 +49,7 @@
 #endif
 
 static const size_t MAX_TEXTURE_CACHE_COUNT = 2048;
-static const size_t MAX_TEXTURE_CACHE_BYTES = 96 * 1024 * 1024;
+static const size_t MAX_TEXTURE_CACHE_BYTES = GR_DEFAULT_TEXTURE_CACHE_MB_LIMIT * 1024 * 1024;
 
 static const size_t DRAW_BUFFER_VBPOOL_BUFFER_SIZE = 1 << 15;
 static const int DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS = 4;
diff --git a/src/ports/SkOSFile_stdio.cpp b/src/ports/SkOSFile_stdio.cpp
index 1254394..6c825b4 100644
--- a/src/ports/SkOSFile_stdio.cpp
+++ b/src/ports/SkOSFile_stdio.cpp
@@ -9,8 +9,17 @@
 
 #include "SkOSFile.h"
 
-#include <stdio.h>
 #include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef _WIN32
+#include <direct.h>
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
 
 SkFILE* sk_fopen(const char path[], SkFILE_Flags flags)
 {
@@ -98,3 +107,46 @@
     ::fclose((FILE*)f);
 }
 
+bool sk_exists(const char *path)
+{
+#ifdef _WIN32
+    return (0 == _access(path, 0));
+#else
+    return (0 == access(path, 0));
+#endif
+}
+
+bool sk_isdir(const char *path)
+{
+    struct stat status;
+    if (0 != stat(path, &status)) {
+        return false;
+    }
+    return (status.st_mode & S_IFDIR);
+}
+
+bool sk_mkdir(const char* path)
+{
+    if (sk_isdir(path)) {
+        return true;
+    }
+    if (sk_exists(path)) {
+        fprintf(stderr,
+                "sk_mkdir: path '%s' already exists but is not a directory\n",
+                path);
+        return false;
+    }
+
+    int retval;
+#ifdef _WIN32
+    retval = _mkdir(path);
+#else
+    retval = mkdir(path, 0777);
+#endif
+    if (0 == retval) {
+        return true;
+    } else {
+        fprintf(stderr, "sk_mkdir: error %d creating dir '%s'\n", errno, path);
+        return false;
+    }
+}
diff --git a/tests/StringTest.cpp b/tests/StringTest.cpp
index 5ae718f..2c5026c 100644
--- a/tests/StringTest.cpp
+++ b/tests/StringTest.cpp
@@ -57,10 +57,14 @@
     REPORTER_ASSERT(reporter, !a.equals("help"));
 
     REPORTER_ASSERT(reporter,  a.startsWith("hell"));
+    REPORTER_ASSERT(reporter,  a.startsWith('h'));
     REPORTER_ASSERT(reporter, !a.startsWith( "ell"));
+    REPORTER_ASSERT(reporter, !a.startsWith( 'e'));
     REPORTER_ASSERT(reporter,  a.startsWith(""));
     REPORTER_ASSERT(reporter,  a.endsWith("llo"));
+    REPORTER_ASSERT(reporter,  a.endsWith('o'));
     REPORTER_ASSERT(reporter, !a.endsWith("ll" ));
+    REPORTER_ASSERT(reporter, !a.endsWith('l'));
     REPORTER_ASSERT(reporter,  a.endsWith(""));
     REPORTER_ASSERT(reporter,  a.contains("he"));
     REPORTER_ASSERT(reporter,  a.contains("ll"));
@@ -68,6 +72,8 @@
     REPORTER_ASSERT(reporter,  a.contains("hello"));
     REPORTER_ASSERT(reporter, !a.contains("hellohello"));
     REPORTER_ASSERT(reporter,  a.contains(""));
+    REPORTER_ASSERT(reporter,  a.contains('e'));
+    REPORTER_ASSERT(reporter, !a.contains('z'));
 
     SkString    e(a);
     SkString    f("hello");
diff --git a/tools/rebaseline.py b/tools/rebaseline.py
new file mode 100755
index 0000000..f069108
--- /dev/null
+++ b/tools/rebaseline.py
@@ -0,0 +1,64 @@
+#!/usr/bin/python
+
+'''
+Copyright 2012 Google Inc.
+
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+'''
+
+'''
+Rebaselines a single GM test, on all bots and all configurations.
+Must be run from an SVN checkout of the gm-expected directory.
+'''
+
+import os, subprocess, sys, tempfile
+
+pairs = [ 
+   ['base-shuttle-win7-intel-float',
+    'Skia_Shuttle_Win7_Intel_Float_Release_32'],
+#    ['base-shuttle-win7-intel-angle',
+#     'Skia_Shuttle_Win7_Intel_Float_ANGLE_Release_32'],
+#    ['base-shuttle-win7-intel-directwrite',
+#     'Skia_Shuttle_Win7_Intel_Float_DirectWrite_Release_32'],
+   ['base-shuttle_ubuntu12_ati5770',
+    'Skia_Shuttle_Ubuntu12_ATI5770_Float_Release_64'],
+   ['base-macmini',
+    'Skia_Mac_Float_Release_32'],
+   ['base-macmini-lion-float',
+    'Skia_MacMiniLion_Float_Release_32'],
+   ['base-android-galaxy-nexus',
+    'Skia_GalaxyNexus_4-1_Float_Release_32'],
+   ['base-android-nexus-7',
+    'Skia_Nexus7_4-1_Float_Release_32'],
+   ['base-android-nexus-s',
+    'Skia_NexusS_4-1_Float_Release_32'],
+   ['base-android-xoom',
+    'Skia_Xoom_4-1_Float_Release_32'],
+]
+
+if len(sys.argv) != 2:
+    print 'Usage:  ' + os.path.basename(sys.argv[0]) + ' <testname>'
+    exit(1)
+
+testname = sys.argv[1]
+testtypes = [ '4444', '565', '8888', 'gpu', 'pdf' ]
+
+for pair in pairs:
+    print pair[0] + ':'
+    for testtype in testtypes:
+        infilename = testname + '_' + testtype + '.png'
+        print infilename
+
+        url = 'http://skia-autogen.googlecode.com/svn/gm-actual/' + pair[0] + '/' + pair[1] + '/' + pair[0] + '/' + infilename
+        cmd = [ 'curl', '--fail', '--silent', url ]
+        temp = tempfile.NamedTemporaryFile()
+        ret = subprocess.call(cmd, stdout=temp)
+        if ret != 0:
+            print 'Couldn\'t fetch ' + url
+            continue
+        outfilename = os.path.join(pair[0], infilename);
+        cmd = [ 'cp', temp.name, outfilename ]
+        subprocess.call(cmd);
+        cmd = [ 'svn', 'add', '--quiet', outfilename ]
+        subprocess.call(cmd)