From 29994cba1a9177f8e113a9aa4ffd68d849c7d216 Mon Sep 17 00:00:00 2001 From: Yochai Shefi Simchon Date: Thu, 4 Aug 2011 14:28:05 +0300 Subject: [PATCH] opengles emulator: make glFramebufferTexture2D work The GLES and OpenGL specs for glFramebufferTexture2D are different, which caused valid GLES calls to fail when moved over to OpenGL. Specifically, a framebuffer in OpenGL must have a color attachment in order to be valid for draw, while in GLES it doesn't. So add a validate function, and call it from each drawing API call. This patch also contains a workaround for a bug in the ATI driver, where changing the framebuffer attachments requires a rebind of the framebuffer. Change-Id: I011fbe0e2b1c66564322268868a24a9d5958a434 --- .../libs/Translator/GLES_CM/GLEScmImp.cpp | 8 ++ .../libs/Translator/GLES_V2/GLESv2Imp.cpp | 7 +- .../Translator/GLcommon/FramebufferData.cpp | 116 +++++++++++++++++- .../libs/Translator/GLcommon/GLEScontext.cpp | 15 +++ .../Translator/GLcommon/objectNameManager.cpp | 20 +++ .../include/GLcommon/FramebufferData.h | 11 +- .../Translator/include/GLcommon/GLEScontext.h | 1 + .../include/GLcommon/objectNameManager.h | 10 ++ 8 files changed, 180 insertions(+), 8 deletions(-) diff --git a/tools/emulator/opengl/host/libs/Translator/GLES_CM/GLEScmImp.cpp b/tools/emulator/opengl/host/libs/Translator/GLES_CM/GLEScmImp.cpp index 0ab7ab5a7..9ef936275 100644 --- a/tools/emulator/opengl/host/libs/Translator/GLES_CM/GLEScmImp.cpp +++ b/tools/emulator/opengl/host/libs/Translator/GLES_CM/GLEScmImp.cpp @@ -354,6 +354,8 @@ GL_API void GL_APIENTRY glBufferSubData( GLenum target, GLintptr offset, GLsize GL_API void GL_APIENTRY glClear( GLbitfield mask) { GET_CTX() + ctx->drawValidate(); + ctx->dispatcher().glClear(mask); } @@ -557,6 +559,8 @@ GL_API void GL_APIENTRY glDrawArrays( GLenum mode, GLint first, GLsizei count) SET_ERROR_IF(count < 0,GL_INVALID_VALUE) SET_ERROR_IF(!GLEScmValidate::drawMode(mode),GL_INVALID_ENUM) + ctx->drawValidate(); + if(!ctx->isArrEnabled(GL_VERTEX_ARRAY)) return; GLESConversionArrays tmpArrs; @@ -576,6 +580,8 @@ GL_API void GL_APIENTRY glDrawElements( GLenum mode, GLsizei count, GLenum type SET_ERROR_IF((!GLEScmValidate::drawMode(mode) || !GLEScmValidate::drawType(type)),GL_INVALID_ENUM) if(!ctx->isArrEnabled(GL_VERTEX_ARRAY)) return; + ctx->drawValidate(); + const GLvoid* indices = elementsIndices; GLESConversionArrays tmpArrs; if(ctx->isBindedBuffer(GL_ELEMENT_ARRAY_BUFFER)) { // if vbo is binded take the indices from the vbo @@ -2215,6 +2221,8 @@ void glDrawTexOES (T x, T y, T z, T width, T height) { SET_ERROR_IF((width<=0 || height<=0),GL_INVALID_VALUE); + ctx->drawValidate(); + int numClipPlanes; GLint viewport[4]; diff --git a/tools/emulator/opengl/host/libs/Translator/GLES_V2/GLESv2Imp.cpp b/tools/emulator/opengl/host/libs/Translator/GLES_V2/GLESv2Imp.cpp index e82c831cc..f696ea6ca 100644 --- a/tools/emulator/opengl/host/libs/Translator/GLES_V2/GLESv2Imp.cpp +++ b/tools/emulator/opengl/host/libs/Translator/GLES_V2/GLESv2Imp.cpp @@ -317,6 +317,8 @@ GL_APICALL GLenum GL_APIENTRY glCheckFramebufferStatus(GLenum target){ GL_APICALL void GL_APIENTRY glClear(GLbitfield mask){ GET_CTX(); + ctx->drawValidate(); + ctx->dispatcher().glClear(mask); } GL_APICALL void GL_APIENTRY glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha){ @@ -535,12 +537,13 @@ GL_APICALL void GL_APIENTRY glDisableVertexAttribArray(GLuint index){ ctx->dispatcher().glDisableVertexAttribArray(index); } - GL_APICALL void GL_APIENTRY glDrawArrays(GLenum mode, GLint first, GLsizei count){ GET_CTX(); SET_ERROR_IF(count < 0,GL_INVALID_VALUE) SET_ERROR_IF(!GLESv2Validate::drawMode(mode),GL_INVALID_ENUM); + ctx->drawValidate(); + GLESConversionArrays tmpArrs; ctx->setupArraysPointers(tmpArrs,first,count,0,NULL,true); @@ -564,6 +567,8 @@ GL_APICALL void GL_APIENTRY glDrawElements(GLenum mode, GLsizei count, GLenum t SET_ERROR_IF(count < 0,GL_INVALID_VALUE) SET_ERROR_IF(!(GLESv2Validate::drawMode(mode) && GLESv2Validate::drawType(type)),GL_INVALID_ENUM); + ctx->drawValidate(); + const GLvoid* indices = elementsIndices; if(ctx->isBindedBuffer(GL_ELEMENT_ARRAY_BUFFER)) { // if vbo is binded take the indices from the vbo const unsigned char* buf = static_cast(ctx->getBindedBuffer(GL_ELEMENT_ARRAY_BUFFER)); diff --git a/tools/emulator/opengl/host/libs/Translator/GLcommon/FramebufferData.cpp b/tools/emulator/opengl/host/libs/Translator/GLcommon/FramebufferData.cpp index 1e38ba8d9..eb8531171 100644 --- a/tools/emulator/opengl/host/libs/Translator/GLcommon/FramebufferData.cpp +++ b/tools/emulator/opengl/host/libs/Translator/GLcommon/FramebufferData.cpp @@ -16,6 +16,7 @@ #include #include #include +#include RenderbufferData::RenderbufferData() : sourceEGLImage(0), eglImageDetach(NULL), @@ -29,7 +30,7 @@ RenderbufferData::~RenderbufferData() { } -FramebufferData::FramebufferData(GLuint name) { +FramebufferData::FramebufferData(GLuint name):m_dirty(false) { m_fbName = name; for (int i=0; iattachedFB = m_fbName; rbData->attachedPoint = attachment; } + + m_dirty = true; } } @@ -85,7 +91,7 @@ int FramebufferData::attachmentPointIndex(GLenum attachment) case GL_DEPTH_ATTACHMENT_OES: return 1; case GL_STENCIL_ATTACHMENT_OES: - return 3; + return 2; default: return MAX_ATTACH_POINTS; } @@ -97,7 +103,109 @@ void FramebufferData::detachObject(int idx) { rbData->attachedFB = 0; rbData->attachedPoint = 0; } + + if(m_attachPoints[idx].owned) + { + switch(m_attachPoints[idx].target) + { + case GL_RENDERBUFFER_OES: + GLEScontext::dispatcher().glDeleteRenderbuffersEXT(1, &(m_attachPoints[idx].name)); + break; + case GL_TEXTURE_2D: + GLEScontext::dispatcher().glDeleteTextures(1, &(m_attachPoints[idx].name)); + break; + } + } + m_attachPoints[idx].target = 0; m_attachPoints[idx].name = 0; m_attachPoints[idx].obj = ObjectDataPtr(NULL); + m_attachPoints[idx].owned = false; } + +void FramebufferData::validate(GLEScontext* ctx) +{ + if(!getAttachment(GL_COLOR_ATTACHMENT0_OES, NULL, NULL)) + { + // GLES does not require the framebuffer to have a color attachment. + // OpenGL does. Therefore, if no color is attached, create a dummy + // color texture and attach it. + // This dummy color texture will is owned by the FramebufferObject, + // and will be released by it when its object is detached. + + GLint type = GL_NONE; + GLint name = 0; + + ctx->dispatcher().glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT_OES, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &type); + if(type != GL_NONE) + { + ctx->dispatcher().glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT_OES, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &name); + } + else + { + ctx->dispatcher().glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT_OES, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &type); + if(type != GL_NONE) + { + ctx->dispatcher().glGetFramebufferAttachmentParameterivEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT_OES, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &name); + } + else + { + // No color, depth or stencil attachments - do nothing + return; + } + } + + // Find the existing attachment(s) dimensions + GLint width = 0; + GLint height = 0; + + if(type == GL_RENDERBUFFER) + { + GLint prev; + ctx->dispatcher().glGetIntegerv(GL_RENDERBUFFER_BINDING, &prev); + ctx->dispatcher().glBindRenderbufferEXT(GL_RENDERBUFFER, name); + ctx->dispatcher().glGetRenderbufferParameterivEXT(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width); + ctx->dispatcher().glGetRenderbufferParameterivEXT(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height); + ctx->dispatcher().glBindRenderbufferEXT(GL_RENDERBUFFER, prev); + } + else if(type == GL_TEXTURE) + { + GLint prev; + ctx->dispatcher().glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev); + ctx->dispatcher().glBindTexture(GL_TEXTURE_2D, name); + ctx->dispatcher().glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width); + ctx->dispatcher().glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height); + ctx->dispatcher().glBindTexture(GL_TEXTURE_2D, prev); + } + + // Create the color attachment and attch it + unsigned int tex = ctx->shareGroup()->genGlobalName(TEXTURE); + GLint prev; + ctx->dispatcher().glGetIntegerv(GL_TEXTURE_BINDING_2D, &prev); + ctx->dispatcher().glBindTexture(GL_TEXTURE_2D, tex); + + ctx->dispatcher().glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + ctx->dispatcher().glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + ctx->dispatcher().glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + ctx->dispatcher().glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + ctx->dispatcher().glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + ctx->dispatcher().glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tex, 0); + setAttachment(GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tex, ObjectDataPtr(NULL), true); + + ctx->dispatcher().glBindTexture(GL_TEXTURE_2D, prev); + } + + if(m_dirty) + { + // This is a workaround for a bug found in several OpenGL + // drivers (e.g. ATI's) - after the framebuffer attachments + // have changed, and before the next draw, unbind and rebind + // the framebuffer to sort things out. + ctx->dispatcher().glBindFramebufferEXT(GL_FRAMEBUFFER,0); + ctx->dispatcher().glBindFramebufferEXT(GL_FRAMEBUFFER,ctx->shareGroup()->getGlobalName(FRAMEBUFFER,m_fbName)); + + m_dirty = false; + } +} + diff --git a/tools/emulator/opengl/host/libs/Translator/GLcommon/GLEScontext.cpp b/tools/emulator/opengl/host/libs/Translator/GLcommon/GLEScontext.cpp index b2e99cd13..3c4c26c06 100644 --- a/tools/emulator/opengl/host/libs/Translator/GLcommon/GLEScontext.cpp +++ b/tools/emulator/opengl/host/libs/Translator/GLcommon/GLEScontext.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include //decleration @@ -676,3 +677,17 @@ ObjectLocalName GLEScontext::getDefaultTextureName(GLenum target) { } return name; } + +void GLEScontext::drawValidate(void) +{ + if(m_framebuffer == 0) + return; + + ObjectDataPtr fbObj = m_shareGroup->getObjectData(FRAMEBUFFER,m_framebuffer); + if (fbObj.Ptr() == NULL) + return; + + FramebufferData *fbData = (FramebufferData *)fbObj.Ptr(); + + fbData->validate(this); +} diff --git a/tools/emulator/opengl/host/libs/Translator/GLcommon/objectNameManager.cpp b/tools/emulator/opengl/host/libs/Translator/GLcommon/objectNameManager.cpp index 950544fbd..cfea8553e 100644 --- a/tools/emulator/opengl/host/libs/Translator/GLcommon/objectNameManager.cpp +++ b/tools/emulator/opengl/host/libs/Translator/GLcommon/objectNameManager.cpp @@ -53,6 +53,13 @@ NameSpace::genName(ObjectLocalName p_localName, bool genGlobal, bool genLocal) return localName; } + +unsigned int +NameSpace::genGlobalName(void) +{ + return m_globalNameSpace->genName(m_type); +} + unsigned int NameSpace::getGlobalName(ObjectLocalName p_localName) { @@ -190,6 +197,18 @@ ShareGroup::genName(NamedObjectType p_type, ObjectLocalName p_localName, bool ge return localName; } +unsigned int +ShareGroup::genGlobalName(NamedObjectType p_type) +{ + if (p_type >= NUM_OBJECT_TYPES) return 0; + + mutex_lock(&m_lock); + unsigned int name = m_nameSpace[p_type]->genGlobalName(); + mutex_unlock(&m_lock); + + return name; +} + unsigned int ShareGroup::getGlobalName(NamedObjectType p_type, ObjectLocalName p_localName) { @@ -389,3 +408,4 @@ void *ObjectNameManager::getGlobalContext() return ret; } + diff --git a/tools/emulator/opengl/host/libs/Translator/include/GLcommon/FramebufferData.h b/tools/emulator/opengl/host/libs/Translator/include/GLcommon/FramebufferData.h index c6ee3b496..46cb651f5 100644 --- a/tools/emulator/opengl/host/libs/Translator/include/GLcommon/FramebufferData.h +++ b/tools/emulator/opengl/host/libs/Translator/include/GLcommon/FramebufferData.h @@ -45,12 +45,15 @@ public: void setAttachment(GLenum attachment, GLenum target, GLuint name, - ObjectDataPtr obj); + ObjectDataPtr obj, + bool takeOwnership = false); GLuint getAttachment(GLenum attachment, GLenum *outTarget, ObjectDataPtr *outObj); + void validate(class GLEScontext* ctx); + private: inline int attachmentPointIndex(GLenum attachment); void detachObject(int idx); @@ -58,10 +61,12 @@ private: private: GLuint m_fbName; struct attachPoint { - GLenum target; - GLuint name; + GLenum target; // OGL if owned, GLES otherwise + GLuint name; // OGL if owned, GLES otherwise ObjectDataPtr obj; + bool owned; } m_attachPoints[MAX_ATTACH_POINTS+1]; + bool m_dirty; }; #endif diff --git a/tools/emulator/opengl/host/libs/Translator/include/GLcommon/GLEScontext.h b/tools/emulator/opengl/host/libs/Translator/include/GLcommon/GLEScontext.h index 8b7007271..0bb830d17 100644 --- a/tools/emulator/opengl/host/libs/Translator/include/GLcommon/GLEScontext.h +++ b/tools/emulator/opengl/host/libs/Translator/include/GLcommon/GLEScontext.h @@ -135,6 +135,7 @@ public: virtual GLSupport* getCaps(){return &s_glSupport;}; virtual ~GLEScontext(); virtual int getMaxTexUnits() = 0; + virtual void drawValidate(void); void setRenderbufferBinding(GLuint rb) { m_renderbuffer = rb; } GLuint getRenderbufferBinding() const { return m_renderbuffer; } diff --git a/tools/emulator/opengl/host/libs/Translator/include/GLcommon/objectNameManager.h b/tools/emulator/opengl/host/libs/Translator/include/GLcommon/objectNameManager.h index 833b38240..605fd2928 100644 --- a/tools/emulator/opengl/host/libs/Translator/include/GLcommon/objectNameManager.h +++ b/tools/emulator/opengl/host/libs/Translator/include/GLcommon/objectNameManager.h @@ -80,6 +80,11 @@ private: // ObjectLocalName genName(ObjectLocalName p_localName, bool genGlobal, bool genLocal); + // genGlobalName() - This function creates a global name + // with no associated local name, for the + // translator internal use. + unsigned int genGlobalName(void); + // // getGlobalName - returns the global name of an object or 0 if the object // does not exist. @@ -150,6 +155,11 @@ public: // ObjectLocalName genName(NamedObjectType p_type, ObjectLocalName p_localName = 0, bool genLocal= false); + // genGlobalName() - This function creates a global name + // with no associated local name, for the + // translator internal use. + unsigned int genGlobalName(NamedObjectType p_type); + // // getGlobalName - retrieves the "global" name of an object or 0 if the // object does not exist.