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