You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2973 lines
104 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* 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 "base/Lock.h"
#include "base/Lookup.h"
#include "base/StreamSerializing.h"
#include "host-common/feature_control.h"
#include "ErrorLog.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_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_glVendor;
std::string GLEScontext::s_glRenderer;
std::string GLEScontext::s_glVersion;
GLSupport GLEScontext::s_glSupport;
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;
}
static 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 (%s,%d)\n", indices_type, __FUNCTION__,__LINE__);
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) {
initCapsLocked(reinterpret_cast<const GLubyte*>(
getHostExtensionsString(&s_glDispatch).c_str()));
s_glExtensions = new std::string();
}
if (!s_glExtensionsGles1) {
s_glExtensionsGles1 = new std::string();
}
s_lock.unlock();
}
void GLEScontext::init() {
if (!m_initialized) {
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);
}
}
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());
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);
});
m_blendEquationRgb = static_cast<GLenum>(stream->getBe32());
m_blendEquationAlpha = static_cast<GLenum>(stream->getBe32());
m_blendSrcRgb = static_cast<GLenum>(stream->getBe32());
m_blendDstRgb = static_cast<GLenum>(stream->getBe32());
m_blendSrcAlpha = static_cast<GLenum>(stream->getBe32());
m_blendDstAlpha = static_cast<GLenum>(stream->getBe32());
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_colorMaskR = static_cast<GLboolean>(stream->getByte());
m_colorMaskG = static_cast<GLboolean>(stream->getByte());
m_colorMaskB = static_cast<GLboolean>(stream->getByte());
m_colorMaskA = static_cast<GLboolean>(stream->getByte());
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);
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(m_blendEquationRgb);
stream->putBe32(m_blendEquationAlpha);
stream->putBe32(m_blendSrcRgb);
stream->putBe32(m_blendDstRgb);
stream->putBe32(m_blendSrcAlpha);
stream->putBe32(m_blendDstAlpha);
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->putByte(m_colorMaskR);
stream->putByte(m_colorMaskG);
stream->putByte(m_colorMaskB);
stream->putByte(m_colorMaskA);
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;
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);
}
}
dispatcher.glBlendEquationSeparate(m_blendEquationRgb,
m_blendEquationAlpha);
dispatcher.glBlendFuncSeparate(m_blendSrcRgb, m_blendDstRgb,
m_blendSrcAlpha, m_blendDstAlpha);
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);
dispatcher.glColorMask(m_colorMaskR, m_colorMaskG, m_colorMaskB,
m_colorMaskA);
// report any GL errors when loading from a snapshot
GLenum err = 0;
do {
err = dispatcher.glGetError();
#ifdef _DEBUG
if (err) {
fprintf(stderr,
"warning: get GL error %d while restoring a snapshot\n",
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
fprintf(stderr, "Warning: Invalid pointer offset %u, arrType %d, type %d\n", 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;
}
GLint GLEScontext::getUnpackAlignment() {
return android::base::findOrDefault(m_glPixelStoreiList,
GL_UNPACK_ALIGNMENT, 4);
}
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;
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;
// 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;
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;
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:
setTextureEnabled(item,true);
break;
default:
m_glEnableList[item] = isEnable;
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:
return m_texState[m_activeTexture][GLTextureTargetToLocal(item)].enabled;
default:
return android::base::findOrDefault(m_glEnableList, item, false);
}
}
void GLEScontext::setBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) {
m_blendEquationRgb = modeRGB;
m_blendEquationAlpha = modeAlpha;
}
void GLEScontext::setBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB,
GLenum srcAlpha, GLenum dstAlpha) {
m_blendSrcRgb = srcRGB;
m_blendDstRgb = dstRGB;
m_blendSrcAlpha = srcAlpha;
m_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) {
m_colorMaskR = red;
m_colorMaskG = green;
m_colorMaskB = blue;
m_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 (s_glExtensions)
ret = s_glExtensions->c_str();
else
ret="";
}
s_lock.unlock();
return ret;
}
const char * GLEScontext::getVendorString(bool isGles1) const {
return isGles1 ? s_glVendorGles1.c_str() : s_glVendor.c_str();
}
const char * GLEScontext::getRendererString(bool isGles1) const {
return isGles1 ? s_glRendererGles1.c_str() : s_glRenderer.c_str();
}
const char * GLEScontext::getVersionString(bool isGles1) const {
return isGles1 ? s_glVersionGles1.c_str() : s_glVersion.c_str();
}
void GLEScontext::getGlobalLock() {
s_lock.lock();
}
void GLEScontext::releaseGlobalLock() {
s_lock.unlock();
}
void GLEScontext::initCapsLocked(const GLubyte * extensionString)
{
const char* cstring = (const char*)extensionString;
s_glDispatch.glGetIntegerv(GL_MAX_VERTEX_ATTRIBS,&s_glSupport.maxVertexAttribs);
if (s_glSupport.maxVertexAttribs > kMaxVertexAttributes) {
s_glSupport.maxVertexAttribs = kMaxVertexAttributes;
}
s_glDispatch.glGetIntegerv(GL_MAX_CLIP_PLANES,&s_glSupport.maxClipPlane);
s_glDispatch.glGetIntegerv(GL_MAX_LIGHTS,&s_glSupport.maxLights);
s_glDispatch.glGetIntegerv(GL_MAX_TEXTURE_SIZE,&s_glSupport.maxTexSize);
s_glDispatch.glGetIntegerv(GL_MAX_TEXTURE_UNITS,&s_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()) &&
!s_glSupport.maxTexUnits) {
s_glSupport.maxTexUnits = 8;
}
s_glDispatch.glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS,&s_glSupport.maxTexImageUnits);
s_glDispatch.glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &s_glSupport.maxCombinedTexImageUnits);
s_glDispatch.glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &s_glSupport.maxTransformFeedbackSeparateAttribs);
s_glDispatch.glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &s_glSupport.maxUniformBufferBindings);
s_glDispatch.glGetIntegerv(GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, &s_glSupport.maxAtomicCounterBufferBindings);
s_glDispatch.glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &s_glSupport.maxShaderStorageBufferBindings);
s_glDispatch.glGetIntegerv(GL_MAX_DRAW_BUFFERS, &s_glSupport.maxDrawBuffers);
s_glDispatch.glGetIntegerv(GL_MAX_VERTEX_ATTRIB_BINDINGS, &s_glSupport.maxVertexAttribBindings);
// Compressed texture format query
if(feature_is_enabled(kFeature_NativeTextureDecompression)) {
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.
fprintf(stderr, "%s: Not supporting etc2: %d vs %d\n", __func__,
numEtc2FormatsSupported, numEtc2Formats);
for (auto it : found) {
if (!it.second) {
fprintf(stderr, "%s: Not found: 0x%x\n", __func__, it.first);
}
}
}
if (numAstcFormats == numAstcFormatsSupported) {
hasAstcSupport = true; // Supports ASTC underneath
} else {
// It is unusual to support only some. Record what happened.
fprintf(stderr, "%s: Not supporting astc: %d vs %d\n", __func__,
numAstcFormatsSupported, numAstcFormats);
for (auto it : found) {
if (!it.second) {
fprintf(stderr, "%s: Not found: 0x%x\n", __func__, it.first);
}
}
}
}
s_glSupport.hasEtc2Support = hasEtc2Support;
s_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);
s_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"))))
s_glSupport.GL_EXT_TEXTURE_FORMAT_BGRA8888 = true;
if (::isCoreProfile() ||
strstr(cstring,"GL_EXT_framebuffer_object ")!=NULL)
s_glSupport.GL_EXT_FRAMEBUFFER_OBJECT = true;
if (strstr(cstring,"GL_ARB_vertex_blend ")!=NULL)
s_glSupport.GL_ARB_VERTEX_BLEND = true;
if (strstr(cstring,"GL_ARB_matrix_palette ")!=NULL)
s_glSupport.GL_ARB_MATRIX_PALETTE = true;
if (strstr(cstring,"GL_EXT_packed_depth_stencil ")!=NULL ||
strstr(cstring,"GL_OES_packed_depth_stencil ")!=NULL)
s_glSupport.GL_EXT_PACKED_DEPTH_STENCIL = true;
if (strstr(cstring,"GL_OES_read_format ")!=NULL)
s_glSupport.GL_OES_READ_FORMAT = true;
if (strstr(cstring,"GL_ARB_half_float_pixel ")!=NULL ||
strstr(cstring,"GL_OES_texture_half_float ")!=NULL)
s_glSupport.GL_ARB_HALF_FLOAT_PIXEL = true;
if (strstr(cstring,"GL_NV_half_float ")!=NULL)
s_glSupport.GL_NV_HALF_FLOAT = true;
if (strstr(cstring,"GL_ARB_half_float_vertex ")!=NULL ||
strstr(cstring,"GL_OES_vertex_half_float ")!=NULL)
s_glSupport.GL_ARB_HALF_FLOAT_VERTEX = true;
if (strstr(cstring,"GL_SGIS_generate_mipmap ")!=NULL)
s_glSupport.GL_SGIS_GENERATE_MIPMAP = true;
if (strstr(cstring,"GL_ARB_ES2_compatibility ")!=NULL
|| isGles2Gles())
s_glSupport.GL_ARB_ES2_COMPATIBILITY = true;
if (strstr(cstring,"GL_OES_standard_derivatives ")!=NULL)
s_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)
s_glSupport.GL_OES_TEXTURE_NPOT = true;
if (::isCoreProfile() ||
strstr(cstring,"GL_ARB_color_buffer_float")!=NULL ||
strstr(cstring,"GL_EXT_color_buffer_float")!=NULL)
s_glSupport.ext_GL_EXT_color_buffer_float = true;
if (::isCoreProfile() ||
strstr(cstring,"GL_EXT_color_buffer_half_float")!=NULL)
s_glSupport.ext_GL_EXT_color_buffer_half_float = true;
if (strstr(cstring,"GL_EXT_shader_framebuffer_fetch")!=NULL) {
s_glSupport.ext_GL_EXT_shader_framebuffer_fetch = true;
}
if (!(Version((const char*)glVersion) < Version("3.0")) || strstr(cstring,"GL_OES_rgb8_rgba8")!=NULL)
s_glSupport.GL_OES_RGB8_RGBA8 = true;
if (strstr(cstring, "GL_EXT_memory_object") != NULL) {
s_glSupport.ext_GL_EXT_memory_object = true;
}
if (strstr(cstring, "GL_EXT_semaphore") != NULL) {
s_glSupport.ext_GL_EXT_semaphore = true;
}
// ASTC
if (strstr(cstring, "GL_KHR_texture_compression_astc_ldr") != NULL) {
s_glSupport.ext_GL_KHR_texture_compression_astc_ldr = true;
}
// BPTC extension detection
if (feature_is_enabled(kFeature_BptcTextureSupport)) {
if ((strstr(cstring, "GL_EXT_texture_compression_bptc") != NULL) ||
(strstr(cstring, "GL_ARB_texture_compression_bptc") != NULL)) {
s_glSupport.hasBptcSupport = true;
}
}
if (feature_is_enabled(kFeature_S3tcTextureSupport)) {
if (strstr(cstring, "GL_EXT_texture_compression_s3tc") != NULL) {
s_glSupport.hasS3tcSupport = true;
}
}
}
void GLEScontext::buildStrings(bool isGles1, 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";
}
std::string& vendorString = isGles1 ? s_glVendorGles1 : s_glVendor;
std::string& rendererString = isGles1 ? s_glRendererGles1 : s_glRenderer;
std::string& versionString = isGles1 ? s_glVersionGles1 : 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;
}
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;
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) {
fprintf(stderr, "%s: error setting up multisampled RBO! 0x%x\n", __func__, 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) {
fprintf(stderr, "%s: error setting up multisampled RBO! 0x%x\n", __func__, 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) {
if (!m_defaultFBO) {
dispatcher().glGenFramebuffers(1, &m_defaultFBO);
m_defaultReadFBO = m_defaultFBO;
}
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);
}
if (needReallocateReadRbo) {
initEmulatedEGLSurface(readWidth, readHeight, readColorFormat, readDepthStencilFormat, readMultisamples,
*eglReadSurfaceRBColorId, *eglReadSurfaceRBDepthId);
}
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);
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);
}
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]);
fprintf(stderr, "%s: fail to compile. infolog %s\n", __func__, &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]);
fprintf(stderr, "%s: fail to link program. infolog: %s\n", __func__,
&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();
gl.glBindTexture(GL_TEXTURE_2D, m_blitState.tex);
GLint read_iformat = getReadBufferInternalFormat();
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);
}
// 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 threads current context, for the current rendering API. This
// restriction may be lifted in future EGL revisions.
//
const GLuint readFboBinding = getFramebufferBinding(GL_READ_FRAMEBUFFER);
if (readFboBinding != 0) {
gl.glBindFramebuffer(GL_READ_FRAMEBUFFER, m_defaultFBO);
}
if (m_blitState.samples > 0) {
GLint rWidth = width;
GLint rHeight = height;
getReadBufferDimensions(&rWidth, &rHeight);
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.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();
bool shouldBlit = setupImageBlitForTexture(width, height, internalFormat);
if (!shouldBlit) 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_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.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_BLEND)) gl.glEnable(GL_BLEND);
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);
}
gl.glColorMask(m_colorMaskR, m_colorMaskG, m_colorMaskB, m_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;
}