Index: src/gpu/gl/debug/GrGLCreateDebugInterface.cpp |
diff --git a/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp b/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..02b5cf704d8f19e9aa49747a4bc86f48f9adf0ef |
--- /dev/null |
+++ b/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp |
@@ -0,0 +1,1236 @@ |
+/* |
+ * Copyright 2012 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+ |
+#include "gl/GrGLInterface.h" |
+ |
+#include "GrBufferObj.h" |
+#include "GrFrameBufferObj.h" |
+#include "GrProgramObj.h" |
+#include "GrRenderBufferObj.h" |
+#include "GrShaderObj.h" |
+#include "GrTextureObj.h" |
+#include "GrTextureUnitObj.h" |
+#include "GrVertexArrayObj.h" |
+#include "gl/GrGLTestInterface.h" |
+ |
+#include "SkMutex.h" |
+ |
+namespace { |
+ |
+// Helper macro to make creating an object (where you need to get back a derived type) easier |
+#define CREATE(className, classEnum) \ |
+ reinterpret_cast<className *>(this->createObj(classEnum)) |
+ |
+// Helper macro to make creating an object (where you need to get back a derived type) easier |
+#define FIND(id, className, classEnum) \ |
+ reinterpret_cast<className *>(this->findObject(id, classEnum)) |
+ |
+class DebugInterface : public GrGLTestInterface { |
+public: |
+ DebugInterface() |
+ : fCurrGenericID(0) |
+ , fCurrTextureUnit(0) |
+ , fArrayBuffer(nullptr) |
+ , fElementArrayBuffer(nullptr) |
+ , fVertexArray(nullptr) |
+ , fPackRowLength(0) |
+ , fUnpackRowLength(0) |
+ , fPackAlignment(4) |
+ , fFrameBuffer(nullptr) |
+ , fRenderBuffer(nullptr) |
+ , fProgram(nullptr) |
+ , fAbandoned(false) { |
+ for (int i = 0; i < kDefaultMaxTextureUnits; ++i) { |
+ fTextureUnits[i] = |
+ reinterpret_cast<GrTextureUnitObj*>(this->createObj(kTextureUnit_ObjTypes)); |
+ fTextureUnits[i]->ref(); |
+ fTextureUnits[i]->setNumber(i); |
+ } |
+ this->init(kGL_GrGLStandard); |
+ } |
+ |
+ ~DebugInterface() override { |
+ // unref & delete the texture units first so they don't show up on the leak report |
+ for (int i = 0; i < kDefaultMaxTextureUnits; ++i) { |
+ fTextureUnits[i]->unref(); |
+ fTextureUnits[i]->deleteAction(); |
+ } |
+ for (int i = 0; i < fObjects.count(); ++i) { |
+ delete fObjects[i]; |
+ } |
+ fObjects.reset(); |
+ |
+ fArrayBuffer = nullptr; |
+ fElementArrayBuffer = nullptr; |
+ fVertexArray = nullptr; |
+ |
+ this->report(); |
+ } |
+ |
+ void abandon() const override { fAbandoned = true; } |
+ |
+ GrGLvoid activeTexture(GrGLenum texture) override { |
+ // Ganesh offsets the texture unit indices |
+ texture -= GR_GL_TEXTURE0; |
+ GrAlwaysAssert(texture < kDefaultMaxTextureUnits); |
+ fCurrTextureUnit = texture; |
+ } |
+ |
+ GrGLvoid attachShader(GrGLuint programID, GrGLuint shaderID) override { |
+ |
+ GrProgramObj *program = FIND(programID, GrProgramObj, kProgram_ObjTypes); |
+ GrAlwaysAssert(program); |
+ |
+ GrShaderObj *shader = FIND(shaderID, GrShaderObj, kShader_ObjTypes); |
+ GrAlwaysAssert(shader); |
+ |
+ program->AttachShader(shader); |
+ } |
+ |
+ //////////////////////////////////////////////////////////////////////////////// |
+ GrGLvoid bindTexture(GrGLenum target, GrGLuint textureID) override { |
+ GrAlwaysAssert(target == GR_GL_TEXTURE_2D || |
+ target == GR_GL_TEXTURE_RECTANGLE || |
+ target == GR_GL_TEXTURE_EXTERNAL); |
+ |
+ // a textureID of 0 is acceptable - it binds to the default texture target |
+ GrTextureObj *texture = FIND(textureID, GrTextureObj, kTexture_ObjTypes); |
+ |
+ this->setTexture(texture); |
+ } |
+ |
+ //////////////////////////////////////////////////////////////////////////////// |
+ GrGLvoid bufferData(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data, |
+ GrGLenum usage) override { |
+ GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target || |
+ GR_GL_ELEMENT_ARRAY_BUFFER == target); |
+ GrAlwaysAssert(size >= 0); |
+ GrAlwaysAssert(GR_GL_STREAM_DRAW == usage || |
+ GR_GL_STATIC_DRAW == usage || |
+ GR_GL_DYNAMIC_DRAW == usage); |
+ |
+ GrBufferObj *buffer = nullptr; |
+ switch (target) { |
+ case GR_GL_ARRAY_BUFFER: |
+ buffer = this->getArrayBuffer(); |
+ break; |
+ case GR_GL_ELEMENT_ARRAY_BUFFER: |
+ buffer = this->getElementArrayBuffer(); |
+ break; |
+ default: |
+ SkFAIL("Unexpected target to glBufferData"); |
+ break; |
+ } |
+ |
+ GrAlwaysAssert(buffer); |
+ GrAlwaysAssert(buffer->getBound()); |
+ |
+ buffer->allocate(size, reinterpret_cast<const GrGLchar *>(data)); |
+ buffer->setUsage(usage); |
+ } |
+ |
+ |
+ GrGLvoid pixelStorei(GrGLenum pname, GrGLint param) override { |
+ |
+ switch (pname) { |
+ case GR_GL_UNPACK_ROW_LENGTH: |
+ fUnpackRowLength = param; |
+ break; |
+ case GR_GL_PACK_ROW_LENGTH: |
+ fPackRowLength = param; |
+ break; |
+ case GR_GL_UNPACK_ALIGNMENT: |
+ break; |
+ case GR_GL_PACK_ALIGNMENT: |
+ fPackAlignment = param; |
+ break; |
+ default: |
+ GrAlwaysAssert(false); |
+ break; |
+ } |
+ } |
+ |
+ GrGLvoid readPixels(GrGLint x, |
+ GrGLint y, |
+ GrGLsizei width, |
+ GrGLsizei height, |
+ GrGLenum format, |
+ GrGLenum type, |
+ GrGLvoid* pixels) override { |
+ |
+ GrGLint pixelsInRow = width; |
+ if (fPackRowLength > 0) { |
+ pixelsInRow = fPackRowLength; |
+ } |
+ |
+ GrGLint componentsPerPixel = 0; |
+ |
+ switch (format) { |
+ case GR_GL_RGBA: |
+ // fallthrough |
+ case GR_GL_BGRA: |
+ componentsPerPixel = 4; |
+ break; |
+ case GR_GL_RGB: |
+ componentsPerPixel = 3; |
+ break; |
+ case GR_GL_RED: |
+ componentsPerPixel = 1; |
+ break; |
+ default: |
+ GrAlwaysAssert(false); |
+ break; |
+ } |
+ |
+ GrGLint alignment = fPackAlignment; |
+ |
+ GrGLint componentSize = 0; // size (in bytes) of a single component |
+ |
+ switch (type) { |
+ case GR_GL_UNSIGNED_BYTE: |
+ componentSize = 1; |
+ break; |
+ default: |
+ GrAlwaysAssert(false); |
+ break; |
+ } |
+ |
+ GrGLint rowStride = 0; // number of components (not bytes) to skip |
+ if (componentSize >= alignment) { |
+ rowStride = componentsPerPixel * pixelsInRow; |
+ } else { |
+ float fTemp = |
+ sk_float_ceil(componentSize * componentsPerPixel * pixelsInRow / |
+ static_cast<float>(alignment)); |
+ rowStride = static_cast<GrGLint>(alignment * fTemp / componentSize); |
+ } |
+ |
+ GrGLchar *scanline = static_cast<GrGLchar *>(pixels); |
+ for (int y = 0; y < height; ++y) { |
+ memset(scanline, 0, componentsPerPixel * componentSize * width); |
+ scanline += rowStride; |
+ } |
+ } |
+ |
+ GrGLvoid useProgram(GrGLuint programID) override { |
+ |
+ // A programID of 0 is legal |
+ GrProgramObj *program = FIND(programID, GrProgramObj, kProgram_ObjTypes); |
+ |
+ this->useProgram(program); |
+ } |
+ |
+ GrGLvoid bindFramebuffer(GrGLenum target, GrGLuint frameBufferID) override { |
+ |
+ GrAlwaysAssert(GR_GL_FRAMEBUFFER == target || |
+ GR_GL_READ_FRAMEBUFFER == target || |
+ GR_GL_DRAW_FRAMEBUFFER); |
+ |
+ // a frameBufferID of 0 is acceptable - it binds to the default |
+ // frame buffer |
+ GrFrameBufferObj *frameBuffer = FIND(frameBufferID, GrFrameBufferObj, |
+ kFrameBuffer_ObjTypes); |
+ |
+ this->setFrameBuffer(frameBuffer); |
+ } |
+ |
+ GrGLvoid bindRenderbuffer(GrGLenum target, GrGLuint renderBufferID) override { |
+ |
+ GrAlwaysAssert(GR_GL_RENDERBUFFER == target); |
+ |
+ // a renderBufferID of 0 is acceptable - it unbinds the bound render buffer |
+ GrRenderBufferObj *renderBuffer = FIND(renderBufferID, GrRenderBufferObj, |
+ kRenderBuffer_ObjTypes); |
+ |
+ this->setRenderBuffer(renderBuffer); |
+ } |
+ |
+ GrGLvoid deleteTextures(GrGLsizei n, const GrGLuint* textures) override { |
+ // first potentially unbind the texture |
+ for (unsigned int i = 0; i < kDefaultMaxTextureUnits; ++i) { |
+ GrTextureUnitObj *pTU = this->getTextureUnit(i); |
+ |
+ if (pTU->getTexture()) { |
+ for (int j = 0; j < n; ++j) { |
+ |
+ if (textures[j] == pTU->getTexture()->getID()) { |
+ // this ID is the current texture - revert the binding to 0 |
+ pTU->setTexture(nullptr); |
+ } |
+ } |
+ } |
+ } |
+ |
+ // TODO: fuse the following block with DeleteRenderBuffers? |
+ // Open GL will remove a deleted render buffer from the active |
+ // frame buffer but not from any other frame buffer |
+ if (this->getFrameBuffer()) { |
+ |
+ GrFrameBufferObj *frameBuffer = this->getFrameBuffer(); |
+ |
+ for (int i = 0; i < n; ++i) { |
+ |
+ if (frameBuffer->getColor() && |
+ textures[i] == frameBuffer->getColor()->getID()) { |
+ frameBuffer->setColor(nullptr); |
+ } |
+ if (frameBuffer->getDepth() && |
+ textures[i] == frameBuffer->getDepth()->getID()) { |
+ frameBuffer->setDepth(nullptr); |
+ } |
+ if (frameBuffer->getStencil() && |
+ textures[i] == frameBuffer->getStencil()->getID()) { |
+ frameBuffer->setStencil(nullptr); |
+ } |
+ } |
+ } |
+ |
+ // then actually "delete" the buffers |
+ for (int i = 0; i < n; ++i) { |
+ GrTextureObj *buffer = FIND(textures[i], GrTextureObj, kTexture_ObjTypes); |
+ GrAlwaysAssert(buffer); |
+ |
+ // OpenGL gives no guarantees if a texture is deleted while attached to |
+ // something other than the currently bound frame buffer |
+ GrAlwaysAssert(!buffer->getBound()); |
+ |
+ GrAlwaysAssert(!buffer->getDeleted()); |
+ buffer->deleteAction(); |
+ } |
+ |
+ } |
+ |
+ GrGLvoid deleteFramebuffers(GrGLsizei n, const GrGLuint *frameBuffers) override { |
+ |
+ // first potentially unbind the buffers |
+ if (this->getFrameBuffer()) { |
+ for (int i = 0; i < n; ++i) { |
+ |
+ if (frameBuffers[i] == |
+ this->getFrameBuffer()->getID()) { |
+ // this ID is the current frame buffer - rebind to the default |
+ this->setFrameBuffer(nullptr); |
+ } |
+ } |
+ } |
+ |
+ // then actually "delete" the buffers |
+ for (int i = 0; i < n; ++i) { |
+ GrFrameBufferObj *buffer = FIND(frameBuffers[i], GrFrameBufferObj, |
+ kFrameBuffer_ObjTypes); |
+ GrAlwaysAssert(buffer); |
+ |
+ GrAlwaysAssert(!buffer->getDeleted()); |
+ buffer->deleteAction(); |
+ } |
+ } |
+ |
+ GrGLvoid deleteRenderbuffers(GrGLsizei n,const GrGLuint *renderBuffers) override { |
+ |
+ // first potentially unbind the buffers |
+ if (this->getRenderBuffer()) { |
+ for (int i = 0; i < n; ++i) { |
+ |
+ if (renderBuffers[i] == |
+ this->getRenderBuffer()->getID()) { |
+ // this ID is the current render buffer - make no |
+ // render buffer be bound |
+ this->setRenderBuffer(nullptr); |
+ } |
+ } |
+ } |
+ |
+ // TODO: fuse the following block with DeleteTextures? |
+ // Open GL will remove a deleted render buffer from the active frame |
+ // buffer but not from any other frame buffer |
+ if (this->getFrameBuffer()) { |
+ |
+ GrFrameBufferObj *frameBuffer = this->getFrameBuffer(); |
+ |
+ for (int i = 0; i < n; ++i) { |
+ |
+ if (frameBuffer->getColor() && |
+ renderBuffers[i] == frameBuffer->getColor()->getID()) { |
+ frameBuffer->setColor(nullptr); |
+ } |
+ if (frameBuffer->getDepth() && |
+ renderBuffers[i] == frameBuffer->getDepth()->getID()) { |
+ frameBuffer->setDepth(nullptr); |
+ } |
+ if (frameBuffer->getStencil() && |
+ renderBuffers[i] == frameBuffer->getStencil()->getID()) { |
+ frameBuffer->setStencil(nullptr); |
+ } |
+ } |
+ } |
+ |
+ // then actually "delete" the buffers |
+ for (int i = 0; i < n; ++i) { |
+ GrRenderBufferObj *buffer = FIND(renderBuffers[i], GrRenderBufferObj, |
+ kRenderBuffer_ObjTypes); |
+ GrAlwaysAssert(buffer); |
+ |
+ // OpenGL gives no guarantees if a render buffer is deleted |
+ // while attached to something other than the currently |
+ // bound frame buffer |
+ GrAlwaysAssert(!buffer->getColorBound()); |
+ GrAlwaysAssert(!buffer->getDepthBound()); |
+ // However, at GrContext destroy time we release all GrRsources and so stencil buffers |
+ // may get deleted before FBOs that refer to them. |
+ //GrAlwaysAssert(!buffer->getStencilBound()); |
+ |
+ GrAlwaysAssert(!buffer->getDeleted()); |
+ buffer->deleteAction(); |
+ } |
+ } |
+ |
+ GrGLvoid framebufferRenderbuffer(GrGLenum target, |
+ GrGLenum attachment, |
+ GrGLenum renderbuffertarget, |
+ GrGLuint renderBufferID) override { |
+ |
+ GrAlwaysAssert(GR_GL_FRAMEBUFFER == target); |
+ GrAlwaysAssert(GR_GL_COLOR_ATTACHMENT0 == attachment || |
+ GR_GL_DEPTH_ATTACHMENT == attachment || |
+ GR_GL_STENCIL_ATTACHMENT == attachment); |
+ GrAlwaysAssert(GR_GL_RENDERBUFFER == renderbuffertarget); |
+ |
+ GrFrameBufferObj *framebuffer = this->getFrameBuffer(); |
+ // A render buffer cannot be attached to the default framebuffer |
+ GrAlwaysAssert(framebuffer); |
+ |
+ // a renderBufferID of 0 is acceptable - it unbinds the current |
+ // render buffer |
+ GrRenderBufferObj *renderbuffer = FIND(renderBufferID, GrRenderBufferObj, |
+ kRenderBuffer_ObjTypes); |
+ |
+ switch (attachment) { |
+ case GR_GL_COLOR_ATTACHMENT0: |
+ framebuffer->setColor(renderbuffer); |
+ break; |
+ case GR_GL_DEPTH_ATTACHMENT: |
+ framebuffer->setDepth(renderbuffer); |
+ break; |
+ case GR_GL_STENCIL_ATTACHMENT: |
+ framebuffer->setStencil(renderbuffer); |
+ break; |
+ default: |
+ GrAlwaysAssert(false); |
+ break; |
+ }; |
+ |
+ } |
+ |
+ //////////////////////////////////////////////////////////////////////////////// |
+ GrGLvoid framebufferTexture2D(GrGLenum target, GrGLenum attachment, GrGLenum textarget, |
+ GrGLuint textureID, GrGLint level) override { |
+ |
+ GrAlwaysAssert(GR_GL_FRAMEBUFFER == target); |
+ GrAlwaysAssert(GR_GL_COLOR_ATTACHMENT0 == attachment || |
+ GR_GL_DEPTH_ATTACHMENT == attachment || |
+ GR_GL_STENCIL_ATTACHMENT == attachment); |
+ GrAlwaysAssert(GR_GL_TEXTURE_2D == textarget); |
+ |
+ GrFrameBufferObj *framebuffer = this->getFrameBuffer(); |
+ // A texture cannot be attached to the default framebuffer |
+ GrAlwaysAssert(framebuffer); |
+ |
+ // A textureID of 0 is allowed - it unbinds the currently bound texture |
+ GrTextureObj *texture = FIND(textureID, GrTextureObj, kTexture_ObjTypes); |
+ if (texture) { |
+ // The texture shouldn't be bound to a texture unit - this |
+ // could lead to a feedback loop |
+ GrAlwaysAssert(!texture->getBound()); |
+ } |
+ |
+ GrAlwaysAssert(0 == level); |
+ |
+ switch (attachment) { |
+ case GR_GL_COLOR_ATTACHMENT0: |
+ framebuffer->setColor(texture); |
+ break; |
+ case GR_GL_DEPTH_ATTACHMENT: |
+ framebuffer->setDepth(texture); |
+ break; |
+ case GR_GL_STENCIL_ATTACHMENT: |
+ framebuffer->setStencil(texture); |
+ break; |
+ default: |
+ GrAlwaysAssert(false); |
+ break; |
+ }; |
+ } |
+ |
+ GrGLuint createProgram() override { |
+ |
+ GrProgramObj *program = CREATE(GrProgramObj, kProgram_ObjTypes); |
+ |
+ return program->getID(); |
+ } |
+ |
+ GrGLuint createShader(GrGLenum type) override { |
+ |
+ GrAlwaysAssert(GR_GL_VERTEX_SHADER == type || |
+ GR_GL_FRAGMENT_SHADER == type); |
+ |
+ GrShaderObj *shader = CREATE(GrShaderObj, kShader_ObjTypes); |
+ shader->setType(type); |
+ |
+ return shader->getID(); |
+ } |
+ |
+ GrGLenum checkFramebufferStatus(GrGLenum target) override { return GR_GL_FRAMEBUFFER_COMPLETE; } |
+ |
+ GrGLvoid deleteProgram(GrGLuint programID) override { |
+ |
+ GrProgramObj *program = FIND(programID, GrProgramObj, kProgram_ObjTypes); |
+ GrAlwaysAssert(program); |
+ |
+ if (program->getRefCount()) { |
+ // someone is still using this program so we can't delete it here |
+ program->setMarkedForDeletion(); |
+ } else { |
+ program->deleteAction(); |
+ } |
+ } |
+ |
+ GrGLvoid deleteShader(GrGLuint shaderID) override { |
+ |
+ GrShaderObj *shader = FIND(shaderID, GrShaderObj, kShader_ObjTypes); |
+ GrAlwaysAssert(shader); |
+ |
+ if (shader->getRefCount()) { |
+ // someone is still using this shader so we can't delete it here |
+ shader->setMarkedForDeletion(); |
+ } else { |
+ shader->deleteAction(); |
+ } |
+ } |
+ |
+ GrGLvoid genBuffers(GrGLsizei n, GrGLuint* ids) override { |
+ this->genObjs(kBuffer_ObjTypes, n, ids); |
+ } |
+ |
+ GrGLvoid genFramebuffers(GrGLsizei n, GrGLuint* ids) override { |
+ this->genObjs(kFrameBuffer_ObjTypes, n, ids); |
+ } |
+ |
+ GrGLvoid genRenderbuffers(GrGLsizei n, GrGLuint* ids) override { |
+ this->genObjs(kRenderBuffer_ObjTypes, n, ids); |
+ } |
+ |
+ GrGLvoid genTextures(GrGLsizei n, GrGLuint* ids) override { |
+ this->genObjs(kTexture_ObjTypes, n, ids); |
+ } |
+ |
+ GrGLvoid genVertexArrays(GrGLsizei n, GrGLuint* ids) override { |
+ this->genObjs(kVertexArray_ObjTypes, n, ids); |
+ } |
+ |
+ GrGLvoid genQueries(GrGLsizei n, GrGLuint *ids) override { this->genGenericIds(n, ids); } |
+ |
+ GrGLenum getError() override { return GR_GL_NO_ERROR; } |
+ |
+ GrGLvoid getIntegerv(GrGLenum pname, GrGLint* params) override { |
+ // TODO: remove from Ganesh the #defines for gets we don't use. |
+ // We would like to minimize gets overall due to performance issues |
+ switch (pname) { |
+ case GR_GL_CONTEXT_PROFILE_MASK: |
+ *params = GR_GL_CONTEXT_COMPATIBILITY_PROFILE_BIT; |
+ break; |
+ case GR_GL_STENCIL_BITS: |
+ *params = 8; |
+ break; |
+ case GR_GL_SAMPLES: |
+ *params = 1; |
+ break; |
+ case GR_GL_FRAMEBUFFER_BINDING: |
+ *params = 0; |
+ break; |
+ case GR_GL_VIEWPORT: |
+ params[0] = 0; |
+ params[1] = 0; |
+ params[2] = 800; |
+ params[3] = 600; |
+ break; |
+ case GR_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: |
+ case GR_GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS: |
+ case GR_GL_MAX_TEXTURE_IMAGE_UNITS: |
+ case GR_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: |
+ *params = 8; |
+ break; |
+ case GR_GL_MAX_TEXTURE_COORDS: |
+ *params = 8; |
+ break; |
+ case GR_GL_MAX_VERTEX_UNIFORM_VECTORS: |
+ *params = kDefaultMaxVertexUniformVectors; |
+ break; |
+ case GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS: |
+ *params = kDefaultMaxFragmentUniformVectors; |
+ break; |
+ case GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS: |
+ *params = 16 * 4; |
+ break; |
+ case GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS: |
+ *params = 0; |
+ break; |
+ case GR_GL_COMPRESSED_TEXTURE_FORMATS: |
+ break; |
+ case GR_GL_MAX_TEXTURE_SIZE: |
+ *params = 8192; |
+ break; |
+ case GR_GL_MAX_RENDERBUFFER_SIZE: |
+ *params = 8192; |
+ break; |
+ case GR_GL_MAX_SAMPLES: |
+ *params = 32; |
+ break; |
+ case GR_GL_MAX_VERTEX_ATTRIBS: |
+ *params = kDefaultMaxVertexAttribs; |
+ break; |
+ case GR_GL_MAX_VARYING_VECTORS: |
+ *params = kDefaultMaxVaryingVectors; |
+ break; |
+ case GR_GL_NUM_EXTENSIONS: { |
+ GrGLint i = 0; |
+ while (kExtensions[i++]); |
+ *params = i; |
+ break; |
+ } |
+ default: |
+ SkFAIL("Unexpected pname to GetIntegerv"); |
+ } |
+ } |
+ |
+ GrGLvoid getMultisamplefv(GrGLenum pname, GrGLuint index, GrGLfloat* val) override { |
+ val[0] = val[1] = 0.5f; |
+ } |
+ |
+ GrGLvoid getProgramiv(GrGLuint program, GrGLenum pname, GrGLint* params) override { |
+ this->getShaderOrProgramiv(program, pname, params); |
+ } |
+ |
+ GrGLvoid getProgramInfoLog(GrGLuint program, GrGLsizei bufsize, GrGLsizei* length, |
+ char* infolog) override { |
+ this->getInfoLog(program, bufsize, length, infolog); |
+ } |
+ |
+ GrGLvoid getQueryiv(GrGLenum GLtarget, GrGLenum pname, GrGLint *params) override { |
+ switch (pname) { |
+ case GR_GL_CURRENT_QUERY: |
+ *params = 0; |
+ break; |
+ case GR_GL_QUERY_COUNTER_BITS: |
+ *params = 32; |
+ break; |
+ default: |
+ SkFAIL("Unexpected pname passed GetQueryiv."); |
+ } |
+ } |
+ |
+ GrGLvoid getQueryObjecti64v(GrGLuint id, GrGLenum pname, GrGLint64 *params) override { |
+ this->queryResult(id, pname, params); |
+ } |
+ |
+ GrGLvoid getQueryObjectiv(GrGLuint id, GrGLenum pname, GrGLint *params) override { |
+ this->queryResult(id, pname, params); |
+ } |
+ |
+ GrGLvoid getQueryObjectui64v(GrGLuint id, GrGLenum pname, GrGLuint64 *params) override { |
+ this->queryResult(id, pname, params); |
+ } |
+ |
+ GrGLvoid getQueryObjectuiv(GrGLuint id, GrGLenum pname, GrGLuint *params) override { |
+ this->queryResult(id, pname, params); |
+ } |
+ |
+ GrGLvoid getShaderiv(GrGLuint shader, GrGLenum pname, GrGLint* params) override { |
+ this->getShaderOrProgramiv(shader, pname, params); |
+ } |
+ |
+ GrGLvoid getShaderInfoLog(GrGLuint shader, GrGLsizei bufsize, GrGLsizei* length, |
+ char* infolog) override { |
+ this->getInfoLog(shader, bufsize, length, infolog); |
+ } |
+ |
+ const GrGLubyte* getString(GrGLenum name) override { |
+ switch (name) { |
+ case GR_GL_EXTENSIONS: |
+ return CombinedExtensionString(); |
+ case GR_GL_VERSION: |
+ return (const GrGLubyte*)"4.0 Debug GL"; |
+ case GR_GL_SHADING_LANGUAGE_VERSION: |
+ return (const GrGLubyte*)"4.20.8 Debug GLSL"; |
+ case GR_GL_VENDOR: |
+ return (const GrGLubyte*)"Debug Vendor"; |
+ case GR_GL_RENDERER: |
+ return (const GrGLubyte*)"The Debug (Non-)Renderer"; |
+ default: |
+ SkFAIL("Unexpected name passed to GetString"); |
+ return nullptr; |
+ } |
+ } |
+ |
+ const GrGLubyte* getStringi(GrGLenum name, GrGLuint i) override { |
+ switch (name) { |
+ case GR_GL_EXTENSIONS: { |
+ GrGLint count; |
+ this->getIntegerv(GR_GL_NUM_EXTENSIONS, &count); |
+ if ((GrGLint)i <= count) { |
+ return (const GrGLubyte*) kExtensions[i]; |
+ } else { |
+ return nullptr; |
+ } |
+ } |
+ default: |
+ SkFAIL("Unexpected name passed to GetStringi"); |
+ return nullptr; |
+ } |
+ } |
+ |
+ GrGLvoid getTexLevelParameteriv(GrGLenum target, GrGLint level, GrGLenum pname, |
+ GrGLint* params) override { |
+ // we used to use this to query stuff about externally created textures, |
+ // now we just require clients to tell us everything about the texture. |
+ SkFAIL("Should never query texture parameters."); |
+ } |
+ |
+ GrGLvoid deleteVertexArrays(GrGLsizei n, const GrGLuint* ids) override { |
+ for (GrGLsizei i = 0; i < n; ++i) { |
+ GrVertexArrayObj* array = FIND(ids[i], GrVertexArrayObj, kVertexArray_ObjTypes); |
+ GrAlwaysAssert(array); |
+ |
+ // Deleting the current vertex array binds object 0 |
+ if (this->getVertexArray() == array) { |
+ this->setVertexArray(nullptr); |
+ } |
+ |
+ if (array->getRefCount()) { |
+ // someone is still using this vertex array so we can't delete it here |
+ array->setMarkedForDeletion(); |
+ } else { |
+ array->deleteAction(); |
+ } |
+ } |
+ } |
+ |
+ GrGLvoid bindVertexArray(GrGLuint id) override { |
+ GrVertexArrayObj* array = FIND(id, GrVertexArrayObj, kVertexArray_ObjTypes); |
+ GrAlwaysAssert((0 == id) || array); |
+ this->setVertexArray(array); |
+ } |
+ |
+ GrGLvoid bindBuffer(GrGLenum target, GrGLuint bufferID) override { |
+ GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target || GR_GL_ELEMENT_ARRAY_BUFFER == target); |
+ |
+ GrBufferObj *buffer = FIND(bufferID, GrBufferObj, kBuffer_ObjTypes); |
+ // 0 is a permissible bufferID - it unbinds the current buffer |
+ |
+ switch (target) { |
+ case GR_GL_ARRAY_BUFFER: |
+ this->setArrayBuffer(buffer); |
+ break; |
+ case GR_GL_ELEMENT_ARRAY_BUFFER: |
+ this->setElementArrayBuffer(buffer); |
+ break; |
+ default: |
+ SkFAIL("Unexpected target to glBindBuffer"); |
+ break; |
+ } |
+ } |
+ |
+ // deleting a bound buffer has the side effect of binding 0 |
+ GrGLvoid deleteBuffers(GrGLsizei n, const GrGLuint* ids) override { |
+ // first potentially unbind the buffers |
+ for (int i = 0; i < n; ++i) { |
+ |
+ if (this->getArrayBuffer() && |
+ ids[i] == this->getArrayBuffer()->getID()) { |
+ // this ID is the current array buffer |
+ this->setArrayBuffer(nullptr); |
+ } |
+ if (this->getElementArrayBuffer() && |
+ ids[i] == this->getElementArrayBuffer()->getID()) { |
+ // this ID is the current element array buffer |
+ this->setElementArrayBuffer(nullptr); |
+ } |
+ } |
+ |
+ // then actually "delete" the buffers |
+ for (int i = 0; i < n; ++i) { |
+ GrBufferObj *buffer = FIND(ids[i], GrBufferObj, kBuffer_ObjTypes); |
+ GrAlwaysAssert(buffer); |
+ |
+ GrAlwaysAssert(!buffer->getDeleted()); |
+ buffer->deleteAction(); |
+ } |
+ } |
+ |
+ // map a buffer to the caller's address space |
+ GrGLvoid* mapBufferRange(GrGLenum target, GrGLintptr offset, GrGLsizeiptr length, |
+ GrGLbitfield access) override { |
+ GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target || |
+ GR_GL_ELEMENT_ARRAY_BUFFER == target); |
+ |
+ // We only expect read access and we expect that the buffer or range is always invalidated. |
+ GrAlwaysAssert(!SkToBool(GR_GL_MAP_READ_BIT & access)); |
+ GrAlwaysAssert((GR_GL_MAP_INVALIDATE_BUFFER_BIT | GR_GL_MAP_INVALIDATE_RANGE_BIT) & access); |
+ |
+ GrBufferObj *buffer = nullptr; |
+ switch (target) { |
+ case GR_GL_ARRAY_BUFFER: |
+ buffer = this->getArrayBuffer(); |
+ break; |
+ case GR_GL_ELEMENT_ARRAY_BUFFER: |
+ buffer = this->getElementArrayBuffer(); |
+ break; |
+ default: |
+ SkFAIL("Unexpected target to glMapBufferRange"); |
+ break; |
+ } |
+ |
+ if (buffer) { |
+ GrAlwaysAssert(offset >= 0 && offset + length <= buffer->getSize()); |
+ GrAlwaysAssert(!buffer->getMapped()); |
+ buffer->setMapped(offset, length); |
+ return buffer->getDataPtr() + offset; |
+ } |
+ |
+ GrAlwaysAssert(false); |
+ return nullptr; // no buffer bound to the target |
+ } |
+ |
+ GrGLvoid* mapBuffer(GrGLenum target, GrGLenum access) override { |
+ GrAlwaysAssert(GR_GL_WRITE_ONLY == access); |
+ |
+ GrBufferObj *buffer = nullptr; |
+ switch (target) { |
+ case GR_GL_ARRAY_BUFFER: |
+ buffer = this->getArrayBuffer(); |
+ break; |
+ case GR_GL_ELEMENT_ARRAY_BUFFER: |
+ buffer = this->getElementArrayBuffer(); |
+ break; |
+ default: |
+ SkFAIL("Unexpected target to glMapBuffer"); |
+ break; |
+ } |
+ |
+ return this->mapBufferRange(target, 0, buffer->getSize(), |
+ GR_GL_MAP_WRITE_BIT | GR_GL_MAP_INVALIDATE_BUFFER_BIT); |
+ } |
+ |
+ // remove a buffer from the caller's address space |
+ // TODO: check if the "access" method from "glMapBuffer" was honored |
+ GrGLboolean unmapBuffer(GrGLenum target) override { |
+ GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target || |
+ GR_GL_ELEMENT_ARRAY_BUFFER == target); |
+ |
+ GrBufferObj *buffer = nullptr; |
+ switch (target) { |
+ case GR_GL_ARRAY_BUFFER: |
+ buffer = this->getArrayBuffer(); |
+ break; |
+ case GR_GL_ELEMENT_ARRAY_BUFFER: |
+ buffer = this->getElementArrayBuffer(); |
+ break; |
+ default: |
+ SkFAIL("Unexpected target to glUnmapBuffer"); |
+ break; |
+ } |
+ |
+ if (buffer) { |
+ GrAlwaysAssert(buffer->getMapped()); |
+ buffer->resetMapped(); |
+ return GR_GL_TRUE; |
+ } |
+ |
+ GrAlwaysAssert(false); |
+ return GR_GL_FALSE; // GR_GL_INVALID_OPERATION; |
+ } |
+ |
+ GrGLvoid flushMappedBufferRange(GrGLenum target, GrGLintptr offset, |
+ GrGLsizeiptr length) override { |
+ GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target || |
+ GR_GL_ELEMENT_ARRAY_BUFFER == target); |
+ |
+ GrBufferObj *buffer = nullptr; |
+ switch (target) { |
+ case GR_GL_ARRAY_BUFFER: |
+ buffer = this->getArrayBuffer(); |
+ break; |
+ case GR_GL_ELEMENT_ARRAY_BUFFER: |
+ buffer = this->getElementArrayBuffer(); |
+ break; |
+ default: |
+ SkFAIL("Unexpected target to glUnmapBuffer"); |
+ break; |
+ } |
+ |
+ if (buffer) { |
+ GrAlwaysAssert(buffer->getMapped()); |
+ GrAlwaysAssert(offset >= 0 && (offset + length) <= buffer->getMappedLength()); |
+ } else { |
+ GrAlwaysAssert(false); |
+ } |
+ } |
+ |
+ GrGLvoid getBufferParameteriv(GrGLenum target, GrGLenum value, GrGLint* params) override { |
+ |
+ GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target || |
+ GR_GL_ELEMENT_ARRAY_BUFFER == target); |
+ GrAlwaysAssert(GR_GL_BUFFER_SIZE == value || |
+ GR_GL_BUFFER_USAGE == value); |
+ |
+ GrBufferObj *buffer = nullptr; |
+ switch (target) { |
+ case GR_GL_ARRAY_BUFFER: |
+ buffer = this->getArrayBuffer(); |
+ break; |
+ case GR_GL_ELEMENT_ARRAY_BUFFER: |
+ buffer = this->getElementArrayBuffer(); |
+ break; |
+ } |
+ |
+ GrAlwaysAssert(buffer); |
+ |
+ switch (value) { |
+ case GR_GL_BUFFER_MAPPED: |
+ *params = GR_GL_FALSE; |
+ if (buffer) |
+ *params = buffer->getMapped() ? GR_GL_TRUE : GR_GL_FALSE; |
+ break; |
+ case GR_GL_BUFFER_SIZE: |
+ *params = 0; |
+ if (buffer) |
+ *params = SkToInt(buffer->getSize()); |
+ break; |
+ case GR_GL_BUFFER_USAGE: |
+ *params = GR_GL_STATIC_DRAW; |
+ if (buffer) |
+ *params = buffer->getUsage(); |
+ break; |
+ default: |
+ SkFAIL("Unexpected value to glGetBufferParamateriv"); |
+ break; |
+ } |
+ } |
+ |
+private: |
+ // the OpenGLES 2.0 spec says this must be >= 128 |
+ static const GrGLint kDefaultMaxVertexUniformVectors = 128; |
+ |
+ // the OpenGLES 2.0 spec says this must be >=16 |
+ static const GrGLint kDefaultMaxFragmentUniformVectors = 16; |
+ |
+ // the OpenGLES 2.0 spec says this must be >= 8 |
+ static const GrGLint kDefaultMaxVertexAttribs = 8; |
+ |
+ // the OpenGLES 2.0 spec says this must be >= 8 |
+ static const GrGLint kDefaultMaxVaryingVectors = 8; |
+ |
+ // the OpenGLES 2.0 spec says this must be >= 2 |
+ static const GrGLint kDefaultMaxTextureUnits = 8; |
+ |
+ static const char* kExtensions[]; |
+ |
+ GrGLuint fCurrGenericID; |
+ GrGLuint fCurrTextureUnit; |
+ GrTextureUnitObj* fTextureUnits[kDefaultMaxTextureUnits]; |
+ GrBufferObj* fArrayBuffer; |
+ GrBufferObj* fElementArrayBuffer; |
+ GrVertexArrayObj* fVertexArray; |
+ GrGLint fPackRowLength; |
+ GrGLint fUnpackRowLength; |
+ GrGLint fPackAlignment; |
+ GrFrameBufferObj* fFrameBuffer; |
+ GrRenderBufferObj* fRenderBuffer; |
+ GrProgramObj* fProgram; |
+ mutable bool fAbandoned; |
+ // global store of all objects |
+ SkTArray<GrFakeRefObj *> fObjects; |
+ |
+ static const GrGLubyte* CombinedExtensionString() { |
+ static SkString gExtString; |
+ static SkMutex gMutex; |
+ gMutex.acquire(); |
+ if (0 == gExtString.size()) { |
+ int i = 0; |
+ while (kExtensions[i]) { |
+ if (i > 0) { |
+ gExtString.append(" "); |
+ } |
+ gExtString.append(kExtensions[i]); |
+ ++i; |
+ } |
+ } |
+ gMutex.release(); |
+ return (const GrGLubyte*) gExtString.c_str(); |
+ } |
+ |
+ GrGLvoid genGenericIds(GrGLsizei n, GrGLuint* ids) { |
+ for (int i = 0; i < n; ++i) { |
+ ids[i] = ++fCurrGenericID; |
+ } |
+ } |
+ |
+ GrGLvoid getInfoLog(GrGLuint object, GrGLsizei bufsize, GrGLsizei* length, |
+ char* infolog) { |
+ if (length) { |
+ *length = 0; |
+ } |
+ if (bufsize > 0) { |
+ *infolog = 0; |
+ } |
+ } |
+ |
+ GrGLvoid getShaderOrProgramiv(GrGLuint object, GrGLenum pname, GrGLint* params) { |
+ switch (pname) { |
+ case GR_GL_LINK_STATUS: // fallthru |
+ case GR_GL_COMPILE_STATUS: |
+ *params = GR_GL_TRUE; |
+ break; |
+ case GR_GL_INFO_LOG_LENGTH: |
+ *params = 0; |
+ break; |
+ // we don't expect any other pnames |
+ default: |
+ SkFAIL("Unexpected pname to GetProgramiv"); |
+ break; |
+ } |
+ } |
+ |
+ template <typename T> |
+ void queryResult(GrGLenum GLtarget, GrGLenum pname, T *params) { |
+ switch (pname) { |
+ case GR_GL_QUERY_RESULT_AVAILABLE: |
+ *params = GR_GL_TRUE; |
+ break; |
+ case GR_GL_QUERY_RESULT: |
+ *params = 0; |
+ break; |
+ default: |
+ SkFAIL("Unexpected pname passed to GetQueryObject."); |
+ break; |
+ } |
+ } |
+ |
+ enum ObjTypes { |
+ kTexture_ObjTypes = 0, |
+ kBuffer_ObjTypes, |
+ kRenderBuffer_ObjTypes, |
+ kFrameBuffer_ObjTypes, |
+ kShader_ObjTypes, |
+ kProgram_ObjTypes, |
+ kTextureUnit_ObjTypes, |
+ kVertexArray_ObjTypes, |
+ kObjTypeCount |
+ }; |
+ |
+ typedef GrFakeRefObj *(*Create)(); |
+ |
+ static Create gFactoryFunc[kObjTypeCount]; |
+ |
+ GrGLvoid genObjs(ObjTypes type, GrGLsizei n, GrGLuint* ids) { |
+ for (int i = 0; i < n; ++i) { |
+ GrAlwaysAssert(ids[i] == 0); |
+ GrFakeRefObj *obj = this->createObj(type); |
+ GrAlwaysAssert(obj); |
+ ids[i] = obj->getID(); |
+ } |
+ } |
+ |
+ GrFakeRefObj* createObj(ObjTypes type) { |
+ GrFakeRefObj *temp = (*gFactoryFunc[type])(); |
+ |
+ fObjects.push_back(temp); |
+ |
+ return temp; |
+ } |
+ |
+ GrFakeRefObj* findObject(GrGLuint ID, ObjTypes type) { |
+ for (int i = 0; i < fObjects.count(); ++i) { |
+ if (fObjects[i]->getID() == ID) { // && fObjects[i]->getType() == type) { |
+ // The application shouldn't be accessing objects |
+ // that (as far as OpenGL knows) were already deleted |
+ GrAlwaysAssert(!fObjects[i]->getDeleted()); |
+ GrAlwaysAssert(!fObjects[i]->getMarkedForDeletion()); |
+ return fObjects[i]; |
+ } |
+ } |
+ return nullptr; |
+ } |
+ |
+ GrTextureUnitObj* getTextureUnit(int unit) { |
+ GrAlwaysAssert(0 <= unit && kDefaultMaxTextureUnits > unit); |
+ |
+ return fTextureUnits[unit]; |
+ } |
+ |
+ void setArrayBuffer(GrBufferObj *arrayBuffer) { |
+ if (fArrayBuffer) { |
+ // automatically break the binding of the old buffer |
+ GrAlwaysAssert(fArrayBuffer->getBound()); |
+ fArrayBuffer->resetBound(); |
+ |
+ GrAlwaysAssert(!fArrayBuffer->getDeleted()); |
+ fArrayBuffer->unref(); |
+ } |
+ |
+ fArrayBuffer = arrayBuffer; |
+ |
+ if (fArrayBuffer) { |
+ GrAlwaysAssert(!fArrayBuffer->getDeleted()); |
+ fArrayBuffer->ref(); |
+ |
+ GrAlwaysAssert(!fArrayBuffer->getBound()); |
+ fArrayBuffer->setBound(); |
+ } |
+ } |
+ |
+ GrBufferObj* getArrayBuffer() { return fArrayBuffer; } |
+ void setElementArrayBuffer(GrBufferObj *elementArrayBuffer) { |
+ if (fElementArrayBuffer) { |
+ // automatically break the binding of the old buffer |
+ GrAlwaysAssert(fElementArrayBuffer->getBound()); |
+ fElementArrayBuffer->resetBound(); |
+ |
+ GrAlwaysAssert(!fElementArrayBuffer->getDeleted()); |
+ fElementArrayBuffer->unref(); |
+ } |
+ |
+ fElementArrayBuffer = elementArrayBuffer; |
+ |
+ if (fElementArrayBuffer) { |
+ GrAlwaysAssert(!fElementArrayBuffer->getDeleted()); |
+ fElementArrayBuffer->ref(); |
+ |
+ GrAlwaysAssert(!fElementArrayBuffer->getBound()); |
+ fElementArrayBuffer->setBound(); |
+ } |
+ } |
+ |
+ GrBufferObj *getElementArrayBuffer() { return fElementArrayBuffer; } |
+ |
+ void setVertexArray(GrVertexArrayObj* vertexArray) { |
+ if (vertexArray) { |
+ SkASSERT(!vertexArray->getDeleted()); |
+ } |
+ SkRefCnt_SafeAssign(fVertexArray, vertexArray); |
+ } |
+ |
+ GrVertexArrayObj* getVertexArray() { return fVertexArray; } |
+ |
+ void setTexture(GrTextureObj *texture) { |
+ fTextureUnits[fCurrTextureUnit]->setTexture(texture); |
+ } |
+ |
+ void setFrameBuffer(GrFrameBufferObj *frameBuffer) { |
+ if (fFrameBuffer) { |
+ GrAlwaysAssert(fFrameBuffer->getBound()); |
+ fFrameBuffer->resetBound(); |
+ |
+ GrAlwaysAssert(!fFrameBuffer->getDeleted()); |
+ fFrameBuffer->unref(); |
+ } |
+ |
+ fFrameBuffer = frameBuffer; |
+ |
+ if (fFrameBuffer) { |
+ GrAlwaysAssert(!fFrameBuffer->getDeleted()); |
+ fFrameBuffer->ref(); |
+ |
+ GrAlwaysAssert(!fFrameBuffer->getBound()); |
+ fFrameBuffer->setBound(); |
+ } |
+ } |
+ |
+ GrFrameBufferObj *getFrameBuffer() { return fFrameBuffer; } |
+ |
+ void setRenderBuffer(GrRenderBufferObj *renderBuffer) { |
+ if (fRenderBuffer) { |
+ GrAlwaysAssert(fRenderBuffer->getBound()); |
+ fRenderBuffer->resetBound(); |
+ |
+ GrAlwaysAssert(!fRenderBuffer->getDeleted()); |
+ fRenderBuffer->unref(); |
+ } |
+ |
+ fRenderBuffer = renderBuffer; |
+ |
+ if (fRenderBuffer) { |
+ GrAlwaysAssert(!fRenderBuffer->getDeleted()); |
+ fRenderBuffer->ref(); |
+ |
+ GrAlwaysAssert(!fRenderBuffer->getBound()); |
+ fRenderBuffer->setBound(); |
+ } |
+ } |
+ GrRenderBufferObj *getRenderBuffer() { return fRenderBuffer; } |
+ |
+ void useProgram(GrProgramObj *program) { |
+ if (fProgram) { |
+ GrAlwaysAssert(fProgram->getInUse()); |
+ fProgram->resetInUse(); |
+ |
+ GrAlwaysAssert(!fProgram->getDeleted()); |
+ fProgram->unref(); |
+ } |
+ |
+ fProgram = program; |
+ |
+ if (fProgram) { |
+ GrAlwaysAssert(!fProgram->getDeleted()); |
+ fProgram->ref(); |
+ |
+ GrAlwaysAssert(!fProgram->getInUse()); |
+ fProgram->setInUse(); |
+ } |
+ } |
+ |
+ void report() const { |
+ for (int i = 0; i < fObjects.count(); ++i) { |
+ if (!fAbandoned) { |
+ GrAlwaysAssert(0 == fObjects[i]->getRefCount()); |
+ GrAlwaysAssert(fObjects[i]->getDeleted()); |
+ } |
+ } |
+ } |
+ |
+ typedef GrGLTestInterface INHERITED; |
+}; |
+ |
+#undef CREATE |
+#undef FIND |
+ |
+DebugInterface::Create DebugInterface::gFactoryFunc[kObjTypeCount] = { |
+ GrTextureObj::createGrTextureObj, |
+ GrBufferObj::createGrBufferObj, |
+ GrRenderBufferObj::createGrRenderBufferObj, |
+ GrFrameBufferObj::createGrFrameBufferObj, |
+ GrShaderObj::createGrShaderObj, |
+ GrProgramObj::createGrProgramObj, |
+ GrTextureUnitObj::createGrTextureUnitObj, |
+ GrVertexArrayObj::createGrVertexArrayObj, |
+}; |
+ |
+const char* DebugInterface::kExtensions[] = { |
+ "GL_ARB_framebuffer_object", |
+ "GL_ARB_blend_func_extended", |
+ "GL_ARB_timer_query", |
+ "GL_ARB_draw_buffers", |
+ "GL_ARB_occlusion_query", |
+ "GL_EXT_stencil_wrap", |
+ nullptr, // signifies the end of the array. |
+}; |
+ |
+} // anonymous namespace |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+ |
+const GrGLInterface* GrGLCreateDebugInterface() { return new DebugInterface; } |