| |
| /* |
| * 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 "DebugGLTestContext.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. |
| }; |
| |
| class DebugGLContext : public sk_gpu_test::GLTestContext { |
| public: |
| DebugGLContext() { |
| this->init(new DebugInterface()); |
| } |
| |
| ~DebugGLContext() override { this->teardown(); } |
| |
| private: |
| void onPlatformMakeCurrent() const override {} |
| void onPlatformSwapBuffers() const override {} |
| GrGLFuncPtr onPlatformGetProcAddress(const char*) const override { return nullptr; } |
| }; |
| } // anonymous namespace |
| |
| namespace sk_gpu_test { |
| GLTestContext* CreateDebugGLTestContext() { |
| GLTestContext* ctx = new DebugGLContext(); |
| if (ctx->isValid()) { |
| return ctx; |
| } |
| delete ctx; |
| return nullptr; |
| } |
| } |