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
BufferObj(GrGLuint id)19 BufferObj(GrGLuint id) : fID(id), fDataPtr(NULL), fSize(0), fMapped(false) {
20 }
~BufferObj()21 ~BufferObj() { SkDELETE_ARRAY(fDataPtr); }
22
allocate(GrGLsizeiptr size,const GrGLchar * dataPtr)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
id() const33 GrGLuint id() const { return fID; }
dataPtr()34 GrGLchar* dataPtr() { return fDataPtr; }
size() const35 GrGLsizeiptr size() const { return fSize; }
36
setMapped(bool mapped)37 void setMapped(bool mapped) { fMapped = mapped; }
mapped() const38 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
BufferManager()52 BufferManager() : fFreeListHead(kFreeListEnd) {}
53
~BufferManager()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
lookUp(GrGLuint id)66 BufferObj* lookUp(GrGLuint id) {
67 BufferObj* buffer = fBuffers[id];
68 SkASSERT(buffer && buffer->id() == id);
69 return buffer;
70 }
71
create()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
free(BufferObj * buffer)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
GetThreadContext127 static ThreadContext* Get() {
128 return reinterpret_cast<ThreadContext*>(SkTLS::Get(Create, Delete));
129 }
130
ThreadContextThreadContext131 ThreadContext()
132 : fCurrArrayBuffer(0)
133 , fCurrElementArrayBuffer(0)
134 , fCurrProgramID(0)
135 , fCurrShaderID(0) {}
136
137 private:
CreateThreadContext138 static void* Create() { return SkNEW(ThreadContext ); }
DeleteThreadContext139 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
nullGLActiveTexture(GrGLenum texture)146 GrGLvoid GR_GL_FUNCTION_TYPE nullGLActiveTexture(GrGLenum texture) {}
nullGLAttachShader(GrGLuint program,GrGLuint shader)147 GrGLvoid GR_GL_FUNCTION_TYPE nullGLAttachShader(GrGLuint program, GrGLuint shader) {}
nullGLBeginQuery(GrGLenum target,GrGLuint id)148 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBeginQuery(GrGLenum target, GrGLuint id) {}
nullGLBindAttribLocation(GrGLuint program,GrGLuint index,const char * name)149 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindAttribLocation(GrGLuint program, GrGLuint index, const char* name) {}
nullGLBindTexture(GrGLenum target,GrGLuint texture)150 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindTexture(GrGLenum target, GrGLuint texture) {}
nullGLBindVertexArray(GrGLuint id)151 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindVertexArray(GrGLuint id) {}
152
nullGLGenBuffers(GrGLsizei n,GrGLuint * ids)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
nullGLGenerateMipmap(GrGLenum target)161 GrGLvoid GR_GL_FUNCTION_TYPE nullGLGenerateMipmap(GrGLenum target) {}
162
nullGLBufferData(GrGLenum target,GrGLsizeiptr size,const GrGLvoid * data,GrGLenum usage)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
nullGLPixelStorei(GrGLenum pname,GrGLint param)188 GrGLvoid GR_GL_FUNCTION_TYPE nullGLPixelStorei(GrGLenum pname, GrGLint param) {}
nullGLReadPixels(GrGLint x,GrGLint y,GrGLsizei width,GrGLsizei height,GrGLenum format,GrGLenum type,GrGLvoid * pixels)189 GrGLvoid GR_GL_FUNCTION_TYPE nullGLReadPixels(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, GrGLvoid* pixels) {}
nullGLUseProgram(GrGLuint program)190 GrGLvoid GR_GL_FUNCTION_TYPE nullGLUseProgram(GrGLuint program) {}
nullGLViewport(GrGLint x,GrGLint y,GrGLsizei width,GrGLsizei height)191 GrGLvoid GR_GL_FUNCTION_TYPE nullGLViewport(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {}
nullGLBindFramebuffer(GrGLenum target,GrGLuint framebuffer)192 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindFramebuffer(GrGLenum target, GrGLuint framebuffer) {}
nullGLBindRenderbuffer(GrGLenum target,GrGLuint renderbuffer)193 GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindRenderbuffer(GrGLenum target, GrGLuint renderbuffer) {}
nullGLDeleteFramebuffers(GrGLsizei n,const GrGLuint * framebuffers)194 GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteFramebuffers(GrGLsizei n, const GrGLuint *framebuffers) {}
nullGLDeleteRenderbuffers(GrGLsizei n,const GrGLuint * renderbuffers)195 GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteRenderbuffers(GrGLsizei n, const GrGLuint *renderbuffers) {}
nullGLFramebufferRenderbuffer(GrGLenum target,GrGLenum attachment,GrGLenum renderbuffertarget,GrGLuint renderbuffer)196 GrGLvoid GR_GL_FUNCTION_TYPE nullGLFramebufferRenderbuffer(GrGLenum target, GrGLenum attachment, GrGLenum renderbuffertarget, GrGLuint renderbuffer) {}
nullGLFramebufferTexture2D(GrGLenum target,GrGLenum attachment,GrGLenum textarget,GrGLuint texture,GrGLint level)197 GrGLvoid GR_GL_FUNCTION_TYPE nullGLFramebufferTexture2D(GrGLenum target, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) {}
198
nullGLCreateProgram()199 GrGLuint GR_GL_FUNCTION_TYPE nullGLCreateProgram() {
200 return ++ThreadContext::Get()->fCurrProgramID;
201 }
202
nullGLCreateShader(GrGLenum type)203 GrGLuint GR_GL_FUNCTION_TYPE nullGLCreateShader(GrGLenum type) {
204 return ++ThreadContext::Get()->fCurrShaderID;
205 }
206
207 // same delete used for shaders and programs
nullGLDelete(GrGLuint program)208 GrGLvoid GR_GL_FUNCTION_TYPE nullGLDelete(GrGLuint program) {
209 }
210
nullGLBindBuffer(GrGLenum target,GrGLuint buffer)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
nullGLDeleteBuffers(GrGLsizei n,const GrGLuint * ids)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
nullGLMapBufferRange(GrGLenum target,GrGLintptr offset,GrGLsizeiptr length,GrGLbitfield access)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
nullGLMapBuffer(GrGLenum target,GrGLenum access)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
nullGLFlushMappedBufferRange(GrGLenum target,GrGLintptr offset,GrGLsizeiptr length)285 GrGLvoid GR_GL_FUNCTION_TYPE nullGLFlushMappedBufferRange(GrGLenum target,
286 GrGLintptr offset,
287 GrGLsizeiptr length) {}
288
289
nullGLUnmapBuffer(GrGLenum target)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
nullGLGetBufferParameteriv(GrGLenum target,GrGLenum pname,GrGLint * params)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
GrGLCreateNullInterface()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