blob: 3587d83160a8d31138e4987c974768b89577d625 [file] [log] [blame]
/*
* 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 "android/base/containers/Lookup.h"
#include "android/base/files/StreamSerializing.h"
#include "emugl/common/feature_control.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 <OpenglCodecCommon/ErrorLog.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>
using emugl::emugl_feature_is_enabled;
//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;
emugl::Mutex 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;
}
}
bool GLEScontext::drawDisabled() const {
return emugl_feature_is_enabled(android::featurecontrol::NoDraw);
}
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(emugl_feature_is_enabled(android::featurecontrol::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);
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 (emugl_feature_is_enabled(android::featurecontrol::BptcTextureSupport)) {
if ((strstr(cstring, "GL_EXT_texture_compression_bptc") != NULL) ||
(strstr(cstring, "GL_ARB_texture_compression_bptc") != NULL)) {
s_glSupport.hasBptcSupport = true;
}
}
if (emugl_feature_is_enabled(android::featurecontrol::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);
}
void GLEScontext::rebindCurrentFramebufferTextures() {
rebindFramebufferTextures(getFramebufferBinding(GL_DRAW_FRAMEBUFFER));
}
void GLEScontext::rebindFramebufferTextures(ObjectLocalName p_framebuffer) {
if (0 == p_framebuffer || drawDisabled()) {
return;
}
FramebufferData* fbObj = getFBOData(p_framebuffer);
if (fbObj) {
fbObj->rebindTextures(p_framebuffer, this);
}
}
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 thread’s 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;
}