| /* |
| * 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 <GLcommon/GLEScontext.h> |
| |
| #include "aemu/base/synchronization/Lock.h" |
| #include "aemu/base/containers/Lookup.h" |
| #include "aemu/base/files/StreamSerializing.h" |
| #include "host-common/logging.h" |
| |
| #include <GLcommon/GLconversion_macros.h> |
| #include <GLcommon/GLSnapshotSerializers.h> |
| #include <GLcommon/GLESmacros.h> |
| #include <GLcommon/TextureData.h> |
| #include <GLES/gl.h> |
| #include <GLES/glext.h> |
| #include <GLES2/gl2.h> |
| #include <GLES2/gl2ext.h> |
| #include <GLES3/gl3.h> |
| #include <GLES3/gl31.h> |
| #include <GLcommon/GLESvalidate.h> |
| #include <GLcommon/TextureUtils.h> |
| #include <GLcommon/FramebufferData.h> |
| #include <GLcommon/ScopedGLState.h> |
| #ifndef _MSC_VER |
| #include <strings.h> |
| #endif |
| #include <string.h> |
| |
| #include <numeric> |
| #include <map> |
| |
| //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<const char*>( |
| 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<const char*>( |
| 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<const GLubyte*>(indices)[i]; |
| case GL_UNSIGNED_SHORT: |
| return static_cast<const GLushort*>(indices)[i]; |
| case GL_UNSIGNED_INT: |
| return static_cast<const GLuint*>(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;i<m_maxTexUnits;++i) { |
| for (int j=0;j<NUM_TEXTURE_TARGETS;++j) |
| { |
| m_texState[i][j].texture = 0; |
| m_texState[i][j].enabled = GL_FALSE; |
| } |
| } |
| |
| m_indexedTransformFeedbackBuffers.resize(getCaps()->maxTransformFeedbackSeparateAttribs); |
| 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<VAOStateMap>(stream, m_vaoStateMap); |
| uint32_t vaoId = stream->getBe32(); |
| setVertexArrayObject(vaoId); |
| |
| m_copyReadBuffer = static_cast<GLuint>(stream->getBe32()); |
| m_copyWriteBuffer = static_cast<GLuint>(stream->getBe32()); |
| m_pixelPackBuffer = static_cast<GLuint>(stream->getBe32()); |
| m_pixelUnpackBuffer = static_cast<GLuint>(stream->getBe32()); |
| m_transformFeedbackBuffer = static_cast<GLuint>(stream->getBe32()); |
| m_uniformBuffer = static_cast<GLuint>(stream->getBe32()); |
| m_atomicCounterBuffer = static_cast<GLuint>(stream->getBe32()); |
| m_dispatchIndirectBuffer = static_cast<GLuint>(stream->getBe32()); |
| m_drawIndirectBuffer = static_cast<GLuint>(stream->getBe32()); |
| m_shaderStorageBuffer = static_cast<GLuint>(stream->getBe32()); |
| m_textureBuffer = static_cast<GLuint>(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<GLint>(stream->getBe32()); |
| m_viewportY = static_cast<GLint>(stream->getBe32()); |
| m_viewportWidth = static_cast<GLsizei>(stream->getBe32()); |
| m_viewportHeight = static_cast<GLsizei>(stream->getBe32()); |
| |
| m_polygonOffsetFactor = static_cast<GLfloat>(stream->getFloat()); |
| m_polygonOffsetUnits = static_cast<GLfloat>(stream->getFloat()); |
| |
| m_isScissor = stream->getByte(); |
| m_scissorX = static_cast<GLint>(stream->getBe32()); |
| m_scissorY = static_cast<GLint>(stream->getBe32()); |
| m_scissorWidth = static_cast<GLsizei>(stream->getBe32()); |
| m_scissorHeight = static_cast<GLsizei>(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<GLenum>(stream->getBe32()); |
| m_frontFace = static_cast<GLenum>(stream->getBe32()); |
| m_depthFunc = static_cast<GLenum>(stream->getBe32()); |
| m_depthMask = static_cast<GLboolean>(stream->getByte()); |
| m_zNear = static_cast<GLclampf>(stream->getFloat()); |
| m_zFar = static_cast<GLclampf>(stream->getFloat()); |
| |
| m_lineWidth = static_cast<GLclampf>(stream->getFloat()); |
| |
| m_sampleCoverageVal = static_cast<GLclampf>(stream->getFloat()); |
| m_sampleCoverageInvert = static_cast<GLboolean>(stream->getByte()); |
| |
| stream->read(m_stencilStates, sizeof(m_stencilStates)); |
| |
| m_clearColorR = static_cast<GLclampf>(stream->getFloat()); |
| m_clearColorG = static_cast<GLclampf>(stream->getFloat()); |
| m_clearColorB = static_cast<GLclampf>(stream->getFloat()); |
| m_clearColorA = static_cast<GLclampf>(stream->getFloat()); |
| |
| m_clearDepth = static_cast<GLclampf>(stream->getFloat()); |
| m_clearStencil = static_cast<GLint>(stream->getBe32()); |
| |
| // share group is supposed to be loaded by EglContext and reset |
| // when loading EglContext |
| //int sharegroupId = stream->getBe32(); |
| m_glError = static_cast<GLenum>(stream->getBe32()); |
| m_maxTexUnits = static_cast<int>(stream->getBe32()); |
| m_maxUsedTexUnit = static_cast<int>(stream->getBe32()); |
| m_texState = new textureUnitState[m_maxTexUnits]; |
| stream->read(m_texState, sizeof(textureUnitState) * m_maxTexUnits); |
| m_arrayBuffer = static_cast<unsigned int>(stream->getBe32()); |
| m_elementBuffer = static_cast<unsigned int>(stream->getBe32()); |
| m_renderbuffer = static_cast<GLuint>(stream->getBe32()); |
| m_drawFramebuffer = static_cast<GLuint>(stream->getBe32()); |
| m_readFramebuffer = static_cast<GLuint>(stream->getBe32()); |
| m_defaultFBODrawBuffer = static_cast<GLenum>(stream->getBe32()); |
| m_defaultFBOReadBuffer = static_cast<GLenum>(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<const GLenum, bool>& 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<const GLenum, GLint>& 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<void(GLenum)> 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<GLESbuffer*>( |
| 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<const unsigned char*>(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<attribSize;j++) { |
| reinterpret_cast<GLfloat*>(&static_cast<unsigned char*>(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<GLfloat*>(static_cast<unsigned char*>(dataOut) + index*strideOut); |
| |
| for(int j=0;j<attribSize;j++) { |
| float_data[j] = X2F(fixed_data[j]); |
| } |
| } |
| } |
| |
| static void convertByteDirectLoop(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 GLbyte* byte_data = (const GLbyte *)dataIn; |
| //filling attrib |
| for(int j=0;j<attribSize;j++) { |
| reinterpret_cast<GLshort*>(&static_cast<unsigned char*>(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<GLshort*>(static_cast<unsigned char*>(dataOut) + index*strideOut); |
| |
| for(int j=0;j<attribSize;j++) { |
| short_data[j] = B2S(bytes_data[j]); |
| } |
| } |
| } |
| static void directToBytesRanges(GLint first,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()+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;i<ranges.size();i++) { |
| int startIndex = (ranges[i].getStart() - offset) / stride; |
| int nElements = ranges[i].getSize()/attribSize; |
| for(int j=0;j<nElements;j++) { |
| indices[n++] = startIndex+j; |
| } |
| } |
| return n; |
| } |
| |
| void GLEScontext::convertDirect(GLESConversionArrays& cArrs,GLint first,GLsizei count,GLenum array_id,GLESpointer* p) { |
| |
| GLenum type = p->getType(); |
| 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<count;i++) { |
| if(b_indices[i] > max) max = b_indices[i]; |
| } |
| } else if (type == GL_UNSIGNED_SHORT) { |
| GLushort* us_indices =(GLushort *)indices; |
| for(int i=0;i<count;i++) { |
| if(us_indices[i] > max) max = us_indices[i]; |
| } |
| } else { // type == GL_UNSIGNED_INT |
| GLuint* ui_indices =(GLuint *)indices; |
| for(int i=0;i<count;i++) { |
| if(ui_indices[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<char*>(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<BufferBinding>& 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<GLESbuffer*>( |
| 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<GLESbuffer*>( |
| m_shareGroup |
| ->getObjectData(NamedObjectType::VERTEXBUFFER, bufferName)); |
| *param = vbo->getSize(); |
| } |
| |
| void GLEScontext::getBufferUsage(GLenum target,GLint* param) { |
| GLuint bufferName = getBuffer(target); |
| GLESbuffer* vbo = static_cast<GLESbuffer*>( |
| 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<GLESbuffer*>( |
| 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<GLESbuffer*>( |
| 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<GLint, bool> found; |
| forEachEtc2Format([&numEtc2Formats, &found](GLint format) { ++numEtc2Formats; found[format] = false;}); |
| forEachAstcFormat([&numAstcFormats, &found](GLint format) { ++numAstcFormats; found[format] = false;}); |
| |
| std::vector<GLint> 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;i<NUM_TEXTURE_TARGETS;++i) { |
| if (m_texState[unit-GL_TEXTURE0][i].enabled) |
| return true; |
| } |
| return false; |
| } |
| |
| bool GLEScontext::glGetBooleanv(GLenum pname, GLboolean *params) |
| { |
| GLint iParam; |
| |
| if(glGetIntegerv(pname, &iParam)) |
| { |
| *params = (iParam != 0); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool GLEScontext::glGetFixedv(GLenum pname, GLfixed *params) |
| { |
| bool result = false; |
| GLint numParams = 1; |
| |
| GLint* iParams = new GLint[numParams]; |
| if (numParams>0 && 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<GLenum> 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<char> 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<char> 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; |
| } |