Home | History | Annotate | Download | only in gl
      1 /*
      2  * Copyright 2011 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 
      9 #include "gl/GrGLInterface.h"
     10 #include "GrGLDefines.h"
     11 #include "SkTDArray.h"
     12 #include "GrGLNoOpInterface.h"
     13 #include "SkTLS.h"
     14 
     15 class BufferObj {
     16 public:
     17     SK_DECLARE_INST_COUNT_ROOT(BufferObj);
     18 
     19     BufferObj(GrGLuint id) : fID(id), fDataPtr(NULL), fSize(0), fMapped(false) {
     20     }
     21     ~BufferObj() { SkDELETE_ARRAY(fDataPtr); }
     22 
     23     void allocate(GrGLsizeiptr size, const GrGLchar* dataPtr) {
     24         if (fDataPtr) {
     25             SkASSERT(0 != fSize);
     26             SkDELETE_ARRAY(fDataPtr);
     27         }
     28 
     29         fSize = size;
     30         fDataPtr = SkNEW_ARRAY(char, size);
     31     }
     32 
     33     GrGLuint id() const          { return fID; }
     34     GrGLchar* dataPtr()          { return fDataPtr; }
     35     GrGLsizeiptr size() const    { return fSize; }
     36 
     37     void setMapped(bool mapped)  { fMapped = mapped; }
     38     bool mapped() const          { return fMapped; }
     39 
     40 private:
     41     GrGLuint     fID;
     42     GrGLchar*    fDataPtr;
     43     GrGLsizeiptr fSize;         // size in bytes
     44     bool         fMapped;
     45 };
     46 
     47 // This class maintains a sparsely populated array of buffer pointers.
     48 class BufferManager {
     49 public:
     50     SK_DECLARE_INST_COUNT_ROOT(BufferManager);
     51 
     52     BufferManager() : fFreeListHead(kFreeListEnd) {}
     53 
     54     ~BufferManager() {
     55         // NULL out the entries that are really free list links rather than ptrs before deleting.
     56         intptr_t curr = fFreeListHead;
     57         while (kFreeListEnd != curr) {
     58             intptr_t next = reinterpret_cast<intptr_t>(fBuffers[SkToS32(curr)]);
     59             fBuffers[SkToS32(curr)] = NULL;
     60             curr = next;
     61         }
     62 
     63         fBuffers.deleteAll();
     64     }
     65 
     66     BufferObj* lookUp(GrGLuint id) {
     67         BufferObj* buffer = fBuffers[id];
     68         SkASSERT(buffer && buffer->id() == id);
     69         return buffer;
     70     }
     71 
     72     BufferObj* create() {
     73         GrGLuint id;
     74         BufferObj* buffer;
     75 
     76         if (kFreeListEnd == fFreeListHead) {
     77             // no free slots - create a new one
     78             id = fBuffers.count();
     79             buffer = SkNEW_ARGS(BufferObj, (id));
     80             *fBuffers.append() = buffer;
     81         } else {
     82             // grab the head of the free list and advance the head to the next free slot.
     83             id = static_cast<GrGLuint>(fFreeListHead);
     84             fFreeListHead = reinterpret_cast<intptr_t>(fBuffers[id]);
     85 
     86             buffer = SkNEW_ARGS(BufferObj, (id));
     87             fBuffers[id] = buffer;
     88         }
     89 
     90         return buffer;
     91     }
     92 
     93     void free(BufferObj* buffer) {
     94         SkASSERT(fBuffers.count() > 0);
     95 
     96         GrGLuint id = buffer->id();
     97         SkDELETE(buffer);
     98 
     99         fBuffers[id] = reinterpret_cast<BufferObj*>(fFreeListHead);
    100         fFreeListHead = id;
    101     }
    102 
    103 private:
    104     static const intptr_t kFreeListEnd = -1;
    105     // Index of the first entry of fBuffers in the free list. Free slots in fBuffers are indices to
    106     // the next free slot. The last free slot has a value of kFreeListEnd.
    107     intptr_t                fFreeListHead;
    108     SkTDArray<BufferObj*>   fBuffers;
    109 };
    110 
    111 /**
    112  * The global-to-thread state object for the null interface. All null interfaces on the
    113  * same thread currently share one of these. This means two null contexts on the same thread
    114  * can interfere with each other. It may make sense to more integrate this into SkNullGLContext
    115  * and use it's makeCurrent mechanism.
    116  */
    117 struct ThreadContext {
    118 public:
    119     SK_DECLARE_INST_COUNT_ROOT(ThreadContext);
    120 
    121     BufferManager   fBufferManager;
    122     GrGLuint        fCurrArrayBuffer;
    123     GrGLuint        fCurrElementArrayBuffer;
    124     GrGLuint        fCurrProgramID;
    125     GrGLuint        fCurrShaderID;
    126 
    127     static ThreadContext* Get() {
    128         return reinterpret_cast<ThreadContext*>(SkTLS::Get(Create, Delete));
    129     }
    130 
    131     ThreadContext()
    132         : fCurrArrayBuffer(0)
    133         , fCurrElementArrayBuffer(0)
    134         , fCurrProgramID(0)
    135         , fCurrShaderID(0) {}
    136 
    137 private:
    138     static void* Create() { return SkNEW(ThreadContext ); }
    139     static void Delete(void* context) { SkDELETE(reinterpret_cast<ThreadContext *>(context)); }
    140 };
    141 
    142 // Functions not declared in GrGLBogusInterface.h (not common with the Debug GL interface).
    143 
    144 namespace { // added to suppress 'no previous prototype' warning
    145 
    146 GrGLvoid GR_GL_FUNCTION_TYPE nullGLActiveTexture(GrGLenum texture) {}
    147 GrGLvoid GR_GL_FUNCTION_TYPE nullGLAttachShader(GrGLuint program, GrGLuint shader) {}
    148 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBeginQuery(GrGLenum target, GrGLuint id) {}
    149 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindAttribLocation(GrGLuint program, GrGLuint index, const char* name) {}
    150 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindTexture(GrGLenum target, GrGLuint texture) {}
    151 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindVertexArray(GrGLuint id) {}
    152 
    153 GrGLvoid GR_GL_FUNCTION_TYPE nullGLGenBuffers(GrGLsizei n, GrGLuint* ids) {
    154     ThreadContext* ctx = ThreadContext::Get();
    155     for (int i = 0; i < n; ++i) {
    156         BufferObj* buffer = ctx->fBufferManager.create();
    157         ids[i] = buffer->id();
    158     }
    159 }
    160 
    161 GrGLvoid GR_GL_FUNCTION_TYPE nullGLGenerateMipmap(GrGLenum target) {}
    162 
    163 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBufferData(GrGLenum target,
    164                                               GrGLsizeiptr size,
    165                                               const GrGLvoid* data,
    166                                               GrGLenum usage) {
    167     ThreadContext* ctx = ThreadContext::Get();
    168     GrGLuint id = 0;
    169 
    170     switch (target) {
    171     case GR_GL_ARRAY_BUFFER:
    172         id = ctx->fCurrArrayBuffer;
    173         break;
    174     case GR_GL_ELEMENT_ARRAY_BUFFER:
    175         id = ctx->fCurrElementArrayBuffer;
    176         break;
    177     default:
    178         SkFAIL("Unexpected target to nullGLBufferData");
    179         break;
    180     }
    181 
    182     if (id > 0) {
    183         BufferObj* buffer = ctx->fBufferManager.lookUp(id);
    184         buffer->allocate(size, (const GrGLchar*) data);
    185     }
    186 }
    187 
    188 GrGLvoid GR_GL_FUNCTION_TYPE nullGLPixelStorei(GrGLenum pname, GrGLint param) {}
    189 GrGLvoid GR_GL_FUNCTION_TYPE nullGLReadPixels(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, GrGLvoid* pixels) {}
    190 GrGLvoid GR_GL_FUNCTION_TYPE nullGLUseProgram(GrGLuint program) {}
    191 GrGLvoid GR_GL_FUNCTION_TYPE nullGLViewport(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {}
    192 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindFramebuffer(GrGLenum target, GrGLuint framebuffer) {}
    193 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindRenderbuffer(GrGLenum target, GrGLuint renderbuffer) {}
    194 GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteFramebuffers(GrGLsizei n, const GrGLuint *framebuffers) {}
    195 GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteRenderbuffers(GrGLsizei n, const GrGLuint *renderbuffers) {}
    196 GrGLvoid GR_GL_FUNCTION_TYPE nullGLFramebufferRenderbuffer(GrGLenum target, GrGLenum attachment, GrGLenum renderbuffertarget, GrGLuint renderbuffer) {}
    197 GrGLvoid GR_GL_FUNCTION_TYPE nullGLFramebufferTexture2D(GrGLenum target, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) {}
    198 
    199 GrGLuint GR_GL_FUNCTION_TYPE nullGLCreateProgram() {
    200     return ++ThreadContext::Get()->fCurrProgramID;
    201 }
    202 
    203 GrGLuint GR_GL_FUNCTION_TYPE nullGLCreateShader(GrGLenum type) {
    204     return ++ThreadContext::Get()->fCurrShaderID;
    205 }
    206 
    207 // same delete used for shaders and programs
    208 GrGLvoid GR_GL_FUNCTION_TYPE nullGLDelete(GrGLuint program) {
    209 }
    210 
    211 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindBuffer(GrGLenum target, GrGLuint buffer) {
    212     ThreadContext* ctx = ThreadContext::Get();
    213     switch (target) {
    214     case GR_GL_ARRAY_BUFFER:
    215         ctx->fCurrArrayBuffer = buffer;
    216         break;
    217     case GR_GL_ELEMENT_ARRAY_BUFFER:
    218         ctx->fCurrElementArrayBuffer = buffer;
    219         break;
    220     }
    221 }
    222 
    223 // deleting a bound buffer has the side effect of binding 0
    224 GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteBuffers(GrGLsizei n, const GrGLuint* ids) {
    225     ThreadContext* ctx = ThreadContext::Get();
    226     for (int i = 0; i < n; ++i) {
    227         if (ids[i] == ctx->fCurrArrayBuffer) {
    228             ctx->fCurrArrayBuffer = 0;
    229         }
    230         if (ids[i] == ctx->fCurrElementArrayBuffer) {
    231             ctx->fCurrElementArrayBuffer = 0;
    232         }
    233 
    234         BufferObj* buffer = ctx->fBufferManager.lookUp(ids[i]);
    235         ctx->fBufferManager.free(buffer);
    236     }
    237 }
    238 
    239 GrGLvoid* GR_GL_FUNCTION_TYPE nullGLMapBufferRange(GrGLenum target, GrGLintptr offset,
    240                                                    GrGLsizeiptr length, GrGLbitfield access) {
    241     ThreadContext* ctx = ThreadContext::Get();
    242     GrGLuint id = 0;
    243     switch (target) {
    244         case GR_GL_ARRAY_BUFFER:
    245             id = ctx->fCurrArrayBuffer;
    246             break;
    247         case GR_GL_ELEMENT_ARRAY_BUFFER:
    248             id = ctx->fCurrElementArrayBuffer;
    249             break;
    250     }
    251 
    252     if (id > 0) {
    253         // We just ignore the offset and length here.
    254         BufferObj* buffer = ctx->fBufferManager.lookUp(id);
    255         SkASSERT(!buffer->mapped());
    256         buffer->setMapped(true);
    257         return buffer->dataPtr();
    258     }
    259     return NULL;
    260 }
    261 
    262 GrGLvoid* GR_GL_FUNCTION_TYPE nullGLMapBuffer(GrGLenum target, GrGLenum access) {
    263     ThreadContext* ctx = ThreadContext::Get();
    264     GrGLuint id = 0;
    265     switch (target) {
    266         case GR_GL_ARRAY_BUFFER:
    267             id = ctx->fCurrArrayBuffer;
    268             break;
    269         case GR_GL_ELEMENT_ARRAY_BUFFER:
    270             id = ctx->fCurrElementArrayBuffer;
    271             break;
    272     }
    273 
    274     if (id > 0) {
    275         BufferObj* buffer = ctx->fBufferManager.lookUp(id);
    276         SkASSERT(!buffer->mapped());
    277         buffer->setMapped(true);
    278         return buffer->dataPtr();
    279     }
    280 
    281     SkASSERT(false);
    282     return NULL;            // no buffer bound to target
    283 }
    284 
    285 GrGLvoid GR_GL_FUNCTION_TYPE nullGLFlushMappedBufferRange(GrGLenum target,
    286                                                           GrGLintptr offset,
    287                                                           GrGLsizeiptr length) {}
    288 
    289 
    290 GrGLboolean GR_GL_FUNCTION_TYPE nullGLUnmapBuffer(GrGLenum target) {
    291     ThreadContext* ctx = ThreadContext::Get();
    292     GrGLuint id = 0;
    293     switch (target) {
    294     case GR_GL_ARRAY_BUFFER:
    295         id = ctx->fCurrArrayBuffer;
    296         break;
    297     case GR_GL_ELEMENT_ARRAY_BUFFER:
    298         id = ctx->fCurrElementArrayBuffer;
    299         break;
    300     }
    301     if (id > 0) {
    302         BufferObj* buffer = ctx->fBufferManager.lookUp(id);
    303         SkASSERT(buffer->mapped());
    304         buffer->setMapped(false);
    305         return GR_GL_TRUE;
    306     }
    307 
    308     GrAlwaysAssert(false);
    309     return GR_GL_FALSE; // GR_GL_INVALID_OPERATION;
    310 }
    311 
    312 GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetBufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) {
    313     ThreadContext* ctx = ThreadContext::Get();
    314     switch (pname) {
    315         case GR_GL_BUFFER_MAPPED: {
    316             *params = GR_GL_FALSE;
    317             GrGLuint id = 0;
    318             switch (target) {
    319                 case GR_GL_ARRAY_BUFFER:
    320                     id = ctx->fCurrArrayBuffer;
    321                     break;
    322                 case GR_GL_ELEMENT_ARRAY_BUFFER:
    323                     id = ctx->fCurrElementArrayBuffer;
    324                     break;
    325             }
    326             if (id > 0) {
    327                 BufferObj* buffer = ctx->fBufferManager.lookUp(id);
    328                 if (buffer->mapped()) {
    329                     *params = GR_GL_TRUE;
    330                 }
    331             }
    332             break; }
    333         default:
    334             SkFAIL("Unexpected pname to GetBufferParamateriv");
    335             break;
    336     }
    337 };
    338 
    339 } // end anonymous namespace
    340 
    341 const GrGLInterface* GrGLCreateNullInterface() {
    342     GrGLInterface* interface = SkNEW(GrGLInterface);
    343 
    344     interface->fStandard = kGL_GrGLStandard;
    345 
    346     GrGLInterface::Functions* functions = &interface->fFunctions;
    347     functions->fActiveTexture = nullGLActiveTexture;
    348     functions->fAttachShader = nullGLAttachShader;
    349     functions->fBeginQuery = nullGLBeginQuery;
    350     functions->fBindAttribLocation = nullGLBindAttribLocation;
    351     functions->fBindBuffer = nullGLBindBuffer;
    352     functions->fBindFragDataLocation = noOpGLBindFragDataLocation;
    353     functions->fBindTexture = nullGLBindTexture;
    354     functions->fBindVertexArray = nullGLBindVertexArray;
    355     functions->fBlendColor = noOpGLBlendColor;
    356     functions->fBlendFunc = noOpGLBlendFunc;
    357     functions->fBufferData = nullGLBufferData;
    358     functions->fBufferSubData = noOpGLBufferSubData;
    359     functions->fClear = noOpGLClear;
    360     functions->fClearColor = noOpGLClearColor;
    361     functions->fClearStencil = noOpGLClearStencil;
    362     functions->fColorMask = noOpGLColorMask;
    363     functions->fCompileShader = noOpGLCompileShader;
    364     functions->fCompressedTexImage2D = noOpGLCompressedTexImage2D;
    365     functions->fCompressedTexSubImage2D = noOpGLCompressedTexSubImage2D;
    366     functions->fCopyTexSubImage2D = noOpGLCopyTexSubImage2D;
    367     functions->fCreateProgram = nullGLCreateProgram;
    368     functions->fCreateShader = nullGLCreateShader;
    369     functions->fCullFace = noOpGLCullFace;
    370     functions->fDeleteBuffers = nullGLDeleteBuffers;
    371     functions->fDeleteProgram = nullGLDelete;
    372     functions->fDeleteQueries = noOpGLDeleteIds;
    373     functions->fDeleteShader = nullGLDelete;
    374     functions->fDeleteTextures = noOpGLDeleteIds;
    375     functions->fDeleteVertexArrays = noOpGLDeleteIds;
    376     functions->fDepthMask = noOpGLDepthMask;
    377     functions->fDisable = noOpGLDisable;
    378     functions->fDisableVertexAttribArray = noOpGLDisableVertexAttribArray;
    379     functions->fDrawArrays = noOpGLDrawArrays;
    380     functions->fDrawBuffer = noOpGLDrawBuffer;
    381     functions->fDrawBuffers = noOpGLDrawBuffers;
    382     functions->fDrawElements = noOpGLDrawElements;
    383     functions->fEnable = noOpGLEnable;
    384     functions->fEnableVertexAttribArray = noOpGLEnableVertexAttribArray;
    385     functions->fEndQuery = noOpGLEndQuery;
    386     functions->fFinish = noOpGLFinish;
    387     functions->fFlush = noOpGLFlush;
    388     functions->fFlushMappedBufferRange = nullGLFlushMappedBufferRange;
    389     functions->fFrontFace = noOpGLFrontFace;
    390     functions->fGenBuffers = nullGLGenBuffers;
    391     functions->fGenerateMipmap = nullGLGenerateMipmap;
    392     functions->fGenQueries = noOpGLGenIds;
    393     functions->fGenTextures = noOpGLGenIds;
    394     functions->fGenVertexArrays = noOpGLGenIds;
    395     functions->fGetBufferParameteriv = nullGLGetBufferParameteriv;
    396     functions->fGetError = noOpGLGetError;
    397     functions->fGetIntegerv = noOpGLGetIntegerv;
    398     functions->fGetQueryObjecti64v = noOpGLGetQueryObjecti64v;
    399     functions->fGetQueryObjectiv = noOpGLGetQueryObjectiv;
    400     functions->fGetQueryObjectui64v = noOpGLGetQueryObjectui64v;
    401     functions->fGetQueryObjectuiv = noOpGLGetQueryObjectuiv;
    402     functions->fGetQueryiv = noOpGLGetQueryiv;
    403     functions->fGetProgramInfoLog = noOpGLGetInfoLog;
    404     functions->fGetProgramiv = noOpGLGetShaderOrProgramiv;
    405     functions->fGetShaderInfoLog = noOpGLGetInfoLog;
    406     functions->fGetShaderiv = noOpGLGetShaderOrProgramiv;
    407     functions->fGetString = noOpGLGetString;
    408     functions->fGetStringi = noOpGLGetStringi;
    409     functions->fGetTexLevelParameteriv = noOpGLGetTexLevelParameteriv;
    410     functions->fGetUniformLocation = noOpGLGetUniformLocation;
    411     functions->fInsertEventMarker = noOpGLInsertEventMarker;
    412     functions->fLineWidth = noOpGLLineWidth;
    413     functions->fLinkProgram = noOpGLLinkProgram;
    414     functions->fMapBuffer = nullGLMapBuffer;
    415     functions->fMapBufferRange = nullGLMapBufferRange;
    416     functions->fPixelStorei = nullGLPixelStorei;
    417     functions->fPopGroupMarker = noOpGLPopGroupMarker;
    418     functions->fPushGroupMarker = noOpGLPushGroupMarker;
    419     functions->fQueryCounter = noOpGLQueryCounter;
    420     functions->fReadBuffer = noOpGLReadBuffer;
    421     functions->fReadPixels = nullGLReadPixels;
    422     functions->fScissor = noOpGLScissor;
    423     functions->fShaderSource = noOpGLShaderSource;
    424     functions->fStencilFunc = noOpGLStencilFunc;
    425     functions->fStencilFuncSeparate = noOpGLStencilFuncSeparate;
    426     functions->fStencilMask = noOpGLStencilMask;
    427     functions->fStencilMaskSeparate = noOpGLStencilMaskSeparate;
    428     functions->fStencilOp = noOpGLStencilOp;
    429     functions->fStencilOpSeparate = noOpGLStencilOpSeparate;
    430     functions->fTexImage2D = noOpGLTexImage2D;
    431     functions->fTexParameteri = noOpGLTexParameteri;
    432     functions->fTexParameteriv = noOpGLTexParameteriv;
    433     functions->fTexSubImage2D = noOpGLTexSubImage2D;
    434     functions->fTexStorage2D = noOpGLTexStorage2D;
    435     functions->fDiscardFramebuffer = noOpGLDiscardFramebuffer;
    436     functions->fUniform1f = noOpGLUniform1f;
    437     functions->fUniform1i = noOpGLUniform1i;
    438     functions->fUniform1fv = noOpGLUniform1fv;
    439     functions->fUniform1iv = noOpGLUniform1iv;
    440     functions->fUniform2f = noOpGLUniform2f;
    441     functions->fUniform2i = noOpGLUniform2i;
    442     functions->fUniform2fv = noOpGLUniform2fv;
    443     functions->fUniform2iv = noOpGLUniform2iv;
    444     functions->fUniform3f = noOpGLUniform3f;
    445     functions->fUniform3i = noOpGLUniform3i;
    446     functions->fUniform3fv = noOpGLUniform3fv;
    447     functions->fUniform3iv = noOpGLUniform3iv;
    448     functions->fUniform4f = noOpGLUniform4f;
    449     functions->fUniform4i = noOpGLUniform4i;
    450     functions->fUniform4fv = noOpGLUniform4fv;
    451     functions->fUniform4iv = noOpGLUniform4iv;
    452     functions->fUniformMatrix2fv = noOpGLUniformMatrix2fv;
    453     functions->fUniformMatrix3fv = noOpGLUniformMatrix3fv;
    454     functions->fUniformMatrix4fv = noOpGLUniformMatrix4fv;
    455     functions->fUnmapBuffer = nullGLUnmapBuffer;
    456     functions->fUseProgram = nullGLUseProgram;
    457     functions->fVertexAttrib4fv = noOpGLVertexAttrib4fv;
    458     functions->fVertexAttribPointer = noOpGLVertexAttribPointer;
    459     functions->fViewport = nullGLViewport;
    460     functions->fBindFramebuffer = nullGLBindFramebuffer;
    461     functions->fBindRenderbuffer = nullGLBindRenderbuffer;
    462     functions->fCheckFramebufferStatus = noOpGLCheckFramebufferStatus;
    463     functions->fDeleteFramebuffers = nullGLDeleteFramebuffers;
    464     functions->fDeleteRenderbuffers = nullGLDeleteRenderbuffers;
    465     functions->fFramebufferRenderbuffer = nullGLFramebufferRenderbuffer;
    466     functions->fFramebufferTexture2D = nullGLFramebufferTexture2D;
    467     functions->fGenFramebuffers = noOpGLGenIds;
    468     functions->fGenRenderbuffers = noOpGLGenIds;
    469     functions->fGetFramebufferAttachmentParameteriv = noOpGLGetFramebufferAttachmentParameteriv;
    470     functions->fGetRenderbufferParameteriv = noOpGLGetRenderbufferParameteriv;
    471     functions->fRenderbufferStorage = noOpGLRenderbufferStorage;
    472     functions->fRenderbufferStorageMultisample = noOpGLRenderbufferStorageMultisample;
    473     functions->fBlitFramebuffer = noOpGLBlitFramebuffer;
    474     functions->fResolveMultisampleFramebuffer = noOpGLResolveMultisampleFramebuffer;
    475     functions->fMatrixLoadf = noOpGLMatrixLoadf;
    476     functions->fMatrixLoadIdentity = noOpGLMatrixLoadIdentity;
    477     functions->fBindFragDataLocationIndexed = noOpGLBindFragDataLocationIndexed;
    478 
    479     interface->fExtensions.init(kGL_GrGLStandard, functions->fGetString, functions->fGetStringi,
    480                                 functions->fGetIntegerv);
    481     return interface;
    482 }
    483