| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #include "GLClientState.h" |
| #include "GLESTextureUtils.h" |
| #include "ErrorLog.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "glUtils.h" |
| |
| #if PLATFORM_SDK_VERSION < 26 |
| #include <cutils/log.h> |
| #else |
| #include <log/log.h> |
| #endif |
| |
| #ifndef MAX |
| #define MAX(a, b) ((a) < (b) ? (b) : (a)) |
| #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> |
| |
| using android::base::guest::AutoReadLock; |
| using android::base::guest::AutoWriteLock; |
| |
| void GLClientState::init() { |
| m_initialized = false; |
| |
| state_GL_STENCIL_TEST = false; |
| state_GL_STENCIL_FUNC = GL_ALWAYS; |
| state_GL_STENCIL_VALUE_MASK = ~(0); |
| state_GL_STENCIL_REF = 0; |
| state_GL_STENCIL_FAIL = GL_KEEP; |
| state_GL_STENCIL_PASS_DEPTH_FAIL = GL_KEEP; |
| state_GL_STENCIL_PASS_DEPTH_PASS = GL_KEEP; |
| state_GL_STENCIL_BACK_FUNC = GL_ALWAYS; |
| state_GL_STENCIL_BACK_VALUE_MASK = ~(0); |
| state_GL_STENCIL_BACK_REF = 0; |
| state_GL_STENCIL_BACK_FAIL = GL_KEEP; |
| state_GL_STENCIL_BACK_PASS_DEPTH_FAIL = GL_KEEP; |
| state_GL_STENCIL_BACK_PASS_DEPTH_PASS = GL_KEEP; |
| state_GL_STENCIL_WRITEMASK = ~(0); |
| state_GL_STENCIL_BACK_WRITEMASK = ~(0); |
| state_GL_STENCIL_CLEAR_VALUE = 0; |
| |
| |
| m_arrayBuffer = 0; |
| m_arrayBuffer_lastEncode = 0; |
| |
| m_attribEnableCache = 0; |
| m_vaoAttribBindingCacheInvalid = 0xffff; |
| m_vaoAttribBindingHasClientArrayCache = 0; |
| m_vaoAttribBindingHasVboCache = 0; |
| m_noClientArraysCache = 0; |
| |
| addVertexArrayObject(0); |
| setVertexArrayObject(0); |
| // init gl constans; |
| m_currVaoState[VERTEX_LOCATION].glConst = GL_VERTEX_ARRAY; |
| m_currVaoState[NORMAL_LOCATION].glConst = GL_NORMAL_ARRAY; |
| m_currVaoState[COLOR_LOCATION].glConst = GL_COLOR_ARRAY; |
| m_currVaoState[POINTSIZE_LOCATION].glConst = GL_POINT_SIZE_ARRAY_OES; |
| m_currVaoState[TEXCOORD0_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY; |
| m_currVaoState[TEXCOORD1_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY; |
| m_currVaoState[TEXCOORD2_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY; |
| m_currVaoState[TEXCOORD3_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY; |
| m_currVaoState[TEXCOORD4_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY; |
| m_currVaoState[TEXCOORD5_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY; |
| m_currVaoState[TEXCOORD6_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY; |
| m_currVaoState[TEXCOORD7_LOCATION].glConst = GL_TEXTURE_COORD_ARRAY; |
| m_currVaoState[MATRIXINDEX_LOCATION].glConst = GL_MATRIX_INDEX_ARRAY_OES; |
| m_currVaoState[WEIGHT_LOCATION].glConst = GL_WEIGHT_ARRAY_OES; |
| |
| m_copyReadBuffer = 0; |
| m_copyWriteBuffer = 0; |
| m_pixelPackBuffer = 0; |
| m_pixelUnpackBuffer = 0; |
| m_transformFeedbackBuffer = 0; |
| m_uniformBuffer = 0; |
| m_atomicCounterBuffer = 0; |
| m_dispatchIndirectBuffer = 0; |
| m_drawIndirectBuffer = 0; |
| m_shaderStorageBuffer = 0; |
| m_textureBuffer = 0; |
| |
| m_transformFeedbackActive = false; |
| m_transformFeedbackUnpaused = false; |
| m_transformFeedbackVaryingsCountForLinking = 0; |
| |
| m_activeTexture = 0; |
| m_currentProgram = 0; |
| m_currentShaderProgram = 0; |
| |
| m_pixelStore.unpack_alignment = 4; |
| m_pixelStore.pack_alignment = 4; |
| |
| m_pixelStore.unpack_row_length = 0; |
| m_pixelStore.unpack_image_height = 0; |
| m_pixelStore.unpack_skip_pixels = 0; |
| m_pixelStore.unpack_skip_rows = 0; |
| m_pixelStore.unpack_skip_images = 0; |
| |
| m_pixelStore.pack_row_length = 0; |
| m_pixelStore.pack_skip_pixels = 0; |
| m_pixelStore.pack_skip_rows = 0; |
| |
| memset(m_tex.unit, 0, sizeof(m_tex.unit)); |
| m_tex.activeUnit = &m_tex.unit[0]; |
| m_tex.textureRecs = NULL; |
| |
| mRboState.boundRenderbuffer = nullptr; |
| |
| mFboState.boundDrawFramebuffer = 0; |
| mFboState.boundReadFramebuffer = 0; |
| mFboState.drawFboCheckStatus = GL_NONE; |
| mFboState.readFboCheckStatus = GL_NONE; |
| |
| 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() |
| { |
| init(); |
| } |
| |
| GLClientState::GLClientState(int majorVersion, int minorVersion) : |
| m_glesMajorVersion(majorVersion), |
| m_glesMinorVersion(minorVersion) { |
| init(); |
| } |
| |
| GLClientState::~GLClientState() |
| { |
| } |
| |
| void GLClientState::enable(int location, int state) |
| { |
| m_currVaoState[location].enableDirty |= (state != m_currVaoState[location].enabled); |
| m_currVaoState[location].enabled = state; |
| if (state) { |
| m_attribEnableCache |= (1 << location); |
| m_noClientArraysCache = 0; |
| } else { |
| m_attribEnableCache &= ~(1 << location); |
| } |
| } |
| |
| void GLClientState::setVertexAttribState(int location, int size, GLenum type, GLboolean normalized, GLsizei stride, const void *data, bool isInt) |
| { |
| m_currVaoState[location].size = size; |
| m_currVaoState[location].type = type; |
| m_currVaoState[location].stride = stride; |
| m_currVaoState[location].data = (void*)data; |
| m_currVaoState[location].bufferObject = m_arrayBuffer; |
| m_currVaoState[location].elementSize = size ? (glSizeof(type) * size) : 0; |
| switch (type) { |
| case GL_INT_2_10_10_10_REV: |
| case GL_UNSIGNED_INT_2_10_10_10_REV: |
| m_currVaoState[location].elementSize = |
| m_currVaoState[location].elementSize / 4; |
| break; |
| default: |
| break; |
| } |
| m_currVaoState[location].normalized = normalized; |
| m_currVaoState[location].isInt = isInt; |
| } |
| |
| void GLClientState::setVertexBindingDivisor(int bindingindex, GLuint divisor) { |
| m_currVaoState.bufferBinding(bindingindex).divisor = divisor; |
| } |
| |
| const GLClientState::BufferBinding& GLClientState::getCurrAttributeBindingInfo(int attribindex) { |
| return m_currVaoState.bufferBindings_const()[m_currVaoState[attribindex].bindingindex]; |
| } |
| |
| void GLClientState::setVertexAttribBinding(int attribindex, int bindingindex) { |
| m_currVaoState[attribindex].bindingindex = bindingindex; |
| m_currVaoState.bufferBinding(bindingindex).vertexAttribLoc = attribindex; |
| m_vaoAttribBindingCacheInvalid |= (1 << attribindex); |
| m_noClientArraysCache = 0; |
| } |
| |
| void GLClientState::setVertexAttribFormat(int location, int size, GLenum type, GLboolean normalized, GLuint reloffset, bool isInt) { |
| m_currVaoState[location].size = size; |
| m_currVaoState[location].type = type; |
| m_currVaoState[location].normalized = normalized; |
| m_currVaoState[location].reloffset = reloffset; |
| m_currVaoState[location].elementSize = size ? (glSizeof(type) * size) : 0; |
| switch (type) { |
| case GL_INT_2_10_10_10_REV: |
| case GL_UNSIGNED_INT_2_10_10_10_REV: |
| m_currVaoState[location].elementSize = |
| m_currVaoState[location].elementSize / 4; |
| break; |
| default: |
| break; |
| } |
| m_currVaoState[location].isInt = isInt; |
| } |
| |
| void GLClientState::addVertexArrayObjects(GLsizei n, GLuint* arrays) { |
| for (GLsizei i = 0; i < n; i++) { |
| addVertexArrayObject(arrays[i]); |
| } |
| } |
| |
| void GLClientState::removeVertexArrayObjects(GLsizei n, const GLuint* arrays) { |
| for (GLsizei i = 0; i < n; i++) { |
| if (arrays[i] && m_currVaoState.vaoId() == arrays[i]) { |
| setVertexArrayObject(0); |
| } |
| removeVertexArrayObject(arrays[i]); |
| } |
| } |
| |
| void GLClientState::addVertexArrayObject(GLuint name) { |
| if (m_vaoMap.find(name) != |
| m_vaoMap.end()) { |
| ALOGE("%s: ERROR: %u already part of current VAO state!", |
| __FUNCTION__, name); |
| return; |
| } |
| |
| m_vaoMap.insert( |
| VAOStateMap::value_type( |
| name, |
| VAOState(0, CODEC_MAX_VERTEX_ATTRIBUTES, CODEC_MAX_VERTEX_ATTRIBUTES))); |
| VertexAttribStateVector& attribState = |
| m_vaoMap.find(name)->second.attribState; |
| for (int i = 0; i < CODEC_MAX_VERTEX_ATTRIBUTES; i++) { |
| attribState[i].enabled = 0; |
| attribState[i].enableDirty = false; |
| attribState[i].data = 0; |
| attribState[i].reloffset = 0; |
| attribState[i].bindingindex = i; |
| attribState[i].divisor = 0; |
| attribState[i].size = 4; // 4 is the default size |
| attribState[i].type = GL_FLOAT; // GL_FLOAT is the default type |
| } |
| |
| VertexAttribBindingVector& bindingState = |
| m_vaoMap.find(name)->second.bindingState; |
| for (int i = 0; i < bindingState.size(); i++) { |
| bindingState[i].effectiveStride = 16; |
| } |
| } |
| |
| void GLClientState::removeVertexArrayObject(GLuint name) { |
| if (name == 0) { |
| ALOGE("%s: ERROR: cannot delete VAO 0!", |
| __FUNCTION__); |
| return; |
| } |
| if (m_vaoMap.find(name) == |
| m_vaoMap.end()) { |
| ALOGE("%s: ERROR: %u not found in VAO state!", |
| __FUNCTION__, name); |
| return; |
| } |
| m_vaoMap.erase(name); |
| } |
| |
| void GLClientState::setVertexArrayObject(GLuint name) { |
| if (m_vaoMap.find(name) == |
| m_vaoMap.end()) { |
| ALOGE("%s: ERROR: %u not found in VAO state!", |
| __FUNCTION__, name); |
| return; |
| } |
| |
| if (name && m_currVaoState.vaoId() == name) { |
| ALOGV("%s: set vao to self, no-op (%u)", |
| __FUNCTION__, name); |
| return; |
| } |
| |
| m_currVaoState = |
| VAOStateRef(m_vaoMap.find(name)); |
| } |
| |
| bool GLClientState::isVertexArrayObject(GLuint vao) const { |
| return m_vaoMap.find(vao) != m_vaoMap.end(); |
| } |
| |
| void GLClientState::getVBOUsage(bool* hasClientArrays, bool* hasVBOs) { |
| uint8_t todo_count = 0; |
| uint8_t todo[CODEC_MAX_VERTEX_ATTRIBUTES]; |
| |
| if (m_noClientArraysCache) { |
| *hasClientArrays = false; |
| *hasVBOs = true; |
| return; |
| } |
| |
| for (int i = 0; i < CODEC_MAX_VERTEX_ATTRIBUTES; i++) { |
| if ((1 << i) & (m_attribEnableCache)) { |
| if (!((1 << i) & m_vaoAttribBindingCacheInvalid)) { |
| if ((1 << i) & m_vaoAttribBindingHasClientArrayCache) { |
| *hasClientArrays = true; |
| } |
| if ((1 << i) & m_vaoAttribBindingHasVboCache) { |
| *hasVBOs = true; |
| } |
| if (*hasClientArrays && *hasVBOs) return; |
| } else { |
| todo[todo_count] = i; |
| ++todo_count; |
| } |
| } |
| } |
| |
| if (todo_count == 0 && |
| !(*hasClientArrays) && |
| *hasVBOs) { |
| m_noClientArraysCache = 1; |
| } |
| |
| for (int k = 0; k < todo_count; ++k) { |
| int i = todo[k]; |
| const GLClientState::BufferBinding& curr_binding = |
| m_currVaoState.bufferBindings_const()[ |
| m_currVaoState[i].bindingindex]; |
| GLuint bufferObject = curr_binding.buffer; |
| if (bufferObject == 0 && curr_binding.offset && hasClientArrays) { |
| *hasClientArrays = true; |
| m_vaoAttribBindingHasClientArrayCache |= (1 << i); |
| } else { |
| m_vaoAttribBindingHasClientArrayCache &= ~(1 << i); |
| } |
| if (bufferObject != 0 && hasVBOs) { |
| *hasVBOs = true; |
| m_vaoAttribBindingHasVboCache |= (1 << i); |
| } else { |
| m_vaoAttribBindingHasVboCache &= ~(1 << i); |
| } |
| m_vaoAttribBindingCacheInvalid &= ~(1 << i); |
| if (*hasClientArrays && *hasVBOs) return; |
| } |
| |
| if (!(*hasClientArrays) && |
| *hasVBOs) { |
| m_noClientArraysCache = 1; |
| } |
| } |
| |
| const GLClientState::VertexAttribState& GLClientState::getState(int location) { |
| return m_currVaoState[location]; |
| } |
| |
| const GLClientState::VertexAttribState& GLClientState::getStateAndEnableDirty(int location, bool *enableChanged) |
| { |
| if (enableChanged) { |
| *enableChanged = m_currVaoState[location].enableDirty; |
| } |
| |
| m_currVaoState[location].enableDirty = false; |
| return m_currVaoState[location]; |
| } |
| |
| void GLClientState::updateEnableDirtyArrayForDraw() { |
| bool enableChanged; |
| VAOState& vaoState = m_currVaoState.vaoState(); |
| |
| int k = 0; |
| for (int i = 0; i < CODEC_MAX_VERTEX_ATTRIBUTES; ++i) { |
| const VertexAttribState &state = getStateAndEnableDirty(i, &enableChanged); |
| if (enableChanged || state.enabled) { |
| vaoState.attributesNeedingUpdateForDraw[k] = i; |
| ++k; |
| } |
| } |
| vaoState.numAttributesNeedingUpdateForDraw = k; |
| } |
| |
| GLClientState::VAOState& GLClientState::currentVaoState() { |
| return m_currVaoState.vaoState(); |
| } |
| |
| int GLClientState::getLocation(GLenum loc) |
| { |
| int retval; |
| |
| switch(loc) { |
| case GL_VERTEX_ARRAY: |
| retval = int(VERTEX_LOCATION); |
| break; |
| case GL_NORMAL_ARRAY: |
| retval = int(NORMAL_LOCATION); |
| break; |
| case GL_COLOR_ARRAY: |
| retval = int(COLOR_LOCATION); |
| break; |
| case GL_POINT_SIZE_ARRAY_OES: |
| retval = int(POINTSIZE_LOCATION); |
| break; |
| case GL_TEXTURE_COORD_ARRAY: |
| retval = int (TEXCOORD0_LOCATION + m_activeTexture); |
| break; |
| case GL_MATRIX_INDEX_ARRAY_OES: |
| retval = int (MATRIXINDEX_LOCATION); |
| break; |
| case GL_WEIGHT_ARRAY_OES: |
| retval = int (WEIGHT_LOCATION); |
| break; |
| default: |
| retval = loc; |
| } |
| return retval; |
| } |
| |
| static void sClearIndexedBufferBinding(GLuint id, std::vector<GLClientState::BufferBinding>& bindings) { |
| for (size_t i = 0; i < bindings.size(); i++) { |
| if (bindings[i].buffer == id) { |
| bindings[i].offset = 0; |
| bindings[i].stride = 0; |
| bindings[i].effectiveStride = 16; |
| bindings[i].size = 0; |
| bindings[i].buffer = 0; |
| } |
| } |
| } |
| |
| #ifdef GFXSTREAM |
| |
| void GLClientState::addBuffer(GLuint id) { |
| mBufferIds.add(id); |
| mBufferIds.set(id, true); |
| mHostMappedBufferDirty.add(id); |
| } |
| |
| void GLClientState::removeBuffer(GLuint id) { |
| mHostMappedBufferDirty.remove(id); |
| mBufferIds.remove(id); |
| } |
| |
| bool GLClientState::bufferIdExists(GLuint id) const { |
| return mBufferIds.get(id); |
| } |
| |
| void GLClientState::setBufferHostMapDirty(GLuint id, bool dirty) { |
| mHostMappedBufferDirty.set(id, dirty); |
| } |
| |
| bool GLClientState::isBufferHostMapDirty(GLuint id) const { |
| 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) { |
| mBufferIds.insert(id); |
| } |
| |
| void GLClientState::removeBuffer(GLuint id) { |
| mBufferIds.erase(id); |
| } |
| |
| bool GLClientState::bufferIdExists(GLuint id) const { |
| return mBufferIds.find(id) != mBufferIds.end(); |
| } |
| |
| void GLClientState::setBufferHostMapDirty(GLuint id, bool dirty) { |
| (void)id; |
| (void)dirty; |
| } |
| |
| 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() { |
| if (m_pixelPackBuffer) |
| setBufferHostMapDirty(m_pixelPackBuffer, true /* dirty */); |
| } |
| |
| void GLClientState::setBoundTransformFeedbackBuffersDirtyForHostMap() { |
| if (m_transformFeedbackBuffer) |
| setBufferHostMapDirty( |
| m_transformFeedbackBuffer, |
| true /* dirty */); |
| |
| for (size_t i = 0; i < m_indexedTransformFeedbackBuffers.size(); ++i) |
| if (m_indexedTransformFeedbackBuffers[i].buffer) |
| setBufferHostMapDirty( |
| m_indexedTransformFeedbackBuffers[i].buffer, |
| true /* dirty */); |
| } |
| |
| void GLClientState::setBoundShaderStorageBuffersDirtyForHostMap() { |
| if (m_glesMajorVersion == 3 && m_glesMinorVersion == 0) return; |
| |
| if (m_shaderStorageBuffer) |
| setBufferHostMapDirty( |
| m_shaderStorageBuffer, |
| true /* dirty */); |
| |
| for (size_t i = 0; i < m_indexedShaderStorageBuffers.size(); ++i) |
| if (m_indexedShaderStorageBuffers[i].buffer) |
| setBufferHostMapDirty( |
| m_indexedShaderStorageBuffers[i].buffer, |
| true /* dirty */); |
| } |
| |
| void GLClientState::setBoundAtomicCounterBuffersDirtyForHostMap() { |
| if (m_glesMajorVersion == 3 && m_glesMinorVersion == 0) return; |
| |
| if (m_atomicCounterBuffer) |
| setBufferHostMapDirty( |
| m_atomicCounterBuffer, |
| true /* dirty */); |
| |
| for (size_t i = 0; i < m_indexedAtomicCounterBuffers.size(); ++i) |
| if (m_indexedAtomicCounterBuffers[i].buffer) |
| setBufferHostMapDirty( |
| m_indexedAtomicCounterBuffers[i].buffer, |
| true /* dirty */); |
| } |
| |
| void GLClientState::unBindBuffer(GLuint id) { |
| if (m_arrayBuffer == id) { |
| m_arrayBuffer = 0; |
| m_arrayBuffer_lastEncode = 0; |
| } |
| |
| if (m_currVaoState.iboId() == id) { |
| m_currVaoState.iboId() = 0; |
| m_currVaoState.iboIdLastEncode() = 0; |
| } |
| |
| if (m_copyReadBuffer == id) |
| m_copyReadBuffer = 0; |
| if (m_copyWriteBuffer == id) |
| m_copyWriteBuffer = 0; |
| if (m_pixelPackBuffer == id) |
| m_pixelPackBuffer = 0; |
| if (m_pixelUnpackBuffer == id) |
| m_pixelUnpackBuffer = 0; |
| if (m_transformFeedbackBuffer == id) |
| m_transformFeedbackBuffer = 0; |
| if (m_uniformBuffer == id) |
| m_uniformBuffer = 0; |
| if (m_atomicCounterBuffer == id) |
| m_atomicCounterBuffer = 0; |
| if (m_dispatchIndirectBuffer == id) |
| m_dispatchIndirectBuffer = 0; |
| if (m_drawIndirectBuffer == id) |
| m_drawIndirectBuffer = 0; |
| if (m_shaderStorageBuffer == id) |
| m_shaderStorageBuffer = 0; |
| if (m_textureBuffer == id) |
| m_textureBuffer = 0; |
| |
| sClearIndexedBufferBinding(id, m_indexedTransformFeedbackBuffers); |
| sClearIndexedBufferBinding(id, m_indexedUniformBuffers); |
| sClearIndexedBufferBinding(id, m_indexedAtomicCounterBuffers); |
| sClearIndexedBufferBinding(id, m_indexedShaderStorageBuffers); |
| sClearIndexedBufferBinding(id, m_currVaoState.bufferBindings()); |
| m_vaoAttribBindingCacheInvalid = 0xffff; |
| m_noClientArraysCache = 0; |
| } |
| |
| int GLClientState::bindBuffer(GLenum target, GLuint id) |
| { |
| int err = 0; |
| switch(target) { |
| case GL_ARRAY_BUFFER: |
| m_arrayBuffer = id; |
| break; |
| case GL_ELEMENT_ARRAY_BUFFER: |
| m_currVaoState.iboId() = id; |
| break; |
| case GL_COPY_READ_BUFFER: |
| m_copyReadBuffer = id; |
| break; |
| case GL_COPY_WRITE_BUFFER: |
| m_copyWriteBuffer = id; |
| break; |
| case GL_PIXEL_PACK_BUFFER: |
| m_pixelPackBuffer = id; |
| break; |
| case GL_PIXEL_UNPACK_BUFFER: |
| m_pixelUnpackBuffer = id; |
| break; |
| case GL_TRANSFORM_FEEDBACK_BUFFER: |
| m_transformFeedbackBuffer = id; |
| break; |
| case GL_UNIFORM_BUFFER: |
| m_uniformBuffer = id; |
| break; |
| case GL_ATOMIC_COUNTER_BUFFER: |
| m_atomicCounterBuffer = id; |
| break; |
| case GL_DISPATCH_INDIRECT_BUFFER: |
| m_dispatchIndirectBuffer = id; |
| break; |
| case GL_DRAW_INDIRECT_BUFFER: |
| m_drawIndirectBuffer = id; |
| break; |
| case GL_SHADER_STORAGE_BUFFER: |
| m_shaderStorageBuffer = id; |
| break; |
| case GL_TEXTURE_BUFFER_OES: |
| m_textureBuffer = id; |
| break; |
| default: |
| err = -1; |
| } |
| return err; |
| } |
| |
| void GLClientState::bindIndexedBuffer(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size, GLintptr stride, GLintptr effectiveStride) { |
| switch (target) { |
| case GL_TRANSFORM_FEEDBACK_BUFFER: |
| m_indexedTransformFeedbackBuffers[index].buffer = buffer; |
| m_indexedTransformFeedbackBuffers[index].offset = offset; |
| m_indexedTransformFeedbackBuffers[index].size = size; |
| m_indexedTransformFeedbackBuffers[index].stride = stride; |
| break; |
| case GL_UNIFORM_BUFFER: |
| m_indexedUniformBuffers[index].buffer = buffer; |
| m_indexedUniformBuffers[index].offset = offset; |
| m_indexedUniformBuffers[index].size = size; |
| m_indexedUniformBuffers[index].stride = stride; |
| break; |
| case GL_ATOMIC_COUNTER_BUFFER: |
| m_indexedAtomicCounterBuffers[index].buffer = buffer; |
| m_indexedAtomicCounterBuffers[index].offset = offset; |
| m_indexedAtomicCounterBuffers[index].size = size; |
| m_indexedAtomicCounterBuffers[index].stride = stride; |
| break; |
| case GL_SHADER_STORAGE_BUFFER: |
| m_indexedShaderStorageBuffers[index].buffer = buffer; |
| m_indexedShaderStorageBuffers[index].offset = offset; |
| m_indexedShaderStorageBuffers[index].size = size; |
| m_indexedShaderStorageBuffers[index].stride = stride; |
| break; |
| default: |
| m_currVaoState.bufferBinding(index).buffer = buffer; |
| m_currVaoState.bufferBinding(index).offset = offset; |
| m_currVaoState.bufferBinding(index).size = size; |
| m_currVaoState.bufferBinding(index).stride = stride; |
| m_currVaoState.bufferBinding(index).effectiveStride = effectiveStride; |
| m_vaoAttribBindingCacheInvalid |= (1 << m_currVaoState.bufferBinding(index).vertexAttribLoc); |
| return; |
| } |
| } |
| |
| int GLClientState::getMaxIndexedBufferBindings(GLenum target) const { |
| switch (target) { |
| case GL_TRANSFORM_FEEDBACK_BUFFER: |
| return m_indexedTransformFeedbackBuffers.size(); |
| case GL_UNIFORM_BUFFER: |
| return m_indexedUniformBuffers.size(); |
| case GL_ATOMIC_COUNTER_BUFFER: |
| return m_indexedAtomicCounterBuffers.size(); |
| case GL_SHADER_STORAGE_BUFFER: |
| return m_indexedShaderStorageBuffers.size(); |
| default: |
| return m_currVaoState.bufferBindings_const().size(); |
| } |
| } |
| |
| bool GLClientState::isNonIndexedBindNoOp(GLenum target, GLuint buffer) { |
| if (buffer != getLastEncodedBufferBind(target)) return false; |
| |
| int idOrError = getBuffer(target); |
| if (idOrError < 0) { |
| return false; |
| } else { |
| return buffer == (GLuint)idOrError; |
| } |
| } |
| |
| bool GLClientState::isIndexedBindNoOp(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size, GLintptr stride, GLintptr effectiveStride) { |
| |
| if (target == GL_TRANSFORM_FEEDBACK_BUFFER) return false; |
| |
| if (buffer != getLastEncodedBufferBind(target)) return false; |
| |
| switch (target) { |
| case GL_TRANSFORM_FEEDBACK_BUFFER: |
| return m_indexedTransformFeedbackBuffers[index].buffer == buffer && |
| m_indexedTransformFeedbackBuffers[index].offset == offset && |
| m_indexedTransformFeedbackBuffers[index].size == size && |
| m_indexedTransformFeedbackBuffers[index].stride == stride; |
| case GL_UNIFORM_BUFFER: |
| return m_indexedUniformBuffers[index].buffer == buffer && |
| m_indexedUniformBuffers[index].offset == offset && |
| m_indexedUniformBuffers[index].size == size && |
| m_indexedUniformBuffers[index].stride == stride; |
| case GL_ATOMIC_COUNTER_BUFFER: |
| return m_indexedAtomicCounterBuffers[index].buffer == buffer && |
| m_indexedAtomicCounterBuffers[index].offset == offset && |
| m_indexedAtomicCounterBuffers[index].size == size && |
| m_indexedAtomicCounterBuffers[index].stride == stride; |
| case GL_SHADER_STORAGE_BUFFER: |
| return m_indexedShaderStorageBuffers[index].buffer == buffer && |
| m_indexedShaderStorageBuffers[index].offset == offset && |
| m_indexedShaderStorageBuffers[index].size == size && |
| m_indexedShaderStorageBuffers[index].stride == stride; |
| default: |
| return m_currVaoState.bufferBinding(index).buffer == buffer && |
| m_currVaoState.bufferBinding(index).offset == offset && |
| m_currVaoState.bufferBinding(index).size == size && |
| m_currVaoState.bufferBinding(index).stride == stride && |
| m_currVaoState.bufferBinding(index).effectiveStride == effectiveStride; |
| } |
| } |
| |
| 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(); |
| setBoundAtomicCounterBuffersDirtyForHostMap(); |
| } |
| |
| void GLClientState::postReadPixels() { |
| setBoundPixelPackBufferDirtyForHostMap(); |
| } |
| |
| void GLClientState::postDispatchCompute() { |
| setBoundShaderStorageBuffersDirtyForHostMap(); |
| setBoundAtomicCounterBuffersDirtyForHostMap(); |
| } |
| |
| bool GLClientState::shouldSkipHostMapBuffer(GLenum target) { |
| GLuint id = getBuffer(target); |
| return !isBufferHostMapDirty(id); |
| } |
| |
| void GLClientState::onHostMappedBuffer(GLenum target) { |
| GLuint id = getBuffer(target); |
| setBufferHostMapDirty(id, false /* not dirty */); |
| } |
| |
| int GLClientState::getBuffer(GLenum target) { |
| int ret=0; |
| switch (target) { |
| case GL_ARRAY_BUFFER: |
| ret = m_arrayBuffer; |
| break; |
| case GL_ELEMENT_ARRAY_BUFFER: |
| ret = m_currVaoState.iboId(); |
| break; |
| case GL_COPY_READ_BUFFER: |
| ret = m_copyReadBuffer; |
| break; |
| case GL_COPY_WRITE_BUFFER: |
| ret = m_copyWriteBuffer; |
| break; |
| case GL_PIXEL_PACK_BUFFER: |
| ret = m_pixelPackBuffer; |
| break; |
| case GL_PIXEL_UNPACK_BUFFER: |
| ret = m_pixelUnpackBuffer; |
| break; |
| case GL_TRANSFORM_FEEDBACK_BUFFER: |
| ret = m_transformFeedbackBuffer; |
| break; |
| case GL_UNIFORM_BUFFER: |
| ret = m_uniformBuffer; |
| break; |
| case GL_ATOMIC_COUNTER_BUFFER: |
| ret = m_atomicCounterBuffer; |
| break; |
| case GL_DISPATCH_INDIRECT_BUFFER: |
| ret = m_dispatchIndirectBuffer; |
| break; |
| case GL_DRAW_INDIRECT_BUFFER: |
| ret = m_drawIndirectBuffer; |
| break; |
| case GL_SHADER_STORAGE_BUFFER: |
| ret = m_shaderStorageBuffer; |
| break; |
| case GL_TEXTURE_BUFFER_OES: |
| ret = m_textureBuffer; |
| break; |
| default: |
| ret = -1; |
| } |
| return ret; |
| } |
| |
| GLuint GLClientState::getLastEncodedBufferBind(GLenum target) { |
| GLuint ret; |
| switch (target) |
| { |
| case GL_ARRAY_BUFFER: |
| ret = m_arrayBuffer_lastEncode; |
| break; |
| case GL_ELEMENT_ARRAY_BUFFER: |
| ret = m_currVaoState.iboIdLastEncode(); |
| break; |
| default: |
| { |
| int idOrError = getBuffer(target); |
| ret = (idOrError < 0) ? 0 : (GLuint)idOrError; |
| } |
| } |
| |
| return ret; |
| } |
| |
| void GLClientState::setLastEncodedBufferBind(GLenum target, GLuint id) |
| { |
| switch (target) |
| { |
| case GL_ARRAY_BUFFER: |
| m_arrayBuffer_lastEncode = id; |
| break; |
| case GL_ELEMENT_ARRAY_BUFFER: |
| m_currVaoState.iboIdLastEncode() = id; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| bool GLClientState::isTexture(GLuint tex_name) const { |
| return getTextureRec(tex_name) != nullptr; |
| } |
| |
| bool GLClientState::isTextureWithStorage(GLuint tex_name) const { |
| TextureRec* rec = getTextureRecPtr(tex_name); |
| if (!rec) return false; |
| return rec->hasStorage; |
| } |
| |
| bool GLClientState::isTextureCubeMap(GLuint tex_name) const { |
| TextureRec* texrec = getTextureRecPtr(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; |
| switch (pname) { |
| case GL_VERTEX_ARRAY_POINTER: { |
| which_state = GLClientState::VERTEX_LOCATION; |
| break; |
| } |
| case GL_NORMAL_ARRAY_POINTER: { |
| which_state = GLClientState::NORMAL_LOCATION; |
| break; |
| } |
| case GL_COLOR_ARRAY_POINTER: { |
| which_state = GLClientState::COLOR_LOCATION; |
| break; |
| } |
| case GL_TEXTURE_COORD_ARRAY_POINTER: { |
| which_state = getActiveTexture() + GLClientState::TEXCOORD0_LOCATION; |
| break; |
| } |
| case GL_POINT_SIZE_ARRAY_POINTER_OES: { |
| which_state = GLClientState::POINTSIZE_LOCATION; |
| break; |
| } |
| case GL_MATRIX_INDEX_ARRAY_POINTER_OES: { |
| which_state = GLClientState::MATRIXINDEX_LOCATION; |
| break; |
| } |
| case GL_WEIGHT_ARRAY_POINTER_OES: { |
| which_state = GLClientState::WEIGHT_LOCATION; |
| break; |
| } |
| } |
| if (which_state != -1) |
| *params = m_currVaoState[which_state].data; |
| } |
| |
| int GLClientState::setPixelStore(GLenum param, GLint value) |
| { |
| int retval = 0; |
| switch(param) { |
| case GL_UNPACK_ALIGNMENT: |
| m_pixelStore.unpack_alignment = value; |
| break; |
| case GL_PACK_ALIGNMENT: |
| m_pixelStore.pack_alignment = value; |
| break; |
| case GL_UNPACK_ROW_LENGTH: |
| m_pixelStore.unpack_row_length = value; |
| break; |
| case GL_UNPACK_IMAGE_HEIGHT: |
| m_pixelStore.unpack_image_height = value; |
| break; |
| case GL_UNPACK_SKIP_PIXELS: |
| m_pixelStore.unpack_skip_pixels = value; |
| break; |
| case GL_UNPACK_SKIP_ROWS: |
| m_pixelStore.unpack_skip_rows = value; |
| break; |
| case GL_UNPACK_SKIP_IMAGES: |
| m_pixelStore.unpack_skip_images = value; |
| break; |
| case GL_PACK_ROW_LENGTH: |
| m_pixelStore.pack_row_length = value; |
| break; |
| case GL_PACK_SKIP_PIXELS: |
| m_pixelStore.pack_skip_pixels = value; |
| break; |
| case GL_PACK_SKIP_ROWS: |
| m_pixelStore.pack_skip_rows = value; |
| break; |
| default: |
| retval = GL_INVALID_ENUM; |
| } |
| return retval; |
| } |
| |
| |
| size_t GLClientState::pixelDataSize(GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, int pack) const |
| { |
| if (width <= 0 || height <= 0 || depth <= 0) return 0; |
| |
| ALOGV("%s: pack? %d", __FUNCTION__, pack); |
| if (pack) { |
| ALOGV("%s: pack stats", __FUNCTION__); |
| ALOGV("%s: pack align %d", __FUNCTION__, m_pixelStore.pack_alignment); |
| ALOGV("%s: pack rowlen %d", __FUNCTION__, m_pixelStore.pack_row_length); |
| ALOGV("%s: pack skippixels %d", __FUNCTION__, m_pixelStore.pack_skip_pixels); |
| ALOGV("%s: pack skiprows %d", __FUNCTION__, m_pixelStore.pack_skip_rows); |
| } else { |
| ALOGV("%s: unpack stats", __FUNCTION__); |
| ALOGV("%s: unpack align %d", __FUNCTION__, m_pixelStore.unpack_alignment); |
| ALOGV("%s: unpack rowlen %d", __FUNCTION__, m_pixelStore.unpack_row_length); |
| ALOGV("%s: unpack imgheight %d", __FUNCTION__, m_pixelStore.unpack_image_height); |
| ALOGV("%s: unpack skippixels %d", __FUNCTION__, m_pixelStore.unpack_skip_pixels); |
| ALOGV("%s: unpack skiprows %d", __FUNCTION__, m_pixelStore.unpack_skip_rows); |
| ALOGV("%s: unpack skipimages %d", __FUNCTION__, m_pixelStore.unpack_skip_images); |
| } |
| return GLESTextureUtils::computeTotalImageSize( |
| width, height, depth, |
| format, type, |
| pack ? m_pixelStore.pack_alignment : m_pixelStore.unpack_alignment, |
| pack ? m_pixelStore.pack_row_length : m_pixelStore.unpack_row_length, |
| pack ? 0 : m_pixelStore.unpack_image_height, |
| pack ? m_pixelStore.pack_skip_pixels : m_pixelStore.unpack_skip_pixels, |
| pack ? m_pixelStore.pack_skip_rows : m_pixelStore.unpack_skip_rows, |
| pack ? 0 : m_pixelStore.unpack_skip_images); |
| } |
| |
| size_t GLClientState::pboNeededDataSize(GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, int pack, int ignoreTrailing) const |
| { |
| if (width <= 0 || height <= 0 || depth <= 0) return 0; |
| |
| ALOGV("%s: pack? %d", __FUNCTION__, pack); |
| if (pack) { |
| ALOGV("%s: pack stats", __FUNCTION__); |
| ALOGV("%s: pack align %d", __FUNCTION__, m_pixelStore.pack_alignment); |
| ALOGV("%s: pack rowlen %d", __FUNCTION__, m_pixelStore.pack_row_length); |
| ALOGV("%s: pack skippixels %d", __FUNCTION__, m_pixelStore.pack_skip_pixels); |
| ALOGV("%s: pack skiprows %d", __FUNCTION__, m_pixelStore.pack_skip_rows); |
| } else { |
| ALOGV("%s: unpack stats", __FUNCTION__); |
| ALOGV("%s: unpack align %d", __FUNCTION__, m_pixelStore.unpack_alignment); |
| ALOGV("%s: unpack rowlen %d", __FUNCTION__, m_pixelStore.unpack_row_length); |
| ALOGV("%s: unpack imgheight %d", __FUNCTION__, m_pixelStore.unpack_image_height); |
| ALOGV("%s: unpack skippixels %d", __FUNCTION__, m_pixelStore.unpack_skip_pixels); |
| ALOGV("%s: unpack skiprows %d", __FUNCTION__, m_pixelStore.unpack_skip_rows); |
| ALOGV("%s: unpack skipimages %d", __FUNCTION__, m_pixelStore.unpack_skip_images); |
| } |
| return GLESTextureUtils::computeNeededBufferSize( |
| width, height, depth, |
| format, type, |
| pack ? m_pixelStore.pack_alignment : m_pixelStore.unpack_alignment, |
| pack ? m_pixelStore.pack_row_length : m_pixelStore.unpack_row_length, |
| pack ? 0 : m_pixelStore.unpack_image_height, |
| pack ? m_pixelStore.pack_skip_pixels : m_pixelStore.unpack_skip_pixels, |
| pack ? m_pixelStore.pack_skip_rows : m_pixelStore.unpack_skip_rows, |
| pack ? 0 : m_pixelStore.unpack_skip_images, |
| ignoreTrailing); |
| } |
| |
| |
| size_t GLClientState::clearBufferNumElts(GLenum buffer) const |
| { |
| switch (buffer) { |
| case GL_COLOR: |
| return 4; |
| case GL_DEPTH: |
| case GL_STENCIL: |
| return 1; |
| } |
| return 1; |
| } |
| |
| void GLClientState::getPackingOffsets2D(GLsizei width, GLsizei height, GLenum format, GLenum type, int* bpp, int* startOffset, int* pixelRowSize, int* totalRowSize, int* skipRows) const |
| { |
| if (width <= 0 || height <= 0) { |
| *startOffset = 0; |
| *pixelRowSize = 0; |
| *totalRowSize = 0; |
| return; |
| } |
| |
| GLESTextureUtils::computePackingOffsets2D( |
| width, height, |
| format, type, |
| m_pixelStore.pack_alignment, |
| m_pixelStore.pack_row_length, |
| m_pixelStore.pack_skip_pixels, |
| m_pixelStore.pack_skip_rows, |
| bpp, |
| startOffset, |
| pixelRowSize, |
| totalRowSize); |
| |
| *skipRows = m_pixelStore.pack_skip_rows; |
| } |
| |
| void GLClientState::getUnpackingOffsets2D(GLsizei width, GLsizei height, GLenum format, GLenum type, int* bpp, int* startOffset, int* pixelRowSize, int* totalRowSize, int* skipRows) const |
| { |
| if (width <= 0 || height <= 0) { |
| *startOffset = 0; |
| *pixelRowSize = 0; |
| *totalRowSize = 0; |
| return; |
| } |
| |
| GLESTextureUtils::computePackingOffsets2D( |
| width, height, |
| format, type, |
| m_pixelStore.unpack_alignment, |
| m_pixelStore.unpack_row_length, |
| m_pixelStore.unpack_skip_pixels, |
| m_pixelStore.unpack_skip_rows, |
| bpp, |
| startOffset, |
| pixelRowSize, |
| totalRowSize); |
| |
| *skipRows = m_pixelStore.unpack_skip_rows; |
| } |
| |
| void GLClientState::getUnpackingOffsets3D(GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, int* bpp, int* startOffset, int* pixelRowSize, int* totalRowSize, int* pixelImageSize, int* totalImageSize, int* skipRows, int* skipImages) const |
| { |
| if (width <= 0 || height <= 0) { |
| *startOffset = 0; |
| *pixelRowSize = 0; |
| *totalRowSize = 0; |
| return; |
| } |
| |
| GLESTextureUtils::computePackingOffsets3D( |
| width, height, depth, |
| format, type, |
| m_pixelStore.unpack_alignment, |
| m_pixelStore.unpack_row_length, |
| m_pixelStore.unpack_image_height, |
| m_pixelStore.unpack_skip_pixels, |
| m_pixelStore.unpack_skip_rows, |
| m_pixelStore.unpack_skip_images, |
| bpp, |
| startOffset, |
| pixelRowSize, |
| totalRowSize, |
| pixelImageSize, |
| totalImageSize); |
| |
| *skipRows = m_pixelStore.unpack_skip_rows; |
| *skipImages = m_pixelStore.unpack_skip_images; |
| } |
| |
| void GLClientState::setNumActiveUniformsInUniformBlock(GLuint program, GLuint uniformBlockIndex, GLint numActiveUniforms) { |
| UniformBlockInfoKey key; |
| key.program = program; |
| key.uniformBlockIndex = uniformBlockIndex; |
| |
| UniformBlockUniformInfo info; |
| info.numActiveUniforms = (size_t)numActiveUniforms; |
| |
| m_uniformBlockInfoMap[key] = info; |
| } |
| |
| size_t GLClientState::numActiveUniformsInUniformBlock(GLuint program, GLuint uniformBlockIndex) const { |
| UniformBlockInfoKey key; |
| key.program = program; |
| key.uniformBlockIndex = uniformBlockIndex; |
| UniformBlockInfoMap::const_iterator it = |
| m_uniformBlockInfoMap.find(key); |
| if (it == m_uniformBlockInfoMap.end()) return 0; |
| return it->second.numActiveUniforms; |
| } |
| |
| void GLClientState::associateProgramWithPipeline(GLuint program, GLuint pipeline) { |
| m_programPipelines[program] = pipeline; |
| } |
| |
| GLClientState::ProgramPipelineIterator GLClientState::programPipelineBegin() { |
| return m_programPipelines.begin(); |
| } |
| |
| GLClientState::ProgramPipelineIterator GLClientState::programPipelineEnd() { |
| return m_programPipelines.end(); |
| } |
| |
| GLenum GLClientState::setActiveTextureUnit(GLenum texture) |
| { |
| GLuint unit = texture - GL_TEXTURE0; |
| if (unit >= MAX_TEXTURE_UNITS) { |
| return GL_INVALID_ENUM; |
| } |
| m_tex.activeUnit = &m_tex.unit[unit]; |
| return GL_NO_ERROR; |
| } |
| |
| GLenum GLClientState::getActiveTextureUnit() const |
| { |
| return GL_TEXTURE0 + (m_tex.activeUnit - &m_tex.unit[0]); |
| } |
| |
| void GLClientState::enableTextureTarget(GLenum target) |
| { |
| switch (target) { |
| case GL_TEXTURE_2D: |
| m_tex.activeUnit->enables |= (1u << TEXTURE_2D); |
| break; |
| case GL_TEXTURE_EXTERNAL_OES: |
| m_tex.activeUnit->enables |= (1u << TEXTURE_EXTERNAL); |
| break; |
| } |
| } |
| |
| void GLClientState::disableTextureTarget(GLenum target) |
| { |
| switch (target) { |
| case GL_TEXTURE_2D: |
| m_tex.activeUnit->enables &= ~(1u << TEXTURE_2D); |
| break; |
| case GL_TEXTURE_EXTERNAL_OES: |
| m_tex.activeUnit->enables &= ~(1u << TEXTURE_EXTERNAL); |
| break; |
| } |
| } |
| |
| 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) { |
| return m_tex.unit[unit].boundSampler == sampler; |
| } |
| |
| void GLClientState::onDeleteSamplers(GLsizei n, const GLuint* samplers) { |
| for (uint32_t i = 0; i < n; ++i) { |
| for (uint32_t j = 0; j < MAX_TEXTURE_UNITS; ++j) { |
| uint32_t currentSampler = m_tex.unit[j].boundSampler; |
| if (currentSampler == samplers[i]) { |
| m_tex.unit[j].boundSampler = 0; |
| } |
| } |
| } |
| } |
| |
| GLenum GLClientState::getPriorityEnabledTarget(GLenum allDisabled) const |
| { |
| unsigned int enables = m_tex.activeUnit->enables; |
| if (enables & (1u << TEXTURE_EXTERNAL)) { |
| return GL_TEXTURE_EXTERNAL_OES; |
| } else if (enables & (1u << TEXTURE_2D)) { |
| return GL_TEXTURE_2D; |
| } else { |
| return allDisabled; |
| } |
| } |
| |
| int GLClientState::compareTexId(const void* pid, const void* prec) |
| { |
| const GLuint* id = (const GLuint*)pid; |
| const TextureRec* rec = (const TextureRec*)prec; |
| return (GLint)(*id) - (GLint)rec->id; |
| } |
| |
| GLenum GLClientState::bindTexture(GLenum target, GLuint texture, |
| GLboolean* firstUse) |
| { |
| GLboolean first = GL_FALSE; |
| |
| TextureRec* texrec = getTextureRecPtr(texture); |
| if (!texrec) { |
| texrec = addTextureRec(texture, target); |
| first = GL_TRUE; |
| } |
| |
| if (texture && target != texrec->target && |
| (target != GL_TEXTURE_EXTERNAL_OES && |
| texrec->target != GL_TEXTURE_EXTERNAL_OES)) { |
| return GL_INVALID_OPERATION; |
| } |
| |
| switch (target) { |
| case GL_TEXTURE_2D: |
| m_tex.activeUnit->texture[TEXTURE_2D] = texture; |
| break; |
| case GL_TEXTURE_EXTERNAL_OES: |
| m_tex.activeUnit->texture[TEXTURE_EXTERNAL] = texture; |
| break; |
| case GL_TEXTURE_CUBE_MAP: |
| m_tex.activeUnit->texture[TEXTURE_CUBE_MAP] = texture; |
| break; |
| case GL_TEXTURE_2D_ARRAY: |
| m_tex.activeUnit->texture[TEXTURE_2D_ARRAY] = texture; |
| break; |
| case GL_TEXTURE_3D: |
| m_tex.activeUnit->texture[TEXTURE_3D] = texture; |
| break; |
| case GL_TEXTURE_2D_MULTISAMPLE: |
| m_tex.activeUnit->texture[TEXTURE_2D_MULTISAMPLE] = texture; |
| break; |
| case GL_TEXTURE_BUFFER_OES: |
| m_tex.activeUnit->texture[TEXTURE_BUFFER] = texture; |
| break; |
| } |
| |
| if (firstUse) { |
| *firstUse = first; |
| } |
| |
| return GL_NO_ERROR; |
| } |
| |
| void GLClientState::setBoundEGLImage(GLenum target, GLeglImageOES image, int width, int height) { |
| (void)image; |
| |
| if (target == GL_RENDERBUFFER) { |
| if (!boundRenderbuffer()) return; |
| setBoundRenderbufferEGLImageBacked(); |
| setBoundRenderbufferFormat(GL_RGBA); |
| setBoundRenderbufferSamples(0); |
| setBoundRenderbufferDimensions(width, height); |
| } else { |
| GLuint texture = getBoundTexture(target); |
| TextureRec* texrec = getTextureRecPtr(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, width, height, 1); |
| } |
| } |
| |
| TextureRec* GLClientState::addTextureRec(GLuint id, GLenum target) |
| { |
| TextureRec* tex = new TextureRec; |
| tex->id = id; |
| tex->target = target; |
| tex->format = -1; |
| tex->multisamples = 0; |
| tex->immutable = false; |
| tex->boundEGLImage = false; |
| 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; |
| |
| AutoWriteLock guard(m_tex.textureRecs->lock); |
| m_tex.textureRecs->map[id] = std::shared_ptr<TextureRec>(tex); |
| return tex; |
| } |
| |
| std::shared_ptr<TextureRec> GLClientState::getTextureRec(GLuint id) const { |
| AutoReadLock guard(m_tex.textureRecs->lock); |
| SharedTextureDataMap::const_iterator it = |
| m_tex.textureRecs->map.find(id); |
| if (it == m_tex.textureRecs->map.end()) { |
| return NULL; |
| } |
| return it->second; |
| } |
| |
| TextureRec* GLClientState::getTextureRecPtrLocked(GLuint id) const { |
| SharedTextureDataMap::const_iterator it = |
| m_tex.textureRecs->map.find(id); |
| if (it == m_tex.textureRecs->map.end()) { |
| return NULL; |
| } |
| return it->second.get(); |
| } |
| |
| TextureRec* GLClientState::getTextureRecPtr(GLuint id) const { |
| AutoReadLock guard(m_tex.textureRecs->lock); |
| return getTextureRecPtrLocked(id); |
| } |
| |
| void GLClientState::setBoundTextureInternalFormat(GLenum target, GLint internalformat) { |
| GLuint texture = getBoundTexture(target); |
| TextureRec* texrec = getTextureRecPtr(texture); |
| if (!texrec) return; |
| texrec->internalformat = internalformat; |
| } |
| |
| void GLClientState::setBoundTextureFormat(GLenum target, GLenum format) { |
| GLuint texture = getBoundTexture(target); |
| TextureRec* texrec = getTextureRecPtr(texture); |
| if (!texrec) return; |
| texrec->format = format; |
| } |
| |
| void GLClientState::setBoundTextureType(GLenum target, GLenum type) { |
| GLuint texture = getBoundTexture(target); |
| TextureRec* texrec = getTextureRecPtr(texture); |
| if (!texrec) return; |
| texrec->type = type; |
| } |
| |
| 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 = getTextureRecPtr(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; |
| GLsizei curr_depth = depth; |
| GLsizei curr_level = 0; |
| |
| while (true) { |
| 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) || |
| true)) { |
| break; |
| } |
| curr_width = (curr_width >> 1) ? (curr_width >> 1) : 1; |
| curr_height = (curr_height >> 1) ? (curr_height >> 1) : 1; |
| if (target == GL_TEXTURE_3D) { |
| curr_depth = (curr_depth >> 1) ? (curr_depth >> 1) : 1; |
| } |
| curr_level++; |
| } |
| |
| } else { |
| 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) { |
| GLuint texture = getBoundTexture(target); |
| TextureRec* texrec = getTextureRecPtr(texture); |
| if (!texrec) return; |
| texrec->multisamples = samples; |
| } |
| |
| void GLClientState::addTextureCubeMapImage(GLenum stateTarget, GLenum cubeTarget) { |
| if (stateTarget != GL_TEXTURE_CUBE_MAP) return; |
| |
| GLuint texture = getBoundTexture(stateTarget); |
| TextureRec* texrec = getTextureRecPtr(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 = getTextureRecPtr(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 { |
| GLuint texture = getBoundTexture(target); |
| TextureRec* texrec = getTextureRecPtr(texture); |
| if (!texrec) return false; |
| return texrec->immutable; |
| } |
| |
| bool GLClientState::isBoundTextureComplete(GLenum target) const { |
| GLuint texture = getBoundTexture(target); |
| TextureRec* texrec = getTextureRecPtr(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) { |
| case GL_TEXTURE_2D: |
| return m_tex.activeUnit->texture[TEXTURE_2D]; |
| case GL_TEXTURE_EXTERNAL_OES: |
| return m_tex.activeUnit->texture[TEXTURE_EXTERNAL]; |
| case GL_TEXTURE_CUBE_MAP: |
| return m_tex.activeUnit->texture[TEXTURE_CUBE_MAP]; |
| case GL_TEXTURE_2D_ARRAY: |
| return m_tex.activeUnit->texture[TEXTURE_2D_ARRAY]; |
| case GL_TEXTURE_3D: |
| return m_tex.activeUnit->texture[TEXTURE_3D]; |
| case GL_TEXTURE_2D_MULTISAMPLE: |
| return m_tex.activeUnit->texture[TEXTURE_2D_MULTISAMPLE]; |
| case GL_TEXTURE_BUFFER_OES: |
| return m_tex.activeUnit->texture[TEXTURE_BUFFER]; |
| default: |
| return 0; |
| } |
| } |
| |
| 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) { |
| // Framebuffers may be missing an attachment if they have nonzero |
| // default width and height |
| if (props.defaultWidth == 0 || props.defaultHeight == 0) { |
| 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 |
| std::shared_ptr<TextureRec> texrec; |
| std::shared_ptr<RboProps> rbo; |
| switch (fbo_format_info.type) { |
| case FBO_ATTACHMENT_RENDERBUFFER: |
| rbo = getFboAttachmentRbo(target, attachment); |
| if (!fbo_format_info.rb_external) { |
| if (!rbo || 0 == rbo->width || 0 == rbo->height) { |
| ALOGD("%s: rbo has zero dimension\n", __func__); |
| return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; |
| } |
| } |
| break; |
| case FBO_ATTACHMENT_TEXTURE: |
| texrec = getFboAttachmentTexture(target, attachment); |
| if (!fbo_format_info.tex_external) { |
| if (0 == texrec->dims->widths[fbo_format_info.tex_level] || |
| 0 == texrec->dims->heights[fbo_format_info.tex_level]) { |
| ALOGD("%s: texture has zero dimension\n", __func__); |
| return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; |
| } |
| GLsizei depth = texrec->dims->depths[fbo_format_info.tex_level]; |
| 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-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- |
| // (>' ')><(' '<)(>' ')><(' '<)(>' ')><(' '<)(>' ')><(' '<)(>' ')><(' '<)(>' ')> |
| |
| static bool unreliableInternalFormat(GLenum internalformat) { |
| switch (internalformat) { |
| case GL_LUMINANCE: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| void GLClientState::writeCopyTexImageState |
| (GLenum target, GLint level, GLenum internalformat) { |
| if (unreliableInternalFormat(internalformat)) { |
| CubeMapDef entry; |
| entry.id = getBoundTexture(GL_TEXTURE_2D); |
| entry.target = target; |
| entry.level = level; |
| entry.internalformat = internalformat; |
| m_cubeMapDefs.insert(entry); |
| } |
| } |
| |
| static GLenum identifyPositiveCubeMapComponent(GLenum target) { |
| switch (target) { |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: |
| return GL_TEXTURE_CUBE_MAP_POSITIVE_X; |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: |
| return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; |
| case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: |
| return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; |
| default: |
| return 0; |
| } |
| } |
| |
| GLenum GLClientState::copyTexImageNeededTarget |
| (GLenum target, GLint level, GLenum internalformat) { |
| if (unreliableInternalFormat(internalformat)) { |
| GLenum positiveComponent = |
| identifyPositiveCubeMapComponent(target); |
| if (positiveComponent) { |
| CubeMapDef query; |
| query.id = getBoundTexture(GL_TEXTURE_2D); |
| query.target = positiveComponent; |
| query.level = level; |
| query.internalformat = internalformat; |
| if (m_cubeMapDefs.find(query) == |
| m_cubeMapDefs.end()) { |
| return positiveComponent; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| GLenum GLClientState::copyTexImageLuminanceCubeMapAMDWorkaround |
| (GLenum target, GLint level, GLenum internalformat) { |
| writeCopyTexImageState(target, level, internalformat); |
| return copyTexImageNeededTarget(target, level, internalformat); |
| } |
| |
| // (>' ')><(' '<)(>' ')><(' '<)(>' ')><(' '<)(>' ')><(' '<)(>' ')><(' '<)(>' ')> |
| // END driver workarounds-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~- |
| |
| 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 |
| // textures are marked, or |
| // - could swap deleted textures to the end and re-sort. |
| TextureRec* texrec; |
| for (const GLuint* texture = textures; texture != textures + n; texture++) { |
| AutoWriteLock guard(m_tex.textureRecs->lock); |
| texrec = getTextureRecPtrLocked(*texture); |
| if (texrec && texrec->dims) { |
| delete [] texrec->dims; |
| } |
| if (texrec) { |
| m_tex.textureRecs->map.erase(*texture); |
| for (TextureUnit* unit = m_tex.unit; |
| unit != m_tex.unit + MAX_TEXTURE_UNITS; |
| unit++) |
| { |
| if (unit->texture[TEXTURE_2D] == *texture) { |
| unit->texture[TEXTURE_2D] = 0; |
| } else if (unit->texture[TEXTURE_EXTERNAL] == *texture) { |
| unit->texture[TEXTURE_EXTERNAL] = 0; |
| } |
| } |
| } |
| } |
| } |
| |
| // RBO////////////////////////////////////////////////////////////////////////// |
| |
| void GLClientState::addFreshRenderbuffer(GLuint name) { |
| if (!name) return; |
| |
| RenderbufferInfo::ScopedView view(mRboState.rboData); |
| view.addFresh(name); |
| } |
| |
| void GLClientState::addRenderbuffers(GLsizei n, GLuint* renderbuffers) { |
| for (size_t i = 0; i < n; i++) { |
| addFreshRenderbuffer(renderbuffers[i]); |
| } |
| } |
| |
| void GLClientState::removeRenderbuffers(GLsizei n, const GLuint* renderbuffers) { |
| bool unbindCurrent = false; |
| { |
| RenderbufferInfo::ScopedView view(mRboState.rboData); |
| for (size_t i = 0; i < n; i++) { |
| if (renderbuffers[i] != 0) { // Never remove the zero rb. |
| auto rboPtr = view.get_shared_ptr(renderbuffers[i]); |
| if (!rboPtr) { |
| continue; |
| } |
| unbindCurrent |= |
| (mRboState.boundRenderbuffer == rboPtr); |
| setFboCompletenessDirtyForRbo(rboPtr); |
| view.remove(renderbuffers[i]); |
| } |
| } |
| } |
| |
| if (unbindCurrent) { |
| bindRenderbuffer(GL_RENDERBUFFER, 0); |
| } |
| } |
| |
| bool GLClientState::usedRenderbufferName(GLuint name) const { |
| if (!name) return false; |
| |
| RenderbufferInfo::ScopedView view(mRboState.rboData); |
| return view.get_const(name) != 0; |
| } |
| |
| void GLClientState::bindRenderbuffer(GLenum target, GLuint name) { |
| |
| (void)target; // Must be GL_RENDERBUFFER |
| RenderbufferInfo::ScopedView view(mRboState.rboData); |
| mRboState.boundRenderbuffer = view.bind(name); |
| } |
| |
| GLuint GLClientState::boundRenderbuffer() const { |
| return mRboState.boundRenderbuffer->id; |
| } |
| |
| void GLClientState::setBoundRenderbufferFormat(GLenum format) { |
| mRboState.boundRenderbuffer->format = format; |
| } |
| |
| void GLClientState::setBoundRenderbufferSamples(GLsizei samples) { |
| mRboState.boundRenderbuffer->multisamples = samples; |
| } |
| |
| void GLClientState::setBoundRenderbufferDimensions(GLsizei width, GLsizei height) { |
| mRboState.boundRenderbuffer->width = width; |
| mRboState.boundRenderbuffer->height = height; |
| } |
| |
| void GLClientState::setBoundRenderbufferEGLImageBacked() { |
| mRboState.boundRenderbuffer->boundEGLImage = true; |
| } |
| |
| // FBO////////////////////////////////////////////////////////////////////////// |
| |
| GLint GLClientState::queryTexInternalFormat(GLuint tex_name) const { |
| TextureRec* texrec = getTextureRecPtr(tex_name); |
| if (!texrec) return -1; |
| return texrec->internalformat; |
| } |
| |
| GLsizei GLClientState::queryTexWidth(GLsizei level, GLuint tex_name) const { |
| TextureRec* texrec = getTextureRecPtr(tex_name); |
| if (!texrec) { |
| return 0; |
| } |
| return texrec->dims->widths[level]; |
| } |
| |
| GLsizei GLClientState::queryTexHeight(GLsizei level, GLuint tex_name) const { |
| TextureRec* texrec = getTextureRecPtr(tex_name); |
| if (!texrec) return 0; |
| return texrec->dims->heights[level]; |
| } |
| |
| GLsizei GLClientState::queryTexDepth(GLsizei level, GLuint tex_name) const { |
| TextureRec* texrec = getTextureRecPtr(tex_name); |
| if (!texrec) return 0; |
| return texrec->dims->depths[level]; |
| } |
| |
| bool GLClientState::queryTexEGLImageBacked(GLuint tex_name) const { |
| TextureRec* texrec = getTextureRecPtr(tex_name); |
| if (!texrec) return false; |
| return texrec->boundEGLImage; |
| } |
| |
| GLenum GLClientState::queryTexFormat(GLuint tex_name) const { |
| TextureRec* texrec = getTextureRecPtr(tex_name); |
| if (!texrec) return -1; |
| return texrec->format; |
| } |
| |
| GLenum GLClientState::queryTexType(GLuint tex_name) const { |
| TextureRec* texrec = getTextureRecPtr(tex_name); |
| if (!texrec) return -1; |
| return texrec->type; |
| } |
| |
| GLsizei GLClientState::queryTexSamples(GLuint tex_name) const { |
| TextureRec* texrec = getTextureRecPtr(tex_name); |
| if (!texrec) return 0; |
| return texrec->multisamples; |
| } |
| |
| GLenum GLClientState::queryTexLastBoundTarget(GLuint tex_name) const { |
| TextureRec* texrec = getTextureRecPtr(tex_name); |
| if (!texrec) return GL_NONE; |
| return texrec->target; |
| } |
| |
| void GLClientState::getBoundFramebufferFormat( |
| GLenum target, |
| GLenum attachment, FboFormatInfo* res_info) const { |
| const FboProps& props = boundFboProps_const(target); |
| |
| 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); |
| |
| if (colorAttachmentIndex != -1) { |
| if (props.colorAttachmenti_hasRbo[colorAttachmentIndex]) { |
| res_info->type = FBO_ATTACHMENT_RENDERBUFFER; |
| res_info->rb_format = props.colorAttachmenti_rbos[colorAttachmentIndex]->format; |
| res_info->rb_multisamples = |
| props.colorAttachmenti_rbos[colorAttachmentIndex]->multisamples; |
| res_info->rb_external = |
| props.colorAttachmenti_rbos[colorAttachmentIndex]->boundEGLImage; |
| } else if (props.colorAttachmenti_hasTex[colorAttachmentIndex]) { |
| res_info->type = FBO_ATTACHMENT_TEXTURE; |
| res_info->tex_external = |
| props.colorAttachmenti_textures[colorAttachmentIndex]->boundEGLImage; |
| res_info->tex_internalformat = |
| props.colorAttachmenti_textures[colorAttachmentIndex]->internalformat; |
| res_info->tex_format = |
| props.colorAttachmenti_textures[colorAttachmentIndex]->format; |
| res_info->tex_type = |
| props.colorAttachmenti_textures[colorAttachmentIndex]->type; |
| res_info->tex_multisamples = |
| props.colorAttachmenti_textures[colorAttachmentIndex]->multisamples; |
| 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; |
| } |
| } |
| |
| switch (attachment) { |
| case GL_DEPTH_ATTACHMENT: |
| if (props.depthAttachment_hasRbo) { |
| res_info->type = FBO_ATTACHMENT_RENDERBUFFER; |
| res_info->rb_format = props.depthAttachment_rbo->format; |
| res_info->rb_multisamples = props.depthAttachment_rbo->multisamples; |
| res_info->rb_external = props.depthAttachment_rbo->boundEGLImage; |
| } else if (props.depthAttachment_hasTexObj) { |
| res_info->type = FBO_ATTACHMENT_TEXTURE; |
| res_info->tex_external = props.depthAttachment_texture->boundEGLImage; |
| res_info->tex_internalformat = props.depthAttachment_texture->internalformat; |
| res_info->tex_format = props.depthAttachment_texture->format; |
| res_info->tex_type = props.depthAttachment_texture->type; |
| res_info->tex_multisamples = props.depthAttachment_texture->multisamples; |
| res_info->tex_level = props.depthAttachment_texture_level; |
| res_info->tex_layer = props.depthAttachment_texture_layer; |
| } else { |
| res_info->type = FBO_ATTACHMENT_NONE; |
| } |
| break; |
| case GL_STENCIL_ATTACHMENT: |
| if (props.stencilAttachment_hasRbo) { |
| res_info->type = FBO_ATTACHMENT_RENDERBUFFER; |
| res_info->rb_format = props.stencilAttachment_rbo->format; |
| res_info->rb_multisamples = props.stencilAttachment_rbo->multisamples; |
| res_info->rb_external = props.stencilAttachment_rbo->boundEGLImage; |
| } else if (props.stencilAttachment_hasTexObj) { |
| res_info->type = FBO_ATTACHMENT_TEXTURE; |
| res_info->tex_external = props.stencilAttachment_texture->boundEGLImage; |
| res_info->tex_internalformat = props.stencilAttachment_texture->internalformat; |
| res_info->tex_format = props.stencilAttachment_texture->format; |
| res_info->tex_type = props.stencilAttachment_texture->type; |
| res_info->tex_multisamples = props.stencilAttachment_texture->multisamples; |
| res_info->tex_level = props.depthAttachment_texture_level; |
| res_info->tex_layer = props.depthAttachment_texture_layer; |
| } else { |
| res_info->type = FBO_ATTACHMENT_NONE; |
| } |
| break; |
| case GL_DEPTH_STENCIL_ATTACHMENT: |
| if (props.depthstencilAttachment_hasRbo) { |
| res_info->type = FBO_ATTACHMENT_RENDERBUFFER; |
| res_info->rb_format = props.depthstencilAttachment_rbo->format; |
| res_info->rb_multisamples = props.depthstencilAttachment_rbo->multisamples; |
| res_info->rb_external = props.depthstencilAttachment_rbo->boundEGLImage; |
| } else if (props.depthstencilAttachment_hasTexObj) { |
| res_info->type = FBO_ATTACHMENT_TEXTURE; |
| res_info->tex_external = props.depthstencilAttachment_texture->boundEGLImage; |
| res_info->tex_internalformat = props.depthstencilAttachment_texture->internalformat; |
| res_info->tex_format = props.depthstencilAttachment_texture->format; |
| res_info->tex_type = props.depthstencilAttachment_texture->type; |
| res_info->tex_multisamples = props.depthstencilAttachment_texture->multisamples; |
| res_info->tex_level = props.depthAttachment_texture_level; |
| res_info->tex_layer = props.depthAttachment_texture_layer; |
| } else { |
| res_info->type = FBO_ATTACHMENT_NONE; |
| } |
| break; |
| } |
| } |
| |
| FboAttachmentType GLClientState::getBoundFramebufferAttachmentType(GLenum target, GLenum attachment) const { |
| FboFormatInfo info; |
| getBoundFramebufferFormat(target, attachment, &info); |
| return info.type; |
| } |
| |
| int GLClientState::getMaxColorAttachments() const { |
| return m_hostDriverCaps.max_color_attachments; |
| } |
| |
| int GLClientState::getMaxDrawBuffers() const { |
| 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); |
| } |
| } |
| } |
| |
| bool GLClientState::isAttribIndexUsedByProgram(int index) { |
| auto info = currentAttribValidationInfo.get_const(index); |
| if (!info) return false; |
| if (!info->validInProgram) return false; |
| return true; |
| } |
| |
| void GLClientState::addFreshFramebuffer(GLuint name) { |
| FboProps props; |
| props.name = name; |
| props.previouslyBound = false; |
| |
| 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_hostDriverCaps.max_color_attachments, false); |
| props.depthAttachment_hasTexObj = false; |
| props.stencilAttachment_hasTexObj = false; |
| props.depthstencilAttachment_hasTexObj = false; |
| |
| 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_hostDriverCaps.max_color_attachments, false); |
| props.depthAttachment_hasRbo = false; |
| props.stencilAttachment_hasRbo = false; |
| props.depthstencilAttachment_hasRbo = false; |
| |
| props.defaultWidth = 0; |
| props.defaultHeight = 0; |
| |
| mFboState.fboData[name] = props; |
| } |
| |
| void GLClientState::addFramebuffers(GLsizei n, GLuint* framebuffers) { |
| for (size_t i = 0; i < n; i++) { |
| addFreshFramebuffer(framebuffers[i]); |
| } |
| } |
| |
| void GLClientState::removeFramebuffers(GLsizei n, const GLuint* framebuffers) { |
| RenderbufferInfo::ScopedView view(mRboState.rboData); |
| for (size_t i = 0; i < n; i++) { |
| if (framebuffers[i] != 0) { // Never remove the zero fb. |
| if (framebuffers[i] == mFboState.boundDrawFramebuffer) { |
| bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); |
| } |
| if (framebuffers[i] == mFboState.boundReadFramebuffer) { |
| bindFramebuffer(GL_READ_FRAMEBUFFER, 0); |
| } |
| mFboState.fboData.erase(framebuffers[i]); |
| } |
| } |
| } |
| |
| bool GLClientState::usedFramebufferName(GLuint name) const { |
| return mFboState.fboData.find(name) != mFboState.fboData.end(); |
| } |
| |
| FboProps& GLClientState::boundFboProps(GLenum target) { |
| switch (target) { |
| case GL_DRAW_FRAMEBUFFER: |
| return mFboState.fboData[mFboState.boundDrawFramebuffer]; |
| case GL_READ_FRAMEBUFFER: |
| return mFboState.fboData[mFboState.boundReadFramebuffer]; |
| case GL_FRAMEBUFFER: |
| return mFboState.fboData[mFboState.boundDrawFramebuffer]; |
| } |
| return mFboState.fboData[mFboState.boundDrawFramebuffer]; |
| } |
| |
| const FboProps& GLClientState::boundFboProps_const(GLenum target) const { |
| switch (target) { |
| case GL_DRAW_FRAMEBUFFER: |
| return mFboState.fboData.find(mFboState.boundDrawFramebuffer)->second; |
| case GL_READ_FRAMEBUFFER: |
| return mFboState.fboData.find(mFboState.boundReadFramebuffer)->second; |
| case GL_FRAMEBUFFER: |
| return mFboState.fboData.find(mFboState.boundDrawFramebuffer)->second; |
| } |
| return mFboState.fboData.find(mFboState.boundDrawFramebuffer)->second; |
| } |
| |
| void GLClientState::bindFramebuffer(GLenum target, GLuint name) { |
| // If unused, add it. |
| if (!usedFramebufferName(name)) { |
| addFreshFramebuffer(name); |
| } |
| switch (target) { |
| case GL_DRAW_FRAMEBUFFER: |
| mFboState.boundDrawFramebuffer = name; |
| break; |
| case GL_READ_FRAMEBUFFER: |
| mFboState.boundReadFramebuffer = name; |
| break; |
| default: // case GL_FRAMEBUFFER: |
| mFboState.boundDrawFramebuffer = name; |
| mFboState.boundReadFramebuffer = name; |
| break; |
| } |
| boundFboProps(target).previouslyBound = true; |
| } |
| |
| void GLClientState::setCheckFramebufferStatus(GLenum target, GLenum status) { |
| switch (target) { |
| case GL_DRAW_FRAMEBUFFER: |
| mFboState.drawFboCheckStatus = status; |
| break; |
| case GL_READ_FRAMEBUFFER: |
| mFboState.readFboCheckStatus = status; |
| break; |
| case GL_FRAMEBUFFER: |
| mFboState.drawFboCheckStatus = status; |
| break; |
| } |
| } |
| |
| void GLClientState::setFramebufferParameter(GLenum target, GLenum pname, GLint param) { |
| switch (pname) { |
| case GL_FRAMEBUFFER_DEFAULT_WIDTH: |
| boundFboProps(target).defaultWidth = param; |
| boundFboProps(target).completenessDirty = true; |
| break; |
| case GL_FRAMEBUFFER_DEFAULT_HEIGHT: |
| boundFboProps(target).defaultHeight = param; |
| boundFboProps(target).completenessDirty = true; |
| break; |
| } |
| } |
| |
| GLenum GLClientState::getCheckFramebufferStatus(GLenum target) const { |
| switch (target) { |
| case GL_DRAW_FRAMEBUFFER: |
| return mFboState.drawFboCheckStatus; |
| case GL_READ_FRAMEBUFFER: |
| return mFboState.readFboCheckStatus; |
| case GL_FRAMEBUFFER: |
| return mFboState.drawFboCheckStatus; |
| } |
| return mFboState.drawFboCheckStatus; |
| } |
| |
| GLuint GLClientState::boundFramebuffer(GLenum target) const { |
| return boundFboProps_const(target).name; |
| } |
| |
| // Texture objects for FBOs///////////////////////////////////////////////////// |
| |
| void GLClientState::attachTextureObject( |
| GLenum target, |
| GLenum attachment, GLuint texture, GLint level, GLint layer) { |
| |
| bool attach = texture != 0; |
| std::shared_ptr<TextureRec> texrec = getTextureRec(texture); |
| |
| int colorAttachmentIndex = |
| glUtilsColorAttachmentIndex(attachment); |
| |
| boundFboProps(target).completenessDirty = true; |
| |
| if (colorAttachmentIndex != -1) { |
| boundFboProps(target).colorAttachmenti_textures[colorAttachmentIndex] = texrec; |
| 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 = texrec; |
| 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 = texrec; |
| 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 = texrec; |
| boundFboProps(target).depthstencilAttachment_hasTexObj = attach; |
| boundFboProps(target).stencilAttachment_texture = texrec; |
| boundFboProps(target).stencilAttachment_hasTexObj = attach; |
| boundFboProps(target).depthAttachment_texture = texrec; |
| 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; |
| } |
| } |
| |
| std::shared_ptr<TextureRec> GLClientState::getFboAttachmentTexture(GLenum target, GLenum attachment) const { |
| std::shared_ptr<TextureRec> res = {}; // conservative |
| |
| int colorAttachmentIndex = |
| glUtilsColorAttachmentIndex(attachment); |
| |
| if (colorAttachmentIndex != -1) { |
| res = boundFboProps_const(target).colorAttachmenti_textures[colorAttachmentIndex]; |
| } |
| |
| switch (attachment) { |
| case GL_DEPTH_ATTACHMENT: |
| res = boundFboProps_const(target).depthAttachment_texture; |
| break; |
| case GL_STENCIL_ATTACHMENT: |
| res = boundFboProps_const(target).stencilAttachment_texture; |
| break; |
| case GL_DEPTH_STENCIL_ATTACHMENT: |
| res = boundFboProps_const(target).depthstencilAttachment_texture; |
| break; |
| } |
| return res; |
| } |
| |
| // RBOs for FBOs//////////////////////////////////////////////////////////////// |
| |
| void GLClientState::detachRbo(GLuint renderbuffer) { |
| 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); |
| } |
| |
| detachRboFromFbo(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, renderbuffer); |
| detachRboFromFbo(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, renderbuffer); |
| |
| detachRboFromFbo(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, renderbuffer); |
| detachRboFromFbo(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, renderbuffer); |
| |
| detachRboFromFbo(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, renderbuffer); |
| detachRboFromFbo(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, renderbuffer); |
| } |
| |
| void GLClientState::detachRboFromFbo(GLenum target, GLenum attachment, GLuint renderbuffer) { |
| int colorAttachmentIndex = |
| glUtilsColorAttachmentIndex(attachment); |
| |
| boundFboProps(target).completenessDirty = true; |
| |
| RenderbufferInfo::ScopedView view(mRboState.rboData); |
| auto renderBufferSharedPtr = view.get_shared_ptr(renderbuffer); |
| if (colorAttachmentIndex != -1) { |
| if (boundFboProps(target).colorAttachmenti_hasRbo[colorAttachmentIndex] && |
| boundFboProps(target).colorAttachmenti_rbos[colorAttachmentIndex] |
| == renderBufferSharedPtr) { |
| boundFboProps(target).colorAttachmenti_rbos[colorAttachmentIndex] = nullptr; |
| boundFboProps(target).colorAttachmenti_hasRbo[colorAttachmentIndex] = false; |
| } |
| } |
| |
| switch (attachment) { |
| case GL_DEPTH_ATTACHMENT: |
| if (boundFboProps(target).depthAttachment_rbo == renderBufferSharedPtr && |
| boundFboProps(target).depthAttachment_hasRbo) { |
| boundFboProps(target).depthAttachment_rbo = nullptr; |
| boundFboProps(target).depthAttachment_hasRbo = false; |
| } |
| break; |
| case GL_STENCIL_ATTACHMENT: |
| if (boundFboProps(target).stencilAttachment_rbo == renderBufferSharedPtr && |
| boundFboProps(target).stencilAttachment_hasRbo) { |
| boundFboProps(target).stencilAttachment_rbo = nullptr; |
| boundFboProps(target).stencilAttachment_hasRbo = false; |
| } |
| break; |
| case GL_DEPTH_STENCIL_ATTACHMENT: |
| if (boundFboProps(target).depthAttachment_rbo == renderBufferSharedPtr && |
| boundFboProps(target).depthAttachment_hasRbo) { |
| boundFboProps(target).depthAttachment_rbo = nullptr; |
| boundFboProps(target).depthAttachment_hasRbo = false; |
| } |
| if (boundFboProps(target).stencilAttachment_rbo == renderBufferSharedPtr && |
| boundFboProps(target).stencilAttachment_hasRbo) { |
| boundFboProps(target).stencilAttachment_rbo = nullptr; |
| boundFboProps(target).stencilAttachment_hasRbo = false; |
| } |
| if (boundFboProps(target).depthstencilAttachment_rbo == renderBufferSharedPtr && |
| boundFboProps(target).depthstencilAttachment_hasRbo) { |
| boundFboProps(target).depthstencilAttachment_rbo = nullptr; |
| boundFboProps(target).depthstencilAttachment_hasRbo = false; |
| } |
| break; |
| } |
| } |
| |
| void GLClientState::attachRbo(GLenum target, GLenum attachment, GLuint renderbuffer) { |
| |
| bool attach = 0 != renderbuffer; |
| |
| int colorAttachmentIndex = |
| glUtilsColorAttachmentIndex(attachment); |
| |
| boundFboProps(target).completenessDirty = true; |
| |
| RenderbufferInfo::ScopedView view(mRboState.rboData); |
| auto rboSharedPtr = view.get_or_add_shared_ptr(renderbuffer); |
| if (colorAttachmentIndex != -1) { |
| boundFboProps(target).colorAttachmenti_rbos[colorAttachmentIndex] = rboSharedPtr; |
| boundFboProps(target).colorAttachmenti_hasRbo[colorAttachmentIndex] = attach; |
| } |
| |
| switch (attachment) { |
| case GL_DEPTH_ATTACHMENT: |
| boundFboProps(target).depthAttachment_rbo = rboSharedPtr; |
| boundFboProps(target).depthAttachment_hasRbo = attach; |
| break; |
| case GL_STENCIL_ATTACHMENT: |
| boundFboProps(target).stencilAttachment_rbo = rboSharedPtr; |
| boundFboProps(target).stencilAttachment_hasRbo = attach; |
| break; |
| case GL_DEPTH_STENCIL_ATTACHMENT: |
| boundFboProps(target).depthAttachment_rbo = rboSharedPtr; |
| boundFboProps(target).depthAttachment_hasRbo = attach; |
| boundFboProps(target).stencilAttachment_rbo = rboSharedPtr; |
| boundFboProps(target).stencilAttachment_hasRbo = attach; |
| boundFboProps(target).depthstencilAttachment_rbo = rboSharedPtr; |
| boundFboProps(target).depthstencilAttachment_hasRbo = attach; |
| break; |
| } |
| } |
| |
| std::shared_ptr<RboProps> GLClientState::getFboAttachmentRbo(GLenum target, GLenum attachment) const { |
| int colorAttachmentIndex = |
| glUtilsColorAttachmentIndex(attachment); |
| |
| if (colorAttachmentIndex != -1) { |
| return boundFboProps_const(target).colorAttachmenti_rbos[colorAttachmentIndex]; |
| } |
| |
| switch (attachment) { |
| case GL_DEPTH_ATTACHMENT: |
| return boundFboProps_const(target).depthAttachment_rbo; |
| case GL_STENCIL_ATTACHMENT: |
| return boundFboProps_const(target).stencilAttachment_rbo; |
| case GL_DEPTH_STENCIL_ATTACHMENT: |
| return boundFboProps_const(target).depthstencilAttachment_rbo; |
| } |
| |
| // Bad attachment enum. Should be unreachable. |
| return nullptr; |
| } |
| |
| void GLClientState::setFboCompletenessDirtyForTexture(GLuint texture) { |
| std::shared_ptr<TextureRec> texrec = getTextureRec(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 (texrec == props.colorAttachmenti_textures[i]) { |
| props.completenessDirty = true; |
| return; |
| } |
| } |
| } |
| |
| if (props.depthAttachment_hasTexObj) { |
| if (texrec == props.depthAttachment_texture) { |
| props.completenessDirty = true; |
| return; |
| } |
| } |
| |
| if (props.stencilAttachment_hasTexObj) { |
| if (texrec == props.stencilAttachment_texture) { |
| props.completenessDirty = true; |
| return; |
| } |
| } |
| |
| if (props.depthstencilAttachment_hasTexObj) { |
| if (texrec == props.depthstencilAttachment_texture) { |
| props.completenessDirty = true; |
| return; |
| } |
| } |
| ++it; |
| } |
| } |
| |
| void GLClientState::setFboCompletenessDirtyForRbo(std::shared_ptr<RboProps> 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_hasRbo[i]) { |
| if (rbo == props.colorAttachmenti_rbos[i]) { |
| props.completenessDirty = true; |
| return; |
| } |
| } |
| } |
| |
| if (props.depthAttachment_hasRbo) { |
| if (rbo == props.depthAttachment_rbo) { |
| props.completenessDirty = true; |
| return; |
| } |
| } |
| |
| if (props.stencilAttachment_hasRbo) { |
| if (rbo == props.stencilAttachment_rbo) { |
| props.completenessDirty = |