/* * Copyright 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "aemu/base/synchronization/Lock.h" #include "aemu/base/containers/Lookup.h" #include "aemu/base/files/StreamSerializing.h" #include "host-common/logging.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _MSC_VER #include #endif #include #include #include //decleration static void convertFixedDirectLoop(const char* dataIn,unsigned int strideIn,void* dataOut,unsigned int nBytes,unsigned int strideOut,int attribSize); static void convertFixedIndirectLoop(const char* dataIn,unsigned int strideIn,void* dataOut,GLsizei count,GLenum indices_type,const GLvoid* indices,unsigned int strideOut,int attribSize); static void convertByteDirectLoop(const char* dataIn,unsigned int strideIn,void* dataOut,unsigned int nBytes,unsigned int strideOut,int attribSize); static void convertByteIndirectLoop(const char* dataIn,unsigned int strideIn,void* dataOut,GLsizei count,GLenum indices_type,const GLvoid* indices,unsigned int strideOut,int attribSize); void BufferBinding::onLoad(android::base::Stream* stream) { buffer = stream->getBe32(); offset = stream->getBe32(); size = stream->getBe32(); stride = stream->getBe32(); divisor = stream->getBe32(); isBindBase = stream->getByte(); } void BufferBinding::onSave(android::base::Stream* stream) const { stream->putBe32(buffer); stream->putBe32(offset); stream->putBe32(size); stream->putBe32(stride); stream->putBe32(divisor); stream->putByte(isBindBase); } VAOState::VAOState(android::base::Stream* stream) { element_array_buffer_binding = stream->getBe32(); vertexAttribInfo.clear(); for (uint32_t i = 0; i < kMaxVertexAttributes; ++i) { vertexAttribInfo.emplace_back(stream); } uint64_t arraysMapPtr = stream->getBe64(); if (arraysMapPtr) { arraysMap.reset(new ArraysMap()); size_t mapSize = stream->getBe32(); for (size_t i = 0; i < mapSize; i++) { GLuint id = stream->getBe32(); arraysMap->emplace(id, new GLESpointer(stream)); } legacy = true; } else { arraysMap.reset(); } loadContainer(stream, bindingState); bufferBacked = stream->getByte(); everBound = stream->getByte(); } void VAOState::onSave(android::base::Stream* stream) const { stream->putBe32(element_array_buffer_binding); for (uint32_t i = 0; i < kMaxVertexAttributes; ++i) { vertexAttribInfo[i].onSave(stream); } if (arraysMap) { stream->putBe64((uint64_t)(uintptr_t)arraysMap.get()); } else { stream->putBe64(0); } if (arraysMap) { stream->putBe32(arraysMap->size()); for (const auto& ite : *arraysMap) { stream->putBe32(ite.first); assert(ite.second); ite.second->onSave(stream); } } saveContainer(stream, bindingState); stream->putByte(bufferBacked); stream->putByte(everBound); } GLESConversionArrays::~GLESConversionArrays() { for(auto it = m_arrays.begin(); it != m_arrays.end(); ++it) { if((*it).second.allocated){ if((*it).second.type == GL_FLOAT){ GLfloat* p = (GLfloat *)((*it).second.data); if(p) delete[] p; } else if((*it).second.type == GL_SHORT){ GLshort* p = (GLshort *)((*it).second.data); if(p) delete[] p; } } } } void GLESConversionArrays::allocArr(unsigned int size,GLenum type){ if(type == GL_FIXED){ m_arrays[m_current].data = new GLfloat[size]; m_arrays[m_current].type = GL_FLOAT; } else if(type == GL_BYTE){ m_arrays[m_current].data = new GLshort[size]; m_arrays[m_current].type = GL_SHORT; } m_arrays[m_current].stride = 0; m_arrays[m_current].allocated = true; } void GLESConversionArrays::setArr(void* data,unsigned int stride,GLenum type){ m_arrays[m_current].type = type; m_arrays[m_current].data = data; m_arrays[m_current].stride = stride; m_arrays[m_current].allocated = false; } void* GLESConversionArrays::getCurrentData(){ return m_arrays[m_current].data; } ArrayData& GLESConversionArrays::getCurrentArray(){ return m_arrays[m_current]; } unsigned int GLESConversionArrays::getCurrentIndex(){ return m_current; } ArrayData& GLESConversionArrays::operator[](int i){ return m_arrays[i]; } void GLESConversionArrays::operator++(){ m_current++; } GLDispatch GLEScontext::s_glDispatch; android::base::Lock GLEScontext::s_lock; std::string* GLEScontext::s_glExtensionsGles1 = NULL; bool GLEScontext::s_glExtensionsGles1Initialized = false; std::string* GLEScontext::s_glExtensionsGles31 = NULL; bool GLEScontext::s_glExtensionsGles31Initialized = false; std::string* GLEScontext::s_glExtensions = NULL; bool GLEScontext::s_glExtensionsInitialized = false; std::string GLEScontext::s_glVendorGles1; std::string GLEScontext::s_glRendererGles1; std::string GLEScontext::s_glVersionGles1; std::string GLEScontext::s_glVendorGles31; std::string GLEScontext::s_glRendererGles31; std::string GLEScontext::s_glVersionGles31; std::string GLEScontext::s_glVendor; std::string GLEScontext::s_glRenderer; std::string GLEScontext::s_glVersion; GLSupport GLEScontext::s_glSupport; GLSupport GLEScontext::s_glSupportGles1; GLSupport GLEScontext::s_glSupportGles31; Version::Version(int major,int minor,int release):m_major(major), m_minor(minor), m_release(release){}; Version::Version(const Version& ver):m_major(ver.m_major), m_minor(ver.m_minor), m_release(ver.m_release){} Version::Version(const char* versionString){ m_release = 0; if((!versionString) || ((!(sscanf(versionString,"%d.%d" ,&m_major,&m_minor) == 2)) && (!(sscanf(versionString,"%d.%d.%d",&m_major,&m_minor,&m_release) == 3)))){ m_major = m_minor = 0; // the version is not in the right format } } Version& Version::operator=(const Version& ver){ m_major = ver.m_major; m_minor = ver.m_minor; m_release = ver.m_release; return *this; } bool Version::operator<(const Version& ver) const{ if(m_major < ver.m_major) return true; if(m_major == ver.m_major){ if(m_minor < ver.m_minor) return true; if(m_minor == ver.m_minor){ return m_release < ver.m_release; } } return false; } std::string getHostExtensionsString(GLDispatch* dispatch) { // glGetString(GL_EXTENSIONS) is deprecated in GL 3.0, one has to use // glGetStringi(GL_EXTENSIONS, index) instead to get individual extension // names. Recent desktop drivers implement glGetStringi() but have a // version of glGetString() that returns NULL, so deal with this by // doing the following: // // - If glGetStringi() is available, and glGetIntegerv(GL_NUM_EXTENSIONS &num_exts) // gives a nonzero value, use it to build the extensions // string, using simple spaces to separate the names. // // - Otherwise, fallback to getGetString(). If it returns NULL, return // an empty string. // std::string result; int num_exts = 0; if (dispatch->glGetStringi) { dispatch->glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts); GLenum err = dispatch->glGetError(); if (err == GL_NO_ERROR) { for (int n = 0; n < num_exts; n++) { const char* ext = reinterpret_cast( dispatch->glGetStringi(GL_EXTENSIONS, n)); if (ext != NULL) { if (!result.empty()) { result += " "; } result += ext; } } } } // If glGetIntegerv does not affect the value, // our system does not actually support // GL 3.0 style extension getting. if (!dispatch->glGetStringi || num_exts == 0) { const char* extensions = reinterpret_cast( dispatch->glGetString(GL_EXTENSIONS)); if (extensions) { result = extensions; } } // For the sake of initCapsLocked() add a starting and trailing space. if (!result.empty()) { if (result[0] != ' ') { result.insert(0, 1, ' '); } if (result[result.size() - 1U] != ' ') { result += ' '; } } return result; } static GLuint getIndex(GLenum indices_type, const GLvoid* indices, unsigned int i) { switch (indices_type) { case GL_UNSIGNED_BYTE: return static_cast(indices)[i]; case GL_UNSIGNED_SHORT: return static_cast(indices)[i]; case GL_UNSIGNED_INT: return static_cast(indices)[i]; default: ERR("**** ERROR unknown type 0x%x", indices_type); return 0; } } void GLEScontext::addVertexArrayObjects(GLsizei n, GLuint* arrays) { for (int i = 0; i < n; i++) { addVertexArrayObject(arrays[i]); } } void GLEScontext::removeVertexArrayObjects(GLsizei n, const GLuint* arrays) { for (int i = 0; i < n; i++) { removeVertexArrayObject(arrays[i]); } } void GLEScontext::addVertexArrayObject(GLuint array) { ArraysMap* map = new ArraysMap(); for (int i = 0; i < s_glSupport.maxVertexAttribs; i++) { map->insert( ArraysMap::value_type( i, new GLESpointer())); } assert(m_vaoStateMap.count(array) == 0); // Overwriting existing entry, leaking memory m_vaoStateMap[array] = VAOState(0, map, std::max(s_glSupport.maxVertexAttribs, s_glSupport.maxVertexAttribBindings)); } void GLEScontext::removeVertexArrayObject(GLuint array) { if (array == 0) return; if (m_vaoStateMap.find(array) == m_vaoStateMap.end()) return; if (array == m_currVaoState.vaoId()) { setVertexArrayObject(0); } auto& state = m_vaoStateMap[array]; if (state.arraysMap) { for (auto elem : *(state.arraysMap)) { delete elem.second; } } m_vaoStateMap.erase(array); } bool GLEScontext::setVertexArrayObject(GLuint array) { VAOStateMap::iterator it = m_vaoStateMap.find(array); if (it != m_vaoStateMap.end()) { m_currVaoState = VAOStateRef(it); return true; } return false; } void GLEScontext::setVAOEverBound() { m_currVaoState.setEverBound(); } GLuint GLEScontext::getVertexArrayObject() const { return m_currVaoState.vaoId(); } bool GLEScontext::vertexAttributesBufferBacked() { const auto& info = m_currVaoState.attribInfo_const(); for (uint32_t i = 0; i < kMaxVertexAttributes; ++i) { const auto& pointerInfo = info[i]; if (pointerInfo.isEnable() && !m_currVaoState.bufferBindings()[pointerInfo.getBindingIndex()].buffer) { return false; } } return true; } static EGLiface* s_eglIface = nullptr; // static EGLiface* GLEScontext::eglIface() { return s_eglIface; } // static void GLEScontext::initEglIface(EGLiface* iface) { if (!s_eglIface) s_eglIface = iface; } void GLEScontext::initGlobal(EGLiface* iface) { initEglIface(iface); s_lock.lock(); if (!s_glExtensions) { s_glExtensions = new std::string(); } if (!s_glExtensionsGles1) { s_glExtensionsGles1 = new std::string(); } if (!s_glExtensionsGles31) { s_glExtensionsGles31 = new std::string(); } s_lock.unlock(); } void GLEScontext::init(bool nativeTextureDecompressionEnabled, bool programBinaryLinkStatusEnabled) { if (!m_initialized) { m_nativeTextureDecompressionEnabled = nativeTextureDecompressionEnabled; m_programBinaryLinkStatusEnabled = programBinaryLinkStatusEnabled; initExtensionString(); m_maxTexUnits = getMaxCombinedTexUnits(); m_texState = new textureUnitState[m_maxTexUnits]; for (int i=0;imaxTransformFeedbackSeparateAttribs); m_indexedUniformBuffers.resize(getCaps()->maxUniformBufferBindings); m_indexedAtomicCounterBuffers.resize(getCaps()->maxAtomicCounterBufferBindings); m_indexedShaderStorageBuffers.resize(getCaps()->maxShaderStorageBufferBindings); m_blendStates.resize(getCaps()->ext_GL_EXT_draw_buffers_indexed ? getCaps()->maxDrawBuffers : 1); } } void GLEScontext::restore() { postLoadRestoreShareGroup(); if (m_needRestoreFromSnapshot) { postLoadRestoreCtx(); m_needRestoreFromSnapshot = false; } } bool GLEScontext::needRestore() { bool ret = m_needRestoreFromSnapshot; if (m_shareGroup) { ret |= m_shareGroup->needRestore(); } return ret; } GLenum GLEScontext::getGLerror() { return m_glError; } void GLEScontext::setGLerror(GLenum err) { m_glError = err; } void GLEScontext::setActiveTexture(GLenum tex) { m_activeTexture = tex - GL_TEXTURE0; m_maxUsedTexUnit = std::max(m_activeTexture, m_maxUsedTexUnit); } GLEScontext::GLEScontext() {} GLEScontext::GLEScontext(GlobalNameSpace* globalNameSpace, android::base::Stream* stream, GlLibrary* glLib) { if (stream) { m_initialized = stream->getByte(); m_glesMajorVersion = stream->getBe32(); m_glesMinorVersion = stream->getBe32(); if (m_initialized) { m_activeTexture = (GLuint)stream->getBe32(); loadNameMap(stream, m_vaoStateMap); uint32_t vaoId = stream->getBe32(); setVertexArrayObject(vaoId); m_copyReadBuffer = static_cast(stream->getBe32()); m_copyWriteBuffer = static_cast(stream->getBe32()); m_pixelPackBuffer = static_cast(stream->getBe32()); m_pixelUnpackBuffer = static_cast(stream->getBe32()); m_transformFeedbackBuffer = static_cast(stream->getBe32()); m_uniformBuffer = static_cast(stream->getBe32()); m_atomicCounterBuffer = static_cast(stream->getBe32()); m_dispatchIndirectBuffer = static_cast(stream->getBe32()); m_drawIndirectBuffer = static_cast(stream->getBe32()); m_shaderStorageBuffer = static_cast(stream->getBe32()); m_textureBuffer = static_cast(stream->getBe32()); loadContainer(stream, m_indexedTransformFeedbackBuffers); loadContainer(stream, m_indexedUniformBuffers); loadContainer(stream, m_indexedAtomicCounterBuffers); loadContainer(stream, m_indexedShaderStorageBuffers); // TODO: handle the case where the loaded size and the supported // side does not match m_isViewport = stream->getByte(); m_viewportX = static_cast(stream->getBe32()); m_viewportY = static_cast(stream->getBe32()); m_viewportWidth = static_cast(stream->getBe32()); m_viewportHeight = static_cast(stream->getBe32()); m_polygonOffsetFactor = static_cast(stream->getFloat()); m_polygonOffsetUnits = static_cast(stream->getFloat()); m_isScissor = stream->getByte(); m_scissorX = static_cast(stream->getBe32()); m_scissorY = static_cast(stream->getBe32()); m_scissorWidth = static_cast(stream->getBe32()); m_scissorHeight = static_cast(stream->getBe32()); loadCollection(stream, &m_glEnableList, [](android::base::Stream* stream) { GLenum item = stream->getBe32(); bool enabled = stream->getByte(); return std::make_pair(item, enabled); }); int blendStateCount = stream->getBe32(); m_blendStates.resize(blendStateCount); stream->read(m_blendStates.data(), sizeof(BlendState) * blendStateCount); loadCollection(stream, &m_glPixelStoreiList, [](android::base::Stream* stream) { GLenum item = stream->getBe32(); GLint val = stream->getBe32(); return std::make_pair(item, val); }); m_cullFace = static_cast(stream->getBe32()); m_frontFace = static_cast(stream->getBe32()); m_depthFunc = static_cast(stream->getBe32()); m_depthMask = static_cast(stream->getByte()); m_zNear = static_cast(stream->getFloat()); m_zFar = static_cast(stream->getFloat()); m_lineWidth = static_cast(stream->getFloat()); m_sampleCoverageVal = static_cast(stream->getFloat()); m_sampleCoverageInvert = static_cast(stream->getByte()); stream->read(m_stencilStates, sizeof(m_stencilStates)); m_clearColorR = static_cast(stream->getFloat()); m_clearColorG = static_cast(stream->getFloat()); m_clearColorB = static_cast(stream->getFloat()); m_clearColorA = static_cast(stream->getFloat()); m_clearDepth = static_cast(stream->getFloat()); m_clearStencil = static_cast(stream->getBe32()); // share group is supposed to be loaded by EglContext and reset // when loading EglContext //int sharegroupId = stream->getBe32(); m_glError = static_cast(stream->getBe32()); m_maxTexUnits = static_cast(stream->getBe32()); m_maxUsedTexUnit = static_cast(stream->getBe32()); m_texState = new textureUnitState[m_maxTexUnits]; stream->read(m_texState, sizeof(textureUnitState) * m_maxTexUnits); m_arrayBuffer = static_cast(stream->getBe32()); m_elementBuffer = static_cast(stream->getBe32()); m_renderbuffer = static_cast(stream->getBe32()); m_drawFramebuffer = static_cast(stream->getBe32()); m_readFramebuffer = static_cast(stream->getBe32()); m_defaultFBODrawBuffer = static_cast(stream->getBe32()); m_defaultFBOReadBuffer = static_cast(stream->getBe32()); m_needRestoreFromSnapshot = true; } } ObjectData::loadObject_t loader = [this](NamedObjectType type, long long unsigned int localName, android::base::Stream* stream) { return loadObject(type, localName, stream); }; m_fboNameSpace = new NameSpace(NamedObjectType::FRAMEBUFFER, globalNameSpace, stream, loader); // do not load m_vaoNameSpace m_vaoNameSpace = new NameSpace(NamedObjectType::VERTEX_ARRAY_OBJECT, globalNameSpace, nullptr, loader); } GLEScontext::~GLEScontext() { auto& gl = dispatcher(); if (m_blitState.program) { gl.glDeleteProgram(m_blitState.program); gl.glDeleteTextures(1, &m_blitState.tex); gl.glDeleteVertexArrays(1, &m_blitState.vao); gl.glDeleteBuffers(1, &m_blitState.vbo); gl.glDeleteFramebuffers(1, &m_blitState.fbo); } if (m_textureEmulationProg) { gl.glDeleteProgram(m_textureEmulationProg); gl.glDeleteTextures(2, m_textureEmulationTextures); gl.glDeleteFramebuffers(1, &m_textureEmulationFBO); gl.glDeleteVertexArrays(1, &m_textureEmulationVAO); } if (m_defaultFBO) { gl.glBindFramebuffer(GL_FRAMEBUFFER, m_defaultFBO); gl.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); gl.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); gl.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); gl.glBindFramebuffer(GL_FRAMEBUFFER, 0); gl.glDeleteFramebuffers(1, &m_defaultFBO); } if (m_defaultReadFBO && (m_defaultReadFBO != m_defaultFBO)) { gl.glBindFramebuffer(GL_READ_FRAMEBUFFER, m_defaultReadFBO); gl.glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); gl.glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); gl.glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); gl.glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); gl.glDeleteFramebuffers(1, &m_defaultReadFBO); } m_defaultFBO = 0; m_defaultReadFBO = 0; for (auto&& vao : m_vaoStateMap) { if (vao.second.arraysMap) { for (auto elem : *(vao.second.arraysMap)) { delete elem.second; } vao.second.arraysMap.reset(); } } delete[] m_texState; m_texState = nullptr; delete m_fboNameSpace; m_fboNameSpace = nullptr; delete m_vaoNameSpace; m_vaoNameSpace = nullptr; } void GLEScontext::postLoad() { m_fboNameSpace->postLoad( [this](NamedObjectType p_type, ObjectLocalName p_localName) { if (p_type == NamedObjectType::FRAMEBUFFER) { return this->getFBODataPtr(p_localName); } else { return m_shareGroup->getObjectDataPtr(p_type, p_localName); } }); } void GLEScontext::onSave(android::base::Stream* stream) const { stream->putByte(m_initialized); stream->putBe32(m_glesMajorVersion); stream->putBe32(m_glesMinorVersion); if (m_initialized) { stream->putBe32(m_activeTexture); saveNameMap(stream, m_vaoStateMap); stream->putBe32(getVertexArrayObject()); stream->putBe32(m_copyReadBuffer); stream->putBe32(m_copyWriteBuffer); stream->putBe32(m_pixelPackBuffer); stream->putBe32(m_pixelUnpackBuffer); stream->putBe32(m_transformFeedbackBuffer); stream->putBe32(m_uniformBuffer); stream->putBe32(m_atomicCounterBuffer); stream->putBe32(m_dispatchIndirectBuffer); stream->putBe32(m_drawIndirectBuffer); stream->putBe32(m_shaderStorageBuffer); stream->putBe32(m_textureBuffer); saveContainer(stream, m_indexedTransformFeedbackBuffers); saveContainer(stream, m_indexedUniformBuffers); saveContainer(stream, m_indexedAtomicCounterBuffers); saveContainer(stream, m_indexedShaderStorageBuffers); stream->putByte(m_isViewport); stream->putBe32(m_viewportX); stream->putBe32(m_viewportY); stream->putBe32(m_viewportWidth); stream->putBe32(m_viewportHeight); stream->putFloat(m_polygonOffsetFactor); stream->putFloat(m_polygonOffsetUnits); stream->putByte(m_isScissor); stream->putBe32(m_scissorX); stream->putBe32(m_scissorY); stream->putBe32(m_scissorWidth); stream->putBe32(m_scissorHeight); saveCollection(stream, m_glEnableList, [](android::base::Stream* stream, const std::pair& enableItem) { stream->putBe32(enableItem.first); stream->putByte(enableItem.second); }); stream->putBe32((int)m_blendStates.size()); stream->write(m_blendStates.data(), sizeof(BlendState) * m_blendStates.size()); saveCollection(stream, m_glPixelStoreiList, [](android::base::Stream* stream, const std::pair& pixelStore) { stream->putBe32(pixelStore.first); stream->putBe32(pixelStore.second); }); stream->putBe32(m_cullFace); stream->putBe32(m_frontFace); stream->putBe32(m_depthFunc); stream->putByte(m_depthMask); stream->putFloat(m_zNear); stream->putFloat(m_zFar); stream->putFloat(m_lineWidth); stream->putFloat(m_sampleCoverageVal); stream->putByte(m_sampleCoverageInvert); stream->write(m_stencilStates, sizeof(m_stencilStates)); stream->putFloat(m_clearColorR); stream->putFloat(m_clearColorG); stream->putFloat(m_clearColorB); stream->putFloat(m_clearColorA); stream->putFloat(m_clearDepth); stream->putBe32(m_clearStencil); // share group is supposed to be saved / loaded by EglContext stream->putBe32(m_glError); stream->putBe32(m_maxTexUnits); stream->putBe32(m_maxUsedTexUnit); stream->write(m_texState, sizeof(textureUnitState) * m_maxTexUnits); stream->putBe32(m_arrayBuffer); stream->putBe32(m_elementBuffer); stream->putBe32(m_renderbuffer); stream->putBe32(m_drawFramebuffer); stream->putBe32(m_readFramebuffer); stream->putBe32(m_defaultFBODrawBuffer); stream->putBe32(m_defaultFBOReadBuffer); } m_fboNameSpace->onSave(stream); // do not save m_vaoNameSpace } void GLEScontext::postSave(android::base::Stream* stream) const { (void)stream; // We need to mark the textures dirty, for those that has been bound to // a potential render target. for (ObjectDataMap::const_iterator it = m_fboNameSpace->objDataMapBegin(); it != m_fboNameSpace->objDataMapEnd(); it ++) { FramebufferData* fbData = (FramebufferData*)it->second.get(); fbData->makeTextureDirty([this](NamedObjectType p_type, ObjectLocalName p_localName) { if (p_type == NamedObjectType::FRAMEBUFFER) { return this->getFBODataPtr(p_localName); } else { return m_shareGroup->getObjectDataPtr(p_type, p_localName); } }); } } void GLEScontext::postLoadRestoreShareGroup() { m_shareGroup->postLoadRestore(); } void GLEScontext::postLoadRestoreCtx() { GLDispatch& dispatcher = GLEScontext::dispatcher(); assert(!m_shareGroup->needRestore()); m_fboNameSpace->postLoadRestore( [this](NamedObjectType p_type, ObjectLocalName p_localName) { if (p_type == NamedObjectType::FRAMEBUFFER) { return getFBOGlobalName(p_localName); } else { return m_shareGroup->getGlobalName(p_type, p_localName); } } ); // buffer bindings auto bindBuffer = [this](GLenum target, GLuint buffer) { this->dispatcher().glBindBuffer(target, m_shareGroup->getGlobalName(NamedObjectType::VERTEXBUFFER, buffer)); }; bindBuffer(GL_ARRAY_BUFFER, m_arrayBuffer); bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_currVaoState.iboId()); // framebuffer binding auto bindFrameBuffer = [this](GLenum target, GLuint buffer) { this->dispatcher().glBindFramebuffer(target, getFBOGlobalName(buffer)); }; bindFrameBuffer(GL_READ_FRAMEBUFFER, m_readFramebuffer); bindFrameBuffer(GL_DRAW_FRAMEBUFFER, m_drawFramebuffer); for (unsigned int i = 0; i <= m_maxUsedTexUnit; i++) { for (unsigned int j = 0; j < NUM_TEXTURE_TARGETS; j++) { textureTargetState& texState = m_texState[i][j]; if (texState.texture || texState.enabled) { this->dispatcher().glActiveTexture(i + GL_TEXTURE0); GLenum texTarget = GL_TEXTURE_2D; switch (j) { case TEXTURE_2D: texTarget = GL_TEXTURE_2D; break; case TEXTURE_CUBE_MAP: texTarget = GL_TEXTURE_CUBE_MAP; break; case TEXTURE_2D_ARRAY: texTarget = GL_TEXTURE_2D_ARRAY; break; case TEXTURE_3D: texTarget = GL_TEXTURE_3D; break; case TEXTURE_2D_MULTISAMPLE: texTarget = GL_TEXTURE_2D_MULTISAMPLE; break; case TEXTURE_BUFFER: texTarget = GL_TEXTURE_BUFFER; break; default: fprintf(stderr, "Warning: unsupported texture target 0x%x.\n", j); break; } // TODO: refactor the following line since it is duplicated in // GLESv2Imp and GLEScmImp as well ObjectLocalName texName = texState.texture != 0 ? texState.texture : getDefaultTextureName(texTarget); this->dispatcher().glBindTexture( texTarget, m_shareGroup->getGlobalName( NamedObjectType::TEXTURE, texName)); if (!isCoreProfile() && texState.enabled) { dispatcher.glEnable(texTarget); } } } } dispatcher.glActiveTexture(m_activeTexture + GL_TEXTURE0); // viewport & scissor if (m_isViewport) { dispatcher.glViewport(m_viewportX, m_viewportY, m_viewportWidth, m_viewportHeight); } if (m_isScissor) { dispatcher.glScissor(m_scissorX, m_scissorY, m_scissorWidth, m_scissorHeight); } dispatcher.glPolygonOffset(m_polygonOffsetFactor, m_polygonOffsetUnits); for (auto item : m_glEnableList) { if (item.first == GL_TEXTURE_2D || item.first == GL_TEXTURE_CUBE_MAP_OES) { continue; } std::function enableFunc = item.second ? dispatcher.glEnable : dispatcher.glDisable; if (item.first==GL_TEXTURE_GEN_STR_OES) { enableFunc(GL_TEXTURE_GEN_S); enableFunc(GL_TEXTURE_GEN_T); enableFunc(GL_TEXTURE_GEN_R); } else { enableFunc(item.first); } } if (getCaps()->ext_GL_EXT_draw_buffers_indexed) { for (GLuint i = 0; i < m_blendStates.size(); ++i) { if (m_blendStates[i].bEnable) dispatcher.glEnableiEXT(GL_BLEND, i); else dispatcher.glDisableiEXT(GL_BLEND, i); auto& blend = m_blendStates[i]; dispatcher.glBlendEquationSeparateiEXT(i, blend.blendEquationRgb, blend.blendEquationAlpha); dispatcher.glBlendFuncSeparateiEXT(i, blend.blendSrcRgb, blend.blendDstRgb, blend.blendSrcAlpha, blend.blendDstAlpha); dispatcher.glColorMaskiEXT(i, blend.colorMaskR, blend.colorMaskG, blend.colorMaskB, blend.colorMaskA); } } else { if (m_blendStates[0].bEnable) dispatcher.glEnable(GL_BLEND); else dispatcher.glDisable(GL_BLEND); auto& blend = m_blendStates[0]; dispatcher.glBlendEquationSeparate(blend.blendEquationRgb, blend.blendEquationAlpha); dispatcher.glBlendFuncSeparate(blend.blendSrcRgb, blend.blendDstRgb, blend.blendSrcAlpha, blend.blendDstAlpha); dispatcher.glColorMask(blend.colorMaskR, blend.colorMaskG, blend.colorMaskB, blend.colorMaskA); } for (const auto& pixelStore : m_glPixelStoreiList) { dispatcher.glPixelStorei(pixelStore.first, pixelStore.second); } dispatcher.glCullFace(m_cullFace); dispatcher.glFrontFace(m_frontFace); dispatcher.glDepthFunc(m_depthFunc); dispatcher.glDepthMask(m_depthMask); dispatcher.glLineWidth(m_lineWidth); dispatcher.glSampleCoverage(m_sampleCoverageVal, m_sampleCoverageInvert); for (int i = 0; i < 2; i++) { GLenum face = i == StencilFront ? GL_FRONT : GL_BACK; dispatcher.glStencilFuncSeparate(face, m_stencilStates[i].m_func, m_stencilStates[i].m_ref, m_stencilStates[i].m_funcMask); dispatcher.glStencilMaskSeparate(face, m_stencilStates[i].m_writeMask); dispatcher.glStencilOpSeparate(face, m_stencilStates[i].m_sfail, m_stencilStates[i].m_dpfail, m_stencilStates[i].m_dppass); } dispatcher.glClearColor(m_clearColorR, m_clearColorG, m_clearColorB, m_clearColorA); if (isGles2Gles()) { dispatcher.glClearDepthf(m_clearDepth); dispatcher.glDepthRangef(m_zNear, m_zFar); } else { dispatcher.glClearDepth(m_clearDepth); dispatcher.glDepthRange(m_zNear, m_zFar); } dispatcher.glClearStencil(m_clearStencil); // report any GL errors when loading from a snapshot GLenum err = 0; do { err = dispatcher.glGetError(); #ifdef _DEBUG if (err) { ERR("warning: get GL error %d while restoring a snapshot", err); } #endif } while (err != 0); } ObjectDataPtr GLEScontext::loadObject(NamedObjectType type, ObjectLocalName localName, android::base::Stream* stream) const { switch (type) { case NamedObjectType::VERTEXBUFFER: return ObjectDataPtr(new GLESbuffer(stream)); case NamedObjectType::TEXTURE: return ObjectDataPtr(new TextureData(stream)); case NamedObjectType::FRAMEBUFFER: return ObjectDataPtr(new FramebufferData(stream)); case NamedObjectType::RENDERBUFFER: return ObjectDataPtr(new RenderbufferData(stream)); default: return {}; } } const GLvoid* GLEScontext::setPointer(GLenum arrType,GLint size,GLenum type,GLsizei stride,const GLvoid* data, GLsizei dataSize, bool normalize, bool isInt) { GLuint bufferName = m_arrayBuffer; GLESpointer* glesPointer = nullptr; if (m_currVaoState.it->second.legacy) { auto vertexAttrib = m_currVaoState.find(arrType); if (vertexAttrib == m_currVaoState.end()) { return nullptr; } glesPointer = m_currVaoState[arrType]; } else { uint32_t attribIndex = (uint32_t)arrType; if (attribIndex > kMaxVertexAttributes) return nullptr; glesPointer = m_currVaoState.attribInfo().data() + (uint32_t)arrType; } if(bufferName) { unsigned int offset = SafeUIntFromPointer(data); GLESbuffer* vbo = static_cast( m_shareGroup ->getObjectData(NamedObjectType::VERTEXBUFFER, bufferName)); if(offset >= vbo->getSize() || vbo->getSize() - offset < size) { #ifdef _DEBUG ERR("Warning: Invalid pointer offset %u, arrType %d, type %d", offset, arrType, type); #endif return nullptr; } glesPointer->setBuffer(size,type,stride,vbo,bufferName,offset,normalize, isInt); return static_cast(vbo->getData()) + offset; } glesPointer->setArray(size,type,stride,data,dataSize,normalize,isInt); return data; } void GLEScontext::enableArr(GLenum arr,bool enable) { auto vertexAttrib = m_currVaoState.find(arr); if (vertexAttrib != m_currVaoState.end()) { vertexAttrib->second->enable(enable); } } bool GLEScontext::isArrEnabled(GLenum arr) { if (m_currVaoState.it->second.legacy) { return m_currVaoState[arr]->isEnable(); } else { if ((uint32_t)arr > kMaxVertexAttributes) return false; return m_currVaoState.attribInfo()[(uint32_t)arr].isEnable(); } } const GLESpointer* GLEScontext::getPointer(GLenum arrType) { const auto it = m_currVaoState.find(arrType); return it != m_currVaoState.end() ? it->second : nullptr; } static void convertFixedDirectLoop(const char* dataIn,unsigned int strideIn,void* dataOut,unsigned int nBytes,unsigned int strideOut,int attribSize) { for(unsigned int i = 0; i < nBytes;i+=strideOut) { const GLfixed* fixed_data = (const GLfixed *)dataIn; //filling attrib for(int j=0;j(&static_cast(dataOut)[i])[j] = X2F(fixed_data[j]); } dataIn += strideIn; } } static void convertFixedIndirectLoop(const char* dataIn,unsigned int strideIn,void* dataOut,GLsizei count,GLenum indices_type,const GLvoid* indices,unsigned int strideOut,int attribSize) { for(int i = 0 ;i < count ;i++) { GLuint index = getIndex(indices_type, indices, i); const GLfixed* fixed_data = (GLfixed *)(dataIn + index*strideIn); GLfloat* float_data = reinterpret_cast(static_cast(dataOut) + index*strideOut); for(int j=0;j(&static_cast(dataOut)[i])[j] = B2S(byte_data[j]); } dataIn += strideIn; } } static void convertByteIndirectLoop(const char* dataIn,unsigned int strideIn,void* dataOut,GLsizei count,GLenum indices_type,const GLvoid* indices,unsigned int strideOut,int attribSize) { for(int i = 0 ;i < count ;i++) { GLuint index = getIndex(indices_type, indices, i); const GLbyte* bytes_data = (GLbyte *)(dataIn + index*strideIn); GLshort* short_data = reinterpret_cast(static_cast(dataOut) + index*strideOut); for(int j=0;jgetSize()*4; //4 is the sizeof GLfixed or GLfloat in bytes int stride = p->getStride()?p->getStride():attribSize; int start = p->getBufferOffset()+first*stride; if(!p->getStride()) { list.addRange(Range(start,count*attribSize)); } else { for(int i = 0 ;i < count; i++,start+=stride) { list.addRange(Range(start,attribSize)); } } } static void indirectToBytesRanges(const GLvoid* indices,GLenum indices_type,GLsizei count,GLESpointer* p,RangeList& list) { int attribSize = p->getSize() * 4; //4 is the sizeof GLfixed or GLfloat in bytes int stride = p->getStride()?p->getStride():attribSize; int start = p->getBufferOffset(); for(int i=0 ; i < count; i++) { GLuint index = getIndex(indices_type, indices, i); list.addRange(Range(start+index*stride,attribSize)); } } int bytesRangesToIndices(RangeList& ranges,GLESpointer* p,GLuint* indices) { int attribSize = p->getSize() * 4; //4 is the sizeof GLfixed or GLfloat in bytes int stride = p->getStride()?p->getStride():attribSize; int offset = p->getBufferOffset(); int n = 0; for(int i=0;igetType(); int attribSize = p->getSize(); unsigned int size = attribSize*count + first; unsigned int bytes = type == GL_FIXED ? sizeof(GLfixed):sizeof(GLbyte); cArrs.allocArr(size,type); int stride = p->getStride()?p->getStride():bytes*attribSize; const char* data = (const char*)p->getArrayData() + (first*stride); if(type == GL_FIXED) { convertFixedDirectLoop(data,stride,cArrs.getCurrentData(),size*sizeof(GLfloat),attribSize*sizeof(GLfloat),attribSize); } else if(type == GL_BYTE) { convertByteDirectLoop(data,stride,cArrs.getCurrentData(),size*sizeof(GLshort),attribSize*sizeof(GLshort),attribSize); } } void GLEScontext::convertDirectVBO(GLESConversionArrays& cArrs,GLint first,GLsizei count,GLenum array_id,GLESpointer* p) { RangeList ranges; RangeList conversions; GLuint* indices = NULL; int attribSize = p->getSize(); int stride = p->getStride()?p->getStride():sizeof(GLfixed)*attribSize; char* data = (char*)p->getBufferData(); if(p->bufferNeedConversion()) { directToBytesRanges(first,count,p,ranges); //converting indices range to buffer bytes ranges by offset p->getBufferConversions(ranges,conversions); // getting from the buffer the relevant ranges that still needs to be converted if(conversions.size()) { // there are some elements to convert indices = new GLuint[count]; int nIndices = bytesRangesToIndices(conversions,p,indices); //converting bytes ranges by offset to indices in this array convertFixedIndirectLoop(data,stride,data,nIndices,GL_UNSIGNED_INT,indices,stride,attribSize); } } if(indices) delete[] indices; cArrs.setArr(data,p->getStride(),GL_FLOAT); } unsigned int GLEScontext::findMaxIndex(GLsizei count,GLenum type,const GLvoid* indices) { //finding max index unsigned int max = 0; if(type == GL_UNSIGNED_BYTE) { GLubyte* b_indices =(GLubyte *)indices; for(int i=0;i max) max = b_indices[i]; } } else if (type == GL_UNSIGNED_SHORT) { GLushort* us_indices =(GLushort *)indices; for(int i=0;i max) max = us_indices[i]; } } else { // type == GL_UNSIGNED_INT GLuint* ui_indices =(GLuint *)indices; for(int i=0;i max) max = ui_indices[i]; } } return max; } void GLEScontext::convertIndirect(GLESConversionArrays& cArrs,GLsizei count,GLenum indices_type,const GLvoid* indices,GLenum array_id,GLESpointer* p) { GLenum type = p->getType(); int maxElements = findMaxIndex(count,indices_type,indices) + 1; int attribSize = p->getSize(); int size = attribSize * maxElements; unsigned int bytes = type == GL_FIXED ? sizeof(GLfixed):sizeof(GLbyte); cArrs.allocArr(size,type); int stride = p->getStride()?p->getStride():bytes*attribSize; const char* data = (const char*)p->getArrayData(); if(type == GL_FIXED) { convertFixedIndirectLoop(data,stride,cArrs.getCurrentData(),count,indices_type,indices,attribSize*sizeof(GLfloat),attribSize); } else if(type == GL_BYTE){ convertByteIndirectLoop(data,stride,cArrs.getCurrentData(),count,indices_type,indices,attribSize*sizeof(GLshort),attribSize); } } void GLEScontext::convertIndirectVBO(GLESConversionArrays& cArrs,GLsizei count,GLenum indices_type,const GLvoid* indices,GLenum array_id,GLESpointer* p) { RangeList ranges; RangeList conversions; GLuint* conversionIndices = NULL; int attribSize = p->getSize(); int stride = p->getStride()?p->getStride():sizeof(GLfixed)*attribSize; char* data = static_cast(p->getBufferData()); if(p->bufferNeedConversion()) { indirectToBytesRanges(indices,indices_type,count,p,ranges); //converting indices range to buffer bytes ranges by offset p->getBufferConversions(ranges,conversions); // getting from the buffer the relevant ranges that still needs to be converted if(conversions.size()) { // there are some elements to convert conversionIndices = new GLuint[count]; int nIndices = bytesRangesToIndices(conversions,p,conversionIndices); //converting bytes ranges by offset to indices in this array convertFixedIndirectLoop(data,stride,data,nIndices,GL_UNSIGNED_INT,conversionIndices,stride,attribSize); } } if(conversionIndices) delete[] conversionIndices; cArrs.setArr(data,p->getStride(),GL_FLOAT); } GLuint GLEScontext::bindBuffer(GLenum target,GLuint buffer) { switch(target) { case GL_ARRAY_BUFFER: m_arrayBuffer = buffer; break; case GL_ELEMENT_ARRAY_BUFFER: m_currVaoState.iboId() = buffer; break; case GL_COPY_READ_BUFFER: m_copyReadBuffer = buffer; break; case GL_COPY_WRITE_BUFFER: m_copyWriteBuffer = buffer; break; case GL_PIXEL_PACK_BUFFER: m_pixelPackBuffer = buffer; break; case GL_PIXEL_UNPACK_BUFFER: m_pixelUnpackBuffer = buffer; break; case GL_TRANSFORM_FEEDBACK_BUFFER: m_transformFeedbackBuffer = buffer; break; case GL_UNIFORM_BUFFER: m_uniformBuffer = buffer; break; case GL_ATOMIC_COUNTER_BUFFER: m_atomicCounterBuffer = buffer; break; case GL_DISPATCH_INDIRECT_BUFFER: m_dispatchIndirectBuffer = buffer; break; case GL_DRAW_INDIRECT_BUFFER: m_drawIndirectBuffer = buffer; break; case GL_SHADER_STORAGE_BUFFER: m_shaderStorageBuffer = buffer; break; case GL_TEXTURE_BUFFER: m_textureBuffer = buffer; break; default: m_arrayBuffer = buffer; break; } if (!buffer) return 0; auto sg = m_shareGroup.get(); if (!sg) return 0; return sg->ensureObjectOnBind(NamedObjectType::VERTEXBUFFER, buffer); } void GLEScontext::bindIndexedBuffer(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size, GLintptr stride, bool isBindBase) { VertexAttribBindingVector* bindings = nullptr; switch (target) { case GL_UNIFORM_BUFFER: bindings = &m_indexedUniformBuffers; break; case GL_ATOMIC_COUNTER_BUFFER: bindings = &m_indexedAtomicCounterBuffers; break; case GL_SHADER_STORAGE_BUFFER: bindings = &m_indexedShaderStorageBuffers; break; default: bindings = &m_currVaoState.bufferBindings(); break; } if (index >= bindings->size()) { return; } auto& bufferBinding = (*bindings)[index]; bufferBinding.buffer = buffer; bufferBinding.offset = offset; bufferBinding.size = size; bufferBinding.stride = stride; bufferBinding.isBindBase = isBindBase; } void GLEScontext::bindIndexedBuffer(GLenum target, GLuint index, GLuint buffer) { GLint sz; getBufferSizeById(buffer, &sz); bindIndexedBuffer(target, index, buffer, 0, sz, 0, true); } static void sClearIndexedBufferBinding(GLuint id, std::vector& bindings) { for (size_t i = 0; i < bindings.size(); i++) { if (bindings[i].buffer == id) { bindings[i].offset = 0; bindings[i].size = 0; bindings[i].stride = 0; bindings[i].buffer = 0; bindings[i].isBindBase = false; } } } void GLEScontext::unbindBuffer(GLuint buffer) { if (m_arrayBuffer == buffer) m_arrayBuffer = 0; if (m_currVaoState.iboId() == buffer) m_currVaoState.iboId() = 0; if (m_copyReadBuffer == buffer) m_copyReadBuffer = 0; if (m_copyWriteBuffer == buffer) m_copyWriteBuffer = 0; if (m_pixelPackBuffer == buffer) m_pixelPackBuffer = 0; if (m_pixelUnpackBuffer == buffer) m_pixelUnpackBuffer = 0; if (m_transformFeedbackBuffer == buffer) m_transformFeedbackBuffer = 0; if (m_uniformBuffer == buffer) m_uniformBuffer = 0; if (m_atomicCounterBuffer == buffer) m_atomicCounterBuffer = 0; if (m_dispatchIndirectBuffer == buffer) m_dispatchIndirectBuffer = 0; if (m_drawIndirectBuffer == buffer) m_drawIndirectBuffer = 0; if (m_shaderStorageBuffer == buffer) m_shaderStorageBuffer = 0; if (m_textureBuffer == buffer) m_textureBuffer = 0; // One might think that indexed buffer bindings for transform feedbacks // must be cleared as well, but transform feedbacks are // considered GL objects with attachments, so even if the buffer is // deleted (unbindBuffer is called), the state query with // glGetIntegeri_v must still return the deleted name [1]. // sClearIndexedBufferBinding(buffer, m_indexedTransformFeedbackBuffers); // [1] OpenGL ES 3.0.5 spec Appendix D.1.3 sClearIndexedBufferBinding(buffer, m_indexedUniformBuffers); sClearIndexedBufferBinding(buffer, m_indexedAtomicCounterBuffers); sClearIndexedBufferBinding(buffer, m_indexedShaderStorageBuffers); sClearIndexedBufferBinding(buffer, m_currVaoState.bufferBindings()); } //checks if any buffer is binded to target bool GLEScontext::isBindedBuffer(GLenum target) { switch(target) { case GL_ARRAY_BUFFER: return m_arrayBuffer != 0; case GL_ELEMENT_ARRAY_BUFFER: return m_currVaoState.iboId() != 0; case GL_COPY_READ_BUFFER: return m_copyReadBuffer != 0; case GL_COPY_WRITE_BUFFER: return m_copyWriteBuffer != 0; case GL_PIXEL_PACK_BUFFER: return m_pixelPackBuffer != 0; case GL_PIXEL_UNPACK_BUFFER: return m_pixelUnpackBuffer != 0; case GL_TRANSFORM_FEEDBACK_BUFFER: return m_transformFeedbackBuffer != 0; case GL_UNIFORM_BUFFER: return m_uniformBuffer != 0; case GL_ATOMIC_COUNTER_BUFFER: return m_atomicCounterBuffer != 0; case GL_DISPATCH_INDIRECT_BUFFER: return m_dispatchIndirectBuffer != 0; case GL_DRAW_INDIRECT_BUFFER: return m_drawIndirectBuffer != 0; case GL_SHADER_STORAGE_BUFFER: return m_shaderStorageBuffer != 0; case GL_TEXTURE_BUFFER: return m_textureBuffer != 0; default: return m_arrayBuffer != 0; } } GLuint GLEScontext::getBuffer(GLenum target) { switch(target) { case GL_ARRAY_BUFFER: return m_arrayBuffer; case GL_ELEMENT_ARRAY_BUFFER: return m_currVaoState.iboId(); case GL_COPY_READ_BUFFER: return m_copyReadBuffer; case GL_COPY_WRITE_BUFFER: return m_copyWriteBuffer; case GL_PIXEL_PACK_BUFFER: return m_pixelPackBuffer; case GL_PIXEL_UNPACK_BUFFER: return m_pixelUnpackBuffer; case GL_TRANSFORM_FEEDBACK_BUFFER: return m_transformFeedbackBuffer; case GL_UNIFORM_BUFFER: return m_uniformBuffer; case GL_ATOMIC_COUNTER_BUFFER: return m_atomicCounterBuffer; case GL_DISPATCH_INDIRECT_BUFFER: return m_dispatchIndirectBuffer; case GL_DRAW_INDIRECT_BUFFER: return m_drawIndirectBuffer; case GL_SHADER_STORAGE_BUFFER: return m_shaderStorageBuffer; case GL_TEXTURE_BUFFER: return m_textureBuffer; default: return m_arrayBuffer; } } GLuint GLEScontext::getIndexedBuffer(GLenum target, GLuint index) { switch (target) { case GL_UNIFORM_BUFFER: return m_indexedUniformBuffers[index].buffer; case GL_ATOMIC_COUNTER_BUFFER: return m_indexedAtomicCounterBuffers[index].buffer; case GL_SHADER_STORAGE_BUFFER: return m_indexedShaderStorageBuffers[index].buffer; default: return m_currVaoState.bufferBindings()[index].buffer; } } GLvoid* GLEScontext::getBindedBuffer(GLenum target) { GLuint bufferName = getBuffer(target); if(!bufferName) return NULL; GLESbuffer* vbo = static_cast( m_shareGroup ->getObjectData(NamedObjectType::VERTEXBUFFER, bufferName)); return vbo->getData(); } void GLEScontext::getBufferSize(GLenum target,GLint* param) { GLuint bufferName = getBuffer(target); getBufferSizeById(bufferName, param); } void GLEScontext::getBufferSizeById(GLuint bufferName, GLint* param) { if (!bufferName) { *param = 0; return; } GLESbuffer* vbo = static_cast( m_shareGroup ->getObjectData(NamedObjectType::VERTEXBUFFER, bufferName)); *param = vbo->getSize(); } void GLEScontext::getBufferUsage(GLenum target,GLint* param) { GLuint bufferName = getBuffer(target); GLESbuffer* vbo = static_cast( m_shareGroup ->getObjectData(NamedObjectType::VERTEXBUFFER, bufferName)); *param = vbo->getUsage(); } bool GLEScontext::setBufferData(GLenum target,GLsizeiptr size,const GLvoid* data,GLenum usage) { GLuint bufferName = getBuffer(target); if(!bufferName) return false; GLESbuffer* vbo = static_cast( m_shareGroup ->getObjectData(NamedObjectType::VERTEXBUFFER, bufferName)); return vbo->setBuffer(size,usage,data); } bool GLEScontext::setBufferSubData(GLenum target,GLintptr offset,GLsizeiptr size,const GLvoid* data) { GLuint bufferName = getBuffer(target); if(!bufferName) return false; GLESbuffer* vbo = static_cast( m_shareGroup ->getObjectData(NamedObjectType::VERTEXBUFFER, bufferName)); return vbo->setSubBuffer(offset,size,data); } void GLEScontext::setViewport(GLint x, GLint y, GLsizei width, GLsizei height) { m_isViewport = true; m_viewportX = x; m_viewportY = y; m_viewportWidth = width; m_viewportHeight = height; } void GLEScontext::getViewport(GLint* params) { if (!m_isViewport) { dispatcher().glGetIntegerv(GL_VIEWPORT, params); } else { params[0] = m_viewportX; params[1] = m_viewportY; params[2] = m_viewportWidth; params[3] = m_viewportHeight; } } void GLEScontext::setScissor(GLint x, GLint y, GLsizei width, GLsizei height) { m_isScissor = true; m_scissorX = x; m_scissorY = y; m_scissorWidth = width; m_scissorHeight = height; } void GLEScontext::setPolygonOffset(GLfloat factor, GLfloat units) { m_polygonOffsetFactor = factor; m_polygonOffsetUnits = units; } void GLEScontext::setEnable(GLenum item, bool isEnable) { switch (item) { case GL_TEXTURE_2D: case GL_TEXTURE_CUBE_MAP_OES: case GL_TEXTURE_3D: case GL_TEXTURE_2D_ARRAY: case GL_TEXTURE_2D_MULTISAMPLE: case GL_TEXTURE_BUFFER: setTextureEnabled(item,true); break; case GL_BLEND: for (auto& blend : m_blendStates) { blend.bEnable = isEnable ? GL_TRUE : GL_FALSE; } break; default: m_glEnableList[item] = isEnable; break; } } void GLEScontext::setEnablei(GLenum item, GLuint index, bool isEnable) { switch (item) { case GL_BLEND: if (index < m_blendStates.size()) { m_blendStates[index].bEnable = isEnable ? GL_TRUE : GL_FALSE; } break; } } bool GLEScontext::isEnabled(GLenum item) const { switch (item) { case GL_TEXTURE_2D: case GL_TEXTURE_CUBE_MAP_OES: case GL_TEXTURE_3D: case GL_TEXTURE_2D_ARRAY: case GL_TEXTURE_2D_MULTISAMPLE: case GL_TEXTURE_BUFFER: return m_texState[m_activeTexture][GLTextureTargetToLocal(item)].enabled; case GL_BLEND: return m_blendStates[0].bEnable!=GL_FALSE; default: return android::base::findOrDefault(m_glEnableList, item, false); } } void GLEScontext::setBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) { for (auto & blendState : m_blendStates) { blendState.blendEquationRgb = modeRGB; blendState.blendEquationAlpha = modeAlpha; } } void GLEScontext::setBlendEquationSeparatei(GLenum buf, GLenum modeRGB, GLenum modeAlpha) { if (buf < m_blendStates.size()) { m_blendStates[buf].blendEquationRgb = modeRGB; m_blendStates[buf].blendEquationAlpha = modeAlpha; } } void GLEScontext::setBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { for (auto& blendState : m_blendStates) { blendState.blendSrcRgb = srcRGB; blendState.blendDstRgb = dstRGB; blendState.blendSrcAlpha = srcAlpha; blendState.blendDstAlpha = dstAlpha; } } void GLEScontext::setBlendFuncSeparatei(GLenum buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) { if (buf < m_blendStates.size()) { m_blendStates[buf].blendSrcRgb = srcRGB; m_blendStates[buf].blendDstRgb = dstRGB; m_blendStates[buf].blendSrcAlpha = srcAlpha; m_blendStates[buf].blendDstAlpha = dstAlpha; } } void GLEScontext::setPixelStorei(GLenum pname, GLint param) { m_glPixelStoreiList[pname] = param; } void GLEScontext::setCullFace(GLenum mode) { m_cullFace = mode; } void GLEScontext::setFrontFace(GLenum mode) { m_frontFace = mode; } void GLEScontext::setDepthFunc(GLenum func) { m_depthFunc = func; } void GLEScontext::setDepthMask(GLboolean flag) { m_depthMask = flag; } void GLEScontext::setDepthRangef(GLclampf zNear, GLclampf zFar) { m_zNear = zNear; m_zFar = zFar; } void GLEScontext::setLineWidth(GLfloat lineWidth) { m_lineWidth = lineWidth; } void GLEScontext::setSampleCoverage(GLclampf value, GLboolean invert) { m_sampleCoverageVal = value; m_sampleCoverageInvert = invert; } void GLEScontext::setStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask) { if (face == GL_FRONT_AND_BACK) { setStencilFuncSeparate(GL_FRONT, func, ref, mask); setStencilFuncSeparate(GL_BACK, func, ref, mask); return; } int idx = 0; switch (face) { case GL_FRONT: idx = StencilFront; break; case GL_BACK: idx = StencilBack; break; default: return; } m_stencilStates[idx].m_func = func; m_stencilStates[idx].m_ref = ref; m_stencilStates[idx].m_funcMask = mask; } void GLEScontext::setStencilMaskSeparate(GLenum face, GLuint mask) { if (face == GL_FRONT_AND_BACK) { setStencilMaskSeparate(GL_FRONT, mask); setStencilMaskSeparate(GL_BACK, mask); return; } int idx = 0; switch (face) { case GL_FRONT: idx = StencilFront; break; case GL_BACK: idx = StencilBack; break; default: return; } m_stencilStates[idx].m_writeMask = mask; } void GLEScontext::setStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) { if (face == GL_FRONT_AND_BACK) { setStencilOpSeparate(GL_FRONT, fail, zfail, zpass); setStencilOpSeparate(GL_BACK, fail, zfail, zpass); return; } int idx = 0; switch (face) { case GL_FRONT: idx = StencilFront; break; case GL_BACK: idx = StencilBack; break; default: return; } m_stencilStates[idx].m_sfail = fail; m_stencilStates[idx].m_dpfail = zfail; m_stencilStates[idx].m_dppass = zpass; } void GLEScontext::setColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) { for (auto& blend : m_blendStates) { blend.colorMaskR = red; blend.colorMaskG = green; blend.colorMaskB = blue; blend.colorMaskA = alpha; } } void GLEScontext::setColorMaski(GLuint buf, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) { if (buf < m_blendStates.size()) { m_blendStates[buf].colorMaskR = red; m_blendStates[buf].colorMaskG = green; m_blendStates[buf].colorMaskB = blue; m_blendStates[buf].colorMaskA = alpha; } } void GLEScontext::setClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) { m_clearColorR = red; m_clearColorG = green; m_clearColorB = blue; m_clearColorA = alpha; } void GLEScontext::setClearDepth(GLclampf depth) { m_clearDepth = depth; } void GLEScontext::setClearStencil(GLint s) { m_clearStencil = s; } const char* GLEScontext::getExtensionString(bool isGles1) { const char * ret; s_lock.lock(); if (isGles1) { if (s_glExtensionsGles1) ret = s_glExtensionsGles1->c_str(); else ret=""; } else { if (m_glesMajorVersion == 3 && m_glesMinorVersion == 1) { if (s_glExtensionsGles31) ret = s_glExtensionsGles31->c_str(); else ret = ""; } else { if (s_glExtensions) ret = s_glExtensions->c_str(); else ret = ""; } } s_lock.unlock(); return ret; } const char* GLEScontext::getVendorString(bool isGles1) const { if (isGles1) { return s_glVendorGles1.c_str(); } else { if (m_glesMajorVersion == 3 && m_glesMinorVersion == 1) { return s_glVendorGles31.c_str(); } else { return s_glVendor.c_str(); } } } const char* GLEScontext::getRendererString(bool isGles1) const { if (isGles1) { return s_glRendererGles1.c_str(); } else { if (m_glesMajorVersion == 3 && m_glesMinorVersion == 1) { return s_glRendererGles31.c_str(); } else { return s_glRenderer.c_str(); } } } const char* GLEScontext::getVersionString(bool isGles1) const { if (isGles1) { return s_glVersionGles1.c_str(); } else { if (m_glesMajorVersion == 3 && m_glesMinorVersion == 1) { return s_glVersionGles31.c_str(); } else { return s_glVersion.c_str(); } } } void GLEScontext::getGlobalLock() { s_lock.lock(); } void GLEScontext::releaseGlobalLock() { s_lock.unlock(); } void GLEScontext::initCapsLocked(const GLubyte * extensionString, bool nativeTextureDecompressionEnabled, GLSupport& glSupport) { const char* cstring = (const char*)extensionString; s_glDispatch.glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &glSupport.maxVertexAttribs); if (glSupport.maxVertexAttribs > kMaxVertexAttributes) { glSupport.maxVertexAttribs = kMaxVertexAttributes; } s_glDispatch.glGetIntegerv(GL_MAX_CLIP_PLANES, &glSupport.maxClipPlane); s_glDispatch.glGetIntegerv(GL_MAX_LIGHTS, &glSupport.maxLights); s_glDispatch.glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glSupport.maxTexSize); s_glDispatch.glGetIntegerv(GL_MAX_TEXTURE_UNITS, &glSupport.maxTexUnits); // Core profile lacks a fixed-function pipeline with texture units, // but we still want glDrawTexOES to work in core profile. // So, set it to 8. if ((::isCoreProfile() || isGles2Gles()) && !glSupport.maxTexUnits) { glSupport.maxTexUnits = 8; } s_glDispatch.glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &glSupport.maxTexImageUnits); s_glDispatch.glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &glSupport.maxCombinedTexImageUnits); s_glDispatch.glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &glSupport.maxTransformFeedbackSeparateAttribs); s_glDispatch.glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &glSupport.maxUniformBufferBindings); s_glDispatch.glGetIntegerv(GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, &glSupport.maxAtomicCounterBufferBindings); s_glDispatch.glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &glSupport.maxShaderStorageBufferBindings); s_glDispatch.glGetIntegerv(GL_MAX_DRAW_BUFFERS, &glSupport.maxDrawBuffers); s_glDispatch.glGetIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &glSupport.maxVertexAttribBindings); // Compressed texture format query if (nativeTextureDecompressionEnabled) { bool hasEtc2Support = false; bool hasAstcSupport = false; int numCompressedFormats = 0; s_glDispatch.glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numCompressedFormats); if (numCompressedFormats > 0) { int numEtc2Formats = 0; int numAstcFormats = 0; int numEtc2FormatsSupported = 0; int numAstcFormatsSupported = 0; std::map found; forEachEtc2Format([&numEtc2Formats, &found](GLint format) { ++numEtc2Formats; found[format] = false;}); forEachAstcFormat([&numAstcFormats, &found](GLint format) { ++numAstcFormats; found[format] = false;}); std::vector formats(numCompressedFormats); s_glDispatch.glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, formats.data()); for (int i = 0; i < numCompressedFormats; ++i) { GLint format = formats[i]; if (isEtc2Format(format)) { ++numEtc2FormatsSupported; found[format] = true; } else if (isAstcFormat(format)) { ++numAstcFormatsSupported; found[format] = true; } } if (numEtc2Formats == numEtc2FormatsSupported) { hasEtc2Support = true; // Supports ETC2 underneath } else { // It is unusual to support only some. Record what happened. ERR("Not supporting etc2: %d vs %d", numEtc2FormatsSupported, numEtc2Formats); for (auto it : found) { if (!it.second) { ERR("Not found: 0x%x", it.first); } } } if (numAstcFormats == numAstcFormatsSupported) { hasAstcSupport = true; // Supports ASTC underneath } else { // It is unusual to support only some. Record what happened. ERR("Not supporting astc: %d vs %d", numAstcFormatsSupported, numAstcFormats); for (auto it : found) { if (!it.second) { ERR("Not found: 0x%x", it.first); } } } } glSupport.hasEtc2Support = hasEtc2Support; glSupport.hasAstcSupport = hasAstcSupport; } // Clear GL error in case these enums not supported. s_glDispatch.glGetError(); const GLubyte* glslVersion = s_glDispatch.glGetString(GL_SHADING_LANGUAGE_VERSION); glSupport.glslVersion = Version((const char*)(glslVersion)); const GLubyte* glVersion = s_glDispatch.glGetString(GL_VERSION); // fprintf(stderr, "%s: vendor renderer version [%s] [%s] [%s]\n", __func__, // s_glDispatch.glGetString(GL_VENDOR), // s_glDispatch.glGetString(GL_RENDERER), // s_glDispatch.glGetString(GL_VERSION)); if (strstr(cstring,"GL_EXT_bgra ")!=NULL || (isGles2Gles() && strstr(cstring, "GL_EXT_texture_format_BGRA8888")) || (!isGles2Gles() && !(Version((const char*)glVersion) < Version("1.2")))) glSupport.GL_EXT_TEXTURE_FORMAT_BGRA8888 = true; if (::isCoreProfile() || strstr(cstring,"GL_EXT_framebuffer_object ")!=NULL) glSupport.GL_EXT_FRAMEBUFFER_OBJECT = true; if (strstr(cstring,"GL_ARB_vertex_blend ")!=NULL) glSupport.GL_ARB_VERTEX_BLEND = true; if (strstr(cstring,"GL_ARB_matrix_palette ")!=NULL) glSupport.GL_ARB_MATRIX_PALETTE = true; if (strstr(cstring,"GL_EXT_packed_depth_stencil ")!=NULL || strstr(cstring,"GL_OES_packed_depth_stencil ")!=NULL) glSupport.GL_EXT_PACKED_DEPTH_STENCIL = true; if (strstr(cstring,"GL_OES_read_format ")!=NULL) glSupport.GL_OES_READ_FORMAT = true; if (strstr(cstring,"GL_ARB_half_float_pixel ")!=NULL || strstr(cstring,"GL_OES_texture_half_float ")!=NULL) glSupport.GL_ARB_HALF_FLOAT_PIXEL = true; if (strstr(cstring,"GL_NV_half_float ")!=NULL) glSupport.GL_NV_HALF_FLOAT = true; if (strstr(cstring,"GL_ARB_half_float_vertex ")!=NULL || strstr(cstring,"GL_OES_vertex_half_float ")!=NULL) glSupport.GL_ARB_HALF_FLOAT_VERTEX = true; if (strstr(cstring,"GL_SGIS_generate_mipmap ")!=NULL) glSupport.GL_SGIS_GENERATE_MIPMAP = true; if (strstr(cstring,"GL_ARB_ES2_compatibility ")!=NULL || isGles2Gles()) glSupport.GL_ARB_ES2_COMPATIBILITY = true; if (strstr(cstring,"GL_OES_standard_derivatives ")!=NULL) glSupport.GL_OES_STANDARD_DERIVATIVES = true; if (::isCoreProfile() || strstr(cstring,"GL_ARB_texture_non_power_of_two")!=NULL || strstr(cstring,"GL_OES_texture_npot")!=NULL) glSupport.GL_OES_TEXTURE_NPOT = true; if (::isCoreProfile() || strstr(cstring,"GL_ARB_color_buffer_float")!=NULL || strstr(cstring,"GL_EXT_color_buffer_float")!=NULL) glSupport.ext_GL_EXT_color_buffer_float = true; if (::isCoreProfile() || strstr(cstring,"GL_EXT_color_buffer_half_float")!=NULL) glSupport.ext_GL_EXT_color_buffer_half_float = true; if (strstr(cstring,"GL_OVR_multiview2")!=NULL) { glSupport.ext_GL_OVR_multiview2 = true; } if (strstr(cstring,"GL_EXT_multiview_texture_multisample")!=NULL) { glSupport.ext_GL_EXT_multiview_texture_multisample = true; } // b/203446380 // Does not really work on hardware GPUs if (strstr(cstring,"GL_EXT_shader_framebuffer_fetch")!=NULL && isGles2Gles()) { glSupport.ext_GL_EXT_shader_framebuffer_fetch = true; } if (!(Version((const char*)glVersion) < Version("3.0")) || strstr(cstring,"GL_OES_rgb8_rgba8")!=NULL) glSupport.GL_OES_RGB8_RGBA8 = true; if (strstr(cstring, "GL_EXT_memory_object") != NULL) { glSupport.ext_GL_EXT_memory_object = true; } if (strstr(cstring, "GL_EXT_semaphore") != NULL) { glSupport.ext_GL_EXT_semaphore = true; } if (strstr(cstring, "GL_EXT_texture_buffer") != NULL) { glSupport.ext_GL_EXT_texture_buffer = true; } if (strstr(cstring, "GL_OES_texture_buffer") != NULL) { glSupport.ext_GL_OES_texture_buffer = true; } if (strstr(cstring, "GL_EXT_draw_buffers_indexed") != NULL) { glSupport.ext_GL_EXT_draw_buffers_indexed = true; } if (strstr(cstring, "GL_EXT_clip_cull_distance") != NULL) { glSupport.ext_GL_EXT_clip_cull_distance = true; } // ASTC if (strstr(cstring, "GL_KHR_texture_compression_astc_ldr") != NULL) { glSupport.ext_GL_KHR_texture_compression_astc_ldr = true; } // BPTC extension detection if (strstr(cstring, "GL_EXT_texture_compression_bptc") != NULL) { glSupport.hasBptcSupport = true; } // S3TC extension detection if (strstr(cstring, "GL_EXT_texture_compression_s3tc") != NULL) { glSupport.hasS3tcSupport = true; } if (strstr(cstring, "GL_EXT_texture_compression_rgtc") != NULL) { glSupport.hasRgtcSupport = true; } } void GLEScontext::buildStrings(int major, int minor, const char* baseVendor, const char* baseRenderer, const char* baseVersion, const char* version) { static const char VENDOR[] = {"Google ("}; static const char RENDERER[] = {"Android Emulator OpenGL ES Translator ("}; const size_t VENDOR_LEN = sizeof(VENDOR) - 1; const size_t RENDERER_LEN = sizeof(RENDERER) - 1; // Sanitize the strings as some OpenGL implementations return NULL // when asked the basic questions (this happened at least once on a client // machine) if (!baseVendor) { baseVendor = "N/A"; } if (!baseRenderer) { baseRenderer = "N/A"; } if (!baseVersion) { baseVersion = "N/A"; } if (!version) { version = "N/A"; } bool isES31 = major == 3 && minor == 1; bool isES11 = major == 1; std::string& vendorString = isES11 ? s_glVendorGles1 : (isES31? s_glVendorGles31 : s_glVendor); std::string& rendererString = isES11 ? s_glRendererGles1 : (isES31? s_glRendererGles31 : s_glRenderer); std::string& versionString = isES11 ? s_glVersionGles1 : (isES31 ? s_glVersionGles31 : s_glVersion); size_t baseVendorLen = strlen(baseVendor); vendorString.clear(); vendorString.reserve(baseVendorLen + VENDOR_LEN + 1); vendorString.append(VENDOR, VENDOR_LEN); vendorString.append(baseVendor, baseVendorLen); vendorString.append(")", 1); size_t baseRendererLen = strlen(baseRenderer); rendererString.clear(); rendererString.reserve(baseRendererLen + RENDERER_LEN + 1); rendererString.append(RENDERER, RENDERER_LEN); rendererString.append(baseRenderer, baseRendererLen); rendererString.append(")", 1); size_t baseVersionLen = strlen(baseVersion); size_t versionLen = strlen(version); versionString.clear(); versionString.reserve(baseVersionLen + versionLen + 3); versionString.append(version, versionLen); versionString.append(" (", 2); versionString.append(baseVersion, baseVersionLen); versionString.append(")", 1); } bool GLEScontext::isTextureUnitEnabled(GLenum unit) { for (int i=0;i0 && glGetIntegerv(pname,iParams)) { while(numParams >= 0) { params[numParams] = I2X(iParams[numParams]); numParams--; } result = true; } delete [] iParams; return result; } bool GLEScontext::glGetFloatv(GLenum pname, GLfloat *params) { bool result = false; GLint numParams = 1; GLint* iParams = new GLint[numParams]; if (numParams>0 && glGetIntegerv(pname,iParams)) { while(numParams >= 0) { params[numParams] = (GLfloat)iParams[numParams]; numParams--; } result = true; } delete [] iParams; return result; } bool GLEScontext::glGetIntegerv(GLenum pname, GLint *params) { switch(pname) { case GL_ARRAY_BUFFER_BINDING: *params = m_arrayBuffer; break; case GL_ELEMENT_ARRAY_BUFFER_BINDING: *params = m_currVaoState.iboId(); break; case GL_TEXTURE_BINDING_CUBE_MAP: *params = m_texState[m_activeTexture][TEXTURE_CUBE_MAP].texture; break; case GL_TEXTURE_BINDING_2D: *params = m_texState[m_activeTexture][TEXTURE_2D].texture; break; case GL_ACTIVE_TEXTURE: *params = m_activeTexture+GL_TEXTURE0; break; case GL_MAX_TEXTURE_SIZE: *params = getMaxTexSize(); break; default: return false; } return true; } TextureTarget GLEScontext::GLTextureTargetToLocal(GLenum target) { TextureTarget value=TEXTURE_2D; switch (target) { case GL_TEXTURE_CUBE_MAP: case GL_TEXTURE_CUBE_MAP_POSITIVE_X: case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: value = TEXTURE_CUBE_MAP; break; case GL_TEXTURE_2D: value = TEXTURE_2D; break; case GL_TEXTURE_2D_ARRAY: value = TEXTURE_2D_ARRAY; break; case GL_TEXTURE_3D: value = TEXTURE_3D; break; case GL_TEXTURE_2D_MULTISAMPLE: value = TEXTURE_2D_MULTISAMPLE; break; case GL_TEXTURE_BUFFER: value = TEXTURE_BUFFER; break; } return value; } unsigned int GLEScontext::getBindedTexture(GLenum target) { TextureTarget pos = GLTextureTargetToLocal(target); return m_texState[m_activeTexture][pos].texture; } unsigned int GLEScontext::getBindedTexture(GLenum unit, GLenum target) { TextureTarget pos = GLTextureTargetToLocal(target); return m_texState[unit-GL_TEXTURE0][pos].texture; } void GLEScontext::setBindedTexture(GLenum target, unsigned int tex) { TextureTarget pos = GLTextureTargetToLocal(target); m_texState[m_activeTexture][pos].texture = tex; } void GLEScontext::setTextureEnabled(GLenum target, GLenum enable) { TextureTarget pos = GLTextureTargetToLocal(target); m_texState[m_activeTexture][pos].enabled = enable; } #define INTERNAL_NAME(x) (x +0x100000000ll); ObjectLocalName GLEScontext::getDefaultTextureName(GLenum target) { ObjectLocalName name = 0; switch (GLTextureTargetToLocal(target)) { case TEXTURE_2D: name = INTERNAL_NAME(0); break; case TEXTURE_CUBE_MAP: name = INTERNAL_NAME(1); break; case TEXTURE_2D_ARRAY: name = INTERNAL_NAME(2); break; case TEXTURE_3D: name = INTERNAL_NAME(3); break; case TEXTURE_2D_MULTISAMPLE: name = INTERNAL_NAME(4); break; case TEXTURE_BUFFER: name = INTERNAL_NAME(5); break; default: name = 0; break; } return name; } ObjectLocalName GLEScontext::getTextureLocalName(GLenum target, unsigned int tex) { return (tex!=0? tex : getDefaultTextureName(target)); } void GLEScontext::drawValidate(void) { if(m_drawFramebuffer == 0) return; auto fbObj = getFBOData(m_drawFramebuffer); if (!fbObj) return; fbObj->validate(this); } void GLEScontext::initEmulatedEGLSurface(GLint width, GLint height, GLint colorFormat, GLint depthstencilFormat, GLint multisamples, GLuint rboColor, GLuint rboDepth) { dispatcher().glBindRenderbuffer(GL_RENDERBUFFER, rboColor); if (multisamples) { dispatcher().glRenderbufferStorageMultisample(GL_RENDERBUFFER, multisamples, colorFormat, width, height); GLint err = dispatcher().glGetError(); if (err != GL_NO_ERROR) { ERR("error setting up multisampled RBO! 0x%x", err); } } else { dispatcher().glRenderbufferStorage(GL_RENDERBUFFER, colorFormat, width, height); } dispatcher().glBindRenderbuffer(GL_RENDERBUFFER, rboDepth); if (multisamples) { dispatcher().glRenderbufferStorageMultisample(GL_RENDERBUFFER, multisamples, depthstencilFormat, width, height); GLint err = dispatcher().glGetError(); if (err != GL_NO_ERROR) { ERR("error setting up multisampled RBO! 0x%x", err); } } else { dispatcher().glRenderbufferStorage(GL_RENDERBUFFER, depthstencilFormat, width, height); } } void GLEScontext::initDefaultFBO( GLint width, GLint height, GLint colorFormat, GLint depthstencilFormat, GLint multisamples, GLuint* eglSurfaceRBColorId, GLuint* eglSurfaceRBDepthId, GLuint readWidth, GLint readHeight, GLint readColorFormat, GLint readDepthStencilFormat, GLint readMultisamples, GLuint* eglReadSurfaceRBColorId, GLuint* eglReadSurfaceRBDepthId) { bool needUpdateDefaultFbo = false; if (!m_defaultFBO) { dispatcher().glGenFramebuffers(1, &m_defaultFBO); m_defaultReadFBO = m_defaultFBO; needUpdateDefaultFbo = true; } bool needReallocateRbo = false; bool separateReadRbo = false; bool needReallocateReadRbo = false; separateReadRbo = eglReadSurfaceRBColorId != eglSurfaceRBColorId; if (separateReadRbo && (m_defaultReadFBO == m_defaultFBO)) { dispatcher().glGenFramebuffers(1, &m_defaultReadFBO); } if (!(*eglSurfaceRBColorId)) { dispatcher().glGenRenderbuffers(1, eglSurfaceRBColorId); dispatcher().glGenRenderbuffers(1, eglSurfaceRBDepthId); needReallocateRbo = true; } if (!(*eglReadSurfaceRBColorId) && separateReadRbo) { dispatcher().glGenRenderbuffers(1, eglReadSurfaceRBColorId); dispatcher().glGenRenderbuffers(1, eglReadSurfaceRBDepthId); needReallocateReadRbo = true; } m_defaultFBOColorFormat = colorFormat; m_defaultFBOWidth = width; m_defaultFBOHeight = height; m_defaultFBOSamples = multisamples; GLint prevRbo; dispatcher().glGetIntegerv(GL_RENDERBUFFER_BINDING, &prevRbo); // OS X in legacy opengl mode does not actually support GL_RGB565 as a renderbuffer. // Just replace it with GL_RGB8 for now. // TODO: Re-enable GL_RGB565 for OS X when we move to core profile. #ifdef __APPLE__ if (colorFormat == GL_RGB565) colorFormat = GL_RGB8; if (readColorFormat == GL_RGB565) readColorFormat = GL_RGB8; #endif if (needReallocateRbo) { initEmulatedEGLSurface(width, height, colorFormat, depthstencilFormat, multisamples, *eglSurfaceRBColorId, *eglSurfaceRBDepthId); needUpdateDefaultFbo = true; } if (needReallocateReadRbo) { initEmulatedEGLSurface(readWidth, readHeight, readColorFormat, readDepthStencilFormat, readMultisamples, *eglReadSurfaceRBColorId, *eglReadSurfaceRBDepthId); needUpdateDefaultFbo = true; } needUpdateDefaultFbo |= m_defaultFboRBColor != *eglSurfaceRBColorId || m_defaultFboRBDepth != *eglSurfaceRBDepthId; needUpdateDefaultFbo |= separateReadRbo && (m_defaultReadFboRBColor != *eglReadSurfaceRBColorId || m_defaultReadFboRBDepth != *eglReadSurfaceRBDepthId); if (!needUpdateDefaultFbo) { return; } dispatcher().glBindFramebuffer(GL_FRAMEBUFFER, m_defaultFBO); dispatcher().glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *eglSurfaceRBColorId); dispatcher().glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, *eglSurfaceRBDepthId); dispatcher().glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, *eglSurfaceRBDepthId); m_defaultFboRBColor = *eglSurfaceRBColorId; m_defaultFboRBDepth = *eglSurfaceRBDepthId; if (m_defaultFBODrawBuffer != GL_COLOR_ATTACHMENT0) { dispatcher().glDrawBuffers(1, &m_defaultFBODrawBuffer); } if (m_defaultFBOReadBuffer != GL_COLOR_ATTACHMENT0) { dispatcher().glReadBuffer(m_defaultFBOReadBuffer); } if (separateReadRbo) { dispatcher().glBindFramebuffer(GL_READ_FRAMEBUFFER, m_defaultReadFBO); dispatcher().glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *eglReadSurfaceRBColorId); dispatcher().glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, *eglReadSurfaceRBDepthId); dispatcher().glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, *eglReadSurfaceRBDepthId); m_defaultReadFboRBColor = *eglReadSurfaceRBColorId; m_defaultReadFboRBDepth = *eglReadSurfaceRBDepthId; } dispatcher().glBindRenderbuffer(GL_RENDERBUFFER, prevRbo); GLuint prevDrawFBOBinding = getFramebufferBinding(GL_FRAMEBUFFER); GLuint prevReadFBOBinding = getFramebufferBinding(GL_READ_FRAMEBUFFER); if (prevDrawFBOBinding) dispatcher().glBindFramebuffer(GL_FRAMEBUFFER, getFBOGlobalName(prevDrawFBOBinding)); if (prevReadFBOBinding) dispatcher().glBindFramebuffer(GL_READ_FRAMEBUFFER, getFBOGlobalName(prevReadFBOBinding)); // We might be initializing a surfaceless context underneath // where the viewport is initialized to 0x0 width and height. // Set to our wanted pbuffer dimensions if this is the first time // the viewport has been set. if (!m_isViewport) { setViewport(0, 0, width, height); dispatcher().glViewport(0, 0, width, height); } // same for the scissor if (!m_isScissor) { setScissor(0, 0, width, height); dispatcher().glScissor(0, 0, width, height); } } void GLEScontext::prepareCoreProfileEmulatedTexture(TextureData* texData, bool is3d, GLenum target, GLenum format, GLenum type, GLint* internalformat_out, GLenum* format_out) { if (format != GL_ALPHA && format != GL_LUMINANCE && format != GL_LUMINANCE_ALPHA) { return; } if (isCubeMapFaceTarget(target)) { target = is3d ? GL_TEXTURE_CUBE_MAP_ARRAY_EXT : GL_TEXTURE_CUBE_MAP; } // Set up the swizzle from the underlying supported // host format to the emulated format. // Make sure to re-apply any user-specified custom swizlz TextureSwizzle userSwz; // initialized to identity map if (texData) { userSwz.toRed = texData->getSwizzle(GL_TEXTURE_SWIZZLE_R); userSwz.toGreen = texData->getSwizzle(GL_TEXTURE_SWIZZLE_G); userSwz.toBlue = texData->getSwizzle(GL_TEXTURE_SWIZZLE_B); userSwz.toAlpha = texData->getSwizzle(GL_TEXTURE_SWIZZLE_A); } TextureSwizzle swz = concatSwizzles(getSwizzleForEmulatedFormat(format), userSwz); dispatcher().glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, swz.toRed); dispatcher().glTexParameteri(target, GL_TEXTURE_SWIZZLE_G, swz.toGreen); dispatcher().glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, swz.toBlue); dispatcher().glTexParameteri(target, GL_TEXTURE_SWIZZLE_A, swz.toAlpha); // Change the format/internalformat communicated to GL. GLenum emulatedFormat = getCoreProfileEmulatedFormat(format); GLint emulatedInternalFormat = getCoreProfileEmulatedInternalFormat(format, type); if (format_out) *format_out = emulatedFormat; if (internalformat_out) *internalformat_out = emulatedInternalFormat; } bool GLEScontext::isFBO(ObjectLocalName p_localName) { return m_fboNameSpace->isObject(p_localName); } ObjectLocalName GLEScontext::genFBOName(ObjectLocalName p_localName, bool genLocal) { return m_fboNameSpace->genName(GenNameInfo(NamedObjectType::FRAMEBUFFER), p_localName, genLocal); } void GLEScontext::setFBOData(ObjectLocalName p_localName, ObjectDataPtr data) { m_fboNameSpace->setObjectData(p_localName, data); } void GLEScontext::deleteFBO(ObjectLocalName p_localName) { m_fboNameSpace->deleteName(p_localName); } FramebufferData* GLEScontext::getFBOData(ObjectLocalName p_localName) const { return (FramebufferData*)getFBODataPtr(p_localName).get(); } ObjectDataPtr GLEScontext::getFBODataPtr(ObjectLocalName p_localName) const { return m_fboNameSpace->getObjectDataPtr(p_localName); } unsigned int GLEScontext::getFBOGlobalName(ObjectLocalName p_localName) const { return m_fboNameSpace->getGlobalName(p_localName); } ObjectLocalName GLEScontext::getFBOLocalName(unsigned int p_globalName) const { return m_fboNameSpace->getLocalName(p_globalName); } int GLEScontext::queryCurrFboBits(ObjectLocalName localFboName, GLenum pname) { GLint colorInternalFormat = 0; GLint depthInternalFormat = 0; GLint stencilInternalFormat = 0; bool combinedDepthStencil = false; if (!localFboName) { colorInternalFormat = m_defaultFBOColorFormat; // FBO 0 defaulting to d24s8 depthInternalFormat = m_defaultFBODepthFormat ? m_defaultFBODepthFormat : GL_DEPTH24_STENCIL8; stencilInternalFormat = m_defaultFBOStencilFormat ? m_defaultFBOStencilFormat : GL_DEPTH24_STENCIL8; } else { FramebufferData* fbData = getFBOData(localFboName); std::vector colorAttachments(getCaps()->maxDrawBuffers); std::iota(colorAttachments.begin(), colorAttachments.end(), GL_COLOR_ATTACHMENT0); bool hasColorAttachment = false; for (auto attachment : colorAttachments) { GLint internalFormat = fbData->getAttachmentInternalFormat(this, attachment); // Only defined if all used color attachments are the same // internal format. if (internalFormat) { if (hasColorAttachment && colorInternalFormat != internalFormat) { colorInternalFormat = 0; break; } colorInternalFormat = internalFormat; hasColorAttachment = true; } } GLint depthStencilFormat = fbData->getAttachmentInternalFormat(this, GL_DEPTH_STENCIL_ATTACHMENT); if (depthStencilFormat) { combinedDepthStencil = true; depthInternalFormat = depthStencilFormat; stencilInternalFormat = depthStencilFormat; } if (!combinedDepthStencil) { depthInternalFormat = fbData->getAttachmentInternalFormat(this, GL_DEPTH_ATTACHMENT); stencilInternalFormat = fbData->getAttachmentInternalFormat(this, GL_STENCIL_ATTACHMENT); } } FramebufferChannelBits res = glFormatToChannelBits(colorInternalFormat, depthInternalFormat, stencilInternalFormat); switch (pname) { case GL_RED_BITS: return res.red; case GL_GREEN_BITS: return res.green; case GL_BLUE_BITS: return res.blue; case GL_ALPHA_BITS: return res.alpha; case GL_DEPTH_BITS: return res.depth; case GL_STENCIL_BITS: return res.stencil; } return 0; } static const char kTexImageEmulationVShaderSrc[] = R"( precision highp float; out vec2 v_texcoord; void main() { const vec2 quad_pos[6] = vec2[6]( vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 0.0), vec2(1.0, 1.0)); gl_Position = vec4((quad_pos[gl_VertexID] * 2.0) - 1.0, 0.0, 1.0); v_texcoord = quad_pos[gl_VertexID]; })"; static const char kTexImageEmulationVShaderSrcFlipped[] = R"( precision highp float; layout (location = 0) in vec2 a_pos; out vec2 v_texcoord; void main() { gl_Position = vec4((a_pos.xy) * 2.0 - 1.0, 0.0, 1.0); v_texcoord = a_pos; v_texcoord.y = 1.0 - v_texcoord.y; })"; static const char kTexImageEmulationFShaderSrc[] = R"( precision highp float; uniform sampler2D source_tex; in vec2 v_texcoord; out vec4 color; void main() { color = texture(source_tex, v_texcoord); })"; void GLEScontext::initTexImageEmulation() { if (m_textureEmulationProg) return; auto& gl = dispatcher(); std::string vshaderSrc = isCoreProfile() ? "#version 330 core\n" : "#version 300 es\n"; vshaderSrc += kTexImageEmulationVShaderSrc; std::string fshaderSrc = isCoreProfile() ? "#version 330 core\n" : "#version 300 es\n"; fshaderSrc += kTexImageEmulationFShaderSrc; GLuint vshader = compileAndValidateCoreShader(GL_VERTEX_SHADER, vshaderSrc.c_str()); GLuint fshader = compileAndValidateCoreShader(GL_FRAGMENT_SHADER, fshaderSrc.c_str()); m_textureEmulationProg = linkAndValidateProgram(vshader, fshader); m_textureEmulationSamplerLoc = gl.glGetUniformLocation(m_textureEmulationProg, "source_tex"); gl.glGenFramebuffers(1, &m_textureEmulationFBO); gl.glGenTextures(2, m_textureEmulationTextures); gl.glGenVertexArrays(1, &m_textureEmulationVAO); } void GLEScontext::copyTexImageWithEmulation( TextureData* texData, bool isSubImage, GLenum target, GLint level, GLenum internalformat, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { // Create objects used for emulation if they don't exist already. initTexImageEmulation(); auto& gl = dispatcher(); // Save all affected state. ScopedGLState state; state.pushForCoreProfileTextureEmulation(); // render to an intermediate texture with the same format: // 1. Get the format FramebufferData* fbData = getFBOData(getFramebufferBinding(GL_READ_FRAMEBUFFER)); GLint readFbInternalFormat = fbData ? fbData->getAttachmentInternalFormat(this, GL_COLOR_ATTACHMENT0) : m_defaultFBOColorFormat; // 2. Create the texture for textures[0] with this format, and initialize // it to the current FBO read buffer. gl.glBindTexture(GL_TEXTURE_2D, m_textureEmulationTextures[0]); gl.glCopyTexImage2D(GL_TEXTURE_2D, 0, readFbInternalFormat, x, y, width, height, 0); // 3. Set swizzle of textures[0] so they are read in the right way // when drawing to textures[1]. TextureSwizzle swz = getInverseSwizzleForEmulatedFormat(texData->format); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, swz.toRed); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, swz.toGreen); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, swz.toBlue); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, swz.toAlpha); // Also, nearest filtering gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // 4. Initialize textures[1] with same width/height, and use it to back // the FBO that holds the swizzled results. gl.glBindTexture(GL_TEXTURE_2D, m_textureEmulationTextures[1]); gl.glTexImage2D(GL_TEXTURE_2D, 0, readFbInternalFormat, width, height, 0, baseFormatOfInternalFormat(readFbInternalFormat), accurateTypeOfInternalFormat(readFbInternalFormat), nullptr); // Also, nearest filtering gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl.glBindFramebuffer(GL_FRAMEBUFFER, m_textureEmulationFBO); gl.glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_textureEmulationTextures[1], 0); // 5. Draw textures[0] to our FBO, making sure all state is compatible. gl.glDisable(GL_BLEND); gl.glDisable(GL_SCISSOR_TEST); gl.glDisable(GL_DEPTH_TEST); gl.glDisable(GL_STENCIL_TEST); gl.glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); gl.glDisable(GL_SAMPLE_COVERAGE); gl.glDisable(GL_CULL_FACE); gl.glDisable(GL_POLYGON_OFFSET_FILL); gl.glDisable(GL_RASTERIZER_DISCARD); gl.glViewport(0, 0, width, height); if (isGles2Gles()) { gl.glDepthRangef(0.0f, 1.0f); } else { gl.glDepthRange(0.0f, 1.0f); } gl.glColorMask(1, 1, 1, 1); gl.glBindTexture(GL_TEXTURE_2D, m_textureEmulationTextures[0]); GLint texUnit; gl.glGetIntegerv(GL_ACTIVE_TEXTURE, &texUnit); gl.glUseProgram(m_textureEmulationProg); gl.glUniform1i(m_textureEmulationSamplerLoc, texUnit - GL_TEXTURE0); gl.glBindVertexArray(m_textureEmulationVAO); gl.glDrawArrays(GL_TRIANGLES, 0, 6); // now the emulated version has been rendered and written to the read FBO // with the correct swizzle. if (isCubeMapFaceTarget(target)) { gl.glBindTexture(GL_TEXTURE_CUBE_MAP, texData->getGlobalName()); } else { gl.glBindTexture(target, texData->getGlobalName()); } if (isSubImage) { gl.glCopyTexSubImage2D(target, level, xoffset, yoffset, 0, 0, width, height); } else { gl.glCopyTexImage2D(target, level, internalformat, 0, 0, width, height, border); } } // static GLuint GLEScontext::compileAndValidateCoreShader(GLenum shaderType, const char* src) { GLDispatch& gl = dispatcher(); GLuint shader = gl.glCreateShader(shaderType); gl.glShaderSource(shader, 1, (const GLchar* const*)&src, nullptr); gl.glCompileShader(shader); GLint compileStatus; gl.glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); if (compileStatus != GL_TRUE) { GLsizei infoLogLength = 0; gl.glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); std::vector infoLog(infoLogLength + 1, 0); gl.glGetShaderInfoLog(shader, infoLogLength, nullptr, &infoLog[0]); ERR("fail to compile. infolog %s", &infoLog[0]); } return shader; } // static GLuint GLEScontext::linkAndValidateProgram(GLuint vshader, GLuint fshader) { GLDispatch& gl = dispatcher(); GLuint program = gl.glCreateProgram(); gl.glAttachShader(program, vshader); gl.glAttachShader(program, fshader); gl.glLinkProgram(program); GLint linkStatus; gl.glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); if (linkStatus != GL_TRUE) { GLsizei infoLogLength = 0; gl.glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength); std::vector infoLog(infoLogLength + 1, 0); gl.glGetProgramInfoLog(program, infoLogLength, nullptr, &infoLog[0]); ERR("fail to link program. infolog: %s", &infoLog[0]); } gl.glDeleteShader(vshader); gl.glDeleteShader(fshader); return program; } int GLEScontext::getReadBufferSamples() { GLuint readFboBinding = getFramebufferBinding(GL_READ_FRAMEBUFFER); bool defaultFboReadBufferBound = readFboBinding == 0; if (defaultFboReadBufferBound) { return m_defaultFBOSamples; } else { FramebufferData* fbData = (FramebufferData*)(getFBODataPtr(readFboBinding).get()); return fbData ? fbData->getAttachmentSamples(this, fbData->getReadBuffer()) : 0; } } int GLEScontext::getReadBufferInternalFormat() { GLuint readFboBinding = getFramebufferBinding(GL_READ_FRAMEBUFFER); bool defaultFboReadBufferBound = readFboBinding == 0; if (defaultFboReadBufferBound) { return m_defaultFBOColorFormat; } else { FramebufferData* fbData = (FramebufferData*)(getFBODataPtr(readFboBinding).get()); return fbData ? fbData->getAttachmentInternalFormat(this, fbData->getReadBuffer()) : 0; } } void GLEScontext::getReadBufferDimensions(GLint* width, GLint* height) { GLuint readFboBinding = getFramebufferBinding(GL_READ_FRAMEBUFFER); bool defaultFboReadBufferBound = readFboBinding == 0; if (defaultFboReadBufferBound) { *width = m_defaultFBOWidth; *height = m_defaultFBOHeight; } else { FramebufferData* fbData = (FramebufferData*)(getFBODataPtr(readFboBinding).get()); if (fbData) { fbData->getAttachmentDimensions( this, fbData->getReadBuffer(), width, height); } } } void GLEScontext::setupImageBlitState() { auto& gl = dispatcher(); m_blitState.prevSamples = m_blitState.samples; m_blitState.samples = getReadBufferSamples(); if (m_blitState.program) return; std::string vshaderSrc = isCoreProfile() ? "#version 330 core\n" : "#version 300 es\n"; vshaderSrc += kTexImageEmulationVShaderSrcFlipped; std::string fshaderSrc = isCoreProfile() ? "#version 330 core\n" : "#version 300 es\n"; fshaderSrc += kTexImageEmulationFShaderSrc; GLuint vshader = compileAndValidateCoreShader(GL_VERTEX_SHADER, vshaderSrc.c_str()); GLuint fshader = compileAndValidateCoreShader(GL_FRAGMENT_SHADER, fshaderSrc.c_str()); m_blitState.program = linkAndValidateProgram(vshader, fshader); m_blitState.samplerLoc = gl.glGetUniformLocation(m_blitState.program, "source_tex"); gl.glGenFramebuffers(1, &m_blitState.fbo); gl.glGenFramebuffers(1, &m_blitState.resolveFbo); gl.glGenTextures(1, &m_blitState.tex); gl.glGenVertexArrays(1, &m_blitState.vao); gl.glGenBuffers(1, &m_blitState.vbo); float blitVbo[] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, }; GLint buf; gl.glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &buf); gl.glBindBuffer(GL_ARRAY_BUFFER, m_blitState.vbo); gl.glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), blitVbo, GL_STATIC_DRAW); gl.glBindVertexArray(m_blitState.vao); gl.glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0); gl.glEnableVertexAttribArray(0); gl.glBindBuffer(GL_ARRAY_BUFFER, buf); } bool GLEScontext::setupImageBlitForTexture(uint32_t width, uint32_t height, GLint internalFormat) { GLint sizedInternalFormat = GL_RGBA8; if (internalFormat != GL_RGBA8 && internalFormat != GL_RGB8 && internalFormat != GL_RGB565) { switch (internalFormat) { case GL_RGB: sizedInternalFormat = GL_RGB8; break; case GL_RGBA: sizedInternalFormat = GL_RGBA8; break; default: break; } } auto& gl = dispatcher(); // In eglSwapBuffers, the surface must be bound as the draw surface of // the current context, which corresponds to m_defaultFBO here. // // EGL 1.4 spec: // // 3.9.3 Posting Semantics surface must be bound to the draw surface of the // calling thread’s current context, for the current rendering API. This // restriction may be lifted in future EGL revisions. // copy the draw buffer to a texture. GLint read_iformat = m_defaultFBOColorFormat; GLint read_format = baseFormatOfInternalFormat(read_iformat); if (isIntegerInternalFormat(read_iformat) || read_iformat == GL_RGB10_A2) { // Is not a blittable format. Just create the texture for now to // make image blit state consistent. gl.glTexImage2D(GL_TEXTURE_2D, 0, sizedInternalFormat, width, height, 0, baseFormatOfInternalFormat(internalFormat), GL_UNSIGNED_BYTE, 0); return false; } if (width != m_blitState.width || height != m_blitState.height || internalFormat != m_blitState.internalFormat || m_blitState.samples != m_blitState.prevSamples) { m_blitState.width = width; m_blitState.height = height; m_blitState.internalFormat = internalFormat; gl.glTexImage2D(GL_TEXTURE_2D, 0, read_iformat, width, height, 0, read_format, GL_UNSIGNED_BYTE, 0); if (m_blitState.samples > 0) { gl.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_blitState.resolveFbo); gl.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_blitState.tex, 0); } gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } if (m_blitState.samples > 0) { // Resolve MSAA GLint rWidth = m_defaultFBOWidth; GLint rHeight = m_defaultFBOHeight; gl.glBindFramebuffer(GL_READ_FRAMEBUFFER, m_defaultFBO); gl.glBindTexture(GL_TEXTURE_2D, 0); gl.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_blitState.resolveFbo); gl.glBlitFramebuffer(0, 0, rWidth, rHeight, 0, 0, rWidth, rHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST); gl.glBindTexture(GL_TEXTURE_2D, m_blitState.tex); } else { gl.glBindFramebuffer(GL_READ_FRAMEBUFFER, m_defaultFBO); gl.glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height); } return true; } void GLEScontext::blitFromReadBufferToTextureFlipped(GLuint globalTexObj, GLuint width, GLuint height, GLint internalFormat, GLenum format, GLenum type) { // TODO: these might also matter (void)format; (void)type; auto& gl = dispatcher(); GLint prevViewport[4]; getViewport(prevViewport); setupImageBlitState(); GLint prevTex2D = 0; gl.glGetIntegerv(GL_TEXTURE_BINDING_2D, &prevTex2D); gl.glBindTexture(GL_TEXTURE_2D, m_blitState.tex); bool shouldBlit = setupImageBlitForTexture(width, height, internalFormat); if (!shouldBlit) { gl.glBindTexture(GL_TEXTURE_2D, prevTex2D); return; } // b/159670873: The texture to blit doesn't necessarily match the display // size. If it doesn't match, then we might not be using the right mipmap // level, which can result in a black screen. Set to always use level 0. gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); gl.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_blitState.fbo); gl.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, globalTexObj, 0); gl.glDisable(GL_SCISSOR_TEST); gl.glDisable(GL_DEPTH_TEST); gl.glDisable(GL_STENCIL_TEST); gl.glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); gl.glDisable(GL_SAMPLE_COVERAGE); gl.glDisable(GL_CULL_FACE); gl.glDisable(GL_POLYGON_OFFSET_FILL); gl.glDisable(GL_RASTERIZER_DISCARD); gl.glViewport(0, 0, width, height); if (isGles2Gles()) { gl.glDepthRangef(0.0f, 1.0f); } else { gl.glDepthRange(0.0f, 1.0f); } if (getCaps()->ext_GL_EXT_draw_buffers_indexed) { gl.glDisableiEXT(GL_BLEND, 0); gl.glColorMaskiEXT(0, 1, 1, 1, 1); } else { gl.glDisable(GL_BLEND); gl.glColorMask(1, 1, 1, 1); } gl.glUseProgram(m_blitState.program); gl.glUniform1i(m_blitState.samplerLoc, m_activeTexture); gl.glBindVertexArray(m_blitState.vao); gl.glDrawArrays(GL_TRIANGLES, 0, 6); // state restore const GLuint globalProgramName = shareGroup()->getGlobalName( NamedObjectType::SHADER_OR_PROGRAM, m_useProgram); gl.glUseProgram(globalProgramName); gl.glBindVertexArray(getVAOGlobalName(m_currVaoState.vaoId())); gl.glBindTexture( GL_TEXTURE_2D, shareGroup()->getGlobalName( NamedObjectType::TEXTURE, getTextureLocalName(GL_TEXTURE_2D, getBindedTexture(GL_TEXTURE_2D)))); GLuint drawFboBinding = getFramebufferBinding(GL_DRAW_FRAMEBUFFER); GLuint readFboBinding = getFramebufferBinding(GL_READ_FRAMEBUFFER); gl.glBindFramebuffer( GL_DRAW_FRAMEBUFFER, drawFboBinding ? getFBOGlobalName(drawFboBinding) : m_defaultFBO); gl.glBindFramebuffer( GL_READ_FRAMEBUFFER, readFboBinding ? getFBOGlobalName(readFboBinding) : m_defaultReadFBO); if (isEnabled(GL_SCISSOR_TEST)) gl.glEnable(GL_SCISSOR_TEST); if (isEnabled(GL_DEPTH_TEST)) gl.glEnable(GL_DEPTH_TEST); if (isEnabled(GL_STENCIL_TEST)) gl.glEnable(GL_STENCIL_TEST); if (isEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE)) gl.glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); if (isEnabled(GL_SAMPLE_COVERAGE)) gl.glEnable(GL_SAMPLE_COVERAGE); if (isEnabled(GL_CULL_FACE)) gl.glEnable(GL_CULL_FACE); if (isEnabled(GL_POLYGON_OFFSET_FILL)) gl.glEnable(GL_POLYGON_OFFSET_FILL); if (isEnabled(GL_RASTERIZER_DISCARD)) gl.glEnable(GL_RASTERIZER_DISCARD); gl.glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]); if (isGles2Gles()) { gl.glDepthRangef(m_zNear, m_zFar); } else { gl.glDepthRange(m_zNear, m_zFar); } if (getCaps()->ext_GL_EXT_draw_buffers_indexed) { if (isEnabled(GL_BLEND)) gl.glEnableiEXT(GL_BLEND, 0); gl.glColorMaskiEXT(0, m_blendStates[0].colorMaskR, m_blendStates[0].colorMaskG, m_blendStates[0].colorMaskB, m_blendStates[0].colorMaskA); } else { if (isEnabled(GL_BLEND)) gl.glEnable(GL_BLEND); gl.glColorMask(m_blendStates[0].colorMaskR, m_blendStates[0].colorMaskG, m_blendStates[0].colorMaskB, m_blendStates[0].colorMaskA); } gl.glFlush(); } void GLEScontext::blitFromReadBufferToEGLImage(EGLImage image, GLint internalFormat, int width, int height) { auto& gl = dispatcher(); GLint prevViewport[4]; getViewport(prevViewport); setupImageBlitState(); GLint prevTex2D = 0; gl.glGetIntegerv(GL_TEXTURE_BINDING_2D, &prevTex2D); gl.glBindTexture(GL_TEXTURE_2D, m_blitState.tex); bool shouldBlit = setupImageBlitForTexture(width, height, internalFormat); if (!shouldBlit) { gl.glBindTexture(GL_TEXTURE_2D, prevTex2D); return; } // b/159670873: The texture to blit doesn't necessarily match the display // size. If it doesn't match, then we might not be using the right mipmap // level, which can result in a black screen. Set to always use level 0. gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); if (!m_blitState.eglImageTex) { gl.glGenTextures(1, &m_blitState.eglImageTex); } gl.glBindTexture(GL_TEXTURE_2D, m_blitState.eglImageTex); gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); gl.glBindTexture(GL_TEXTURE_2D, m_blitState.tex); gl.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_blitState.fbo); gl.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_blitState.eglImageTex, 0); gl.glDisable(GL_SCISSOR_TEST); gl.glDisable(GL_DEPTH_TEST); gl.glDisable(GL_STENCIL_TEST); gl.glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); gl.glDisable(GL_SAMPLE_COVERAGE); gl.glDisable(GL_CULL_FACE); gl.glDisable(GL_POLYGON_OFFSET_FILL); gl.glDisable(GL_RASTERIZER_DISCARD); gl.glViewport(0, 0, width, height); if (isGles2Gles()) { gl.glDepthRangef(0.0f, 1.0f); } else { gl.glDepthRange(0.0f, 1.0f); } if (getCaps()->ext_GL_EXT_draw_buffers_indexed) { gl.glDisableiEXT(GL_BLEND, 0); gl.glColorMaskiEXT(0, 1, 1, 1, 1); } else { gl.glDisable(GL_BLEND); gl.glColorMask(1, 1, 1, 1); } gl.glUseProgram(m_blitState.program); gl.glUniform1i(m_blitState.samplerLoc, m_activeTexture); gl.glBindVertexArray(m_blitState.vao); gl.glDrawArrays(GL_TRIANGLES, 0, 6); // state restore const GLuint globalProgramName = shareGroup()->getGlobalName( NamedObjectType::SHADER_OR_PROGRAM, m_useProgram); gl.glUseProgram(globalProgramName); gl.glBindVertexArray(getVAOGlobalName(m_currVaoState.vaoId())); gl.glBindTexture( GL_TEXTURE_2D, shareGroup()->getGlobalName( NamedObjectType::TEXTURE, getTextureLocalName(GL_TEXTURE_2D, getBindedTexture(GL_TEXTURE_2D)))); GLuint drawFboBinding = getFramebufferBinding(GL_DRAW_FRAMEBUFFER); GLuint readFboBinding = getFramebufferBinding(GL_READ_FRAMEBUFFER); gl.glBindFramebuffer( GL_DRAW_FRAMEBUFFER, drawFboBinding ? getFBOGlobalName(drawFboBinding) : m_defaultFBO); gl.glBindFramebuffer( GL_READ_FRAMEBUFFER, readFboBinding ? getFBOGlobalName(readFboBinding) : m_defaultReadFBO); if (isEnabled(GL_SCISSOR_TEST)) gl.glEnable(GL_SCISSOR_TEST); if (isEnabled(GL_DEPTH_TEST)) gl.glEnable(GL_DEPTH_TEST); if (isEnabled(GL_STENCIL_TEST)) gl.glEnable(GL_STENCIL_TEST); if (isEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE)) gl.glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); if (isEnabled(GL_SAMPLE_COVERAGE)) gl.glEnable(GL_SAMPLE_COVERAGE); if (isEnabled(GL_CULL_FACE)) gl.glEnable(GL_CULL_FACE); if (isEnabled(GL_POLYGON_OFFSET_FILL)) gl.glEnable(GL_POLYGON_OFFSET_FILL); if (isEnabled(GL_RASTERIZER_DISCARD)) gl.glEnable(GL_RASTERIZER_DISCARD); gl.glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]); if (isGles2Gles()) { gl.glDepthRangef(m_zNear, m_zFar); } else { gl.glDepthRange(m_zNear, m_zFar); } if (getCaps()->ext_GL_EXT_draw_buffers_indexed) { if (isEnabled(GL_BLEND)) gl.glEnableiEXT(GL_BLEND, 0); gl.glColorMaskiEXT(0, m_blendStates[0].colorMaskR, m_blendStates[0].colorMaskG, m_blendStates[0].colorMaskB, m_blendStates[0].colorMaskA); } else { if (isEnabled(GL_BLEND)) gl.glEnable(GL_BLEND); gl.glColorMask(m_blendStates[0].colorMaskR, m_blendStates[0].colorMaskG, m_blendStates[0].colorMaskB, m_blendStates[0].colorMaskA); } gl.glFlush(); } // Primitive restart emulation #define GL_PRIMITIVE_RESTART 0x8F9D #define GL_PRIMITIVE_RESTART_INDEX 0x8F9E void GLEScontext::setPrimitiveRestartEnabled(bool enabled) { auto& gl = dispatcher(); if (enabled) { gl.glEnable(GL_PRIMITIVE_RESTART); } else { gl.glDisable(GL_PRIMITIVE_RESTART); } m_primitiveRestartEnabled = enabled; } void GLEScontext::updatePrimitiveRestartIndex(GLenum type) { auto& gl = dispatcher(); switch (type) { case GL_UNSIGNED_BYTE: gl.glPrimitiveRestartIndex(0xff); break; case GL_UNSIGNED_SHORT: gl.glPrimitiveRestartIndex(0xffff); break; case GL_UNSIGNED_INT: gl.glPrimitiveRestartIndex(0xffffffff); break; } } bool GLEScontext::isVAO(ObjectLocalName p_localName) { VAOStateMap::iterator it = m_vaoStateMap.find(p_localName); if (it == m_vaoStateMap.end()) return false; VAOStateRef vao(it); return vao.isEverBound(); } ObjectLocalName GLEScontext::genVAOName(ObjectLocalName p_localName, bool genLocal) { return m_vaoNameSpace->genName(GenNameInfo(NamedObjectType::VERTEX_ARRAY_OBJECT), p_localName, genLocal); } void GLEScontext::deleteVAO(ObjectLocalName p_localName) { m_vaoNameSpace->deleteName(p_localName); } unsigned int GLEScontext::getVAOGlobalName(ObjectLocalName p_localName) { return m_vaoNameSpace->getGlobalName(p_localName); } ObjectLocalName GLEScontext::getVAOLocalName(unsigned int p_globalName) { return m_vaoNameSpace->getLocalName(p_globalName); } void GLEScontext::setDefaultFBODrawBuffer(GLenum buffer) { m_defaultFBODrawBuffer = buffer; } void GLEScontext::setDefaultFBOReadBuffer(GLenum buffer) { m_defaultFBOReadBuffer = buffer; }