/* * 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 "SkMutex.h" #include "SkTo.h" #include "gl/GrGLTestInterface.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(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(this->findObject(id, classEnum)) class DebugInterface : public GrGLTestInterface { public: DebugInterface() : fCurrGenericID(0) , fCurrTextureUnit(0) , 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(this->createObj(kTextureUnit_ObjTypes)); fTextureUnits[i]->ref(); fTextureUnits[i]->setNumber(i); } memset(fBoundBuffers, 0, sizeof(fBoundBuffers)); 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(); memset(fBoundBuffers, 0, sizeof(fBoundBuffers)); fVertexArray = nullptr; this->report(); } void abandon() const override { INHERITED::abandon(); 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); } GrGLboolean isTexture(GrGLuint textureID) override { GrTextureObj *texture = FIND(textureID, GrTextureObj, kTexture_ObjTypes); return texture ? GR_GL_TRUE : GR_GL_FALSE; } //////////////////////////////////////////////////////////////////////////////// GrGLvoid bufferData(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data, GrGLenum usage) override { GrAlwaysAssert(size >= 0); GrAlwaysAssert(GR_GL_STREAM_DRAW == usage || GR_GL_STATIC_DRAW == usage || GR_GL_DYNAMIC_DRAW == usage); GrBufferObj *buffer = fBoundBuffers[GetBufferIndex(target)]; GrAlwaysAssert(buffer); GrAlwaysAssert(buffer->getBound()); buffer->allocate(size, reinterpret_cast(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(alignment)); rowStride = static_cast(alignment * fTemp / componentSize); } GrGLchar *scanline = static_cast(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 renderbufferStorage(GrGLenum target, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) override { GrAlwaysAssert(GR_GL_RENDERBUFFER == target); GrRenderBufferObj* renderBuffer = this->getRenderBuffer(); GrAlwaysAssert(renderBuffer); renderBuffer->setNumSamples(1); } GrGLvoid renderbufferStorageMultisample(GrGLenum target, GrGLsizei samples, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) override { GrAlwaysAssert(GR_GL_RENDERBUFFER == target); GrRenderBufferObj* renderBuffer = this->getRenderBuffer(); GrAlwaysAssert(renderBuffer); renderBuffer->setNumSamples(samples); } GrGLvoid namedRenderbufferStorage(GrGLuint renderbuffer, GrGLenum GrGLinternalformat, GrGLsizei width, GrGLsizei height) override { SK_ABORT("Not implemented"); } GrGLvoid namedRenderbufferStorageMultisample(GrGLuint renderbuffer, GrGLsizei samples, GrGLenum GrGLinternalformat, GrGLsizei width, GrGLsizei height) override { SK_ABORT("Not implemented"); } 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 namedFramebufferRenderbuffer(GrGLuint framebuffer, GrGLenum attachment, GrGLenum renderbuffertarget, GrGLuint renderbuffer) override { SK_ABORT("Not implemented"); } //////////////////////////////////////////////////////////////////////////////// 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; }; } GrGLvoid framebufferTexture2DMultisample(GrGLenum target, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level, GrGLsizei samples) override { SK_ABORT("Not implemented"); } GrGLvoid namedFramebufferTexture1D(GrGLuint framebuffer, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) override { SK_ABORT("Not implemented"); } GrGLvoid namedFramebufferTexture2D(GrGLuint framebuffer, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) override { SK_ABORT("Not implemented"); } GrGLvoid namedFramebufferTexture3D(GrGLuint framebuffer, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level, GrGLint zoffset) override { SK_ABORT("Not implemented"); } 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: { GrFrameBufferObj* framebuffer = this->getFrameBuffer(); GrAlwaysAssert(framebuffer); int numSamples = 0; if (GrFBBindableObj* stencil = framebuffer->getStencil()) { numSamples = stencil->numSamples(); } if (GrFBBindableObj* depth = framebuffer->getDepth()) { GrAlwaysAssert(!numSamples || numSamples == depth->numSamples()); numSamples = depth->numSamples(); } if (GrFBBindableObj* color = framebuffer->getColor()) { GrAlwaysAssert(!numSamples || numSamples == color->numSamples()); numSamples = color->numSamples(); } GrAlwaysAssert(numSamples); *params = numSamples; 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: SK_ABORT("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: SK_ABORT("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: SK_ABORT("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: SK_ABORT("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. SK_ABORT("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(sk_ref_sp(array)); } GrGLvoid bindBuffer(GrGLenum target, GrGLuint bufferID) override { GrBufferObj *buffer = FIND(bufferID, GrBufferObj, kBuffer_ObjTypes); // 0 is a permissible bufferID - it unbinds the current buffer this->setBuffer(GetBufferIndex(target), buffer); } // 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 buffIdx = 0; buffIdx < kNumBufferTargets; ++buffIdx) { GrBufferObj* buffer = fBoundBuffers[buffIdx]; if (!buffer) { continue; } for (int i = 0; i < n; ++i) { if (ids[i] == buffer->getID()) { this->setBuffer(buffIdx, nullptr); break; } } } // 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 { // 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 = fBoundBuffers[GetBufferIndex(target)]; 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 = fBoundBuffers[GetBufferIndex(target)]; 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 { GrBufferObj *buffer = fBoundBuffers[GetBufferIndex(target)]; 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 { GrBufferObj *buffer = fBoundBuffers[GetBufferIndex(target)]; 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_BUFFER_SIZE == value || GR_GL_BUFFER_USAGE == value); GrBufferObj *buffer = fBoundBuffers[GetBufferIndex(target)]; 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: SK_ABORT("Unexpected value to glGetBufferParamateriv"); break; } } private: inline int static GetBufferIndex(GrGLenum glTarget) { switch (glTarget) { default: SK_ABORT("Unexpected GL target to GetBufferIndex"); case GR_GL_ARRAY_BUFFER: return 0; case GR_GL_ELEMENT_ARRAY_BUFFER: return 1; case GR_GL_TEXTURE_BUFFER: return 2; case GR_GL_DRAW_INDIRECT_BUFFER: return 3; } } constexpr int static kNumBufferTargets = 4; // 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* fBoundBuffers[kNumBufferTargets]; sk_sp fVertexArray; GrGLint fPackRowLength; GrGLint fUnpackRowLength; GrGLint fPackAlignment; GrFrameBufferObj* fFrameBuffer; GrRenderBufferObj* fRenderBuffer; GrProgramObj* fProgram; mutable bool fAbandoned; // global store of all objects SkTArray 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: // fallthru case GL_PROGRAM_BINARY_LENGTH: *params = 0; break; // we don't expect any other pnames default: SK_ABORT("Unexpected pname to GetProgramiv"); break; } } template 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: SK_ABORT("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]; } GrGLvoid setBuffer(int buffIdx, GrBufferObj* buffer) { if (fBoundBuffers[buffIdx]) { // automatically break the binding of the old buffer GrAlwaysAssert(fBoundBuffers[buffIdx]->getBound()); fBoundBuffers[buffIdx]->resetBound(); GrAlwaysAssert(!fBoundBuffers[buffIdx]->getDeleted()); fBoundBuffers[buffIdx]->unref(); } if (buffer) { GrAlwaysAssert(!buffer->getDeleted()); buffer->ref(); GrAlwaysAssert(!buffer->getBound()); buffer->setBound(); } fBoundBuffers[buffIdx] = buffer; } void setVertexArray(sk_sp vertexArray) { if (vertexArray) { SkASSERT(!vertexArray->getDeleted()); } fVertexArray = std::move(vertexArray); } GrVertexArrayObj* getVertexArray() { return fVertexArray.get(); } 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(sk_make_sp()); } ~DebugGLContext() override { this->teardown(); } private: void onPlatformMakeCurrent() const override {} std::function onPlatformGetAutoContextRestore() const override { return nullptr; } void onPlatformSwapBuffers() const override {} GrGLFuncPtr onPlatformGetProcAddress(const char*) const override { return nullptr; } }; } // anonymous namespace namespace sk_gpu_test { GLTestContext* CreateDebugGLTestContext(GLTestContext* shareContext) { if (shareContext) { return nullptr; } GLTestContext* ctx = new DebugGLContext(); if (ctx->isValid()) { return ctx; } delete ctx; return nullptr; } }