Improve null gpu's memory handling

https://codereview.chromium.org/19678010/



git-svn-id: http://skia.googlecode.com/svn/trunk@10148 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
index 0cf7a18..98525a5 100644
--- a/gm/gmmain.cpp
+++ b/gm/gmmain.cpp
@@ -1258,6 +1258,11 @@
     /* The gpudebug context does not generate meaningful images, so don't record
      * the images it generates!  We only run it to look for asserts. */
     { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kDebug_GLContextType,   0, kNone_ConfigFlag,  "gpudebug",     GR_DEBUG},
+    /* The gpunull context does the least amount of work possible and doesn't
+       generate meaninful images, so don't record them!. It can be run to
+       isolate the CPU-side processing expense from the GPU-side.
+      */
+    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kNull_GLContextType,    0, kNone_ConfigFlag,  "gpunull",      GR_DEBUG},
 #if SK_ANGLE
     { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kANGLE_GLContextType,   0, kRW_ConfigFlag,    "angle",        true },
     { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kANGLE_GLContextType,  16, kRW_ConfigFlag,    "anglemsaa16",  true },
diff --git a/src/gpu/gl/GrGLCreateNullInterface.cpp b/src/gpu/gl/GrGLCreateNullInterface.cpp
index 1c2e1cd..0fa2b9e 100644
--- a/src/gpu/gl/GrGLCreateNullInterface.cpp
+++ b/src/gpu/gl/GrGLCreateNullInterface.cpp
@@ -15,13 +15,124 @@
 
 namespace { // added to suppress 'no previous prototype' warning
 
+class GrBufferObj {
+public:
+    GrBufferObj(GrGLuint id) : fID(id), fDataPtr(NULL), fSize(0), fMapped(false) {
+    }
+    ~GrBufferObj() { SkDELETE_ARRAY(fDataPtr); }
+
+    void allocate(GrGLint size, const GrGLchar* dataPtr) {
+        if (NULL != fDataPtr) {
+            GrAssert(0 != fSize);
+            SkDELETE_ARRAY(fDataPtr);
+        }
+
+        fSize = size;
+        fDataPtr = SkNEW_ARRAY(char, size);
+    }
+
+    GrGLuint id() const          { return fID; }
+    GrGLchar* dataPtr()          { return fDataPtr; }
+    GrGLint size() const         { return fSize; }
+
+    void setMapped(bool mapped)  { fMapped = mapped; }
+    bool mapped() const          { return fMapped; }
+
+private:
+    GrGLuint     fID;
+    GrGLchar*    fDataPtr;
+    GrGLint      fSize;         // size in bytes
+    bool         fMapped;
+};
+
+// In debug builds we do asserts that ensure we agree with GL about when a buffer
+// is mapped.
+static SkTDArray<GrBufferObj*> gBuffers;  // slot 0 is reserved for head of free list
+static GrGLuint gCurrArrayBuffer;
+static GrGLuint gCurrElementArrayBuffer;
+
+static GrBufferObj* look_up(GrGLuint id) {
+    GrBufferObj* buffer = gBuffers[id];
+    GrAssert(NULL != buffer && buffer->id() == id);
+    return buffer;
+}
+
+static GrBufferObj* create_buffer() {
+    if (0 == gBuffers.count()) {
+        // slot zero is reserved for the head of the free list
+        *gBuffers.append() = NULL;
+    }
+
+    GrGLuint id;
+    GrBufferObj* buffer;
+
+    if (NULL == gBuffers[0]) {
+        // no free slots - create a new one
+        id = gBuffers.count();
+        buffer = SkNEW_ARGS(GrBufferObj, (id));
+        gBuffers.append(1, &buffer);
+    } else {
+        // recycle a slot from the free list
+        id = (GrGLuint) gBuffers[0];
+        gBuffers[0] = gBuffers[id];
+
+        buffer = SkNEW_ARGS(GrBufferObj, (id));
+        gBuffers[id] = buffer;
+    }
+
+    return buffer;
+}
+
+static void delete_buffer(GrBufferObj* buffer) {
+    GrAssert(gBuffers.count() > 0);
+
+    GrGLuint id = buffer->id();
+    SkDELETE(buffer);
+
+    // Add this slot to the free list
+    gBuffers[id] = gBuffers[0];
+    gBuffers[0] = (GrBufferObj*) id;
+}
+
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLActiveTexture(GrGLenum texture) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLAttachShader(GrGLuint program, GrGLuint shader) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBeginQuery(GrGLenum target, GrGLuint id) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindAttribLocation(GrGLuint program, GrGLuint index, const char* name) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindTexture(GrGLenum target, GrGLuint texture) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindVertexArray(GrGLuint id) {}
-GrGLvoid GR_GL_FUNCTION_TYPE nullGLBufferData(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data, GrGLenum usage) {}
+
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGenBuffers(GrGLsizei n, GrGLuint* ids) {
+
+    for (int i = 0; i < n; ++i) {
+        GrBufferObj* buffer = create_buffer();
+        ids[i] = buffer->id();
+    }
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBufferData(GrGLenum target, 
+                                              GrGLsizeiptr size, 
+                                              const GrGLvoid* data, 
+                                              GrGLenum usage) {
+    GrGLuint id = 0;
+
+    switch (target) {
+    case GR_GL_ARRAY_BUFFER:
+        id = gCurrArrayBuffer;
+        break;
+    case GR_GL_ELEMENT_ARRAY_BUFFER:
+        id = gCurrElementArrayBuffer;
+        break;
+    default:
+        GrCrash("Unexpected target to nullGLBufferData");
+        break;
+    }
+
+    if (id > 0) {
+        GrBufferObj* buffer = look_up(id);
+        buffer->allocate(size, (const GrGLchar*) data);
+    }
+}
+
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLPixelStorei(GrGLenum pname, GrGLint param) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLReadPixels(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, GrGLvoid* pixels) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLUseProgram(GrGLuint program) {}
@@ -34,12 +145,12 @@
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLFramebufferTexture2D(GrGLenum target, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) {}
 
 GrGLuint GR_GL_FUNCTION_TYPE nullGLCreateProgram() {
-    static int gCurrID = 0;
+    static GrGLuint gCurrID = 0;
     return ++gCurrID;
 }
 
 GrGLuint GR_GL_FUNCTION_TYPE nullGLCreateShader(GrGLenum type) {
-    static int gCurrID = 0;
+    static GrGLuint gCurrID = 0;
     return ++gCurrID;
 }
 
@@ -47,12 +158,6 @@
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLDelete(GrGLuint program) {
 }
 
-// In debug builds we do asserts that ensure we agree with GL about when a buffer
-// is mapped.
-static SkTDArray<GrGLuint> gMappedBuffers;
-static GrGLuint gCurrArrayBuffer;
-static GrGLuint gCurrElementArrayBuffer;
-
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindBuffer(GrGLenum target, GrGLuint buffer) {
     switch (target) {
     case GR_GL_ARRAY_BUFFER:
@@ -73,75 +178,73 @@
         if (ids[i] == gCurrElementArrayBuffer) {
             gCurrElementArrayBuffer = 0;
         }
-        for (int j = 0; j < gMappedBuffers.count(); ++j) {
-            if (gMappedBuffers[j] == ids[i]) {
-                gMappedBuffers.remove(j);
-                // don't break b/c we didn't check for dupes on insert
-                --j;
-            }
-        }
+
+        GrBufferObj* buffer = look_up(ids[i]);
+        delete_buffer(buffer);
     }
 }
 
 GrGLvoid* GR_GL_FUNCTION_TYPE nullGLMapBuffer(GrGLenum target, GrGLenum access) {
-    // We just reserve 32MB of RAM for all locks and hope its big enough
-    static SkAutoMalloc gBufferData(32 * (1 << 20));
-    GrGLuint buf = 0;
+
+    GrGLuint id = 0;
     switch (target) {
         case GR_GL_ARRAY_BUFFER:
-            buf = gCurrArrayBuffer;
+            id = gCurrArrayBuffer;
             break;
         case GR_GL_ELEMENT_ARRAY_BUFFER:
-            buf = gCurrElementArrayBuffer;
+            id = gCurrElementArrayBuffer;
             break;
     }
-    if (buf) {
-        *gMappedBuffers.append() = buf;
+
+    if (id > 0) {
+        GrBufferObj* buffer = look_up(id);
+        GrAssert(!buffer->mapped());
+        buffer->setMapped(true);
+        return buffer->dataPtr();
     }
-    return gBufferData.get();
+
+    GrAssert(false);
+    return NULL;            // no buffer bound to target
 }
 
 GrGLboolean GR_GL_FUNCTION_TYPE nullGLUnmapBuffer(GrGLenum target) {
-    GrGLuint buf = 0;
+    GrGLuint id = 0;
     switch (target) {
     case GR_GL_ARRAY_BUFFER:
-        buf = gCurrArrayBuffer;
+        id = gCurrArrayBuffer;
         break;
     case GR_GL_ELEMENT_ARRAY_BUFFER:
-        buf = gCurrElementArrayBuffer;
+        id = gCurrElementArrayBuffer;
         break;
     }
-    if (buf) {
-        for (int i = 0; i < gMappedBuffers.count(); ++i) {
-            if (gMappedBuffers[i] == buf) {
-                gMappedBuffers.remove(i);
-                // don't break b/c we didn't check for dupes on insert
-                --i;
-            }
-        }
+    if (id > 0) {
+        GrBufferObj* buffer = look_up(id);
+        GrAssert(buffer->mapped());
+        buffer->setMapped(false);
+        return GR_GL_TRUE;
     }
-    return GR_GL_TRUE;
+
+    GrAlwaysAssert(false);
+    return GR_GL_FALSE; // GR_GL_INVALID_OPERATION;
 }
 
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetBufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) {
     switch (pname) {
         case GR_GL_BUFFER_MAPPED: {
             *params = GR_GL_FALSE;
-            GrGLuint buf = 0;
+            GrGLuint id = 0;
             switch (target) {
                 case GR_GL_ARRAY_BUFFER:
-                    buf = gCurrArrayBuffer;
+                    id = gCurrArrayBuffer;
                     break;
                 case GR_GL_ELEMENT_ARRAY_BUFFER:
-                    buf = gCurrElementArrayBuffer;
+                    id = gCurrElementArrayBuffer;
                     break;
             }
-            if (buf) {
-                for (int i = 0; i < gMappedBuffers.count(); ++i) {
-                    if (gMappedBuffers[i] == buf) {
-                        *params = GR_GL_TRUE;
-                        break;
-                    }
+            if (id > 0) {
+                GrBufferObj* buffer = look_up(id);
+                if (buffer->mapped()) {
+                    *params = GR_GL_TRUE;
                 }
             }
             break; }
@@ -202,7 +305,7 @@
         interface->fFinish = noOpGLFinish;
         interface->fFlush = noOpGLFlush;
         interface->fFrontFace = noOpGLFrontFace;
-        interface->fGenBuffers = noOpGLGenIds;
+        interface->fGenBuffers = nullGLGenBuffers;
         interface->fGenQueries = noOpGLGenIds;
         interface->fGenTextures = noOpGLGenIds;
         interface->fGenVertexArrays = noOpGLGenIds;