Move host validation to guest
Test: dEQP-GLES3 no failures when not using host-side glGetError
Change-Id: If602d7c784ea12c5ae47ce56a91669e14a9ac62f
diff --git a/shared/OpenglCodecCommon/Android.mk b/shared/OpenglCodecCommon/Android.mk
index ba2b412..46d3fda 100644
--- a/shared/OpenglCodecCommon/Android.mk
+++ b/shared/OpenglCodecCommon/Android.mk
@@ -13,6 +13,7 @@
SocketStream.cpp \
TcpStream.cpp \
auto_goldfish_dma_context.cpp \
+ etc.cpp \
ifeq (true,$(GOLDFISH_OPENGL_BUILD_FOR_HOST))
@@ -42,7 +43,8 @@
ifeq (true,$(GOLDFISH_OPENGL_BUILD_FOR_HOST))
-$(call emugl-export,SHARED_LIBRARIES,libcutils libutils liblog android-emu-shared)
+$(call emugl-import,libandroidemu)
+$(call emugl-export,SHARED_LIBRARIES,libcutils libutils liblog)
else
ifeq (true,$(GFXSTREAM))
$(call emugl-export,SHARED_LIBRARIES,libcutils libutils liblog libandroidemu)
diff --git a/shared/OpenglCodecCommon/CMakeLists.txt b/shared/OpenglCodecCommon/CMakeLists.txt
index 01fe056..545525f 100644
--- a/shared/OpenglCodecCommon/CMakeLists.txt
+++ b/shared/OpenglCodecCommon/CMakeLists.txt
@@ -1,10 +1,10 @@
# This is an autogenerated file! Do not edit!
# instead run make from .../device/generic/goldfish-opengl
# which will re-generate this file.
-android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon/Android.mk" "b86c7ce26ad32ec1fc3080efc119211a77d696104f340b59e33882984bffe97a")
-set(OpenglCodecCommon_host_src GLClientState.cpp GLESTextureUtils.cpp ChecksumCalculator.cpp GLSharedGroup.cpp glUtils.cpp IndexRangeCache.cpp SocketStream.cpp TcpStream.cpp auto_goldfish_dma_context.cpp goldfish_dma_host.cpp)
-android_add_library(TARGET OpenglCodecCommon_host SHARED LICENSE Apache-2.0 SRC GLClientState.cpp GLESTextureUtils.cpp ChecksumCalculator.cpp GLSharedGroup.cpp glUtils.cpp IndexRangeCache.cpp SocketStream.cpp TcpStream.cpp auto_goldfish_dma_context.cpp goldfish_dma_host.cpp)
-target_include_directories(OpenglCodecCommon_host PRIVATE ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
+android_validate_sha256("${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon/Android.mk" "a9f19fd167bcc8bf102e288cccc0c4a780fa6671ff52f1f94c3b5b26d7482728")
+set(OpenglCodecCommon_host_src GLClientState.cpp GLESTextureUtils.cpp ChecksumCalculator.cpp GLSharedGroup.cpp glUtils.cpp IndexRangeCache.cpp SocketStream.cpp TcpStream.cpp auto_goldfish_dma_context.cpp etc.cpp goldfish_dma_host.cpp)
+android_add_library(TARGET OpenglCodecCommon_host SHARED LICENSE Apache-2.0 SRC GLClientState.cpp GLESTextureUtils.cpp ChecksumCalculator.cpp GLSharedGroup.cpp glUtils.cpp IndexRangeCache.cpp SocketStream.cpp TcpStream.cpp auto_goldfish_dma_context.cpp etc.cpp goldfish_dma_host.cpp)
+target_include_directories(OpenglCodecCommon_host PRIVATE ${GOLDFISH_DEVICE_ROOT}/shared/OpenglCodecCommon ${GOLDFISH_DEVICE_ROOT}/android-emu ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include-types ${GOLDFISH_DEVICE_ROOT}/shared/qemupipe/include ${GOLDFISH_DEVICE_ROOT}/./host/include/libOpenglRender ${GOLDFISH_DEVICE_ROOT}/./system/include ${GOLDFISH_DEVICE_ROOT}/./../../../external/qemu/android/android-emugl/guest)
target_compile_definitions(OpenglCodecCommon_host PRIVATE "-DWITH_GLES2" "-DPLATFORM_SDK_VERSION=29" "-DGOLDFISH_HIDL_GRALLOC" "-DEMULATOR_OPENGL_POST_O=1" "-DHOST_BUILD" "-DANDROID" "-DGL_GLEXT_PROTOTYPES" "-DPAGE_SIZE=4096" "-DGFXSTREAM" "-DLOG_TAG=\"eglCodecCommon\"")
target_compile_options(OpenglCodecCommon_host PRIVATE "-fvisibility=default" "-Wno-unused-parameter" "-Wno-unused-private-field")
-target_link_libraries(OpenglCodecCommon_host PRIVATE cutils utils log android-emu-shared PRIVATE qemupipe_host)
\ No newline at end of file
+target_link_libraries(OpenglCodecCommon_host PRIVATE cutils utils log androidemu android-emu-shared PRIVATE qemupipe_host)
\ No newline at end of file
diff --git a/shared/OpenglCodecCommon/GLClientState.cpp b/shared/OpenglCodecCommon/GLClientState.cpp
index f61f133..039b763 100644
--- a/shared/OpenglCodecCommon/GLClientState.cpp
+++ b/shared/OpenglCodecCommon/GLClientState.cpp
@@ -32,12 +32,12 @@
#endif
// Don't include these in the .h file, or we get weird compile errors.
+#include <GLES2/gl2ext.h>
#include <GLES3/gl3.h>
#include <GLES3/gl31.h>
void GLClientState::init() {
m_initialized = false;
- m_nLocations = CODEC_MAX_VERTEX_ATTRIBUTES;
m_arrayBuffer = 0;
m_arrayBuffer_lastEncode = 0;
@@ -48,7 +48,6 @@
m_vaoAttribBindingHasVboCache = 0;
m_noClientArraysCache = 0;
- m_max_vertex_attrib_bindings = m_nLocations;
addVertexArrayObject(0);
setVertexArrayObject(0);
// init gl constans;
@@ -78,13 +77,9 @@
m_drawIndirectBuffer = 0;
m_shaderStorageBuffer = 0;
- m_transformFeedbackActiveUnpaused = false;
-
- // to be modified later when these are queried from host.
- m_max_transform_feedback_separate_attribs = 0;
- m_max_uniform_buffer_bindings = 0;
- m_max_atomic_counter_buffer_bindings = 0;
- m_max_shader_storage_buffer_bindings = 0;
+ m_transformFeedbackActive = false;
+ m_transformFeedbackUnpaused = false;
+ m_transformFeedbackVaryingsCountForLinking = 0;
m_activeTexture = 0;
m_currentProgram = 0;
@@ -108,14 +103,28 @@
m_tex.textureRecs = NULL;
mRboState.boundRenderbuffer = 0;
- mRboState.boundRenderbufferIndex = 0;
mFboState.boundDrawFramebuffer = 0;
mFboState.boundReadFramebuffer = 0;
mFboState.drawFboCheckStatus = GL_NONE;
mFboState.readFboCheckStatus = GL_NONE;
- m_maxVertexAttribsDirty = true;
+ m_extensions_set = false;
+
+#ifdef GFXSTREAM
+ // The default transform feedback buffer object
+ // The default sampler object
+ GLuint defaultId = 0;
+ setExistence(ObjectType::TransformFeedback, true, 1, &defaultId);
+
+ mBoundTransformFeedbackValidity.id = 0;
+ mBoundTransformFeedbackValidity.valid = true;
+
+ // query must take id that was created via glGenQueries
+ mBoundQueryValidity_AnySamplesPassed.valid = false;
+ mBoundQueryValidity_AnySamplesPassedConservative.valid = false;
+ mBoundQueryValidity_TransformFeedbackPrimitivesWritten.valid = false;
+#endif
}
GLClientState::GLClientState()
@@ -225,10 +234,10 @@
m_vaoMap.insert(
VAOStateMap::value_type(
name,
- VAOState(0, m_nLocations, std::max(m_nLocations, m_max_vertex_attrib_bindings))));
+ VAOState(0, CODEC_MAX_VERTEX_ATTRIBUTES, CODEC_MAX_VERTEX_ATTRIBUTES)));
VertexAttribStateVector& attribState =
m_vaoMap.find(name)->second.attribState;
- for (int i = 0; i < m_nLocations; i++) {
+ for (int i = 0; i < CODEC_MAX_VERTEX_ATTRIBUTES; i++) {
attribState[i].enabled = 0;
attribState[i].enableDirty = false;
attribState[i].data = 0;
@@ -446,6 +455,195 @@
return mHostMappedBufferDirty.get(id);
}
+void GLClientState::setExistence(ObjectType type, bool exists, GLsizei count, const GLuint* ids) {
+ if (type == ObjectType::Sampler) {
+ SamplerInfo::ScopedView view(mSamplerInfo);
+ if (exists) {
+ for (GLsizei i = 0; i < count; ++i) {
+ view.addFresh(ids[i]);
+ }
+ } else {
+ for (GLsizei i = 0; i < count; ++i) {
+ view.unref(ids[i]);
+ }
+ }
+ } else {
+ ExistenceMap* existenceMap = &mBufferIds;
+
+ switch (type) {
+ case ObjectType::Buffer:
+ existenceMap = &mBufferIds;
+ break;
+ case ObjectType::TransformFeedback:
+ existenceMap = &mTransformFeedbackIds;
+ break;
+ case ObjectType::Query:
+ existenceMap = &mQueryIds;
+ for (GLsizei i = 0; i < count; ++i) {
+ // reset the last query target
+ mLastQueryTargets.add(ids[i], 0);
+ }
+ break;
+ case ObjectType::Sampler:
+ default:
+ ALOGE("%s: Unreachable code\n", __func__);
+ abort();
+ }
+
+ if (exists) {
+ for (GLsizei i = 0; i < count; ++i) {
+ existenceMap->add(ids[i]);
+ existenceMap->set(ids[i], true);
+ }
+ } else {
+ for (GLsizei i = 0; i < count; ++i) {
+ existenceMap->remove(ids[i]);
+ }
+ }
+ }
+}
+
+bool GLClientState::queryExistence(ObjectType type, GLuint id) const {
+ switch (type) {
+ case ObjectType::Buffer:
+ return mBufferIds.get(id);
+ case ObjectType::TransformFeedback:
+ return mTransformFeedbackIds.get(id);
+ case ObjectType::Sampler:
+ return samplerExists(id);
+ case ObjectType::Query:
+ return mQueryIds.get(id);
+ default:
+ ALOGD("%s: unknown object type: 0x%x\n", __func__, type);
+ abort();
+ }
+}
+
+bool GLClientState::samplerExists(GLuint id) const {
+ if (!id) return true;
+ SamplerInfo::ScopedView view(mSamplerInfo);
+ return view.samplerExists(id);
+}
+
+bool GLClientState::tryBind(GLenum target, GLuint id) {
+ if (0 == id) { // unbind operation
+ switch (target) {
+ case GL_TRANSFORM_FEEDBACK:
+ mBoundTransformFeedbackValidity.id = 0;
+ mBoundTransformFeedbackValidity.valid = true;
+ break;
+ case GL_ANY_SAMPLES_PASSED:
+ mBoundQueryValidity_AnySamplesPassed.id = 0;
+ mBoundQueryValidity_AnySamplesPassed.valid = false;
+ break;
+ case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+ mBoundQueryValidity_AnySamplesPassedConservative.id = 0;
+ mBoundQueryValidity_AnySamplesPassedConservative.valid = false;
+ break;
+ case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
+ mBoundQueryValidity_TransformFeedbackPrimitivesWritten.id = 0;
+ mBoundQueryValidity_TransformFeedbackPrimitivesWritten.valid = false;
+ break;
+ default:
+ ALOGE("%s: target 0x%x not yet supported in new state tracking model\n", __func__, target);
+ abort();
+ }
+ return true;
+ }
+
+ switch (target) {
+ case GL_TRANSFORM_FEEDBACK:
+ if (!queryExistence(ObjectType::TransformFeedback, id)) return false;
+ break;
+ case GL_ANY_SAMPLES_PASSED:
+ case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+ case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
+ if (!queryExistence(ObjectType::Query, id)) {
+ return false;
+ }
+ break;
+ default:
+ ALOGE("%s: target 0x%x not yet supported in new state tracking model\n", __func__, target);
+ abort();
+ }
+
+ // valid bind
+ switch (target) {
+ case GL_TRANSFORM_FEEDBACK:
+ mBoundTransformFeedbackValidity.id = id;
+ mBoundTransformFeedbackValidity.valid = true;
+ break;
+ case GL_ANY_SAMPLES_PASSED:
+ mBoundQueryValidity_AnySamplesPassed.id = id;
+ mBoundQueryValidity_AnySamplesPassed.valid = true;
+ break;
+ case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+ mBoundQueryValidity_AnySamplesPassedConservative.id = id;
+ mBoundQueryValidity_AnySamplesPassedConservative.valid = true;
+ break;
+ case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
+ mBoundQueryValidity_TransformFeedbackPrimitivesWritten.id = id;
+ mBoundQueryValidity_TransformFeedbackPrimitivesWritten.valid = true;
+ break;
+ default:
+ ALOGE("%s: target 0x%x not yet supported in new state tracking model\n", __func__, target);
+ abort();
+ }
+ return true;
+}
+
+bool GLClientState::isBoundTargetValid(GLenum target) {
+ switch (target) {
+ case GL_TRANSFORM_FEEDBACK:
+ return mBoundTransformFeedbackValidity.valid;
+ case GL_ANY_SAMPLES_PASSED:
+ return mBoundQueryValidity_AnySamplesPassed.valid;
+ case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+ return mBoundQueryValidity_AnySamplesPassedConservative.valid;
+ case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
+ return mBoundQueryValidity_TransformFeedbackPrimitivesWritten.valid;
+ default:
+ ALOGE("%s: target 0x%x not yet supported in new state tracking model\n", __func__, target);
+ abort();
+ }
+}
+
+bool GLClientState::isQueryBound(GLenum target) {
+ switch (target) {
+ case GL_ANY_SAMPLES_PASSED:
+ return mBoundQueryValidity_AnySamplesPassed.valid;
+ case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+ return mBoundQueryValidity_AnySamplesPassedConservative.valid;
+ case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
+ return mBoundQueryValidity_TransformFeedbackPrimitivesWritten.valid;
+ default:
+ return false;
+ }
+}
+
+bool GLClientState::isQueryObjectActive(GLuint id) {
+ if (mBoundQueryValidity_AnySamplesPassed.valid &&
+ (id == mBoundQueryValidity_AnySamplesPassed.id))
+ return true;
+ if (mBoundQueryValidity_AnySamplesPassedConservative.valid &&
+ (id == mBoundQueryValidity_AnySamplesPassedConservative.id))
+ return true;
+ if (mBoundQueryValidity_TransformFeedbackPrimitivesWritten.valid &&
+ (id == mBoundQueryValidity_TransformFeedbackPrimitivesWritten.id))
+ return true;
+ return false;
+}
+
+void GLClientState::setLastQueryTarget(GLenum target, GLuint id) {
+ mLastQueryTargets.add(id, target);
+}
+
+GLenum GLClientState::getLastQueryTarget(GLuint id) {
+ auto targetPtr = mLastQueryTargets.get_const(id);
+ if (!targetPtr) return 0;
+ return *targetPtr;
+}
+
#else // GFXSTREAM
void GLClientState::addBuffer(GLuint id) {
@@ -460,16 +658,20 @@
return mBufferIds.find(id) != mBufferIds.end();
}
-void setBufferHostMapDirty(GLuint id, bool dirty) {
+void GLClientState::setBufferHostMapDirty(GLuint id, bool dirty) {
(void)id;
(void)dirty;
}
-bool isBufferHostMapDirty(GLuint id) const {
+bool GLClientState::isBufferHostMapDirty(GLuint id) const {
(void)id;
return true;
}
+void GLClientState::setExistence(ObjectType, bool, GLsizei, const GLuint*) {
+ // no-op in non-gfxstream
+}
+
#endif // !GFXSTREAM
void GLClientState::setBoundPixelPackBufferDirtyForHostMap() {
@@ -706,6 +908,22 @@
}
}
+int GLClientState::getMaxTextureSize() const {
+ return m_hostDriverCaps.max_texture_size;
+}
+
+int GLClientState::getMaxTextureSize3D() const {
+ return m_hostDriverCaps.max_texture_size_3d;
+}
+
+int GLClientState::getMaxTextureSizeCubeMap() const {
+ return m_hostDriverCaps.max_texture_size_cube_map;
+}
+
+int GLClientState::getLog2MaxTextureSize() const {
+ return m_log2MaxTextureSize;
+}
+
void GLClientState::postDraw() {
setBoundTransformFeedbackBuffersDirtyForHostMap();
setBoundShaderStorageBuffersDirtyForHostMap();
@@ -811,6 +1029,50 @@
}
}
+bool GLClientState::isTexture(GLuint tex_name) const {
+ return getTextureRec(tex_name);
+}
+
+bool GLClientState::isTextureWithStorage(GLuint tex_name) const {
+ TextureRec* rec = getTextureRec(tex_name);
+ if (!rec) return false;
+ return rec->hasStorage;
+}
+
+bool GLClientState::isTextureCubeMap(GLuint tex_name) const {
+ TextureRec* texrec = getTextureRec(tex_name);
+ if (!texrec) return false;
+ switch (texrec->target) {
+ case GL_TEXTURE_CUBE_MAP:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool GLClientState::isRenderbuffer(GLuint name) const {
+ if (!name) return false;
+
+ RenderbufferInfo::ScopedView view(mRboState.rboData);
+ return view.hasRbo(name);
+}
+
+bool GLClientState::isRenderbufferThatWasBound(GLuint name) const {
+ if (!name) return true;
+
+ RenderbufferInfo::ScopedView view(mRboState.rboData);
+ if (!view.hasRbo(name)) return false;
+
+ const RboProps* props = view.get_const(name);
+ return props->previouslyBound;
+}
+
void GLClientState::getClientStatePointer(GLenum pname, GLvoid** params)
{
GLenum which_state = -1;
@@ -1113,8 +1375,14 @@
}
}
-void GLClientState::bindSampler(GLuint unit, GLuint sampler) {
+bool GLClientState::bindSampler(GLuint unit, GLuint sampler) {
+ SamplerInfo::ScopedView view(mSamplerInfo);
+ view.ref(sampler);
+ if (m_tex.unit[unit].boundSampler) {
+ view.unref(sampler);
+ }
m_tex.unit[unit].boundSampler = sampler;
+ return true;
}
bool GLClientState::isSamplerBindNoOp(GLuint unit, GLuint sampler) {
@@ -1164,7 +1432,7 @@
if (texture && target != texrec->target &&
(target != GL_TEXTURE_EXTERNAL_OES &&
texrec->target != GL_TEXTURE_EXTERNAL_OES)) {
- ALOGD("%s: issue GL_INVALID_OPERATION: target 0x%x texrectarget 0x%x texture %u", __FUNCTION__, target, texrec->target, texture);
+ return GL_INVALID_OPERATION;
}
switch (target) {
@@ -1198,10 +1466,23 @@
void GLClientState::setBoundEGLImage(GLenum target, GLeglImageOES image) {
(void)image;
- GLuint texture = getBoundTexture(target);
- TextureRec* texrec = getTextureRec(texture);
- if (!texrec) return;
- texrec->boundEGLImage = true;
+ if (target == GL_RENDERBUFFER) {
+ if (!boundRenderbuffer()) return;
+ setBoundRenderbufferEGLImageBacked();
+ setBoundRenderbufferFormat(GL_RGBA);
+ setBoundRenderbufferSamples(0);
+ setBoundRenderbufferDimensions(1, 1);
+ } else {
+ GLuint texture = getBoundTexture(target);
+ TextureRec* texrec = getTextureRec(texture);
+ if (!texrec) return;
+ texrec->boundEGLImage = true;
+ setBoundTextureInternalFormat(target, GL_RGBA);
+ setBoundTextureFormat(target, GL_RGBA);
+ setBoundTextureType(target, GL_UNSIGNED_BYTE);
+ setBoundTextureSamples(target, 0);
+ setBoundTextureDims(target, target, 0, 1, 1, 1);
+ }
}
TextureRec* GLClientState::addTextureRec(GLuint id, GLenum target)
@@ -1213,7 +1494,14 @@
tex->multisamples = 0;
tex->immutable = false;
tex->boundEGLImage = false;
- tex->dims = new TextureDims;
+ tex->hasStorage = false;
+ tex->dims = new TextureDims[6];
+ tex->hasCubeNegX = false;
+ tex->hasCubePosX = false;
+ tex->hasCubeNegY = false;
+ tex->hasCubePosY = false;
+ tex->hasCubeNegZ = false;
+ tex->hasCubePosZ = false;
(*(m_tex.textureRecs))[id] = tex;
return tex;
@@ -1249,13 +1537,49 @@
texrec->type = type;
}
-void GLClientState::setBoundTextureDims(GLenum target, GLsizei level, GLsizei width, GLsizei height, GLsizei depth) {
+static size_t textureDimArrayOfCubeTarget(GLenum cubetarget) {
+ switch (cubetarget) {
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ return 0;
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ return 1;
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ return 2;
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ return 3;
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ return 4;
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ return 5;
+ }
+ return 0;
+}
+
+void GLClientState::setBoundTextureDims(GLenum target, GLenum cubetarget, GLsizei level, GLsizei width, GLsizei height, GLsizei depth) {
GLuint texture = getBoundTexture(target);
TextureRec* texrec = getTextureRec(texture);
if (!texrec) {
return;
}
+ texrec->hasStorage = true;
+
+ size_t indexToSet = 0;
+
+ if (target == GL_TEXTURE_CUBE_MAP) {
+ if (-1 == cubetarget) {
+ setBoundTextureDims(target, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, level, width, height, depth);
+ setBoundTextureDims(target, GL_TEXTURE_CUBE_MAP_POSITIVE_X, level, width, height, depth);
+ setBoundTextureDims(target, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, level, width, height, depth);
+ setBoundTextureDims(target, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, level, width, height, depth);
+ setBoundTextureDims(target, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, level, width, height, depth);
+ setBoundTextureDims(target, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, level, width, height, depth);
+ return;
+ }
+ indexToSet = textureDimArrayOfCubeTarget(cubetarget);
+ }
+
+
if (level == -1) {
GLsizei curr_width = width;
GLsizei curr_height = height;
@@ -1263,9 +1587,9 @@
GLsizei curr_level = 0;
while (true) {
- texrec->dims->widths[curr_level] = curr_width;
- texrec->dims->heights[curr_level] = curr_height;
- texrec->dims->depths[curr_level] = curr_depth;
+ texrec->dims[indexToSet].widths[curr_level] = curr_width;
+ texrec->dims[indexToSet].heights[curr_level] = curr_height;
+ texrec->dims[indexToSet].depths[curr_level] = curr_depth;
if (curr_width >> 1 == 0 &&
curr_height >> 1 == 0 &&
((target == GL_TEXTURE_3D && curr_depth == 0) ||
@@ -1281,10 +1605,12 @@
}
} else {
- texrec->dims->widths[level] = width;
- texrec->dims->heights[level] = height;
- texrec->dims->depths[level] = depth;
+ texrec->dims[indexToSet].widths[level] = width;
+ texrec->dims[indexToSet].heights[level] = height;
+ texrec->dims[indexToSet].depths[level] = depth;
}
+
+ setFboCompletenessDirtyForTexture(texture);
}
void GLClientState::setBoundTextureSamples(GLenum target, GLsizei samples) {
@@ -1294,11 +1620,48 @@
texrec->multisamples = samples;
}
+void GLClientState::addTextureCubeMapImage(GLenum stateTarget, GLenum cubeTarget) {
+ if (stateTarget != GL_TEXTURE_CUBE_MAP) return;
+
+ GLuint texture = getBoundTexture(stateTarget);
+ TextureRec* texrec = getTextureRec(texture);
+ if (!texrec) return;
+
+ switch (cubeTarget) {
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
+ texrec->hasCubeNegX = true;
+ return;
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
+ texrec->hasCubePosX = true;
+ return;
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
+ texrec->hasCubeNegY = true;
+ return;
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
+ texrec->hasCubePosY = true;
+ return;
+ case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
+ texrec->hasCubeNegZ = true;
+ return;
+ case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
+ texrec->hasCubePosZ = true;
+ return;
+ }
+}
+
void GLClientState::setBoundTextureImmutableFormat(GLenum target) {
GLuint texture = getBoundTexture(target);
TextureRec* texrec = getTextureRec(texture);
if (!texrec) return;
texrec->immutable = true;
+ if (target == GL_TEXTURE_CUBE_MAP) {
+ texrec->hasCubeNegX = true;
+ texrec->hasCubePosX = true;
+ texrec->hasCubeNegY = true;
+ texrec->hasCubePosY = true;
+ texrec->hasCubeNegZ = true;
+ texrec->hasCubePosZ = true;
+ }
}
bool GLClientState::isBoundTextureImmutableFormat(GLenum target) const {
@@ -1308,6 +1671,41 @@
return texrec->immutable;
}
+bool GLClientState::isBoundTextureComplete(GLenum target) const {
+ GLuint texture = getBoundTexture(target);
+ TextureRec* texrec = getTextureRec(texture);
+ if (!texrec) return false;
+
+ if (texrec->immutable) return true;
+ if (!texrec->hasStorage) return true;
+
+ if (target == GL_TEXTURE_CUBE_MAP) {
+ if (!(texrec->hasCubeNegX &&
+ texrec->hasCubePosX &&
+ texrec->hasCubeNegY &&
+ texrec->hasCubePosY &&
+ texrec->hasCubeNegZ &&
+ texrec->hasCubePosZ)) return false;
+
+ size_t currBaseLevel = texrec->dims[0].widths.begin()->first;
+ size_t currWidth = texrec->dims[0].widths.begin()->second;
+ size_t currHeight = texrec->dims[0].heights.begin()->second;
+ for (size_t i = 1; i < 6; ++i) {
+ size_t nextLevel = texrec->dims[i].widths.begin()->first;
+ size_t nextWidth = texrec->dims[i].widths.begin()->second;
+ size_t nextHeight = texrec->dims[i].heights.begin()->second;
+ if (currBaseLevel != nextLevel) return false;
+ if (currWidth != nextWidth) return false;
+ if (currHeight != nextHeight) return false;
+ }
+
+ return true;
+ }
+
+ return true;
+}
+
+
GLuint GLClientState::getBoundTexture(GLenum target) const
{
switch (target) {
@@ -1328,6 +1726,219 @@
}
}
+GLuint GLClientState::getBoundFramebuffer(GLenum target) const
+{
+ switch (target) {
+ case GL_FRAMEBUFFER:
+ case GL_DRAW_FRAMEBUFFER:
+ return mFboState.boundDrawFramebuffer;
+ case GL_READ_FRAMEBUFFER:
+ return mFboState.boundReadFramebuffer;
+ default:
+ return 0;
+ }
+}
+
+GLenum GLClientState::checkFramebufferCompleteness(GLenum target) {
+ // Default framebuffer is complete
+ // TODO: Check the case where the default framebuffer is 0x0
+ if (0 == boundFramebuffer(target)) return GL_FRAMEBUFFER_COMPLETE;
+
+ bool hasAttachment = false;
+ FboProps& props = boundFboProps(target);
+
+ if (!props.completenessDirty) {
+ return props.cachedCompleteness;
+ }
+
+ int currentSamples = -1;
+
+ for (int i = 0; i < getMaxColorAttachments(); i++) {
+ if (!props.colorAttachmenti_hasTex[i] &&
+ !props.colorAttachmenti_hasRbo[i]) continue;
+
+ GLenum attachmentRes = checkFramebufferAttachmentCompleteness(target, glUtilsColorAttachmentName(i), ¤tSamples);
+ if (attachmentRes != GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) {
+ hasAttachment = true;
+ }
+ if (attachmentRes) {
+ ALOGD("%s: color attachment %d not complete: 0x%x\n", __func__, i, attachmentRes);
+ return attachmentRes;
+ }
+ }
+
+ bool hasDepth = (props.depthAttachment_hasTexObj || props.depthAttachment_hasRbo || props.depthstencilAttachment_hasTexObj || props.depthstencilAttachment_hasRbo);
+ bool hasStencil = (props.stencilAttachment_hasTexObj || props.stencilAttachment_hasRbo || props.depthstencilAttachment_hasTexObj || props.depthstencilAttachment_hasRbo);
+
+ if (hasDepth) {
+ GLenum depthAttachmentRes = checkFramebufferAttachmentCompleteness(target, GL_DEPTH_ATTACHMENT, ¤tSamples);
+ if (depthAttachmentRes != GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) {
+ hasAttachment = true;
+ }
+ if (depthAttachmentRes) {
+ ALOGD("%s: depth attachment not complete: 0x%x\n", __func__, depthAttachmentRes);
+ return depthAttachmentRes;
+ }
+ }
+
+ if (hasStencil) {
+ GLenum stencilAttachmentRes = checkFramebufferAttachmentCompleteness(target, GL_STENCIL_ATTACHMENT, ¤tSamples);
+ if (stencilAttachmentRes != GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) {
+ hasAttachment = true;
+ }
+ if (stencilAttachmentRes) {
+ ALOGD("%s: stencil attachment not complete: 0x%x\n", __func__, stencilAttachmentRes);
+ return stencilAttachmentRes;
+ }
+ }
+
+ if (hasDepth && hasStencil) {
+ // In gles3, depth/stencil must use the same image.
+ if (m_glesMajorVersion > 2) {
+ if ((props.depthAttachment_hasTexObj && props.stencilAttachment_hasRbo) ||
+ (props.stencilAttachment_hasTexObj && props.depthAttachment_hasRbo)) {
+ ALOGD("%s: GL_FRAMEBUFFER_UNSUPPORTED: using different types of depth/stencil attachment images in GLES 3+\n", __func__);
+ return GL_FRAMEBUFFER_UNSUPPORTED;
+ }
+ if (props.depthAttachment_hasTexObj) {
+ if (props.depthAttachment_texture != props.stencilAttachment_texture) {
+ ALOGD("%s: GL_FRAMEBUFFER_UNSUPPORTED: using different texture images for depth and stencil attachments in GLES 3+\n", __func__);
+ return GL_FRAMEBUFFER_UNSUPPORTED;
+ }
+ }
+ if (props.depthAttachment_hasRbo) {
+ if (props.depthAttachment_rbo != props.stencilAttachment_rbo) {
+ ALOGD("%s: GL_FRAMEBUFFER_UNSUPPORTED: using different renderbuffers for depth and stencil attachments in GLES 3+\n", __func__);
+ return GL_FRAMEBUFFER_UNSUPPORTED;
+ }
+ }
+ }
+ }
+
+ if (!hasAttachment) {
+ return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
+ }
+
+ props.completenessDirty = false;
+ props.cachedCompleteness = GL_FRAMEBUFFER_COMPLETE;
+ return GL_FRAMEBUFFER_COMPLETE;
+}
+
+GLenum GLClientState::checkFramebufferAttachmentCompleteness(GLenum target, GLenum attachment, int* currentSamples) const {
+ FboFormatInfo fbo_format_info;
+ getBoundFramebufferFormat(target, attachment, &fbo_format_info);
+
+ // Check format and renderability
+ bool renderable = false;
+ switch (fbo_format_info.type) {
+ case FBO_ATTACHMENT_RENDERBUFFER:
+ switch (attachment) {
+ case GL_DEPTH_ATTACHMENT:
+ renderable = fbo_format_info.rb_external || depthRenderableFormat(fbo_format_info.rb_format);
+ break;
+ case GL_STENCIL_ATTACHMENT:
+ renderable = fbo_format_info.rb_external || stencilRenderableFormat(fbo_format_info.rb_format);
+ break;
+ default:
+ renderable = fbo_format_info.rb_external || colorRenderableFormat(
+ fbo_format_info.rb_format,
+ GL_UNSIGNED_BYTE,
+ m_glesMajorVersion, m_glesMinorVersion,
+ m_has_color_buffer_float_extension,
+ m_has_color_buffer_half_float_extension);
+ if (!renderable) {
+ ALOGD("%s: rbo not color renderable. format: 0x%x\n", __func__, fbo_format_info.rb_format); }
+ break;
+ }
+ break;
+ case FBO_ATTACHMENT_TEXTURE:
+ switch (attachment) {
+ case GL_DEPTH_ATTACHMENT:
+ renderable = fbo_format_info.tex_external || depthRenderableFormat(fbo_format_info.tex_internalformat);
+ break;
+ case GL_STENCIL_ATTACHMENT:
+ renderable = fbo_format_info.tex_external || stencilRenderableFormat(fbo_format_info.tex_internalformat);
+ break;
+ default:
+ renderable = fbo_format_info.tex_external || colorRenderableFormat(
+ fbo_format_info.tex_internalformat,
+ fbo_format_info.tex_type,
+ m_glesMajorVersion, m_glesMinorVersion,
+ m_has_color_buffer_float_extension,
+ m_has_color_buffer_half_float_extension);
+ if (!renderable) {
+ ALOGD("%s: tex not color renderable. format: 0x%x type 0x%x maj min %d %d floatext %d hfloatext %d\n", __func__, fbo_format_info.tex_internalformat, fbo_format_info.tex_type, m_glesMajorVersion, m_glesMinorVersion, m_has_color_buffer_float_extension, m_has_color_buffer_half_float_extension);
+ }
+ break;
+ }
+ break;
+ case FBO_ATTACHMENT_NONE:
+ default:
+ return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
+ }
+
+ if (!renderable) return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+
+ // Check dimensions
+ GLuint id;
+ switch (fbo_format_info.type) {
+ case FBO_ATTACHMENT_RENDERBUFFER:
+ id = getFboAttachmentRboId(target, attachment);
+ if (!fbo_format_info.rb_external) {
+ if (0 == queryRboWidth(id) || 0 == queryRboHeight(id)) {
+ ALOGD("%s: rbo has zero dimension\n", __func__);
+ return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+ }
+ }
+ break;
+ case FBO_ATTACHMENT_TEXTURE:
+ id = getFboAttachmentTextureId(target, attachment);
+ if (!fbo_format_info.tex_external) {
+ if (0 == queryTexWidth(fbo_format_info.tex_level, id) || 0 == queryTexHeight(fbo_format_info.tex_level, id)) {
+ ALOGD("%s: texture has zero dimension\n", __func__);
+ return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+ }
+ GLsizei depth = queryTexDepth(fbo_format_info.tex_level, id);
+ if (fbo_format_info.tex_layer >= depth) {
+ ALOGD("%s: texture layer/zoffset too high, wanted %d but only have %d layers\n", __func__,
+ fbo_format_info.tex_layer, depth);
+ return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+ }
+ }
+ break;
+ case FBO_ATTACHMENT_NONE:
+ default:
+ return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
+ }
+
+ // Check samples
+ int currSamplesVal = *currentSamples;
+ bool firstTime = -1 == currSamplesVal;
+ int samplesThisAttachment = 0;
+ switch (fbo_format_info.type) {
+ case FBO_ATTACHMENT_RENDERBUFFER:
+ samplesThisAttachment = fbo_format_info.rb_multisamples;
+ break;
+ case FBO_ATTACHMENT_TEXTURE:
+ samplesThisAttachment = fbo_format_info.tex_multisamples;
+ break;
+ case FBO_ATTACHMENT_NONE:
+ break;
+ default:
+ return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
+ }
+
+ if (firstTime) {
+ *currentSamples = samplesThisAttachment;
+ } else {
+ if (samplesThisAttachment != currSamplesVal) {
+ return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE;
+ }
+ }
+
+ return 0;
+}
+
// BEGIN driver workarounds-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
// (>' ')><(' '<)(>' ')><(' '<)(>' ')><(' '<)(>' ')><(' '<)(>' ')><(' '<)(>' ')>
@@ -1396,6 +2007,10 @@
void GLClientState::deleteTextures(GLsizei n, const GLuint* textures)
{
+ for (const GLuint* texture = textures; texture != textures + n; texture++) {
+ setFboCompletenessDirtyForTexture(*texture);
+ }
+
// Updating the textures array could be made more efficient when deleting
// several textures:
// - compacting the array could be done in a single pass once the deleted
@@ -1405,7 +2020,7 @@
for (const GLuint* texture = textures; texture != textures + n; texture++) {
texrec = getTextureRec(*texture);
if (texrec && texrec->dims) {
- delete texrec->dims;
+ delete [] texrec->dims;
}
if (texrec) {
m_tex.textureRecs->erase(*texture);
@@ -1427,20 +2042,10 @@
// RBO//////////////////////////////////////////////////////////////////////////
void GLClientState::addFreshRenderbuffer(GLuint name) {
- // if underlying opengl says these are fresh names,
- // but we are keeping a stale one, reset it.
- RboProps props;
- props.target = GL_RENDERBUFFER;
- props.name = name;
- props.format = GL_NONE;
- props.multisamples = 0;
- props.previouslyBound = false;
+ if (!name) return;
- if (usedRenderbufferName(name)) {
- mRboState.rboData[getRboIndex(name)] = props;
- } else {
- mRboState.rboData.push_back(props);
- }
+ RenderbufferInfo::ScopedView view(mRboState.rboData);
+ view.addFresh(name);
}
void GLClientState::addRenderbuffers(GLsizei n, GLuint* renderbuffers) {
@@ -1449,84 +2054,87 @@
}
}
-size_t GLClientState::getRboIndex(GLuint name) const {
- for (size_t i = 0; i < mRboState.rboData.size(); i++) {
- if (mRboState.rboData[i].name == name) {
- return i;
- }
- }
- return -1;
-}
-
void GLClientState::removeRenderbuffers(GLsizei n, const GLuint* renderbuffers) {
- size_t bound_rbo_idx = getRboIndex(boundRboProps_const().name);
-
std::vector<GLuint> to_remove;
for (size_t i = 0; i < n; i++) {
if (renderbuffers[i] != 0) { // Never remove the zero rb.
- to_remove.push_back(getRboIndex(renderbuffers[i]));
+ to_remove.push_back(renderbuffers[i]);
+ setFboCompletenessDirtyForRbo(renderbuffers[i]);
}
}
- for (size_t i = 0; i < to_remove.size(); i++) {
- mRboState.rboData[to_remove[i]] = mRboState.rboData.back();
- mRboState.rboData.pop_back();
+ bool unbindCurrent = false;
+ {
+ RenderbufferInfo::ScopedView view(mRboState.rboData);
+ for (size_t i = 0; i < to_remove.size(); i++) {
+ view.unref(to_remove[i]);
+ }
+ for (size_t i = 0; i < to_remove.size(); i++) {
+ if (mRboState.boundRenderbuffer == renderbuffers[i]) {
+ unbindCurrent = true;
+ break;
+ }
+ }
}
- // If we just deleted the currently bound rb,
- // bind the zero rb
- if (getRboIndex(boundRboProps_const().name) != bound_rbo_idx) {
+ if (unbindCurrent) {
bindRenderbuffer(GL_RENDERBUFFER, 0);
}
}
bool GLClientState::usedRenderbufferName(GLuint name) const {
- for (size_t i = 0; i < mRboState.rboData.size(); i++) {
- if (mRboState.rboData[i].name == name) {
- return true;
- }
- }
- return false;
-}
+ if (!name) return false;
-void GLClientState::setBoundRenderbufferIndex() {
- for (size_t i = 0; i < mRboState.rboData.size(); i++) {
- if (mRboState.rboData[i].name == mRboState.boundRenderbuffer) {
- mRboState.boundRenderbufferIndex = i;
- break;
- }
- }
-}
-
-RboProps& GLClientState::boundRboProps() {
- return mRboState.rboData[mRboState.boundRenderbufferIndex];
-}
-
-const RboProps& GLClientState::boundRboProps_const() const {
- return mRboState.rboData[mRboState.boundRenderbufferIndex];
+ RenderbufferInfo::ScopedView view(mRboState.rboData);
+ return view.get_const(name) != 0;
}
void GLClientState::bindRenderbuffer(GLenum target, GLuint name) {
- // If unused, add it.
- if (!usedRenderbufferName(name)) {
- addFreshRenderbuffer(name);
+
+ (void)target; // Must be GL_RENDERBUFFER
+ RenderbufferInfo::ScopedView view(mRboState.rboData);
+ if (name != mRboState.boundRenderbuffer) {
+ view.unref(mRboState.boundRenderbuffer);
}
+
mRboState.boundRenderbuffer = name;
- setBoundRenderbufferIndex();
- boundRboProps().target = target;
- boundRboProps().previouslyBound = true;
+
+ if (!name) return;
+
+ view.bind(name);
}
GLuint GLClientState::boundRenderbuffer() const {
- return boundRboProps_const().name;
+ return mRboState.boundRenderbuffer;
}
void GLClientState::setBoundRenderbufferFormat(GLenum format) {
- boundRboProps().format = format;
+ RenderbufferInfo::ScopedView view(mRboState.rboData);
+ RboProps* props = view.get(mRboState.boundRenderbuffer);
+ if (!props) return;
+ props->format = format;
}
void GLClientState::setBoundRenderbufferSamples(GLsizei samples) {
- boundRboProps().multisamples = samples;
+ RenderbufferInfo::ScopedView view(mRboState.rboData);
+ RboProps* props = view.get(mRboState.boundRenderbuffer);
+ if (!props) return;
+ props->multisamples = samples;
+}
+
+void GLClientState::setBoundRenderbufferDimensions(GLsizei width, GLsizei height) {
+ RenderbufferInfo::ScopedView view(mRboState.rboData);
+ RboProps* props = view.get(mRboState.boundRenderbuffer);
+ if (!props) return;
+ props->width = width;
+ props->height = height;
+}
+
+void GLClientState::setBoundRenderbufferEGLImageBacked() {
+ RenderbufferInfo::ScopedView view(mRboState.rboData);
+ RboProps* props = view.get(mRboState.boundRenderbuffer);
+ if (!props) return;
+ props->boundEGLImage = true;
}
// FBO//////////////////////////////////////////////////////////////////////////
@@ -1534,11 +2142,38 @@
// Format querying
GLenum GLClientState::queryRboFormat(GLuint rbo_name) const {
- return mRboState.rboData[getRboIndex(rbo_name)].format;
+ RenderbufferInfo::ScopedView view(mRboState.rboData);
+ const RboProps* props = view.get(rbo_name);
+ if (!props) return 0;
+ return props->format;
}
GLsizei GLClientState::queryRboSamples(GLuint rbo_name) const {
- return mRboState.rboData[getRboIndex(rbo_name)].multisamples;
+ RenderbufferInfo::ScopedView view(mRboState.rboData);
+ const RboProps* props = view.get(rbo_name);
+ if (!props) return 0;
+ return props->multisamples;
+}
+
+GLsizei GLClientState::queryRboWidth(GLuint rbo_name) const {
+ RenderbufferInfo::ScopedView view(mRboState.rboData);
+ const RboProps* props = view.get(rbo_name);
+ if (!props) return 0;
+ return props->width;
+}
+
+GLsizei GLClientState::queryRboHeight(GLuint rbo_name) const {
+ RenderbufferInfo::ScopedView view(mRboState.rboData);
+ const RboProps* props = view.get(rbo_name);
+ if (!props) return 0;
+ return props->height;
+}
+
+bool GLClientState::queryRboEGLImageBacked(GLuint rbo_name) const {
+ RenderbufferInfo::ScopedView view(mRboState.rboData);
+ const RboProps* props = view.get(rbo_name);
+ if (!props) return 0;
+ return props->boundEGLImage;
}
GLint GLClientState::queryTexInternalFormat(GLuint tex_name) const {
@@ -1605,10 +2240,12 @@
res_info->type = FBO_ATTACHMENT_NONE;
res_info->rb_format = GL_NONE;
res_info->rb_multisamples = 0;
+ res_info->rb_external = false;
res_info->tex_internalformat = -1;
res_info->tex_format = GL_NONE;
res_info->tex_type = GL_NONE;
res_info->tex_multisamples = 0;
+ res_info->tex_external = false;
int colorAttachmentIndex =
glUtilsColorAttachmentIndex(attachment);
@@ -1622,8 +2259,13 @@
res_info->rb_multisamples =
queryRboSamples(
props.colorAttachmenti_rbos[colorAttachmentIndex]);
+ res_info->rb_external =
+ queryRboEGLImageBacked(
+ props.colorAttachmenti_rbos[colorAttachmentIndex]);
} else if (props.colorAttachmenti_hasTex[colorAttachmentIndex]) {
res_info->type = FBO_ATTACHMENT_TEXTURE;
+ res_info->tex_external = queryTexEGLImageBacked(
+ props.colorAttachmenti_textures[colorAttachmentIndex]);
res_info->tex_internalformat =
queryTexInternalFormat(
props.colorAttachmenti_textures[colorAttachmentIndex]);
@@ -1634,6 +2276,8 @@
queryTexType(props.colorAttachmenti_textures[colorAttachmentIndex]);
res_info->tex_multisamples =
queryTexSamples(props.colorAttachmenti_textures[colorAttachmentIndex]);
+ res_info->tex_level = props.colorAttachmenti_texture_levels[colorAttachmentIndex];
+ res_info->tex_layer = props.colorAttachmenti_texture_layers[colorAttachmentIndex];
} else {
res_info->type = FBO_ATTACHMENT_NONE;
}
@@ -1647,13 +2291,19 @@
res_info->rb_multisamples =
queryRboSamples(
props.depthAttachment_rbo);
+ res_info->rb_external =
+ queryRboEGLImageBacked(
+ props.depthAttachment_rbo);
} else if (props.depthAttachment_hasTexObj) {
res_info->type = FBO_ATTACHMENT_TEXTURE;
+ res_info->tex_external = queryTexEGLImageBacked(props.depthAttachment_texture);
res_info->tex_internalformat = queryTexInternalFormat(props.depthAttachment_texture);
res_info->tex_format = queryTexFormat(props.depthAttachment_texture);
res_info->tex_type = queryTexType(props.depthAttachment_texture);
res_info->tex_multisamples =
queryTexSamples(props.depthAttachment_texture);
+ res_info->tex_level = props.depthAttachment_texture_level;
+ res_info->tex_layer = props.depthAttachment_texture_layer;
} else {
res_info->type = FBO_ATTACHMENT_NONE;
}
@@ -1665,13 +2315,19 @@
res_info->rb_multisamples =
queryRboSamples(
props.stencilAttachment_rbo);
+ res_info->rb_external =
+ queryRboEGLImageBacked(
+ props.stencilAttachment_rbo);
} else if (props.stencilAttachment_hasTexObj) {
res_info->type = FBO_ATTACHMENT_TEXTURE;
+ res_info->tex_external = queryTexEGLImageBacked(props.stencilAttachment_texture);
res_info->tex_internalformat = queryTexInternalFormat(props.stencilAttachment_texture);
res_info->tex_format = queryTexFormat(props.stencilAttachment_texture);
res_info->tex_type = queryTexType(props.stencilAttachment_texture);
res_info->tex_multisamples =
queryTexSamples(props.stencilAttachment_texture);
+ res_info->tex_level = props.depthAttachment_texture_level;
+ res_info->tex_layer = props.depthAttachment_texture_layer;
} else {
res_info->type = FBO_ATTACHMENT_NONE;
}
@@ -1683,13 +2339,19 @@
res_info->rb_multisamples =
queryRboSamples(
props.depthstencilAttachment_rbo);
+ res_info->rb_external =
+ queryRboEGLImageBacked(
+ props.depthstencilAttachment_rbo);
} else if (props.depthstencilAttachment_hasTexObj) {
res_info->type = FBO_ATTACHMENT_TEXTURE;
+ res_info->tex_external = queryTexEGLImageBacked(props.depthstencilAttachment_texture);
res_info->tex_internalformat = queryTexInternalFormat(props.depthstencilAttachment_texture);
res_info->tex_format = queryTexFormat(props.depthstencilAttachment_texture);
res_info->tex_type = queryTexType(props.depthstencilAttachment_texture);
res_info->tex_multisamples =
queryTexSamples(props.depthstencilAttachment_texture);
+ res_info->tex_level = props.depthAttachment_texture_level;
+ res_info->tex_layer = props.depthAttachment_texture_layer;
} else {
res_info->type = FBO_ATTACHMENT_NONE;
}
@@ -1703,13 +2365,45 @@
return info.type;
}
-
int GLClientState::getMaxColorAttachments() const {
- return m_max_color_attachments;
+ return m_hostDriverCaps.max_color_attachments;
}
int GLClientState::getMaxDrawBuffers() const {
- return m_max_draw_buffers;
+ return m_hostDriverCaps.max_draw_buffers;
+}
+
+#define UNIFORM_VALIDATION_ERR_COND(cond, code) if (cond) { *err = code; return; }
+
+#define UNIFORM_VALIDATION_INFO_VAR_NAME info
+
+#define UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_FLOATS \
+ (!(UNIFORM_VALIDATION_INFO_VAR_NAME->isBool) && (UNIFORM_VALIDATION_INFO_VAR_NAME->isInt || UNIFORM_VALIDATION_INFO_VAR_NAME->isSampler))
+
+#define UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_INTS \
+ (!(UNIFORM_VALIDATION_INFO_VAR_NAME->isBool) && (!UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_FLOATS || UNIFORM_VALIDATION_INFO_VAR_NAME->isUnsigned))
+
+#define UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_UNSIGNED_INTS \
+ (!(UNIFORM_VALIDATION_INFO_VAR_NAME->isBool) && (!UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_FLOATS || !(UNIFORM_VALIDATION_INFO_VAR_NAME->isUnsigned)))
+
+#define UNIFORM_VALIDATION_INLINING
+
+void GLClientState::validateUniform(bool isFloat, bool isUnsigned, GLint columns, GLint rows, GLint location, GLsizei count, GLenum* err) {
+ UNIFORM_VALIDATION_ERR_COND(!m_currentProgram && !m_currentShaderProgram, GL_INVALID_OPERATION);
+ if (-1 == location) return; \
+ auto info = currentUniformValidationInfo.get_const(location); \
+ UNIFORM_VALIDATION_ERR_COND(!info || !info->valid, GL_INVALID_OPERATION); \
+ UNIFORM_VALIDATION_ERR_COND(columns != info->columns || rows != info->rows, GL_INVALID_OPERATION); \
+ UNIFORM_VALIDATION_ERR_COND(count > 1 && !info->isArray, GL_INVALID_OPERATION);
+ if (isFloat) {
+ UNIFORM_VALIDATION_ERR_COND(UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_FLOATS, GL_INVALID_OPERATION);
+ } else {
+ if (isUnsigned) {
+ UNIFORM_VALIDATION_ERR_COND(UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_UNSIGNED_INTS, GL_INVALID_OPERATION);
+ } else {
+ UNIFORM_VALIDATION_ERR_COND(UNIFORM_VALIDATION_TYPE_VIOLATION_FOR_INTS, GL_INVALID_OPERATION);
+ }
+ }
}
void GLClientState::addFreshFramebuffer(GLuint name) {
@@ -1717,22 +2411,32 @@
props.name = name;
props.previouslyBound = false;
- props.colorAttachmenti_textures.resize(m_max_color_attachments, 0);
+ props.completenessDirty = true;
+
+ props.colorAttachmenti_textures.resize(m_hostDriverCaps.max_color_attachments, 0);
+ props.colorAttachmenti_texture_levels.resize(m_hostDriverCaps.max_color_attachments, 0);
+ props.colorAttachmenti_texture_layers.resize(m_hostDriverCaps.max_color_attachments, 0);
+
+ props.depthAttachment_texture_level = 0;
+ props.depthAttachment_texture_layer = 0;
+ props.stencilAttachment_texture_level = 0;
+ props.stencilAttachment_texture_layer = 0;
+
props.depthAttachment_texture = 0;
props.stencilAttachment_texture = 0;
props.depthstencilAttachment_texture = 0;
- props.colorAttachmenti_hasTex.resize(m_max_color_attachments, false);
+ props.colorAttachmenti_hasTex.resize(m_hostDriverCaps.max_color_attachments, false);
props.depthAttachment_hasTexObj = false;
props.stencilAttachment_hasTexObj = false;
props.depthstencilAttachment_hasTexObj = false;
- props.colorAttachmenti_rbos.resize(m_max_color_attachments, 0);
+ props.colorAttachmenti_rbos.resize(m_hostDriverCaps.max_color_attachments, 0);
props.depthAttachment_rbo = 0;
props.stencilAttachment_rbo = 0;
props.depthstencilAttachment_rbo = 0;
- props.colorAttachmenti_hasRbo.resize(m_max_color_attachments, false);
+ props.colorAttachmenti_hasRbo.resize(m_hostDriverCaps.max_color_attachments, false);
props.depthAttachment_hasRbo = false;
props.stencilAttachment_hasRbo = false;
props.depthstencilAttachment_hasRbo = false;
@@ -1841,32 +2545,46 @@
void GLClientState::attachTextureObject(
GLenum target,
- GLenum attachment, GLuint texture) {
+ GLenum attachment, GLuint texture, GLint level, GLint layer) {
+
+ bool attach = texture != 0;
int colorAttachmentIndex =
glUtilsColorAttachmentIndex(attachment);
+ boundFboProps(target).completenessDirty = true;
+
if (colorAttachmentIndex != -1) {
boundFboProps(target).colorAttachmenti_textures[colorAttachmentIndex] = texture;
- boundFboProps(target).colorAttachmenti_hasTex[colorAttachmentIndex] = true;
+ boundFboProps(target).colorAttachmenti_texture_levels[colorAttachmentIndex] = level;
+ boundFboProps(target).colorAttachmenti_texture_layers[colorAttachmentIndex] = layer;
+ boundFboProps(target).colorAttachmenti_hasTex[colorAttachmentIndex] = attach;
}
switch (attachment) {
case GL_DEPTH_ATTACHMENT:
boundFboProps(target).depthAttachment_texture = texture;
- boundFboProps(target).depthAttachment_hasTexObj = true;
+ boundFboProps(target).depthAttachment_texture_level = level;
+ boundFboProps(target).depthAttachment_texture_layer = layer;
+ boundFboProps(target).depthAttachment_hasTexObj = attach;
break;
case GL_STENCIL_ATTACHMENT:
boundFboProps(target).stencilAttachment_texture = texture;
- boundFboProps(target).stencilAttachment_hasTexObj = true;
+ boundFboProps(target).stencilAttachment_texture_level = level;
+ boundFboProps(target).stencilAttachment_texture_layer = layer;
+ boundFboProps(target).stencilAttachment_hasTexObj = attach;
break;
case GL_DEPTH_STENCIL_ATTACHMENT:
boundFboProps(target).depthstencilAttachment_texture = texture;
- boundFboProps(target).depthstencilAttachment_hasTexObj = true;
+ boundFboProps(target).depthstencilAttachment_hasTexObj = attach;
boundFboProps(target).stencilAttachment_texture = texture;
- boundFboProps(target).stencilAttachment_hasTexObj = true;
+ boundFboProps(target).stencilAttachment_hasTexObj = attach;
boundFboProps(target).depthAttachment_texture = texture;
- boundFboProps(target).depthAttachment_hasTexObj = true;
+ boundFboProps(target).depthAttachment_hasTexObj = attach;
+ boundFboProps(target).depthAttachment_texture_level = level;
+ boundFboProps(target).depthAttachment_texture_layer = layer;
+ boundFboProps(target).stencilAttachment_texture_level = level;
+ boundFboProps(target).stencilAttachment_texture_layer = layer;
break;
}
}
@@ -1898,7 +2616,7 @@
// RBOs for FBOs////////////////////////////////////////////////////////////////
void GLClientState::detachRbo(GLuint renderbuffer) {
- for (int i = 0; i < m_max_color_attachments; i++) {
+ for (int i = 0; i < m_hostDriverCaps.max_color_attachments; i++) {
detachRboFromFbo(GL_DRAW_FRAMEBUFFER, glUtilsColorAttachmentName(i), renderbuffer);
detachRboFromFbo(GL_READ_FRAMEBUFFER, glUtilsColorAttachmentName(i), renderbuffer);
}
@@ -1917,6 +2635,8 @@
int colorAttachmentIndex =
glUtilsColorAttachmentIndex(attachment);
+ boundFboProps(target).completenessDirty = true;
+
if (colorAttachmentIndex != -1) {
if (boundFboProps(target).colorAttachmenti_hasRbo[colorAttachmentIndex] &&
boundFboProps(target).colorAttachmenti_rbos[colorAttachmentIndex] == renderbuffer) {
@@ -1962,30 +2682,34 @@
void GLClientState::attachRbo(GLenum target, GLenum attachment, GLuint renderbuffer) {
+ bool attach = 0 != renderbuffer;
+
int colorAttachmentIndex =
glUtilsColorAttachmentIndex(attachment);
+ boundFboProps(target).completenessDirty = true;
+
if (colorAttachmentIndex != -1) {
boundFboProps(target).colorAttachmenti_rbos[colorAttachmentIndex] = renderbuffer;
- boundFboProps(target).colorAttachmenti_hasRbo[colorAttachmentIndex] = true;
+ boundFboProps(target).colorAttachmenti_hasRbo[colorAttachmentIndex] = attach;
}
switch (attachment) {
case GL_DEPTH_ATTACHMENT:
boundFboProps(target).depthAttachment_rbo = renderbuffer;
- boundFboProps(target).depthAttachment_hasRbo = true;
+ boundFboProps(target).depthAttachment_hasRbo = attach;
break;
case GL_STENCIL_ATTACHMENT:
boundFboProps(target).stencilAttachment_rbo = renderbuffer;
- boundFboProps(target).stencilAttachment_hasRbo = true;
+ boundFboProps(target).stencilAttachment_hasRbo = attach;
break;
case GL_DEPTH_STENCIL_ATTACHMENT:
boundFboProps(target).depthAttachment_rbo = renderbuffer;
- boundFboProps(target).depthAttachment_hasRbo = true;
+ boundFboProps(target).depthAttachment_hasRbo = attach;
boundFboProps(target).stencilAttachment_rbo = renderbuffer;
- boundFboProps(target).stencilAttachment_hasRbo = true;
+ boundFboProps(target).stencilAttachment_hasRbo = attach;
boundFboProps(target).depthstencilAttachment_rbo = renderbuffer;
- boundFboProps(target).depthstencilAttachment_hasRbo = true;
+ boundFboProps(target).depthstencilAttachment_hasRbo = attach;
break;
}
}
@@ -2014,6 +2738,80 @@
return res;
}
+void GLClientState::setFboCompletenessDirtyForTexture(GLuint texture) {
+ std::map<GLuint, FboProps>::iterator it = mFboState.fboData.begin();
+ while (it != mFboState.fboData.end()) {
+ FboProps& props = it->second;
+ for (int i = 0; i < m_hostDriverCaps.max_color_attachments; ++i) {
+ if (props.colorAttachmenti_hasTex[i]) {
+ if (texture == props.colorAttachmenti_textures[i]) {
+ props.completenessDirty = true;
+ return;
+ }
+ }
+ }
+
+ if (props.depthAttachment_hasTexObj) {
+ if (texture == props.depthAttachment_texture) {
+ props.completenessDirty = true;
+ return;
+ }
+ }
+
+ if (props.stencilAttachment_hasTexObj) {
+ if (texture == props.stencilAttachment_texture) {
+ props.completenessDirty = true;
+ return;
+ }
+ }
+
+ if (props.depthstencilAttachment_hasTexObj) {
+ if (texture == props.depthstencilAttachment_texture) {
+ props.completenessDirty = true;
+ return;
+ }
+ }
+ ++it;
+ }
+}
+
+void GLClientState::setFboCompletenessDirtyForRbo(GLuint rbo) {
+ std::map<GLuint, FboProps>::iterator it = mFboState.fboData.begin();
+ while (it != mFboState.fboData.end()) {
+ FboProps& props = it->second;
+ for (int i = 0; i < m_hostDriverCaps.max_color_attachments; ++i) {
+ if (props.colorAttachmenti_hasTex[i]) {
+ if (rbo == props.colorAttachmenti_rbos[i]) {
+ props.completenessDirty = true;
+ return;
+ }
+ }
+ }
+
+ if (props.depthAttachment_hasTexObj) {
+ if (rbo == props.depthAttachment_rbo) {
+ props.completenessDirty = true;
+ return;
+ }
+ }
+
+ if (props.stencilAttachment_hasTexObj) {
+ if (rbo == props.stencilAttachment_rbo) {
+ props.completenessDirty = true;
+ return;
+ }
+ }
+
+ if (props.depthstencilAttachment_hasRbo) {
+ if (rbo == props.depthstencilAttachment_rbo) {
+ props.completenessDirty = true;
+ return;
+ }
+ }
+ ++it;
+ }
+}
+
bool GLClientState::attachmentHasObject(GLenum target, GLenum attachment) const {
bool res = true; // liberal
@@ -2089,22 +2887,63 @@
return 0;
}
-void GLClientState::setTransformFeedbackActiveUnpaused(bool activeUnpaused) {
- m_transformFeedbackActiveUnpaused = activeUnpaused;
+void GLClientState::setTransformFeedbackActive(bool active) {
+ m_transformFeedbackActive = active;
+}
+
+void GLClientState::setTransformFeedbackUnpaused(bool unpaused) {
+ m_transformFeedbackUnpaused = unpaused;
+}
+
+void GLClientState::setTransformFeedbackVaryingsCountForLinking(uint32_t count) {
+ m_transformFeedbackVaryingsCountForLinking = count;
+}
+
+bool GLClientState::getTransformFeedbackActive() const {
+ return m_transformFeedbackActive;
+}
+
+bool GLClientState::getTransformFeedbackUnpaused() const {
+ return m_transformFeedbackUnpaused;
}
bool GLClientState::getTransformFeedbackActiveUnpaused() const {
- return m_transformFeedbackActiveUnpaused;
+ return m_transformFeedbackActive && m_transformFeedbackUnpaused;
+}
+
+uint32_t GLClientState::getTransformFeedbackVaryingsCountForLinking() const {
+ return m_transformFeedbackVaryingsCountForLinking;
}
void GLClientState::setTextureData(SharedTextureDataMap* sharedTexData) {
m_tex.textureRecs = sharedTexData;
}
+void GLClientState::setRenderbufferInfo(RenderbufferInfo* rbInfo) {
+ mRboState.rboData = rbInfo;
+}
+
+void GLClientState::setSamplerInfo(SamplerInfo* samplerInfo) {
+ mSamplerInfo = samplerInfo;
+}
+
+bool GLClientState::compressedTexImageSizeCompatible(GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize) {
+ bool error = false;
+ GLsizei compressedSize = GLESTextureUtils::getCompressedImageSize(internalformat, width, height, depth, &error);
+ if (error) return false;
+ return imageSize == compressedSize;
+}
+
void GLClientState::fromMakeCurrent() {
if (mFboState.fboData.find(0) == mFboState.fboData.end()) {
addFreshFramebuffer(0);
}
+
+ if (!samplerExists(0)) {
+ GLuint id = 0;
+ setExistence(ObjectType::Sampler, true, 1, &id);
+ }
+
FboProps& default_fb_props = mFboState.fboData[0];
default_fb_props.colorAttachmenti_hasRbo[0] = true;
default_fb_props.depthAttachment_hasRbo = true;
@@ -2113,30 +2952,30 @@
}
void GLClientState::initFromCaps(
- int max_transform_feedback_separate_attribs,
- int max_uniform_buffer_bindings,
- int max_atomic_counter_buffer_bindings,
- int max_shader_storage_buffer_bindings,
- int max_vertex_attrib_bindings,
- int max_color_attachments,
- int max_draw_buffers) {
+ const HostDriverCaps& caps) {
+ m_hostDriverCaps = caps;
- m_max_vertex_attrib_bindings = max_vertex_attrib_bindings;
+ // Override some of them
+ m_hostDriverCaps.max_vertex_attribs = CODEC_MAX_VERTEX_ATTRIBUTES;
+ m_hostDriverCaps.max_vertex_attrib_bindings = m_hostDriverCaps.max_vertex_attribs;
+
+ // Derive some other settings
+ m_log2MaxTextureSize = 0;
+ uint32_t current = 1;
+ while (current < m_hostDriverCaps.max_texture_size) {
+ current = current << 1;
+ ++m_log2MaxTextureSize;
+ }
if (m_glesMajorVersion >= 3) {
- m_max_transform_feedback_separate_attribs = max_transform_feedback_separate_attribs;
- m_max_uniform_buffer_bindings = max_uniform_buffer_bindings;
- m_max_atomic_counter_buffer_bindings = max_atomic_counter_buffer_bindings;
- m_max_shader_storage_buffer_bindings = max_shader_storage_buffer_bindings;
-
- if (m_max_transform_feedback_separate_attribs)
- m_indexedTransformFeedbackBuffers.resize(m_max_transform_feedback_separate_attribs);
- if (m_max_uniform_buffer_bindings)
- m_indexedUniformBuffers.resize(m_max_uniform_buffer_bindings);
- if (m_max_atomic_counter_buffer_bindings)
- m_indexedAtomicCounterBuffers.resize(m_max_atomic_counter_buffer_bindings);
- if (m_max_shader_storage_buffer_bindings)
- m_indexedShaderStorageBuffers.resize(m_max_shader_storage_buffer_bindings);
+ if (m_hostDriverCaps.max_transform_feedback_separate_attribs)
+ m_indexedTransformFeedbackBuffers.resize(m_hostDriverCaps.max_transform_feedback_separate_attribs);
+ if (m_hostDriverCaps.max_uniform_buffer_bindings)
+ m_indexedUniformBuffers.resize(m_hostDriverCaps.max_uniform_buffer_bindings);
+ if (m_hostDriverCaps.max_atomic_counter_buffer_bindings)
+ m_indexedAtomicCounterBuffers.resize(m_hostDriverCaps.max_atomic_counter_buffer_bindings);
+ if (m_hostDriverCaps.max_shader_storage_buffer_bindings)
+ m_indexedShaderStorageBuffers.resize(m_hostDriverCaps.max_shader_storage_buffer_bindings);
BufferBinding buf0Binding;
buf0Binding.buffer = 0;
@@ -2155,10 +2994,6 @@
m_indexedShaderStorageBuffers[i] = buf0Binding;
}
- m_max_color_attachments = max_color_attachments;
- m_max_draw_buffers = max_draw_buffers;
-
- addFreshRenderbuffer(0);
addFreshFramebuffer(0);
m_initialized = true;
@@ -2167,3 +3002,60 @@
bool GLClientState::needsInitFromCaps() const {
return !m_initialized;
}
+
+void GLClientState::setExtensions(const std::string& extensions) {
+ if (!m_extensions_set) m_extensions = extensions;
+
+ m_has_color_buffer_float_extension =
+ hasExtension("GL_EXT_color_buffer_float");
+ m_has_color_buffer_half_float_extension =
+ hasExtension("GL_EXT_color_buffer_half_float");
+ m_extensions_set = true;
+}
+
+bool GLClientState::hasExtension(const char* ext) const {
+ return m_extensions.find(ext) != std::string::npos;
+}
+
+using android::base::guest::AutoLock;
+using android::base::guest::Lock;
+
+// A process-wide fence registry (because we can use fence sync objects across multiple contexts)
+struct FenceRegistry {
+ Lock lock;
+ PredicateMap<uint64_t, false> existence;
+
+ void onFenceCreated(GLsync sync) {
+ AutoLock scopedLock(lock);
+ uint64_t asUint64 = (uint64_t)(uintptr_t)(sync);
+ existence.add(asUint64);
+ existence.set(asUint64, true);
+ }
+
+ void onFenceDestroyed(GLsync sync) {
+ AutoLock scopedLock(lock);
+ uint64_t asUint64 = (uint64_t)(uintptr_t)(sync);
+ existence.remove(asUint64);
+ }
+
+ bool exists(GLsync sync) {
+ AutoLock scopedLock(lock);
+ uint64_t asUint64 = (uint64_t)(uintptr_t)(sync);
+ return existence.get(asUint64);
+ }
+};
+
+static FenceRegistry sFenceRegistry;
+
+void GLClientState::onFenceCreated(GLsync sync) {
+ sFenceRegistry.onFenceCreated(sync);
+}
+
+void GLClientState::onFenceDestroyed(GLsync sync) {
+ sFenceRegistry.onFenceDestroyed(sync);
+}
+
+bool GLClientState::fenceExists(GLsync sync) {
+ return sFenceRegistry.exists(sync);
+}
+
diff --git a/shared/OpenglCodecCommon/GLClientState.h b/shared/OpenglCodecCommon/GLClientState.h
index 1137041..c98190c 100644
--- a/shared/OpenglCodecCommon/GLClientState.h
+++ b/shared/OpenglCodecCommon/GLClientState.h
@@ -41,6 +41,36 @@
#include <vector>
#include <map>
#include <set>
+#include <string>
+
+// Caps of host driver that make it easy to validate stuff
+struct HostDriverCaps {
+ // ES 2
+ int max_vertex_attribs;
+ int max_combined_texture_image_units;
+ int max_color_attachments;
+
+ int max_texture_size;
+ int max_texture_size_cube_map;
+ int max_renderbuffer_size;
+
+ // ES 3.0
+ int max_draw_buffers;
+
+ int ubo_offset_alignment;
+ int max_uniform_buffer_bindings;
+ int max_transform_feedback_separate_attribs;
+
+ int max_texture_size_3d;
+ int max_array_texture_layers;
+
+ // ES 3.1
+ int max_atomic_counter_buffer_bindings;
+ int max_shader_storage_buffer_bindings;
+ int max_vertex_attrib_bindings;
+ int max_vertex_attrib_stride;
+ int ssbo_offset_alignment;
+};
// Tracking framebuffer objects:
// which framebuffer is bound,
@@ -49,7 +79,17 @@
struct FboProps {
GLuint name;
bool previouslyBound;
+ bool completenessDirty;
+ GLenum cachedCompleteness;
std::vector<GLuint> colorAttachmenti_textures;
+ std::vector<GLint> colorAttachmenti_texture_levels;
+ std::vector<GLint> colorAttachmenti_texture_layers;
+
+ GLint depthAttachment_texture_level;
+ GLint depthAttachment_texture_layer;
+ GLint stencilAttachment_texture_level;
+ GLint stencilAttachment_texture_layer;
+
GLuint depthAttachment_texture;
GLuint stencilAttachment_texture;
GLuint depthstencilAttachment_texture;
@@ -70,15 +110,6 @@
bool depthstencilAttachment_hasRbo;
};
-// Same for Rbo's
-struct RboProps {
- GLenum target;
- GLuint name;
- GLenum format;
- GLsizei multisamples;
- bool previouslyBound;
-};
-
// Enum for describing whether a framebuffer attachment
// is a texture or renderbuffer.
enum FboAttachmentType {
@@ -92,15 +123,27 @@
FboAttachmentType type;
GLenum rb_format;
GLsizei rb_multisamples;
+ bool rb_external;
GLint tex_internalformat;
GLenum tex_format;
GLenum tex_type;
GLsizei tex_multisamples;
+ GLint tex_level;
+ GLint tex_layer;
+ bool tex_external;
};
class GLClientState {
public:
+ // TODO: Unify everything in here
+ typedef enum {
+ Buffer,
+ TransformFeedback,
+ Sampler,
+ Query,
+ } ObjectType;
+
typedef enum {
VERTEX_LOCATION = 0,
NORMAL_LOCATION = 1,
@@ -203,7 +246,7 @@
GLClientState();
GLClientState(int majorVersion, int minorVersion);
~GLClientState();
- int nLocations() { return m_nLocations; }
+ int nLocations() { return CODEC_MAX_VERTEX_ATTRIBUTES; }
const PixelStoreState *pixelStoreState() { return &m_pixelStore; }
int setPixelStore(GLenum param, GLint value);
GLuint currentVertexArrayObject() const { return m_currVaoState.vaoId(); }
@@ -234,10 +277,6 @@
int getLocation(GLenum loc);
void setActiveTexture(int texUnit) {m_activeTexture = texUnit; };
int getActiveTexture() const { return m_activeTexture; }
- void setMaxVertexAttribs(int val) {
- m_maxVertexAttribs = val;
- m_maxVertexAttribsDirty = false;
- }
void addBuffer(GLuint id);
void removeBuffer(GLuint id);
@@ -247,6 +286,20 @@
void setBufferHostMapDirty(GLuint id, bool dirty);
bool isBufferHostMapDirty(GLuint id) const;
+ void setExistence(ObjectType type, bool exists, GLsizei count, const GLuint* ids);
+ bool queryExistence(ObjectType type, GLuint id) const;
+ bool samplerExists(GLuint id) const;
+ bool tryBind(GLenum target, GLuint id);
+ bool isBoundTargetValid(GLenum target);
+ bool isQueryBound(GLenum target);
+ bool isQueryObjectActive(GLuint id);
+ void setLastQueryTarget(GLenum target, GLuint id);
+ GLenum getLastQueryTarget(GLuint id);
+
+ static void onFenceCreated(GLsync sync);
+ static void onFenceDestroyed(GLsync sync);
+ static bool fenceExists(GLsync sync);
+
void setBoundPixelPackBufferDirtyForHostMap();
void setBoundTransformFeedbackBuffersDirtyForHostMap();
void setBoundShaderStorageBuffersDirtyForHostMap();
@@ -258,6 +311,11 @@
bool isNonIndexedBindNoOp(GLenum target, GLuint buffer);
bool isIndexedBindNoOp(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size, GLintptr stride, GLintptr effectiveStride);
+ int getMaxTextureSize() const;
+ int getMaxTextureSize3D() const;
+ int getMaxTextureSizeCubeMap() const;
+ int getLog2MaxTextureSize() const;
+
void postDraw();
void postReadPixels();
void postDispatchCompute();
@@ -332,7 +390,7 @@
// glDisable(GL_TEXTURE_(2D|EXTERNAL_OES))
void disableTextureTarget(GLenum target);
- void bindSampler(GLuint unit, GLuint sampler);
+ bool bindSampler(GLuint unit, GLuint sampler);
bool isSamplerBindNoOp(GLuint unit, GLuint sampler);
void onDeleteSamplers(GLsizei n, const GLuint* samplers);
@@ -357,6 +415,15 @@
// Return the texture currently bound to GL_TEXTURE_(2D|EXTERNAL_OES).
GLuint getBoundTexture(GLenum target) const;
+ // Return bound framebuffer for target
+ GLuint getBoundFramebuffer(GLenum target) const;
+
+ // Check framebuffer completeness
+ GLenum checkFramebufferCompleteness(GLenum target);
+ // |currentSamples|: threads through the current sample count of attachments so far,
+ // for validating consistent number of samples across attachments
+ GLenum checkFramebufferAttachmentCompleteness(GLenum target, GLenum attachment, int* currentSamples) const;
+
// Other publicly-visible texture queries
GLenum queryTexLastBoundTarget(GLuint name) const;
GLenum queryTexFormat(GLuint name) const;
@@ -383,12 +450,14 @@
void setBoundTextureInternalFormat(GLenum target, GLint format);
void setBoundTextureFormat(GLenum target, GLenum format);
void setBoundTextureType(GLenum target, GLenum type);
- void setBoundTextureDims(GLenum target, GLsizei level, GLsizei width, GLsizei height, GLsizei depth);
+ void setBoundTextureDims(GLenum target, GLenum cubetarget, GLsizei level, GLsizei width, GLsizei height, GLsizei depth);
void setBoundTextureSamples(GLenum target, GLsizei samples);
+ void addTextureCubeMapImage(GLenum stateTarget, GLenum cubeTarget);
// glTexStorage2D disallows any change in texture format after it is set for a particular texture.
void setBoundTextureImmutableFormat(GLenum target);
bool isBoundTextureImmutableFormat(GLenum target) const;
+ bool isBoundTextureComplete(GLenum target) const;
// glDeleteTextures(...)
// Remove references to the to-be-deleted textures.
@@ -402,6 +471,8 @@
GLuint boundRenderbuffer() const;
void setBoundRenderbufferFormat(GLenum format);
void setBoundRenderbufferSamples(GLsizei samples);
+ void setBoundRenderbufferDimensions(GLsizei width, GLsizei height);
+ void setBoundRenderbufferEGLImageBacked();
// Frame buffer objects
void addFramebuffers(GLsizei n, GLuint* framebuffers);
@@ -413,7 +484,7 @@
GLuint boundFramebuffer(GLenum target) const;
// Texture object -> FBO
- void attachTextureObject(GLenum target, GLenum attachment, GLuint texture);
+ void attachTextureObject(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);
GLuint getFboAttachmentTextureId(GLenum target, GLenum attachment) const;
// RBO -> FBO
@@ -426,11 +497,24 @@
bool attachmentHasObject(GLenum target, GLenum attachment) const;
GLuint objectOfAttachment(GLenum target, GLenum attachment) const;
+ // Dirty FBO completeness
+ void setFboCompletenessDirtyForTexture(GLuint texture);
+ void setFboCompletenessDirtyForRbo(GLuint rbo_name);
+
// Transform feedback state
- void setTransformFeedbackActiveUnpaused(bool activeUnpaused);
+ void setTransformFeedbackActive(bool active);
+ void setTransformFeedbackUnpaused(bool unpaused);
+ void setTransformFeedbackVaryingsCountForLinking(uint32_t count);
+ bool getTransformFeedbackActive() const;
+ bool getTransformFeedbackUnpaused() const;
bool getTransformFeedbackActiveUnpaused() const;
+ uint32_t getTransformFeedbackVaryingsCountForLinking() const;
void setTextureData(SharedTextureDataMap* sharedTexData);
+ void setRenderbufferInfo(RenderbufferInfo* rbInfo);
+ void setSamplerInfo(SamplerInfo* samplerInfo);
+
+ bool compressedTexImageSizeCompatible(GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize);
// set eglsurface property on default framebuffer
// if coming from eglMakeCurrent
void fromMakeCurrent();
@@ -439,14 +523,10 @@
// accurate values for indexed buffers
// and # render targets.
void initFromCaps(
- int max_transform_feedback_separate_attribs,
- int max_uniform_buffer_bindings,
- int max_atomic_counter_buffer_bindings,
- int max_shader_storage_buffer_bindings,
- int max_vertex_attrib_bindings,
- int max_color_attachments,
- int max_draw_buffers);
+ const HostDriverCaps& caps);
bool needsInitFromCaps() const;
+ void setExtensions(const std::string& extensions);
+ bool hasExtension(const char* ext) const;
// Queries the format backing the current framebuffer.
// Type differs depending on whether the attachment
@@ -460,14 +540,47 @@
GLenum attachment) const;
int getMaxColorAttachments() const;
int getMaxDrawBuffers() const;
+
+ // Uniform validation info
+ UniformValidationInfo currentUniformValidationInfo;
+ // TODO: Program uniform validation info
+
+ // Uniform validation api
+ void validateUniform(bool isFloat, bool isUnsigned, GLint columns, GLint rows, GLint location, GLsizei count, GLenum* err);
+
private:
void init();
bool m_initialized;
PixelStoreState m_pixelStore;
#ifdef GFXSTREAM
- PredicateMap<false> mBufferIds;
- PredicateMap<true> mHostMappedBufferDirty;
+ using DirtyMap = PredicateMap<uint32_t, true>;
+
+ ExistenceMap mBufferIds;
+ ExistenceMap mTransformFeedbackIds;
+ SamplerInfo* mSamplerInfo;
+ ExistenceMap mQueryIds;
+ LastQueryTargetInfo mLastQueryTargets;
+
+ // Bound query target validity and tracking
+ struct BoundTargetInfo {
+ GLuint id;
+ bool valid;
+ };
+
+ // Transform feedback
+ BoundTargetInfo mBoundTransformFeedbackValidity;
+
+ // Queries
+ // GL_ANY_SAMPLES_PASSED
+ BoundTargetInfo mBoundQueryValidity_AnySamplesPassed;
+ // GL_ANY_SAMPLES_PASSED_CONSERVATIVE
+ BoundTargetInfo mBoundQueryValidity_AnySamplesPassedConservative;
+ // GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN
+ BoundTargetInfo mBoundQueryValidity_TransformFeedbackPrimitivesWritten;
+
+ // Dirty maps
+ DirtyMap mHostMappedBufferDirty;
#else
std::set<GLuint> mBufferIds;
#endif
@@ -499,23 +612,23 @@
GLuint m_drawIndirectBuffer;
GLuint m_shaderStorageBuffer;
- bool m_transformFeedbackActiveUnpaused;
+ bool m_transformFeedbackActive;
+ bool m_transformFeedbackUnpaused;
+ uint32_t m_transformFeedbackVaryingsCountForLinking;
- int m_max_transform_feedback_separate_attribs;
- int m_max_uniform_buffer_bindings;
- int m_max_atomic_counter_buffer_bindings;
- int m_max_shader_storage_buffer_bindings;
- int m_max_vertex_attrib_bindings;
+ HostDriverCaps m_hostDriverCaps;
+ bool m_extensions_set;
+ std::string m_extensions;
+ bool m_has_color_buffer_float_extension;
+ bool m_has_color_buffer_half_float_extension;
std::vector<BufferBinding> m_indexedTransformFeedbackBuffers;
std::vector<BufferBinding> m_indexedUniformBuffers;
std::vector<BufferBinding> m_indexedAtomicCounterBuffers;
std::vector<BufferBinding> m_indexedShaderStorageBuffers;
+ int m_log2MaxTextureSize;
int m_glesMajorVersion;
int m_glesMinorVersion;
- int m_maxVertexAttribs;
- bool m_maxVertexAttribsDirty;
- int m_nLocations;
int m_activeTexture;
GLint m_currentProgram;
GLint m_currentShaderProgram;
@@ -570,19 +683,14 @@
GLenum copyTexImageNeededTarget(GLenum target, GLint level,
GLenum internalformat);
- int m_max_color_attachments;
- int m_max_draw_buffers;
struct RboState {
GLuint boundRenderbuffer;
- size_t boundRenderbufferIndex;
- std::vector<RboProps> rboData;
+ // Connects to share group.
+ // Expected that share group lifetime outlives this context.
+ RenderbufferInfo* rboData;
};
RboState mRboState;
void addFreshRenderbuffer(GLuint name);
- void setBoundRenderbufferIndex();
- size_t getRboIndex(GLuint name) const;
- RboProps& boundRboProps();
- const RboProps& boundRboProps_const() const;
struct FboState {
GLuint boundDrawFramebuffer;
@@ -600,6 +708,9 @@
// Querying framebuffer format
GLenum queryRboFormat(GLuint name) const;
GLsizei queryRboSamples(GLuint name) const;
+ GLsizei queryRboWidth(GLuint name) const;
+ GLsizei queryRboHeight(GLuint name) const;
+ bool queryRboEGLImageBacked(GLuint name) const;
GLenum queryTexType(GLuint name) const;
GLsizei queryTexSamples(GLuint name) const;
@@ -608,6 +719,13 @@
TextureRec* getTextureRec(GLuint id) const;
public:
+ bool isTexture(GLuint name) const;
+ bool isTextureWithStorage(GLuint name) const;
+ bool isTextureWithTarget(GLuint name) const;
+ bool isTextureCubeMap(GLuint name) const;
+ bool isRenderbuffer(GLuint name) const;
+ bool isRenderbufferThatWasBound(GLuint name) const;
+
void getClientStatePointer(GLenum pname, GLvoid** params);
template <class T>
@@ -839,12 +957,8 @@
break;
}
case GL_MAX_VERTEX_ATTRIBS: {
- if (m_maxVertexAttribsDirty) {
- isClientStateParam = false;
- } else {
- *out = m_maxVertexAttribs;
- isClientStateParam = true;
- }
+ *out = CODEC_MAX_VERTEX_ATTRIBUTES;
+ isClientStateParam = true;
break;
}
}
diff --git a/shared/OpenglCodecCommon/GLESTextureUtils.cpp b/shared/OpenglCodecCommon/GLESTextureUtils.cpp
index cedcda8..8b4c0c2 100644
--- a/shared/OpenglCodecCommon/GLESTextureUtils.cpp
+++ b/shared/OpenglCodecCommon/GLESTextureUtils.cpp
@@ -1,6 +1,8 @@
#include "GLESTextureUtils.h"
#include "glUtils.h"
+#include "etc.h"
+#include "astc-codec.h"
#if PLATFORM_SDK_VERSION < 26
#include <cutils/log.h>
@@ -8,6 +10,36 @@
#include <log/log.h>
#endif
+#define ASTC_FORMATS_LIST(EXPAND_MACRO) \
+ EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_4x4_KHR, astc_codec::FootprintType::k4x4, false) \
+ EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_5x4_KHR, astc_codec::FootprintType::k5x4, false) \
+ EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_5x5_KHR, astc_codec::FootprintType::k5x5, false) \
+ EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_6x5_KHR, astc_codec::FootprintType::k6x5, false) \
+ EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_6x6_KHR, astc_codec::FootprintType::k6x6, false) \
+ EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_8x5_KHR, astc_codec::FootprintType::k8x5, false) \
+ EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_8x6_KHR, astc_codec::FootprintType::k8x6, false) \
+ EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_8x8_KHR, astc_codec::FootprintType::k8x8, false) \
+ EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_10x5_KHR, astc_codec::FootprintType::k10x5, false) \
+ EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_10x6_KHR, astc_codec::FootprintType::k10x6, false) \
+ EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_10x8_KHR, astc_codec::FootprintType::k10x8, false) \
+ EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_10x10_KHR, astc_codec::FootprintType::k10x10, false) \
+ EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_12x10_KHR, astc_codec::FootprintType::k12x10, false) \
+ EXPAND_MACRO(GL_COMPRESSED_RGBA_ASTC_12x12_KHR, astc_codec::FootprintType::k12x12, false) \
+ EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, astc_codec::FootprintType::k4x4, true) \
+ EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, astc_codec::FootprintType::k5x4, true) \
+ EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, astc_codec::FootprintType::k5x5, true) \
+ EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, astc_codec::FootprintType::k6x5, true) \
+ EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, astc_codec::FootprintType::k6x6, true) \
+ EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, astc_codec::FootprintType::k8x5, true) \
+ EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, astc_codec::FootprintType::k8x6, true) \
+ EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, astc_codec::FootprintType::k8x8, true) \
+ EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR, astc_codec::FootprintType::k10x5, true) \
+ EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, astc_codec::FootprintType::k10x6, true) \
+ EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, astc_codec::FootprintType::k10x8, true) \
+ EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, astc_codec::FootprintType::k10x10, true) \
+ EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, astc_codec::FootprintType::k12x10, true) \
+ EXPAND_MACRO(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, astc_codec::FootprintType::k12x12, true) \
+
namespace GLESTextureUtils {
// Based on computations in
@@ -332,6 +364,8 @@
int* packingPixelImageSize,
int* packingTotalImageSize) {
+ (void)depth;
+
int widthTotal = (packRowLength == 0) ? width : packRowLength;
int totalRowSize = computePitch(widthTotal, format, type, packAlignment);
int pixelsOnlyRowSize = computePitch(width, format, type, packAlignment);
@@ -352,4 +386,184 @@
if (packingTotalImageSize) *packingTotalImageSize = totalImageSize;
}
+bool isEtcFormat(GLenum internalformat) {
+ switch (internalformat) {
+ case GL_ETC1_RGB8_OES:
+ case GL_COMPRESSED_RGB8_ETC2:
+ case GL_COMPRESSED_SRGB8_ETC2:
+ case GL_COMPRESSED_RGBA8_ETC2_EAC:
+ case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
+ case GL_COMPRESSED_R11_EAC:
+ case GL_COMPRESSED_SIGNED_R11_EAC:
+ case GL_COMPRESSED_RG11_EAC:
+ case GL_COMPRESSED_SIGNED_RG11_EAC:
+ case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+ case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+ return true;
+ }
+ return false;
+}
+
+bool isEtc2Format(GLenum internalformat) {
+ return internalformat != GL_ETC1_RGB8_OES &&
+ isEtcFormat(internalformat);
+}
+
+ETC2ImageFormat getEtcFormat(GLenum internalformat) {
+ ETC2ImageFormat etcFormat = EtcRGB8;
+ switch (internalformat) {
+ case GL_COMPRESSED_RGB8_ETC2:
+ case GL_ETC1_RGB8_OES:
+ break;
+ case GL_COMPRESSED_RGBA8_ETC2_EAC:
+ etcFormat = EtcRGBA8;
+ break;
+ case GL_COMPRESSED_SRGB8_ETC2:
+ break;
+ case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
+ etcFormat = EtcRGBA8;
+ break;
+ case GL_COMPRESSED_R11_EAC:
+ etcFormat = EtcR11;
+ break;
+ case GL_COMPRESSED_SIGNED_R11_EAC:
+ etcFormat = EtcSignedR11;
+ break;
+ case GL_COMPRESSED_RG11_EAC:
+ etcFormat = EtcRG11;
+ break;
+ case GL_COMPRESSED_SIGNED_RG11_EAC:
+ etcFormat = EtcSignedRG11;
+ break;
+ case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+ etcFormat = EtcRGB8A1;
+ break;
+ case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+ etcFormat = EtcRGB8A1;
+ break;
+ }
+ return etcFormat;
+}
+
+bool isAstcFormat(GLenum internalformat) {
+ switch (internalformat) {
+#define ASTC_FORMAT(typeName, footprintType, srgbValue) \
+ case typeName:
+
+ ASTC_FORMATS_LIST(ASTC_FORMAT)
+#undef ASTC_FORMAT
+ return true;
+ default:
+ return false;
+ }
+}
+
+void getAstcFormatInfo(GLenum internalformat,
+ astc_codec::FootprintType* footprint,
+ bool* srgb) {
+ switch (internalformat) {
+#define ASTC_FORMAT(typeName, footprintType, srgbValue) \
+ case typeName: \
+ *footprint = footprintType; *srgb = srgbValue; break; \
+
+ ASTC_FORMATS_LIST(ASTC_FORMAT)
+#undef ASTC_FORMAT
+ default:
+ ALOGE("%s: invalid astc format: 0x%x\n", __func__, internalformat);
+ abort();
+ }
+}
+
+int getAstcFootprintWidth(astc_codec::FootprintType footprint) {
+ switch (footprint) {
+ case astc_codec::FootprintType::k4x4: return 4;
+ case astc_codec::FootprintType::k5x4: return 5;
+ case astc_codec::FootprintType::k5x5: return 5;
+ case astc_codec::FootprintType::k6x5: return 6;
+ case astc_codec::FootprintType::k6x6: return 6;
+ case astc_codec::FootprintType::k8x5: return 8;
+ case astc_codec::FootprintType::k8x6: return 8;
+ case astc_codec::FootprintType::k10x5: return 10;
+ case astc_codec::FootprintType::k10x6: return 10;
+ case astc_codec::FootprintType::k8x8: return 8;
+ case astc_codec::FootprintType::k10x8: return 10;
+ case astc_codec::FootprintType::k10x10: return 10;
+ case astc_codec::FootprintType::k12x10: return 12;
+ case astc_codec::FootprintType::k12x12: return 12;
+ default:
+ ALOGE("%s: invalid astc footprint: 0x%x\n", __func__, footprint);
+ abort();
+ }
+}
+
+int getAstcFootprintHeight(astc_codec::FootprintType footprint) {
+ switch (footprint) {
+ case astc_codec::FootprintType::k4x4: return 4;
+ case astc_codec::FootprintType::k5x4: return 4;
+ case astc_codec::FootprintType::k5x5: return 5;
+ case astc_codec::FootprintType::k6x5: return 5;
+ case astc_codec::FootprintType::k6x6: return 6;
+ case astc_codec::FootprintType::k8x5: return 5;
+ case astc_codec::FootprintType::k8x6: return 6;
+ case astc_codec::FootprintType::k10x5: return 5;
+ case astc_codec::FootprintType::k10x6: return 6;
+ case astc_codec::FootprintType::k8x8: return 8;
+ case astc_codec::FootprintType::k10x8: return 8;
+ case astc_codec::FootprintType::k10x10: return 10;
+ case astc_codec::FootprintType::k12x10: return 10;
+ case astc_codec::FootprintType::k12x12: return 12;
+ default:
+ ALOGE("%s: invalid astc footprint: 0x%x\n", __func__, footprint);
+ abort();
+ }
+}
+
+GLsizei getAstcCompressedSize(GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, bool* error) {
+ bool srgb;
+ astc_codec::FootprintType footprintType;
+ getAstcFormatInfo(internalformat, &footprintType, &srgb);
+
+ int fpWidth = getAstcFootprintWidth(footprintType);
+ int fpHeight = getAstcFootprintHeight(footprintType);
+
+ if (width == 0 || height == 0 || depth == 0) {
+ *error = true;
+ return 0;
+ }
+
+ const size_t blocks_wide = (width + fpWidth - 1) / fpWidth;
+ if (blocks_wide == 0) {
+ *error = true;
+ return 0;
+ }
+
+ const size_t expected_block_count =
+ ((width + fpWidth - 1) / fpWidth) *
+ ((height + fpHeight - 1) / fpHeight);
+
+ const size_t kPhysBlockSizeBytes = 16;
+
+ GLsizei res = kPhysBlockSizeBytes * expected_block_count * depth;
+
+ return res;
+}
+
+GLsizei getCompressedImageSize(GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, bool* error) {
+ if (isEtcFormat(internalformat)) {
+ GLsizei total = 0;
+ GLsizei one = etc_get_encoded_data_size(getEtcFormat(internalformat), width, height);
+ for (GLsizei i = 0; i < depth; ++i) {
+ total += one;
+ }
+ return total;
+ }
+
+ if (isAstcFormat(internalformat)) {
+ return getAstcCompressedSize(internalformat, width, height, depth, error);
+ }
+
+ ALOGE("%s: Unknown compressed internal format: 0x%x\n", __func__, internalformat);
+ abort();
+}
+
} // namespace GLESTextureUtils
diff --git a/shared/OpenglCodecCommon/GLESTextureUtils.h b/shared/OpenglCodecCommon/GLESTextureUtils.h
index 1d26b3a..c2948bd 100644
--- a/shared/OpenglCodecCommon/GLESTextureUtils.h
+++ b/shared/OpenglCodecCommon/GLESTextureUtils.h
@@ -71,5 +71,12 @@
int* packingPixelImageSize,
int* packingTotalImageSize);
+// For calculating compressed sizes of ETC/EAC formatted images in the guest.
+GLsizei getCompressedImageSize(GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, bool* error);
+
+// Format queries
+bool isEtc2Format(GLenum internalformat);
+bool isAstcFormat(GLenum internalformat);
+
} // namespace GLESTextureUtils
#endif
diff --git a/shared/OpenglCodecCommon/GLSharedGroup.cpp b/shared/OpenglCodecCommon/GLSharedGroup.cpp
index 5273e4c..848dfdb 100755
--- a/shared/OpenglCodecCommon/GLSharedGroup.cpp
+++ b/shared/OpenglCodecCommon/GLSharedGroup.cpp
@@ -17,6 +17,7 @@
#include "GLSharedGroup.h"
#include "KeyedVectorUtils.h"
+#include "glUtils.h"
/**** BufferData ****/
@@ -36,13 +37,19 @@
/**** ProgramData ****/
ProgramData::ProgramData() : m_numIndexes(0),
+ m_numAttributes(0),
m_initialized(false) {
m_Indexes = NULL;
+ m_refcount = 1;
+ m_linkStatus = 0;
+ m_activeUniformBlockCount = 0;
+ m_transformFeedbackVaryingsCount = 0;
}
-void ProgramData::initProgramData(GLuint numIndexes) {
+void ProgramData::initProgramData(GLuint numIndexes, GLuint numAttributes) {
m_initialized = true;
m_numIndexes = numIndexes;
+ m_numAttributes = numAttributes;
delete [] m_Indexes;
@@ -104,6 +111,16 @@
return 0;
}
+bool ProgramData::isValidUniformLocation(GLint location) {
+ for (GLuint i = 0; i < m_numIndexes; ++i) {
+ if (location >= m_Indexes[i].base &&
+ location < m_Indexes[i].base + m_Indexes[i].size)
+ return true;
+ }
+
+ return false;
+}
+
GLint ProgramData::getNextSamplerUniform(
GLint index, GLint* val, GLenum* target) {
@@ -154,15 +171,18 @@
return false;
}
-bool ProgramData::attachShader(GLuint shader) {
+bool ProgramData::attachShader(GLuint shader, GLenum shaderType) {
size_t n = m_shaders.size();
for (size_t i = 0; i < n; i++) {
if (m_shaders[i] == shader) {
return false;
+ } else if (m_shaderTypes[i] == shaderType) {
+ return false;
}
}
m_shaders.push_back(shader);
+ m_shaderTypes.push_back(shaderType);
return true;
}
@@ -172,6 +192,7 @@
for (size_t i = 0; i < n; i++) {
if (m_shaders[i] == shader) {
m_shaders.erase(m_shaders.begin() + i);
+ m_shaderTypes.erase(m_shaderTypes.begin() + i);
return true;
}
}
@@ -179,6 +200,34 @@
return false;
}
+UniformValidationInfo ProgramData::compileValidationInfo(bool* error) const {
+ UniformValidationInfo res;
+ if (!m_Indexes) {
+ *error = true;
+ return res;
+ }
+
+ for (GLuint i = 0; i < m_numIndexes; ++i) {
+ if (m_Indexes[i].base < 0) continue;
+
+ UniformLocationInfo info = {
+ .valid = true,
+ .columns = getColumnsOfType(m_Indexes[i].type),
+ .rows = getRowsOfType(m_Indexes[i].type),
+ .isSampler = isSamplerType(m_Indexes[i].type),
+ .isInt = isIntegerType(m_Indexes[i].type),
+ .isArray = m_Indexes[i].size > 1,
+ .isUnsigned = isUnsignedIntType(m_Indexes[i].type),
+ .isBool = isBoolType(m_Indexes[i].type),
+ };
+ for (GLuint j = 0; j < m_Indexes[i].size; ++j) {
+ res.add(m_Indexes[i].base + j, info);
+ }
+ }
+
+ return res;
+}
+
/***** GLSharedGroup ****/
GLSharedGroup::GLSharedGroup() { }
@@ -212,6 +261,14 @@
return &m_textureRecs;
}
+RenderbufferInfo* GLSharedGroup::getRenderbufferInfo() {
+ return &m_renderbufferInfo;
+}
+
+SamplerInfo* GLSharedGroup::getSamplerInfo() {
+ return &m_samplerInfo;
+}
+
void GLSharedGroup::addBufferData(GLuint bufferId, GLsizeiptr size, const void* data) {
android::AutoMutex _lock(m_lock);
@@ -302,16 +359,37 @@
m_programs[program] = new ProgramData();
}
-void GLSharedGroup::initProgramData(GLuint program, GLuint numIndexes) {
+void GLSharedGroup::initProgramData(GLuint program, GLuint numIndexes, GLuint numAttributes) {
android::AutoMutex _lock(m_lock);
ProgramData* pData = findObjectOrDefault(m_programs, program);
if (pData) {
- pData->initProgramData(numIndexes);
+ pData->initProgramData(numIndexes, numAttributes);
}
}
+void GLSharedGroup::refProgramData(GLuint program) {
+ android::AutoMutex _lock(m_lock);
+ ProgramData* pData = findObjectOrDefault(m_programs, program);
+ if (!pData) return;
+ pData->incRef();
+}
+
+void GLSharedGroup::onUseProgram(GLuint previous, GLuint next) {
+ if (previous == next) return;
+
+ android::AutoMutex _lock(m_lock);
+
+ if (previous) {
+ deleteProgramDataLocked(previous);
+ }
+
+ ProgramData* pData = findObjectOrDefault(m_programs, next);
+ if (!pData) return;
+ pData->incRef();
+}
+
bool GLSharedGroup::isProgramInitialized(GLuint program) {
android::AutoMutex _lock(m_lock);
@@ -337,14 +415,23 @@
}
void GLSharedGroup::deleteProgramData(GLuint program) {
-
android::AutoMutex _lock(m_lock);
+ deleteProgramDataLocked(program);
+}
+
+void GLSharedGroup::deleteProgramDataLocked(GLuint program) {
ProgramData* pData = findObjectOrDefault(m_programs, program);
- if (pData) delete pData;
-
- m_programs.erase(program);
+ if (pData && pData->decRef()) {
+ size_t numShaders = pData->getNumShaders();
+ for (size_t i = 0; i < numShaders; ++i) {
+ // changes the first one
+ detachShaderLocked(program, pData->getShader(0));
+ }
+ delete pData;
+ m_programs.erase(program);
+ }
if (m_shaderProgramIdMap.find(program) ==
m_shaderProgramIdMap.end()) return;
@@ -360,31 +447,43 @@
}
// No such thing for separable shader programs.
-void GLSharedGroup::attachShader(GLuint program, GLuint shader) {
-
+bool GLSharedGroup::attachShader(GLuint program, GLuint shader) {
android::AutoMutex _lock(m_lock);
ProgramData* pData = findObjectOrDefault(m_programs, program);
ShaderData* sData = findObjectOrDefault(m_shaders, shader);
+ bool res = false;
+
if (pData && sData) {
- if (pData->attachShader(shader)) {
+ res = pData->attachShader(shader, sData->shaderType);
+ if (res) {
refShaderDataLocked(shader);
}
}
+
+ return res;
}
-void GLSharedGroup::detachShader(GLuint program, GLuint shader) {
-
+bool GLSharedGroup::detachShader(GLuint program, GLuint shader) {
android::AutoMutex _lock(m_lock);
+ return detachShaderLocked(program, shader);
+}
+bool GLSharedGroup::detachShaderLocked(GLuint program, GLuint shader) {
ProgramData* pData = findObjectOrDefault(m_programs, program);
ShaderData* sData = findObjectOrDefault(m_shaders, shader);
+
+ bool res = false;
+
if (pData && sData) {
- if (pData->detachShader(shader)) {
+ res = pData->detachShader(shader);
+ if (res) {
unrefShaderDataLocked(shader);
}
}
+
+ return res;
}
// Not needed/used for separate shader programs.
@@ -509,6 +608,19 @@
return false;
}
+bool GLSharedGroup::isProgramUniformLocationValid(GLuint program, GLint location) {
+ if (location < 0) return false;
+
+ android::AutoMutex _lock(m_lock);
+
+ ProgramData* pData =
+ findObjectOrDefault(m_programs, program);
+
+ if (!pData) return false;
+
+ return pData->isValidUniformLocation(location);
+}
+
bool GLSharedGroup::isShader(GLuint shader) {
android::AutoMutex _lock(m_lock);
@@ -518,7 +630,7 @@
return pData != NULL;
}
-bool GLSharedGroup::addShaderData(GLuint shader) {
+bool GLSharedGroup::addShaderData(GLuint shader, GLenum shaderType) {
android::AutoMutex _lock(m_lock);
@@ -527,6 +639,7 @@
if (data) {
m_shaders[shader] = data;
data->refcount = 1;
+ data->shaderType = shaderType;
}
return data != NULL;
@@ -562,6 +675,21 @@
}
}
+ProgramData* GLSharedGroup::getProgramDataLocked(GLuint program) {
+ // Check the space of normal programs, then separable ones
+ ProgramData* pData = findObjectOrDefault(m_programs, program);
+
+ if (pData) return pData;
+
+ std::map<GLuint, uint32_t>::const_iterator it =
+ m_shaderProgramIdMap.find(program);
+ if (it == m_shaderProgramIdMap.end()) return NULL;
+
+ ShaderProgramData* spData = findObjectOrDefault(m_shaderPrograms, it->second);
+ if (!spData) return NULL;
+ return &spData->programData;
+}
+
uint32_t GLSharedGroup::addNewShaderProgramData() {
android::AutoMutex _lock(m_lock);
@@ -569,8 +697,6 @@
ShaderProgramData* data = new ShaderProgramData;
uint32_t currId = m_shaderProgramId;
- ALOGD("%s: new data %p id %u", __FUNCTION__, data, currId);
-
m_shaderPrograms[currId] = data;
m_shaderProgramId++;
return currId;
@@ -590,8 +716,6 @@
ShaderProgramData* res = findObjectOrDefault(m_shaderPrograms, id);
- ALOGD("%s: id=%u res=%p", __FUNCTION__, id, res);
-
return res;
}
@@ -630,9 +754,9 @@
m_shaderProgramIdMap.erase(shaderProgramName);
}
-void GLSharedGroup::initShaderProgramData(GLuint shaderProgram, GLuint numIndices) {
+void GLSharedGroup::initShaderProgramData(GLuint shaderProgram, GLuint numIndices, GLuint numAttributes) {
ShaderProgramData* spData = getShaderProgramData(shaderProgram);
- spData->programData.initProgramData(numIndices);
+ spData->programData.initProgramData(numIndices, numAttributes);
}
void GLSharedGroup::setShaderProgramIndexInfo(
@@ -662,3 +786,87 @@
}
}
}
+
+UniformValidationInfo GLSharedGroup::getUniformValidationInfo(GLuint program) {
+ UniformValidationInfo res;
+
+ android::AutoMutex _lock(m_lock);
+
+ ProgramData* pData =
+ getProgramDataLocked(program);
+
+ if (!pData) return res;
+
+ bool error; (void)error;
+ return pData->compileValidationInfo(&error);
+}
+
+void GLSharedGroup::setProgramLinkStatus(GLuint program, GLint linkStatus) {
+ android::AutoMutex _lock(m_lock);
+ ProgramData* pData =
+ getProgramDataLocked(program);
+ if (!pData) return;
+ pData->setLinkStatus(linkStatus);
+}
+
+GLint GLSharedGroup::getProgramLinkStatus(GLuint program) {
+ android::AutoMutex _lock(m_lock);
+ ProgramData* pData = getProgramDataLocked(program);
+ if (!pData) return 0;
+ return pData->getLinkStatus();
+}
+
+void GLSharedGroup::setActiveUniformBlockCountForProgram(GLuint program, GLint count) {
+ android::AutoMutex _lock(m_lock);
+ ProgramData* pData =
+ getProgramDataLocked(program);
+
+ if (!pData) return;
+
+ pData->setActiveUniformBlockCount(count);
+}
+
+GLint GLSharedGroup::getActiveUniformBlockCount(GLuint program) {
+ android::AutoMutex _lock(m_lock);
+ ProgramData* pData =
+ getProgramDataLocked(program);
+
+ if (!pData) return 0;
+
+ return pData->getActiveUniformBlockCount();
+}
+
+void GLSharedGroup::setTransformFeedbackVaryingsCountForProgram(GLuint program, GLint count) {
+ android::AutoMutex _lock(m_lock);
+ ProgramData* pData = getProgramDataLocked(program);
+ if (!pData) return;
+ pData->setTransformFeedbackVaryingsCount(count);
+}
+
+GLint GLSharedGroup::getTransformFeedbackVaryingsCountForProgram(GLuint program) {
+ android::AutoMutex _lock(m_lock);
+ ProgramData* pData = getProgramDataLocked(program);
+ if (!pData) return 0;
+ return pData->getTransformFeedbackVaryingsCount();
+}
+
+int GLSharedGroup::getActiveUniformsCountForProgram(GLuint program) {
+ android::AutoMutex _lock(m_lock);
+ ProgramData* pData =
+ getProgramDataLocked(program);
+
+ if (!pData) return 0;
+
+ return pData->getActiveUniformsCount();
+}
+
+int GLSharedGroup::getActiveAttributesCountForProgram(GLuint program) {
+ android::AutoMutex _lock(m_lock);
+ ProgramData* pData =
+ getProgramDataLocked(program);
+
+ if (!pData) return 0;
+
+ return pData->getActiveAttributesCount();
+}
+
diff --git a/shared/OpenglCodecCommon/GLSharedGroup.h b/shared/OpenglCodecCommon/GLSharedGroup.h
index bbee27a..b423951 100755
--- a/shared/OpenglCodecCommon/GLSharedGroup.h
+++ b/shared/OpenglCodecCommon/GLSharedGroup.h
@@ -40,6 +40,7 @@
#include "auto_goldfish_dma_context.h"
#include "IndexRangeCache.h"
#include "SmartPtr.h"
+#include "StateTrackingSupport.h"
struct BufferData {
BufferData();
@@ -76,10 +77,18 @@
} IndexInfo;
GLuint m_numIndexes;
+ GLuint m_numAttributes;
IndexInfo* m_Indexes;
bool m_initialized;
std::vector<GLuint> m_shaders;
+ std::vector<GLenum> m_shaderTypes;
+
+ uint32_t m_refcount;
+ GLint m_linkStatus;
+
+ uint32_t m_activeUniformBlockCount;
+ uint32_t m_transformFeedbackVaryingsCount;;
public:
enum {
@@ -87,21 +96,56 @@
};
ProgramData();
- void initProgramData(GLuint numIndexes);
+ void initProgramData(GLuint numIndexes, GLuint numAttributes);
bool isInitialized();
virtual ~ProgramData();
void setIndexInfo(GLuint index, GLint base, GLint size, GLenum type);
void setIndexFlags(GLuint index, GLuint flags);
GLuint getIndexForLocation(GLint location);
GLenum getTypeForLocation(GLint location);
+ bool isValidUniformLocation(GLint location);
GLint getNextSamplerUniform(GLint index, GLint* val, GLenum* target);
bool setSamplerUniform(GLint appLoc, GLint val, GLenum* target);
- bool attachShader(GLuint shader);
+ bool attachShader(GLuint shader, GLenum shaderType);
bool detachShader(GLuint shader);
size_t getNumShaders() const { return m_shaders.size(); }
GLuint getShader(size_t i) const { return m_shaders[i]; }
+
+ void incRef() { ++m_refcount; }
+ bool decRef() {
+ --m_refcount;
+ return 0 == m_refcount;
+ }
+
+ UniformValidationInfo compileValidationInfo(bool* error) const;
+ void setLinkStatus(GLint status) { m_linkStatus = status; }
+ GLint getLinkStatus() { return m_linkStatus; }
+
+ void setActiveUniformBlockCount(uint32_t count) {
+ m_activeUniformBlockCount = count;
+ }
+
+ uint32_t getActiveUniformBlockCount() const {
+ return m_activeUniformBlockCount;
+ }
+
+ void setTransformFeedbackVaryingsCount(uint32_t count) {
+ m_transformFeedbackVaryingsCount = count;
+ }
+
+ uint32_t getTransformFeedbackVaryingsCount() const {
+ return m_transformFeedbackVaryingsCount;
+ }
+
+ GLuint getActiveUniformsCount() const {
+ return m_numIndexes;
+ }
+
+ GLuint getActiveAttributesCount() const {
+ return m_numAttributes;
+ }
};
struct ShaderData {
@@ -109,6 +153,7 @@
StringList samplerExternalNames;
int refcount;
std::vector<std::string> sources;
+ GLenum shaderType;
};
class ShaderProgramData {
@@ -125,6 +170,8 @@
std::map<GLuint, ShaderData*> m_shaders;
std::map<uint32_t, ShaderProgramData*> m_shaderPrograms;
std::map<GLuint, uint32_t> m_shaderProgramIdMap;
+ RenderbufferInfo m_renderbufferInfo;
+ SamplerInfo m_samplerInfo;
mutable android::Mutex m_lock;
@@ -133,12 +180,15 @@
uint32_t m_shaderProgramId;
+ ProgramData* getProgramDataLocked(GLuint program);
public:
GLSharedGroup();
~GLSharedGroup();
bool isShaderOrProgramObject(GLuint obj);
BufferData * getBufferData(GLuint bufferId);
SharedTextureDataMap* getTextureData();
+ RenderbufferInfo* getRenderbufferInfo();
+ SamplerInfo* getSamplerInfo();
void addBufferData(GLuint bufferId, GLsizeiptr size, const void* data);
void updateBufferData(GLuint bufferId, GLsizeiptr size, const void* data);
void setBufferUsage(GLuint bufferId, GLenum usage);
@@ -151,17 +201,22 @@
bool isProgram(GLuint program);
bool isProgramInitialized(GLuint program);
void addProgramData(GLuint program);
- void initProgramData(GLuint program, GLuint numIndexes);
- void attachShader(GLuint program, GLuint shader);
- void detachShader(GLuint program, GLuint shader);
+ void initProgramData(GLuint program, GLuint numIndexes, GLuint numAttributes);
+ void refProgramData(GLuint program);
+ void onUseProgram(GLuint previous, GLuint next);
+ bool attachShader(GLuint program, GLuint shader);
+ bool detachShader(GLuint program, GLuint shader);
+ bool detachShaderLocked(GLuint program, GLuint shader);
void deleteProgramData(GLuint program);
+ void deleteProgramDataLocked(GLuint program);
void setProgramIndexInfo(GLuint program, GLuint index, GLint base, GLint size, GLenum type, const char* name);
GLenum getProgramUniformType(GLuint program, GLint location);
GLint getNextSamplerUniform(GLuint program, GLint index, GLint* val, GLenum* target) const;
bool setSamplerUniform(GLuint program, GLint appLoc, GLint val, GLenum* target);
+ bool isProgramUniformLocationValid(GLuint program, GLint location);
bool isShader(GLuint shader);
- bool addShaderData(GLuint shader);
+ bool addShaderData(GLuint shader, GLenum shaderType);
// caller must hold a reference to the shader as long as it holds the pointer
ShaderData* getShaderData(GLuint shader);
void unrefShaderData(GLuint shader);
@@ -173,8 +228,23 @@
ShaderProgramData* getShaderProgramData(GLuint shaderProgramName);
void deleteShaderProgramDataById(uint32_t id);
void deleteShaderProgramData(GLuint shaderProgramName);
- void initShaderProgramData(GLuint shaderProgram, GLuint numIndices);
+ void initShaderProgramData(GLuint shaderProgram, GLuint numIndices, GLuint numAttributes);
void setShaderProgramIndexInfo(GLuint shaderProgram, GLuint index, GLint base, GLint size, GLenum type, const char* name);
+
+ // Validation info
+ UniformValidationInfo getUniformValidationInfo(GLuint program);
+
+ void setProgramLinkStatus(GLuint program, GLint linkStatus);
+ GLint getProgramLinkStatus(GLuint program);
+
+ void setActiveUniformBlockCountForProgram(GLuint program, GLint numBlocks);
+ GLint getActiveUniformBlockCount(GLuint program);
+
+ void setTransformFeedbackVaryingsCountForProgram(GLuint program, GLint count);
+ GLint getTransformFeedbackVaryingsCountForProgram(GLuint program);
+
+ int getActiveUniformsCountForProgram(GLuint program);
+ int getActiveAttributesCountForProgram(GLuint program);
};
typedef SmartPtr<GLSharedGroup> GLSharedGroupPtr;
diff --git a/shared/OpenglCodecCommon/StateTrackingSupport.h b/shared/OpenglCodecCommon/StateTrackingSupport.h
index c9abf1c..c6715c4 100644
--- a/shared/OpenglCodecCommon/StateTrackingSupport.h
+++ b/shared/OpenglCodecCommon/StateTrackingSupport.h
@@ -17,21 +17,24 @@
#define _STATE_TRACKING_SUPPORT_H_
#include "android/base/containers/HybridComponentManager.h"
+#include "android/base/synchronization/AndroidLock.h"
-template <bool initialIsTrue>
+#include <GLES2/gl2.h>
+
+template <class IndexType, bool initialIsTrue>
class PredicateMap {
public:
static const uint64_t kBitsPerEntry = 64;
- void add(uint32_t objId) {
+ void add(IndexType objId) {
static const uint64_t kNone = 0ULL;
static const uint64_t kAll = ~kNone;
- uint32_t index = objId / kBitsPerEntry;
+ IndexType index = objId / kBitsPerEntry;
if (!mStorage.get_const(index)) {
mStorage.add(index, initialIsTrue ? kAll : kNone);
}
}
- void remove(uint32_t objId) {
+ void remove(IndexType objId) {
if (initialIsTrue) {
set(objId, true);
} else {
@@ -39,8 +42,8 @@
}
}
- void set(uint32_t objId, bool predicate) {
- uint32_t index = objId / kBitsPerEntry;
+ void set(IndexType objId, bool predicate) {
+ IndexType index = objId / kBitsPerEntry;
if (!mStorage.get_const(index)) return;
@@ -55,8 +58,8 @@
}
}
- bool get(uint32_t objId) const {
- uint32_t index = objId / kBitsPerEntry;
+ bool get(IndexType objId) const {
+ IndexType index = objId / kBitsPerEntry;
const uint64_t* current = mStorage.get_const(index);
@@ -67,8 +70,170 @@
}
private:
- using Storage = android::base::HybridComponentManager<10000, uint32_t, uint64_t>;
+ using Storage = android::base::HybridComponentManager<10000, IndexType, uint64_t>;
Storage mStorage;
};
+// A structure for fast validation of uniform uploads and other uniform related api calls.
+struct UniformLocationInfo {
+ bool valid = false;
+ uint32_t columns;
+ uint32_t rows;
+ bool isSampler;
+ bool isInt;
+ bool isArray;
+ bool isUnsigned;
+ bool isBool;
+};
+
+using UniformValidationInfo = android::base::HybridComponentManager<1000, uint32_t, UniformLocationInfo>;
+
+using LastQueryTargetInfo = android::base::HybridComponentManager<1000, uint32_t, uint32_t>;
+
+using ExistenceMap = PredicateMap<uint32_t, false>;
+
+struct RboProps {
+ GLenum format;
+ GLsizei multisamples;
+ GLsizei width;
+ GLsizei height;
+ bool previouslyBound;
+ uint32_t refcount;
+ bool boundEGLImage;
+};
+
+struct SamplerProps {
+ uint32_t refcount;
+};
+
+template <class T>
+class ScopedLockedView {
+public:
+ ScopedLockedView(T* info) : mInfo(info) {
+ mInfo->lock();
+ }
+ virtual ~ScopedLockedView() {
+ mInfo->unlock();
+ }
+protected:
+ T* mInfo;
+
+ T* internalInfo() { return mInfo; }
+ const T* internalInfo_const() const { return mInfo; }
+};
+
+struct RenderbufferInfo {
+ android::base::guest::Lock infoLock;
+ android::base::HybridComponentManager<1000, uint32_t, RboProps> component;
+
+ void lock() { infoLock.lock(); }
+ void unlock() { infoLock.unlock(); }
+
+ class ScopedView : public ScopedLockedView<RenderbufferInfo> {
+ public:
+ ScopedView(RenderbufferInfo* info) : ScopedLockedView<RenderbufferInfo>(info) { }
+ bool hasRbo(GLuint id) const {
+ const RboProps* info = internalInfo_const()->component.get_const(id);
+ if (!info) return false;
+ return 0 != info->refcount;
+ }
+ virtual ~ScopedView() = default;
+ RboProps* get(GLuint id) {
+ return internalInfo()->component.get(id);
+ }
+ const RboProps* get_const(GLuint id) {
+ return internalInfo_const()->component.get_const(id);
+ }
+ void addFresh(GLuint id) {
+ RboProps props;
+ props.format = GL_NONE;
+ props.multisamples = 0;
+ props.width = 0;
+ props.height = 0;
+ props.previouslyBound = false;
+ props.refcount = 1;
+ props.boundEGLImage = false;
+ internalInfo()->component.add(id, props);
+ }
+ RboProps* bind(GLuint id) {
+ if (!hasRbo(id)) addFresh(id);
+ ref(id);
+ RboProps* res = get(id);
+ res->previouslyBound = true;
+ return res;
+ }
+ void ref(GLuint id) {
+ RboProps* props = get(id);
+ if (!props) return;
+ ++props->refcount;
+ }
+ bool unref(GLuint id) {
+ RboProps* props = get(id);
+ if (!props) return false;
+ if (!props->refcount) return false;
+ --props->refcount;
+ bool gone = 0 == props->refcount;
+ if (gone) {
+ props->format = 0;
+ props->multisamples = 0;
+ props->width = 0;
+ props->height = 0;
+ props->previouslyBound = false;
+ props->boundEGLImage = false;
+ }
+ return gone;
+ }
+ };
+};
+
+struct SamplerInfo {
+ android::base::guest::Lock infoLock;
+ android::base::HybridComponentManager<1000, uint32_t, SamplerProps> component;
+
+ void lock() { infoLock.lock(); }
+ void unlock() { infoLock.unlock(); }
+
+ class ScopedView : public ScopedLockedView<SamplerInfo> {
+ public:
+ ScopedView(SamplerInfo* info) : ScopedLockedView<SamplerInfo>(info) { }
+ bool samplerExists(GLuint id) const {
+ const SamplerProps* info = internalInfo_const()->component.get_const(id);
+ if (!info) return false;
+ return 0 != info->refcount;
+ }
+ virtual ~ScopedView() = default;
+ SamplerProps* get(GLuint id) {
+ return internalInfo()->component.get(id);
+ }
+ const SamplerProps* get_const(GLuint id) {
+ return internalInfo_const()->component.get_const(id);
+ }
+ void addFresh(GLuint id) {
+ SamplerProps props;
+ props.refcount = 1;
+ internalInfo()->component.add(id, props);
+ }
+ SamplerProps* bind(GLuint id) {
+ if (!samplerExists(id)) return 0;
+ ref(id);
+ SamplerProps* res = get(id);
+ return res;
+ }
+ void ref(GLuint id) {
+ SamplerProps* props = get(id);
+ if (!props) return;
+ ++props->refcount;
+ }
+ bool unref(GLuint id) {
+ SamplerProps* props = get(id);
+ if (!props) return false;
+ if (!props->refcount) return false;
+ --props->refcount;
+ bool gone = 0 == props->refcount;
+ return gone;
+ }
+ };
+};
+
+
#endif
diff --git a/shared/OpenglCodecCommon/TextureSharedData.h b/shared/OpenglCodecCommon/TextureSharedData.h
index 1372f7a..96827ef 100644
--- a/shared/OpenglCodecCommon/TextureSharedData.h
+++ b/shared/OpenglCodecCommon/TextureSharedData.h
@@ -35,6 +35,13 @@
TextureDims* dims;
bool immutable;
bool boundEGLImage;
+ bool hasStorage;
+ bool hasCubeNegX;
+ bool hasCubePosX;
+ bool hasCubeNegY;
+ bool hasCubePosY;
+ bool hasCubeNegZ;
+ bool hasCubePosZ;
};
typedef std::map<GLuint, TextureRec*> SharedTextureDataMap;
diff --git a/shared/OpenglCodecCommon/astc-codec.h b/shared/OpenglCodecCommon/astc-codec.h
new file mode 100644
index 0000000..e15fe47
--- /dev/null
+++ b/shared/OpenglCodecCommon/astc-codec.h
@@ -0,0 +1,47 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+#ifndef ASTC_CODEC_ASTC_CODEC_H_
+#define ASTC_CODEC_ASTC_CODEC_H_
+
+namespace astc_codec {
+
+// These are the valid ASTC footprints according to the specification in
+// Section C.2.7.
+enum FootprintType {
+ k4x4,
+ k5x4,
+ k5x5,
+ k6x5,
+ k6x6,
+ k8x5,
+ k8x6,
+ k10x5,
+ k10x6,
+ k8x8,
+ k10x8,
+ k10x10,
+ k12x10,
+ k12x12,
+
+ kCount
+};
+
+struct AstcFootprint {
+ AstcFootprint(int width, int height);
+};
+
+} // namespace astc_codec
+
+#endif // ASTC_CODEC_ASTC_CODEC_H_
diff --git a/shared/OpenglCodecCommon/etc.cpp b/shared/OpenglCodecCommon/etc.cpp
new file mode 100644
index 0000000..dea3228
--- /dev/null
+++ b/shared/OpenglCodecCommon/etc.cpp
@@ -0,0 +1,1031 @@
+// Copyright 2009 Google Inc.
+//
+// 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 "etc.h"
+
+#include <algorithm>
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+
+typedef uint16_t etc1_uint16;
+
+/* From http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
+
+ The number of bits that represent a 4x4 texel block is 64 bits if
+ <internalformat> is given by ETC1_RGB8_OES.
+
+ The data for a block is a number of bytes,
+
+ {q0, q1, q2, q3, q4, q5, q6, q7}
+
+ where byte q0 is located at the lowest memory address and q7 at
+ the highest. The 64 bits specifying the block is then represented
+ by the following 64 bit integer:
+
+ int64bit = 256*(256*(256*(256*(256*(256*(256*q0+q1)+q2)+q3)+q4)+q5)+q6)+q7;
+
+ ETC1_RGB8_OES:
+
+ a) bit layout in bits 63 through 32 if diffbit = 0
+
+ 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
+ -----------------------------------------------
+ | base col1 | base col2 | base col1 | base col2 |
+ | R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)|
+ -----------------------------------------------
+
+ 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
+ ---------------------------------------------------
+ | base col1 | base col2 | table | table |diff|flip|
+ | B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit |
+ ---------------------------------------------------
+
+
+ b) bit layout in bits 63 through 32 if diffbit = 1
+
+ 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
+ -----------------------------------------------
+ | base col1 | dcol 2 | base col1 | dcol 2 |
+ | R1' (5 bits) | dR2 | G1' (5 bits) | dG2 |
+ -----------------------------------------------
+
+ 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
+ ---------------------------------------------------
+ | base col 1 | dcol 2 | table | table |diff|flip|
+ | B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit |
+ ---------------------------------------------------
+
+
+ c) bit layout in bits 31 through 0 (in both cases)
+
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
+ -----------------------------------------------
+ | most significant pixel index bits |
+ | p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a|
+ -----------------------------------------------
+
+ 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ --------------------------------------------------
+ | least significant pixel index bits |
+ | p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a |
+ --------------------------------------------------
+
+
+ Add table 3.17.2: Intensity modifier sets for ETC1 compressed textures:
+
+ table codeword modifier table
+ ------------------ ----------------------
+ 0 -8 -2 2 8
+ 1 -17 -5 5 17
+ 2 -29 -9 9 29
+ 3 -42 -13 13 42
+ 4 -60 -18 18 60
+ 5 -80 -24 24 80
+ 6 -106 -33 33 106
+ 7 -183 -47 47 183
+
+
+ Add table 3.17.3 Mapping from pixel index values to modifier values for
+ ETC1 compressed textures:
+
+ pixel index value
+ ---------------
+ msb lsb resulting modifier value
+ ----- ----- -------------------------
+ 1 1 -b (large negative value)
+ 1 0 -a (small negative value)
+ 0 0 a (small positive value)
+ 0 1 b (large positive value)
+
+ ETC2 codec:
+ from https://www.khronos.org/registry/gles/specs/3.0/es_spec_3.0.4.pdf
+ page 289
+ */
+
+static const int kRGBModifierTable[] = {
+/* 0 */2, 8, -2, -8,
+/* 1 */5, 17, -5, -17,
+/* 2 */9, 29, -9, -29,
+/* 3 */13, 42, -13, -42,
+/* 4 */18, 60, -18, -60,
+/* 5 */24, 80, -24, -80,
+/* 6 */33, 106, -33, -106,
+/* 7 */47, 183, -47, -183 };
+
+static const int kRGBOpaqueModifierTable[] = {
+/* 0 */0, 8, 0, -8,
+/* 1 */0, 17, 0, -17,
+/* 2 */0, 29, 0, -29,
+/* 3 */0, 42, 0, -42,
+/* 4 */0, 60, 0, -60,
+/* 5 */0, 80, 0, -80,
+/* 6 */0, 106, 0, -106,
+/* 7 */0, 183, 0, -183 };
+
+static const int kAlphaModifierTable[] = {
+/* 0 */ -3, -6, -9, -15, 2, 5, 8, 14,
+/* 1 */ -3, -7, -10, -13, 2, 6, 9, 12,
+/* 2 */ -2, -5, -8, -13, 1, 4, 7, 12,
+/* 3 */ -2, -4, -6, -13, 1, 3, 5, 12,
+/* 4 */ -3, -6, -8, -12, 2, 5, 7, 11,
+/* 5 */ -3, -7, -9, -11, 2, 6, 8, 10,
+/* 6 */ -4, -7, -8, -11, 3, 6, 7, 10,
+/* 7 */ -3, -5, -8, -11, 2, 4, 7, 10,
+/* 8 */ -2, -6, -8, -10, 1, 5, 7, 9,
+/* 9 */ -2, -5, -8, -10, 1, 4, 7, 9,
+/* 10 */ -2, -4, -8, -10, 1, 3, 7, 9,
+/* 11 */ -2, -5, -7, -10, 1, 4, 6, 9,
+/* 12 */ -3, -4, -7, -10, 2, 3, 6, 9,
+/* 13 */ -1, -2, -3, -10, 0, 1, 2, 9,
+/* 14 */ -4, -6, -8, -9, 3, 5, 7, 8,
+/* 15 */ -3, -5, -7, -9, 2, 4, 6, 8
+};
+
+static const int kLookup[8] = { 0, 1, 2, 3, -4, -3, -2, -1 };
+
+static inline int clamp(int x) {
+ return (x >= 0 ? (x < 255 ? x : 255) : 0);
+}
+
+static inline int clamp2047(int x) {
+ return (x >= 0 ? (x < 2047 ? x : 2047) : 0);
+}
+
+static inline int clampSigned1023(int x) {
+ return (x >= -1023 ? (x < 1023 ? x : 1023) : -1023);
+}
+
+static
+inline int convert4To8(int b) {
+ int c = b & 0xf;
+ return (c << 4) | c;
+}
+
+static
+inline int convert5To8(int b) {
+ int c = b & 0x1f;
+ return (c << 3) | (c >> 2);
+}
+
+static
+inline int convert6To8(int b) {
+ int c = b & 0x3f;
+ return (c << 2) | (c >> 4);
+}
+
+static
+inline int convert7To8(int b) {
+ int c = b & 0x7f;
+ return (c << 1) | (c >> 6);
+}
+
+static
+inline int divideBy255(int d) {
+ return (d + 128 + (d >> 8)) >> 8;
+}
+
+static
+inline int convert8To4(int b) {
+ int c = b & 0xff;
+ return divideBy255(c * 15);
+}
+
+static
+inline int convert8To5(int b) {
+ int c = b & 0xff;
+ return divideBy255(c * 31);
+}
+
+static
+inline int convertDiff(int base, int diff) {
+ return convert5To8((0x1f & base) + kLookup[0x7 & diff]);
+}
+static
+int isOverflowed(int base, int diff) {
+ int val = (0x1f & base) + kLookup[0x7 & diff];
+ return val < 0 || val >= 32;
+}
+
+static
+void decode_subblock(etc1_byte* pOut, int r, int g, int b, const int* table,
+ etc1_uint32 low, bool second, bool flipped, bool isPunchthroughAlpha,
+ bool opaque) {
+ int baseX = 0;
+ int baseY = 0;
+ int channels = isPunchthroughAlpha ? 4 : 3;
+ if (second) {
+ if (flipped) {
+ baseY = 2;
+ } else {
+ baseX = 2;
+ }
+ }
+ for (int i = 0; i < 8; i++) {
+ int x, y;
+ if (flipped) {
+ x = baseX + (i >> 1);
+ y = baseY + (i & 1);
+ } else {
+ x = baseX + (i >> 2);
+ y = baseY + (i & 3);
+ }
+ int k = y + (x * 4);
+ int msb = ((low >> (k + 15)) & 2);
+ int lsb = ((low >> k) & 1);
+ etc1_byte* q = pOut + channels * (x + 4 * y);
+ if (isPunchthroughAlpha && !opaque && msb && !lsb) {
+ // rgba all 0
+ memset(q, 0, 4);
+ q += 4;
+ } else {
+ int offset = lsb | msb;
+ int delta = table[offset];
+ *q++ = clamp(r + delta);
+ *q++ = clamp(g + delta);
+ *q++ = clamp(b + delta);
+ if (isPunchthroughAlpha) {
+ *q++ = 255;
+ }
+ }
+ }
+}
+
+static void etc2_T_H_index(const int* clrTable, etc1_uint32 low,
+ bool isPunchthroughAlpha, bool opaque,
+ etc1_byte* pOut) {
+ etc1_byte* q = pOut;
+ for (int y = 0; y < 4; y++) {
+ for (int x = 0; x < 4; x++) {
+ int k = y + x * 4;
+ int msb = (low >> (k + 15)) & 2;
+ int lsb = (low >> k) & 1;
+ if (isPunchthroughAlpha && !opaque && msb && !lsb) {
+ // rgba all 0
+ memset(q, 0, 4);
+ q += 4;
+ } else {
+ int offset = lsb | msb;
+ for (int c = 0; c < 3; c++) {
+ *q++ = clrTable[offset*3 + c];
+ }
+ if (isPunchthroughAlpha) {
+ *q++ = 255;
+ }
+ }
+ }
+ }
+}
+
+// ETC2 codec:
+// from https://www.khronos.org/registry/gles/specs/3.0/es_spec_3.0.4.pdf
+// page 289
+
+static void etc2_decode_block_T(etc1_uint32 high, etc1_uint32 low,
+ bool isPunchthroughAlpha, bool opaque, etc1_byte* pOut) {
+ const int LUT[] = {3, 6, 11, 16, 23, 32, 41, 64};
+ int r1, r2, g1, g2, b1, b2;
+ r1 = convert4To8((((high >> 27) & 3) << 2) | ((high >> 24) & 3));
+ g1 = convert4To8(high >> 20);
+ b1 = convert4To8(high >> 16);
+ r2 = convert4To8(high >> 12);
+ g2 = convert4To8(high >> 8);
+ b2 = convert4To8(high >> 4);
+ // 3 bits intense modifier
+ int intenseIdx = (((high >> 2) & 3) << 1) | (high & 1);
+ int intenseMod = LUT[intenseIdx];
+ int clrTable[12];
+ clrTable[0] = r1;
+ clrTable[1] = g1;
+ clrTable[2] = b1;
+ clrTable[3] = clamp(r2 + intenseMod);
+ clrTable[4] = clamp(g2 + intenseMod);
+ clrTable[5] = clamp(b2 + intenseMod);
+ clrTable[6] = r2;
+ clrTable[7] = g2;
+ clrTable[8] = b2;
+ clrTable[9] = clamp(r2 - intenseMod);
+ clrTable[10] = clamp(g2 - intenseMod);
+ clrTable[11] = clamp(b2 - intenseMod);
+ etc2_T_H_index(clrTable, low, isPunchthroughAlpha, opaque, pOut);
+}
+
+static void etc2_decode_block_H(etc1_uint32 high, etc1_uint32 low,
+ bool isPunchthroughAlpha, bool opaque, etc1_byte* pOut) {
+ const int LUT[] = {3, 6, 11, 16, 23, 32, 41, 64};
+ int r1, r2, g1, g2, b1, b2;
+ r1 = convert4To8(high >> 27);
+ g1 = convert4To8((high >> 24) << 1 | ((high >> 20) & 1));
+ b1 = convert4To8((high >> 19) << 3 | ((high >> 15) & 7));
+ r2 = convert4To8(high >> 11);
+ g2 = convert4To8(high >> 7);
+ b2 = convert4To8(high >> 3);
+ // 3 bits intense modifier
+ int intenseIdx = high & 4;
+ intenseIdx |= (high & 1) << 1;
+ intenseIdx |= (((r1 << 16) | (g1 << 8) | b1) >= ((r2 << 16) | (g2 << 8) | b2));
+ int intenseMod = LUT[intenseIdx];
+ int clrTable[12];
+ clrTable[0] = clamp(r1 + intenseMod);
+ clrTable[1] = clamp(g1 + intenseMod);
+ clrTable[2] = clamp(b1 + intenseMod);
+ clrTable[3] = clamp(r1 - intenseMod);
+ clrTable[4] = clamp(g1 - intenseMod);
+ clrTable[5] = clamp(b1 - intenseMod);
+ clrTable[6] = clamp(r2 + intenseMod);
+ clrTable[7] = clamp(g2 + intenseMod);
+ clrTable[8] = clamp(b2 + intenseMod);
+ clrTable[9] = clamp(r2 - intenseMod);
+ clrTable[10] = clamp(g2 - intenseMod);
+ clrTable[11] = clamp(b2 - intenseMod);
+ etc2_T_H_index(clrTable, low, isPunchthroughAlpha, opaque, pOut);
+}
+
+static void etc2_decode_block_P(etc1_uint32 high, etc1_uint32 low,
+ bool isPunchthroughAlpha, etc1_byte* pOut) {
+ int ro, go, bo, rh, gh, bh, rv, gv, bv;
+ uint64_t data = high;
+ data = data << 32 | low;
+ ro = convert6To8(data >> 57);
+ go = convert7To8((data >> 56 << 6) | ((data >> 49) & 63));
+ bo = convert6To8((data >> 48 << 5)
+ | (((data >> 43) & 3 ) << 3)
+ | ((data >> 39) & 7));
+ rh = convert6To8((data >> 34 << 1) | ((data >> 32) & 1));
+ gh = convert7To8(data >> 25);
+ bh = convert6To8(data >> 19);
+ rv = convert6To8(data >> 13);
+ gv = convert7To8(data >> 6);
+ bv = convert6To8(data);
+ etc1_byte* q = pOut;
+ for (int i = 0; i < 16; i++) {
+ int y = i >> 2;
+ int x = i & 3;
+ *q++ = clamp((x * (rh - ro) + y * (rv - ro) + 4 * ro + 2) >> 2);
+ *q++ = clamp((x * (gh - go) + y * (gv - go) + 4 * go + 2) >> 2);
+ *q++ = clamp((x * (bh - bo) + y * (bv - bo) + 4 * bo + 2) >> 2);
+ if (isPunchthroughAlpha) *q++ = 255;
+ }
+}
+
+// Input is an ETC1 / ETC2 compressed version of the data.
+// Output is a 4 x 4 square of 3-byte pixels in form R, G, B
+// ETC2 codec:
+// from https://www.khronos.org/registry/gles/specs/3.0/es_spec_3.0.4.pdf
+// page 289
+
+void etc2_decode_rgb_block(const etc1_byte* pIn, bool isPunchthroughAlpha,
+ etc1_byte* pOut) {
+ etc1_uint32 high = (pIn[0] << 24) | (pIn[1] << 16) | (pIn[2] << 8) | pIn[3];
+ etc1_uint32 low = (pIn[4] << 24) | (pIn[5] << 16) | (pIn[6] << 8) | pIn[7];
+ bool opaque = (high >> 1) & 1;
+ int r1, r2, g1, g2, b1, b2;
+ if (isPunchthroughAlpha || high & 2) {
+ // differential
+ int rBase = high >> 27;
+ int gBase = high >> 19;
+ int bBase = high >> 11;
+ if (isOverflowed(rBase, high >> 24)) {
+ etc2_decode_block_T(high, low, isPunchthroughAlpha, opaque, pOut);
+ return;
+ }
+ if (isOverflowed(gBase, high >> 16)) {
+ etc2_decode_block_H(high, low, isPunchthroughAlpha, opaque, pOut);
+ return;
+ }
+ if (isOverflowed(bBase, high >> 8)) {
+ etc2_decode_block_P(high, low, isPunchthroughAlpha, pOut);
+ return;
+ }
+ r1 = convert5To8(rBase);
+ r2 = convertDiff(rBase, high >> 24);
+ g1 = convert5To8(gBase);
+ g2 = convertDiff(gBase, high >> 16);
+ b1 = convert5To8(bBase);
+ b2 = convertDiff(bBase, high >> 8);
+ } else {
+ // not differential
+ r1 = convert4To8(high >> 28);
+ r2 = convert4To8(high >> 24);
+ g1 = convert4To8(high >> 20);
+ g2 = convert4To8(high >> 16);
+ b1 = convert4To8(high >> 12);
+ b2 = convert4To8(high >> 8);
+ }
+ int tableIndexA = 7 & (high >> 5);
+ int tableIndexB = 7 & (high >> 2);
+ const int* rgbModifierTable = opaque || !isPunchthroughAlpha ?
+ kRGBModifierTable : kRGBOpaqueModifierTable;
+ const int* tableA = rgbModifierTable + tableIndexA * 4;
+ const int* tableB = rgbModifierTable + tableIndexB * 4;
+ bool flipped = (high & 1) != 0;
+ decode_subblock(pOut, r1, g1, b1, tableA, low, false, flipped,
+ isPunchthroughAlpha, opaque);
+ decode_subblock(pOut, r2, g2, b2, tableB, low, true, flipped,
+ isPunchthroughAlpha, opaque);
+}
+
+void eac_decode_single_channel_block(const etc1_byte* pIn,
+ int decodedElementBytes, bool isSigned,
+ etc1_byte* pOut) {
+ assert(decodedElementBytes == 1 || decodedElementBytes == 2 || decodedElementBytes == 4);
+ int base_codeword = isSigned ? reinterpret_cast<const char*>(pIn)[0]
+ : pIn[0];
+ if (base_codeword == -128) base_codeword = -127;
+ int multiplier = pIn[1] >> 4;
+ int tblIdx = pIn[1] & 15;
+ const int* table = kAlphaModifierTable + tblIdx * 8;
+ const etc1_byte* p = pIn + 2;
+ // position in a byte of the next 3-bit index:
+ // | a a a | b b b | c c c | d d d ...
+ // | byte | byte...
+ int bitOffset = 5;
+ for (int i = 0; i < 16; i ++) {
+ // flip x, y in output
+ int outIdx = (i % 4) * 4 + i / 4;
+ etc1_byte* q = pOut + outIdx * decodedElementBytes;
+
+ int modifier = 0;
+ if (bitOffset < 0) { // (Part of) the index is in the next byte.
+ modifier += p[0] << (-bitOffset);
+ p ++;
+ bitOffset += 8;
+ }
+ modifier += p[0] >> bitOffset;
+ modifier &= 7;
+ bitOffset -= 3; // move to the next index
+ if (bitOffset == -3) {
+ bitOffset = 5;
+ p++;
+ }
+ int modifierValue = table[modifier];
+ int decoded = base_codeword + modifierValue * multiplier;
+ if (decodedElementBytes == 1) {
+ *q = clamp(decoded);
+ } else { // decodedElementBytes == 4
+ decoded *= 8;
+ if (multiplier == 0) {
+ decoded += modifierValue;
+ }
+ if (isSigned) {
+ decoded = clampSigned1023(decoded);
+ reinterpret_cast<float*>(q)[0] = (float)decoded / 1023.0;
+ } else {
+ decoded += 4;
+ decoded = clamp2047(decoded);
+ reinterpret_cast<float*>(q)[0] = (float)decoded / 2047.0;
+ }
+ }
+ }
+}
+
+typedef struct {
+ etc1_uint32 high;
+ etc1_uint32 low;
+ etc1_uint32 score; // Lower is more accurate
+} etc_compressed;
+
+static
+inline void take_best(etc_compressed* a, const etc_compressed* b) {
+ if (a->score > b->score) {
+ *a = *b;
+ }
+}
+
+static
+void etc_average_colors_subblock(const etc1_byte* pIn, etc1_uint32 inMask,
+ etc1_byte* pColors, bool flipped, bool second) {
+ int r = 0;
+ int g = 0;
+ int b = 0;
+
+ if (flipped) {
+ int by = 0;
+ if (second) {
+ by = 2;
+ }
+ for (int y = 0; y < 2; y++) {
+ int yy = by + y;
+ for (int x = 0; x < 4; x++) {
+ int i = x + 4 * yy;
+ if (inMask & (1 << i)) {
+ const etc1_byte* p = pIn + i * 3;
+ r += *(p++);
+ g += *(p++);
+ b += *(p++);
+ }
+ }
+ }
+ } else {
+ int bx = 0;
+ if (second) {
+ bx = 2;
+ }
+ for (int y = 0; y < 4; y++) {
+ for (int x = 0; x < 2; x++) {
+ int xx = bx + x;
+ int i = xx + 4 * y;
+ if (inMask & (1 << i)) {
+ const etc1_byte* p = pIn + i * 3;
+ r += *(p++);
+ g += *(p++);
+ b += *(p++);
+ }
+ }
+ }
+ }
+ pColors[0] = (etc1_byte)((r + 4) >> 3);
+ pColors[1] = (etc1_byte)((g + 4) >> 3);
+ pColors[2] = (etc1_byte)((b + 4) >> 3);
+}
+
+static
+inline int square(int x) {
+ return x * x;
+}
+
+static etc1_uint32 chooseModifier(const etc1_byte* pBaseColors,
+ const etc1_byte* pIn, etc1_uint32 *pLow, int bitIndex,
+ const int* pModifierTable) {
+ etc1_uint32 bestScore = ~0;
+ int bestIndex = 0;
+ int pixelR = pIn[0];
+ int pixelG = pIn[1];
+ int pixelB = pIn[2];
+ int r = pBaseColors[0];
+ int g = pBaseColors[1];
+ int b = pBaseColors[2];
+ for (int i = 0; i < 4; i++) {
+ int modifier = pModifierTable[i];
+ int decodedG = clamp(g + modifier);
+ etc1_uint32 score = (etc1_uint32) (6 * square(decodedG - pixelG));
+ if (score >= bestScore) {
+ continue;
+ }
+ int decodedR = clamp(r + modifier);
+ score += (etc1_uint32) (3 * square(decodedR - pixelR));
+ if (score >= bestScore) {
+ continue;
+ }
+ int decodedB = clamp(b + modifier);
+ score += (etc1_uint32) square(decodedB - pixelB);
+ if (score < bestScore) {
+ bestScore = score;
+ bestIndex = i;
+ }
+ }
+ etc1_uint32 lowMask = (((bestIndex >> 1) << 16) | (bestIndex & 1))
+ << bitIndex;
+ *pLow |= lowMask;
+ return bestScore;
+}
+
+static
+void etc_encode_subblock_helper(const etc1_byte* pIn, etc1_uint32 inMask,
+ etc_compressed* pCompressed, bool flipped, bool second,
+ const etc1_byte* pBaseColors, const int* pModifierTable) {
+ int score = pCompressed->score;
+ if (flipped) {
+ int by = 0;
+ if (second) {
+ by = 2;
+ }
+ for (int y = 0; y < 2; y++) {
+ int yy = by + y;
+ for (int x = 0; x < 4; x++) {
+ int i = x + 4 * yy;
+ if (inMask & (1 << i)) {
+ score += chooseModifier(pBaseColors, pIn + i * 3,
+ &pCompressed->low, yy + x * 4, pModifierTable);
+ }
+ }
+ }
+ } else {
+ int bx = 0;
+ if (second) {
+ bx = 2;
+ }
+ for (int y = 0; y < 4; y++) {
+ for (int x = 0; x < 2; x++) {
+ int xx = bx + x;
+ int i = xx + 4 * y;
+ if (inMask & (1 << i)) {
+ score += chooseModifier(pBaseColors, pIn + i * 3,
+ &pCompressed->low, y + xx * 4, pModifierTable);
+ }
+ }
+ }
+ }
+ pCompressed->score = score;
+}
+
+static bool inRange4bitSigned(int color) {
+ return color >= -4 && color <= 3;
+}
+
+static void etc_encodeBaseColors(etc1_byte* pBaseColors,
+ const etc1_byte* pColors, etc_compressed* pCompressed) {
+ int r1, g1, b1, r2, g2, b2; // 8 bit base colors for sub-blocks
+ bool differential;
+ {
+ int r51 = convert8To5(pColors[0]);
+ int g51 = convert8To5(pColors[1]);
+ int b51 = convert8To5(pColors[2]);
+ int r52 = convert8To5(pColors[3]);
+ int g52 = convert8To5(pColors[4]);
+ int b52 = convert8To5(pColors[5]);
+
+ r1 = convert5To8(r51);
+ g1 = convert5To8(g51);
+ b1 = convert5To8(b51);
+
+ int dr = r52 - r51;
+ int dg = g52 - g51;
+ int db = b52 - b51;
+
+ differential = inRange4bitSigned(dr) && inRange4bitSigned(dg)
+ && inRange4bitSigned(db);
+ if (differential) {
+ r2 = convert5To8(r51 + dr);
+ g2 = convert5To8(g51 + dg);
+ b2 = convert5To8(b51 + db);
+ pCompressed->high |= (r51 << 27) | ((7 & dr) << 24) | (g51 << 19)
+ | ((7 & dg) << 16) | (b51 << 11) | ((7 & db) << 8) | 2;
+ }
+ }
+
+ if (!differential) {
+ int r41 = convert8To4(pColors[0]);
+ int g41 = convert8To4(pColors[1]);
+ int b41 = convert8To4(pColors[2]);
+ int r42 = convert8To4(pColors[3]);
+ int g42 = convert8To4(pColors[4]);
+ int b42 = convert8To4(pColors[5]);
+ r1 = convert4To8(r41);
+ g1 = convert4To8(g41);
+ b1 = convert4To8(b41);
+ r2 = convert4To8(r42);
+ g2 = convert4To8(g42);
+ b2 = convert4To8(b42);
+ pCompressed->high |= (r41 << 28) | (r42 << 24) | (g41 << 20) | (g42
+ << 16) | (b41 << 12) | (b42 << 8);
+ }
+ pBaseColors[0] = r1;
+ pBaseColors[1] = g1;
+ pBaseColors[2] = b1;
+ pBaseColors[3] = r2;
+ pBaseColors[4] = g2;
+ pBaseColors[5] = b2;
+}
+
+static
+void etc_encode_block_helper(const etc1_byte* pIn, etc1_uint32 inMask,
+ const etc1_byte* pColors, etc_compressed* pCompressed, bool flipped) {
+ pCompressed->score = ~0;
+ pCompressed->high = (flipped ? 1 : 0);
+ pCompressed->low = 0;
+
+ etc1_byte pBaseColors[6];
+
+ etc_encodeBaseColors(pBaseColors, pColors, pCompressed);
+
+ int originalHigh = pCompressed->high;
+
+ const int* pModifierTable = kRGBModifierTable;
+ for (int i = 0; i < 8; i++, pModifierTable += 4) {
+ etc_compressed temp;
+ temp.score = 0;
+ temp.high = originalHigh | (i << 5);
+ temp.low = 0;
+ etc_encode_subblock_helper(pIn, inMask, &temp, flipped, false,
+ pBaseColors, pModifierTable);
+ take_best(pCompressed, &temp);
+ }
+ pModifierTable = kRGBModifierTable;
+ etc_compressed firstHalf = *pCompressed;
+ for (int i = 0; i < 8; i++, pModifierTable += 4) {
+ etc_compressed temp;
+ temp.score = firstHalf.score;
+ temp.high = firstHalf.high | (i << 2);
+ temp.low = firstHalf.low;
+ etc_encode_subblock_helper(pIn, inMask, &temp, flipped, true,
+ pBaseColors + 3, pModifierTable);
+ if (i == 0) {
+ *pCompressed = temp;
+ } else {
+ take_best(pCompressed, &temp);
+ }
+ }
+}
+
+static void writeBigEndian(etc1_byte* pOut, etc1_uint32 d) {
+ pOut[0] = (etc1_byte)(d >> 24);
+ pOut[1] = (etc1_byte)(d >> 16);
+ pOut[2] = (etc1_byte)(d >> 8);
+ pOut[3] = (etc1_byte) d;
+}
+
+// Input is a 4 x 4 square of 3-byte pixels in form R, G, B
+// inmask is a 16-bit mask where bit (1 << (x + y * 4)) tells whether the corresponding (x,y)
+// pixel is valid or not. Invalid pixel color values are ignored when compressing.
+// Output is an ETC1 compressed version of the data.
+
+void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 inMask,
+ etc1_byte* pOut) {
+ etc1_byte colors[6];
+ etc1_byte flippedColors[6];
+ etc_average_colors_subblock(pIn, inMask, colors, false, false);
+ etc_average_colors_subblock(pIn, inMask, colors + 3, false, true);
+ etc_average_colors_subblock(pIn, inMask, flippedColors, true, false);
+ etc_average_colors_subblock(pIn, inMask, flippedColors + 3, true, true);
+
+ etc_compressed a, b;
+ etc_encode_block_helper(pIn, inMask, colors, &a, false);
+ etc_encode_block_helper(pIn, inMask, flippedColors, &b, true);
+ take_best(&a, &b);
+ writeBigEndian(pOut, a.high);
+ writeBigEndian(pOut + 4, a.low);
+}
+
+// Return the size of the encoded image data (does not include size of PKM header).
+
+etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height) {
+ return (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1;
+}
+
+etc1_uint32 etc_get_encoded_data_size(ETC2ImageFormat format, etc1_uint32 width,
+ etc1_uint32 height) {
+ etc1_uint32 size = ((width + 3) & ~3) * ((height + 3) & ~3);
+ switch (format) {
+ case EtcRGB8:
+ case EtcRGB8A1:
+ case EtcR11:
+ case EtcSignedR11:
+ return size >> 1;
+ case EtcRG11:
+ case EtcSignedRG11:
+ case EtcRGBA8:
+ return size;
+ default:
+ assert(0);
+ return 0;
+ }
+}
+
+etc1_uint32 etc_get_decoded_pixel_size(ETC2ImageFormat format) {
+ switch (format) {
+ case EtcRGB8:
+ return 3;
+ case EtcRGBA8:
+ return 4;
+ case EtcRGB8A1:
+ case EtcR11:
+ case EtcSignedR11:
+ return 4;
+ case EtcRG11:
+ case EtcSignedRG11:
+ return 8;
+ default:
+ assert(0);
+ return 0;
+ }
+}
+
+// Encode an entire image.
+// pIn - pointer to the image data. Formatted such that the Red component of
+// pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
+// pOut - pointer to encoded data. Must be large enough to store entire encoded image.
+
+int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
+ etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut) {
+ if (pixelSize < 2 || pixelSize > 3) {
+ return -1;
+ }
+ static const unsigned short kYMask[] = { 0x0, 0xf, 0xff, 0xfff, 0xffff };
+ static const unsigned short kXMask[] = { 0x0, 0x1111, 0x3333, 0x7777,
+ 0xffff };
+ etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
+ etc1_byte encoded[ETC1_ENCODED_BLOCK_SIZE];
+
+ etc1_uint32 encodedWidth = (width + 3) & ~3;
+ etc1_uint32 encodedHeight = (height + 3) & ~3;
+
+ for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
+ etc1_uint32 yEnd = height - y;
+ if (yEnd > 4) {
+ yEnd = 4;
+ }
+ int ymask = kYMask[yEnd];
+ for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
+ etc1_uint32 xEnd = width - x;
+ if (xEnd > 4) {
+ xEnd = 4;
+ }
+ int mask = ymask & kXMask[xEnd];
+ for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
+ etc1_byte* q = block + (cy * 4) * 3;
+ const etc1_byte* p = pIn + pixelSize * x + stride * (y + cy);
+ if (pixelSize == 3) {
+ memcpy(q, p, xEnd * 3);
+ } else {
+ for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
+ int pixel = (p[1] << 8) | p[0];
+ *q++ = convert5To8(pixel >> 11);
+ *q++ = convert6To8(pixel >> 5);
+ *q++ = convert5To8(pixel);
+ p += pixelSize;
+ }
+ }
+ }
+ etc1_encode_block(block, mask, encoded);
+ memcpy(pOut, encoded, sizeof(encoded));
+ pOut += sizeof(encoded);
+ }
+ }
+ return 0;
+}
+
+// Decode an entire image.
+// pIn - pointer to encoded data.
+// pOut - pointer to the image data. Will be written such that the Red component of
+// pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset. Must be
+// large enough to store entire image.
+
+
+int etc2_decode_image(const etc1_byte* pIn, ETC2ImageFormat format,
+ etc1_byte* pOut,
+ etc1_uint32 width, etc1_uint32 height,
+ etc1_uint32 stride) {
+ etc1_byte block[std::max({ETC1_DECODED_BLOCK_SIZE,
+ ETC2_DECODED_RGB8A1_BLOCK_SIZE,
+ EAC_DECODED_R11_BLOCK_SIZE,
+ EAC_DECODED_RG11_BLOCK_SIZE})];
+ etc1_byte alphaBlock[EAC_DECODED_ALPHA_BLOCK_SIZE];
+
+ etc1_uint32 encodedWidth = (width + 3) & ~3;
+ etc1_uint32 encodedHeight = (height + 3) & ~3;
+
+ int pixelSize = etc_get_decoded_pixel_size(format);
+ bool isSigned = (format == EtcSignedR11 || format == EtcSignedRG11);
+
+ for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
+ etc1_uint32 yEnd = height - y;
+ if (yEnd > 4) {
+ yEnd = 4;
+ }
+ for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
+ etc1_uint32 xEnd = width - x;
+ if (xEnd > 4) {
+ xEnd = 4;
+ }
+ switch (format) {
+ case EtcRGBA8:
+ eac_decode_single_channel_block(pIn, 1, false, alphaBlock);
+ pIn += EAC_ENCODE_ALPHA_BLOCK_SIZE;
+ // Do not break
+ // Fall through to EtcRGB8 to decode the RGB part
+ [[fallthrough]];
+ case EtcRGB8:
+ etc2_decode_rgb_block(pIn, false, block);
+ pIn += ETC1_ENCODED_BLOCK_SIZE;
+ break;
+ case EtcRGB8A1:
+ etc2_decode_rgb_block(pIn, true, block);
+ pIn += ETC1_ENCODED_BLOCK_SIZE;
+ break;
+ case EtcR11:
+ case EtcSignedR11:
+ eac_decode_single_channel_block(pIn, 4, isSigned, block);
+ pIn += EAC_ENCODE_R11_BLOCK_SIZE;
+ break;
+ case EtcRG11:
+ case EtcSignedRG11:
+ // r channel
+ eac_decode_single_channel_block(pIn, 4, isSigned, block);
+ pIn += EAC_ENCODE_R11_BLOCK_SIZE;
+ // g channel
+ eac_decode_single_channel_block(pIn, 4, isSigned,
+ block + EAC_DECODED_R11_BLOCK_SIZE);
+ pIn += EAC_ENCODE_R11_BLOCK_SIZE;
+ break;
+ default:
+ assert(0);
+ }
+ for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
+ etc1_byte* p = pOut + pixelSize * x + stride * (y + cy);
+ switch (format) {
+ case EtcRGB8:
+ case EtcRGB8A1:
+ case EtcR11:
+ case EtcSignedR11: {
+ const etc1_byte* q = block + (cy * 4) * pixelSize;
+ memcpy(p, q, xEnd * pixelSize);
+ }
+ break;
+ case EtcRG11:
+ case EtcSignedRG11: {
+ const etc1_byte* r = block + cy * EAC_DECODED_R11_BLOCK_SIZE / 4;
+ const etc1_byte* g = block + cy * EAC_DECODED_R11_BLOCK_SIZE / 4 + EAC_DECODED_R11_BLOCK_SIZE;
+ int channelSize = pixelSize / 2;
+ for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
+ memcpy(p, r, channelSize);
+ p += channelSize;
+ r += channelSize;
+ memcpy(p, g, channelSize);
+ p += channelSize;
+ g += channelSize;
+ }
+ }
+ break;
+ case EtcRGBA8: {
+ const etc1_byte* q = block + (cy * 4) * 3;
+ const etc1_byte* qa = alphaBlock + cy * 4;
+ for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
+ // copy rgb data
+ memcpy(p, q, 3);
+ p += 3;
+ q += 3;
+ *p++ = *qa++;
+ }
+ }
+ break;
+ default:
+ assert(0);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' };
+
+static const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6;
+static const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8;
+static const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10;
+static const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12;
+static const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14;
+
+static const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0;
+
+static void writeBEUint16(etc1_byte* pOut, etc1_uint32 data) {
+ pOut[0] = (etc1_byte) (data >> 8);
+ pOut[1] = (etc1_byte) data;
+}
+
+static etc1_uint32 readBEUint16(const etc1_byte* pIn) {
+ return (pIn[0] << 8) | pIn[1];
+}
+
+// Format a PKM header
+
+void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height) {
+ memcpy(pHeader, kMagic, sizeof(kMagic));
+ etc1_uint32 encodedWidth = (width + 3) & ~3;
+ etc1_uint32 encodedHeight = (height + 3) & ~3;
+ writeBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET, ETC1_RGB_NO_MIPMAPS);
+ writeBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET, encodedWidth);
+ writeBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET, encodedHeight);
+ writeBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET, width);
+ writeBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET, height);
+}
+
+// Check if a PKM header is correctly formatted.
+
+etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader) {
+ if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
+ return false;
+ }
+ etc1_uint32 format = readBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET);
+ etc1_uint32 encodedWidth = readBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET);
+ etc1_uint32 encodedHeight = readBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET);
+ etc1_uint32 width = readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
+ etc1_uint32 height = readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
+ return format == ETC1_RGB_NO_MIPMAPS &&
+ encodedWidth >= width && encodedWidth - width < 4 &&
+ encodedHeight >= height && encodedHeight - height < 4;
+}
+
+// Read the image width from a PKM header
+
+etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader) {
+ return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
+}
+
+// Read the image height from a PKM header
+
+etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){
+ return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
+}
diff --git a/shared/OpenglCodecCommon/etc.h b/shared/OpenglCodecCommon/etc.h
new file mode 100644
index 0000000..7d43c89
--- /dev/null
+++ b/shared/OpenglCodecCommon/etc.h
@@ -0,0 +1,130 @@
+// Copyright 2009 Google Inc.
+//
+// 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.
+
+#pragma once
+
+#define MAX_ETC_SUPPORTED 12
+
+#define ETC1_ENCODED_BLOCK_SIZE 8
+#define ETC1_DECODED_BLOCK_SIZE 48
+#define ETC2_DECODED_RGB8A1_BLOCK_SIZE 64
+#define EAC_ENCODE_ALPHA_BLOCK_SIZE 8
+#define EAC_DECODED_ALPHA_BLOCK_SIZE 16
+#define EAC_ENCODE_R11_BLOCK_SIZE 8
+#define EAC_DECODED_R11_BLOCK_SIZE 64
+#define EAC_DECODED_RG11_BLOCK_SIZE (EAC_DECODED_R11_BLOCK_SIZE*2)
+
+#ifndef ETC1_RGB8_OES
+#define ETC1_RGB8_OES 0x8D64
+#endif
+
+typedef unsigned char etc1_byte;
+typedef int etc1_bool;
+typedef unsigned int etc1_uint32;
+
+enum ETC2ImageFormat {
+ EtcRGB8, EtcRGBA8, EtcR11, EtcSignedR11, EtcRG11, EtcSignedRG11, EtcRGB8A1
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Encode a block of pixels.
+//
+// pIn is a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a
+// 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
+// value of pixel (x, y).
+//
+// validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether
+// the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing.
+//
+// pOut is an ETC1 compressed version of the data.
+
+void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 validPixelMask, etc1_byte* pOut);
+
+// Decode a block of rgb pixels.
+//
+// pIn is an ETC2 compressed version of the data.
+//
+// pOut is a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a
+// 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
+// value of pixel (x, y).
+
+void etc2_decode_rgb_block(const etc1_byte* pIn, bool isPunchthroughAlpha, etc1_byte* pOut);
+
+// Decode a block of single channel pixels
+// This is used when decoding the alpha channel of RGBA8_ETC2_EAC format, or
+// when decoding R11_EAC format
+// decodedElementBytes: number of bits per element after decoding.
+// For RGBA8_ETC2_EAC it must be 1, for R11_EAC it must be 2
+
+void eac_decode_single_channel_block(const etc1_byte* pIn,
+ int decodedElementBytes, bool isSigned,
+ etc1_byte* pOut);
+
+// Return the size of the encoded image data (does not include size of PKM header).
+
+etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height);
+
+etc1_uint32 etc_get_encoded_data_size(ETC2ImageFormat format, etc1_uint32 width,
+ etc1_uint32 height);
+etc1_uint32 etc_get_decoded_pixel_size(ETC2ImageFormat format);
+
+// Encode an entire image.
+// pIn - pointer to the image data. Formatted such that
+// pixel (x,y) is at pIn + pixelSize * x + stride * y;
+// pOut - pointer to encoded data. Must be large enough to store entire encoded image.
+// pixelSize can be 2 or 3. 2 is an GL_UNSIGNED_SHORT_5_6_5 image, 3 is a GL_BYTE RGB image.
+// returns non-zero if there is an error.
+
+int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
+ etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut);
+
+// Decode an entire image.
+// pIn - pointer to encoded data.
+// pOut - pointer to the image data. Will be written such that
+// pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be
+// large enough to store entire image.
+// (pixelSize=3 for rgb images, pixelSize=4 for images with alpha channel)
+// returns non-zero if there is an error.
+
+int etc2_decode_image(const etc1_byte* pIn, ETC2ImageFormat format,
+ etc1_byte* pOut,
+ etc1_uint32 width, etc1_uint32 height,
+ etc1_uint32 stride);
+
+// Size of a PKM header, in bytes.
+
+#define ETC_PKM_HEADER_SIZE 16
+
+// Format a PKM header
+
+void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height);
+
+// Check if a PKM header is correctly formatted.
+
+etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader);
+
+// Read the image width from a PKM header
+
+etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader);
+
+// Read the image height from a PKM header
+
+etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/shared/OpenglCodecCommon/glUtils.cpp b/shared/OpenglCodecCommon/glUtils.cpp
index a0ec240..de62139 100644
--- a/shared/OpenglCodecCommon/glUtils.cpp
+++ b/shared/OpenglCodecCommon/glUtils.cpp
@@ -20,6 +20,273 @@
#include <GLES3/gl31.h>
+bool isSamplerType(GLenum type) {
+ switch (type) {
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_3D:
+ case GL_SAMPLER_CUBE:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_MULTISAMPLE:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_INT_SAMPLER_2D:
+ case GL_INT_SAMPLER_3D:
+ case GL_INT_SAMPLER_CUBE:
+ case GL_INT_SAMPLER_2D_ARRAY:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_UNSIGNED_INT_SAMPLER_2D:
+ case GL_UNSIGNED_INT_SAMPLER_3D:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE:
+ case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool isIntegerType(GLenum type) {
+ switch (type) {
+ case GL_UNSIGNED_INT:
+ case GL_INT:
+ case GL_INT_VEC2:
+ case GL_UNSIGNED_INT_VEC2:
+ case GL_INT_VEC3:
+ case GL_UNSIGNED_INT_VEC3:
+ case GL_INT_VEC4:
+ case GL_UNSIGNED_INT_VEC4:
+ case GL_INT_IMAGE_2D:
+ case GL_INT_IMAGE_3D:
+ case GL_INT_IMAGE_CUBE:
+ case GL_INT_IMAGE_2D_ARRAY:
+ case GL_UNSIGNED_INT_IMAGE_2D:
+ case GL_UNSIGNED_INT_IMAGE_3D:
+ case GL_UNSIGNED_INT_IMAGE_CUBE:
+ case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
+ case GL_UNSIGNED_INT_ATOMIC_COUNTER:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool isUnsignedIntType(GLenum type) {
+ switch (type) {
+ case GL_UNSIGNED_INT:
+ case GL_UNSIGNED_INT_VEC2:
+ case GL_UNSIGNED_INT_VEC3:
+ case GL_UNSIGNED_INT_VEC4:
+ case GL_UNSIGNED_INT_IMAGE_2D:
+ case GL_UNSIGNED_INT_IMAGE_3D:
+ case GL_UNSIGNED_INT_IMAGE_CUBE:
+ case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
+ case GL_UNSIGNED_INT_ATOMIC_COUNTER:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool isBoolType(GLenum type) {
+ switch (type) {
+ case GL_BOOL:
+ case GL_BOOL_VEC2:
+ case GL_BOOL_VEC3:
+ case GL_BOOL_VEC4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+uint32_t getColumnsOfType(GLenum type) {
+ switch (type) {
+ case GL_BYTE:
+ case GL_UNSIGNED_BYTE:
+ case GL_SHORT:
+ case GL_UNSIGNED_SHORT:
+ case GL_HALF_FLOAT:
+ case GL_HALF_FLOAT_OES:
+ case GL_IMAGE_2D:
+ case GL_IMAGE_3D:
+ case GL_UNSIGNED_INT:
+ case GL_INT:
+ case GL_FLOAT:
+ case GL_FIXED:
+ case GL_BOOL:
+ return 1;
+#ifdef GL_DOUBLE
+ case GL_DOUBLE:
+ return 1;
+ case GL_DOUBLE_VEC2:
+ case GL_DOUBLE_MAT2:
+ case GL_DOUBLE_MAT2x3:
+ case GL_DOUBLE_MAT2x4:
+ return 2;
+ case GL_DOUBLE_VEC3:
+ case GL_DOUBLE_MAT3:
+ case GL_DOUBLE_MAT3x2:
+ case GL_DOUBLE_MAT3x4:
+ return 3;
+ case GL_DOUBLE_VEC4:
+ case GL_DOUBLE_MAT4:
+ case GL_DOUBLE_MAT4x2:
+ case GL_DOUBLE_MAT4x3:
+ return 4;
+#endif
+ case GL_FLOAT_VEC2:
+ case GL_INT_VEC2:
+ case GL_UNSIGNED_INT_VEC2:
+ case GL_BOOL_VEC2:
+ case GL_FLOAT_MAT2:
+ case GL_FLOAT_MAT2x3:
+ case GL_FLOAT_MAT2x4:
+ return 2;
+ case GL_INT_VEC3:
+ case GL_UNSIGNED_INT_VEC3:
+ case GL_BOOL_VEC3:
+ case GL_FLOAT_VEC3:
+ case GL_FLOAT_MAT3:
+ case GL_FLOAT_MAT3x2:
+ case GL_FLOAT_MAT3x4:
+ return 3;
+ case GL_FLOAT_VEC4:
+ case GL_BOOL_VEC4:
+ case GL_INT_VEC4:
+ case GL_UNSIGNED_INT_VEC4:
+ case GL_FLOAT_MAT4:
+ case GL_FLOAT_MAT4x2:
+ case GL_FLOAT_MAT4x3:
+ return 4;
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_3D:
+ case GL_SAMPLER_CUBE:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_MULTISAMPLE:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_INT_SAMPLER_2D:
+ case GL_INT_SAMPLER_3D:
+ case GL_INT_SAMPLER_CUBE:
+ case GL_INT_SAMPLER_2D_ARRAY:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_UNSIGNED_INT_SAMPLER_2D:
+ case GL_UNSIGNED_INT_SAMPLER_3D:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE:
+ case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_IMAGE_CUBE:
+ case GL_IMAGE_2D_ARRAY:
+ case GL_INT_IMAGE_2D:
+ case GL_INT_IMAGE_3D:
+ case GL_INT_IMAGE_CUBE:
+ case GL_INT_IMAGE_2D_ARRAY:
+ case GL_UNSIGNED_INT_IMAGE_2D:
+ case GL_UNSIGNED_INT_IMAGE_3D:
+ case GL_UNSIGNED_INT_IMAGE_CUBE:
+ case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
+ case GL_UNSIGNED_INT_ATOMIC_COUNTER:
+ default:
+ return 1;
+ }
+}
+
+uint32_t getRowsOfType(GLenum type) {
+ switch (type) {
+ case GL_BYTE:
+ case GL_UNSIGNED_BYTE:
+ case GL_SHORT:
+ case GL_UNSIGNED_SHORT:
+ case GL_HALF_FLOAT:
+ case GL_HALF_FLOAT_OES:
+ case GL_IMAGE_2D:
+ case GL_IMAGE_3D:
+ case GL_UNSIGNED_INT:
+ case GL_INT:
+ case GL_FLOAT:
+ case GL_FIXED:
+ case GL_BOOL:
+ return 1;
+#ifdef GL_DOUBLE
+ case GL_DOUBLE:
+ case GL_DOUBLE_VEC2:
+ case GL_DOUBLE_VEC3:
+ case GL_DOUBLE_VEC4:
+ return 1;
+ case GL_DOUBLE_MAT2:
+ case GL_DOUBLE_MAT3x2:
+ case GL_DOUBLE_MAT4x2:
+ return 2;
+ case GL_DOUBLE_MAT3:
+ case GL_DOUBLE_MAT2x3:
+ case GL_DOUBLE_MAT4x3:
+ return 3;
+ case GL_DOUBLE_MAT4:
+ case GL_DOUBLE_MAT3x4:
+ case GL_DOUBLE_MAT2x4:
+ return 4;
+#endif
+ case GL_FLOAT_VEC2:
+ case GL_INT_VEC2:
+ case GL_UNSIGNED_INT_VEC2:
+ case GL_BOOL_VEC2:
+ case GL_INT_VEC3:
+ case GL_UNSIGNED_INT_VEC3:
+ case GL_BOOL_VEC3:
+ case GL_FLOAT_VEC3:
+ case GL_FLOAT_VEC4:
+ case GL_BOOL_VEC4:
+ case GL_INT_VEC4:
+ case GL_UNSIGNED_INT_VEC4:
+ return 1;
+ case GL_FLOAT_MAT2:
+ case GL_FLOAT_MAT3x2:
+ case GL_FLOAT_MAT4x2:
+ return 2;
+ case GL_FLOAT_MAT3:
+ case GL_FLOAT_MAT2x3:
+ case GL_FLOAT_MAT4x3:
+ return 3;
+ case GL_FLOAT_MAT4:
+ case GL_FLOAT_MAT2x4:
+ case GL_FLOAT_MAT3x4:
+ return 4;
+ case GL_SAMPLER_2D:
+ case GL_SAMPLER_3D:
+ case GL_SAMPLER_CUBE:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_MULTISAMPLE:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_INT_SAMPLER_2D:
+ case GL_INT_SAMPLER_3D:
+ case GL_INT_SAMPLER_CUBE:
+ case GL_INT_SAMPLER_2D_ARRAY:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_UNSIGNED_INT_SAMPLER_2D:
+ case GL_UNSIGNED_INT_SAMPLER_3D:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE:
+ case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_IMAGE_CUBE:
+ case GL_IMAGE_2D_ARRAY:
+ case GL_INT_IMAGE_2D:
+ case GL_INT_IMAGE_3D:
+ case GL_INT_IMAGE_CUBE:
+ case GL_INT_IMAGE_2D_ARRAY:
+ case GL_UNSIGNED_INT_IMAGE_2D:
+ case GL_UNSIGNED_INT_IMAGE_3D:
+ case GL_UNSIGNED_INT_IMAGE_CUBE:
+ case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
+ case GL_UNSIGNED_INT_ATOMIC_COUNTER:
+ default:
+ return 1;
+ }
+}
+
size_t glSizeof(GLenum type)
{
size_t retval = 0;
@@ -47,6 +314,36 @@
case GL_DOUBLE:
retval = 8;
break;
+ case GL_DOUBLE_VEC2:
+ retval = 16;
+ break;
+ case GL_DOUBLE_VEC3:
+ retval = 24;
+ break;
+ case GL_DOUBLE_VEC4:
+ retval = 32;
+ break;
+ case GL_DOUBLE_MAT2:
+ retval = 8 * 4;
+ break;
+ case GL_DOUBLE_MAT3:
+ retval = 8 * 9;
+ break;
+ case GL_DOUBLE_MAT4:
+ retval = 8 * 16;
+ break;
+ case GL_DOUBLE_MAT2x3:
+ case GL_DOUBLE_MAT3x2:
+ retval = 8 * 6;
+ break;
+ case GL_DOUBLE_MAT2x4:
+ case GL_DOUBLE_MAT4x2:
+ retval = 8 * 8;
+ break;
+ case GL_DOUBLE_MAT3x4:
+ case GL_DOUBLE_MAT4x3:
+ retval = 8 * 12;
+ break;
#endif
case GL_FLOAT_VEC2:
case GL_INT_VEC2:
@@ -88,6 +385,32 @@
case GL_SAMPLER_2D:
case GL_SAMPLER_3D:
case GL_SAMPLER_CUBE:
+ case GL_SAMPLER_2D_SHADOW:
+ case GL_SAMPLER_2D_ARRAY:
+ case GL_SAMPLER_2D_ARRAY_SHADOW:
+ case GL_SAMPLER_2D_MULTISAMPLE:
+ case GL_SAMPLER_CUBE_SHADOW:
+ case GL_INT_SAMPLER_2D:
+ case GL_INT_SAMPLER_3D:
+ case GL_INT_SAMPLER_CUBE:
+ case GL_INT_SAMPLER_2D_ARRAY:
+ case GL_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_UNSIGNED_INT_SAMPLER_2D:
+ case GL_UNSIGNED_INT_SAMPLER_3D:
+ case GL_UNSIGNED_INT_SAMPLER_CUBE:
+ case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+ case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
+ case GL_IMAGE_CUBE:
+ case GL_IMAGE_2D_ARRAY:
+ case GL_INT_IMAGE_2D:
+ case GL_INT_IMAGE_3D:
+ case GL_INT_IMAGE_CUBE:
+ case GL_INT_IMAGE_2D_ARRAY:
+ case GL_UNSIGNED_INT_IMAGE_2D:
+ case GL_UNSIGNED_INT_IMAGE_3D:
+ case GL_UNSIGNED_INT_IMAGE_CUBE:
+ case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
+ case GL_UNSIGNED_INT_ATOMIC_COUNTER:
retval = 4;
break;
case GL_UNSIGNED_SHORT_4_4_4_4:
@@ -669,3 +992,92 @@
}
return 4;
}
+
+bool colorRenderableFormat(GLint internalformat, GLenum texturetype, int majorVersion, int minorVersion, bool hasColorBufferFloatExtension, bool hasColorBufferHalfFloatExtension) {
+ switch (internalformat) {
+ case GL_RGB:
+ case GL_RGBA:
+ case GL_BGRA_EXT:
+ switch (texturetype) {
+ case GL_FLOAT:
+ case GL_HALF_FLOAT_OES:
+ case GL_UNSIGNED_INT_10F_11F_11F_REV:
+ case GL_UNSIGNED_INT_2_10_10_10_REV:
+ return false;
+ default:
+ return true;
+ }
+ break;
+ case GL_R8:
+ case GL_RG8:
+ case GL_RGB8:
+ case GL_RGB565:
+ case GL_RGBA4:
+ case GL_RGB5_A1:
+ case GL_RGBA8:
+ case GL_RGB10_A2:
+ case GL_RGB10_A2UI:
+ case GL_SRGB8_ALPHA8:
+ case GL_R8I:
+ case GL_R8UI:
+ case GL_R16I:
+ case GL_R16UI:
+ case GL_R32I:
+ case GL_R32UI:
+ case GL_RG8I:
+ case GL_RG8UI:
+ case GL_RG16I:
+ case GL_RG16UI:
+ case GL_RG32I:
+ case GL_RG32UI:
+ case GL_RGBA8I:
+ case GL_RGBA8UI:
+ case GL_RGBA16I:
+ case GL_RGBA16UI:
+ case GL_RGBA32I:
+ case GL_RGBA32UI:
+ case GL_BGRA8_EXT:
+ return true;
+ case GL_R16F:
+ case GL_RG16F:
+ case GL_RGBA16F:
+ case GL_R32F:
+ case GL_RG32F:
+ case GL_RGBA32F:
+ case GL_R11F_G11F_B10F:
+ return majorVersion >= 3 && hasColorBufferFloatExtension;
+ case GL_RGB16F:
+ return majorVersion >= 3 && hasColorBufferHalfFloatExtension;
+ default:
+ return false;
+ }
+}
+
+bool depthRenderableFormat(GLint internalformat) {
+ switch (internalformat) {
+ case GL_DEPTH_STENCIL:
+ case GL_DEPTH_COMPONENT:
+ case GL_DEPTH_COMPONENT16:
+ case GL_DEPTH_COMPONENT24:
+ case GL_DEPTH_COMPONENT32F:
+ case GL_DEPTH24_STENCIL8:
+ case GL_DEPTH32F_STENCIL8:
+ case 0X81A7: // GL_DEPTH_COMPONENT32
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool stencilRenderableFormat(GLint internalformat) {
+ switch (internalformat) {
+ case GL_STENCIL_INDEX:
+ case GL_DEPTH_STENCIL:
+ case GL_STENCIL_INDEX8:
+ case GL_DEPTH24_STENCIL8:
+ case GL_DEPTH32F_STENCIL8:
+ return true;
+ default:
+ return false;
+ }
+}
diff --git a/shared/OpenglCodecCommon/glUtils.h b/shared/OpenglCodecCommon/glUtils.h
index 2f20b68..489ac18 100644
--- a/shared/OpenglCodecCommon/glUtils.h
+++ b/shared/OpenglCodecCommon/glUtils.h
@@ -57,6 +57,12 @@
INDIRECT_COMMAND_DRAWELEMENTS = 1,
} IndirectCommandType;
+ bool isSamplerType(GLenum type);
+ bool isIntegerType(GLenum type);
+ bool isUnsignedIntType(GLenum type);
+ bool isBoolType(GLenum type);
+ uint32_t getColumnsOfType(GLenum type);
+ uint32_t getRowsOfType(GLenum type);
size_t glSizeof(GLenum type);
size_t glUtilsParamSize(GLenum param);
void glUtilsPackPointerData(unsigned char *dst, unsigned char *str,
@@ -73,6 +79,11 @@
GLuint glUtilsIndirectStructSize(IndirectCommandType cmdType);
+ bool colorRenderableFormat(GLint internalformat, GLenum texturetype, int majorVersion, int minorVersion, bool hasColorBufferFloatExtension, bool hasColorBufferHalfFloatExtension);
+
+ bool depthRenderableFormat(GLint internalformat);
+ bool stencilRenderableFormat(GLint internalformat);
+
#ifdef __cplusplus
};
#endif