/* * 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 "GLSharedGroup.h" /**** KeyedVector utilities ****/ template <typename T> static void clearObjectMap(android::DefaultKeyedVector<GLuint, T>& v) { for (size_t i = 0; i < v.size(); i++) delete v.valueAt(i); v.clear(); } /**** BufferData ****/ BufferData::BufferData() : m_size(0), m_usage(0), m_mapped(false) {}; BufferData::BufferData(GLsizeiptr size, void * data) : m_size(size), m_usage(0), m_mapped(false) { void * buffer = NULL; if (size>0) buffer = m_fixedBuffer.alloc(size); if (data) memcpy(buffer, data, size); } /**** ProgramData ****/ ProgramData::ProgramData() : m_numIndexes(0), m_initialized(false), m_locShiftWAR(false) { m_Indexes = NULL; } void ProgramData::initProgramData(GLuint numIndexes) { m_initialized = true; m_numIndexes = numIndexes; delete[] m_Indexes; m_Indexes = new IndexInfo[numIndexes]; m_locShiftWAR = false; } bool ProgramData::isInitialized() { return m_initialized; } ProgramData::~ProgramData() { delete[] m_Indexes; m_Indexes = NULL; } void ProgramData::setIndexInfo(GLuint index, GLint base, GLint size, GLenum type) { if (index>=m_numIndexes) return; m_Indexes[index].base = base; m_Indexes[index].size = size; m_Indexes[index].type = type; if (index > 0) { m_Indexes[index].appBase = m_Indexes[index-1].appBase + m_Indexes[index-1].size; } else { m_Indexes[index].appBase = 0; } m_Indexes[index].hostLocsPerElement = 1; m_Indexes[index].flags = 0; m_Indexes[index].samplerValue = 0; } void ProgramData::setIndexFlags(GLuint index, GLuint flags) { if (index >= m_numIndexes) return; m_Indexes[index].flags |= flags; } GLuint ProgramData::getIndexForLocation(GLint location) { GLuint index = m_numIndexes; GLint minDist = -1; for (GLuint i=0;i<m_numIndexes;++i) { GLint dist = location - m_Indexes[i].base; if (dist >= 0 && (minDist < 0 || dist < minDist)) { index = i; minDist = dist; } } return index; } GLenum ProgramData::getTypeForLocation(GLint location) { GLuint index = getIndexForLocation(location); if (index<m_numIndexes) { return m_Indexes[index].type; } return 0; } void ProgramData::setupLocationShiftWAR() { m_locShiftWAR = false; for (GLuint i=0; i<m_numIndexes; i++) { if (0 != (m_Indexes[i].base & 0xffff)) { return; } } // if we have one uniform at location 0, we do not need the WAR. if (m_numIndexes > 1) { m_locShiftWAR = true; } } GLint ProgramData::locationWARHostToApp(GLint hostLoc, GLint arrIndex) { if (!m_locShiftWAR) return hostLoc; GLuint index = getIndexForLocation(hostLoc); if (index<m_numIndexes) { if (arrIndex > 0) { m_Indexes[index].hostLocsPerElement = (hostLoc - m_Indexes[index].base) / arrIndex; } return m_Indexes[index].appBase + arrIndex; } return -1; } GLint ProgramData::locationWARAppToHost(GLint appLoc) { if (!m_locShiftWAR) return appLoc; for(GLuint i=0; i<m_numIndexes; i++) { GLint elemIndex = appLoc - m_Indexes[i].appBase; if (elemIndex >= 0 && elemIndex < m_Indexes[i].size) { return m_Indexes[i].base + elemIndex * m_Indexes[i].hostLocsPerElement; } } return -1; } GLint ProgramData::getNextSamplerUniform(GLint index, GLint* val, GLenum* target) { for (GLint i = index + 1; i >= 0 && i < (GLint)m_numIndexes; i++) { if (m_Indexes[i].type == GL_SAMPLER_2D) { if (val) *val = m_Indexes[i].samplerValue; if (target) { if (m_Indexes[i].flags & INDEX_FLAG_SAMPLER_EXTERNAL) { *target = GL_TEXTURE_EXTERNAL_OES; } else { *target = GL_TEXTURE_2D; } } return i; } } return -1; } bool ProgramData::setSamplerUniform(GLint appLoc, GLint val, GLenum* target) { for (GLuint i = 0; i < m_numIndexes; i++) { GLint elemIndex = appLoc - m_Indexes[i].appBase; if (elemIndex >= 0 && elemIndex < m_Indexes[i].size) { if (m_Indexes[i].type == GL_SAMPLER_2D) { m_Indexes[i].samplerValue = val; if (target) { if (m_Indexes[i].flags & INDEX_FLAG_SAMPLER_EXTERNAL) { *target = GL_TEXTURE_EXTERNAL_OES; } else { *target = GL_TEXTURE_2D; } } return true; } } } return false; } bool ProgramData::attachShader(GLuint shader) { size_t n = m_shaders.size(); for (size_t i = 0; i < n; i++) { if (m_shaders[i] == shader) { return false; } } // AKA m_shaders.push_back(), but that has an ambiguous call to insertAt() // due to the default parameters. This is the desired insertAt() overload. m_shaders.insertAt(shader, m_shaders.size(), 1); return true; } bool ProgramData::detachShader(GLuint shader) { size_t n = m_shaders.size(); for (size_t i = 0; i < n; i++) { if (m_shaders[i] == shader) { m_shaders.removeAt(i); return true; } } return false; } /***** GLSharedGroup ****/ GLSharedGroup::GLSharedGroup() : m_buffers(android::DefaultKeyedVector<GLuint, BufferData*>(NULL)), m_programs(android::DefaultKeyedVector<GLuint, ProgramData*>(NULL)), m_shaders(android::DefaultKeyedVector<GLuint, ShaderData*>(NULL)), m_shaderPrograms(android::DefaultKeyedVector<GLuint, ShaderProgramData*>(NULL)) { } GLSharedGroup::~GLSharedGroup() { m_buffers.clear(); m_programs.clear(); clearObjectMap(m_buffers); clearObjectMap(m_programs); clearObjectMap(m_shaders); clearObjectMap(m_shaderPrograms); } bool GLSharedGroup::isShaderOrProgramObject(GLuint obj) { android::AutoMutex _lock(m_lock); return ((m_shaders.valueFor(obj)!=NULL) || (m_programs.valueFor(obj)!=NULL) || (m_shaderPrograms.valueFor(m_shaderProgramIdMap[obj]) !=NULL)); } BufferData * GLSharedGroup::getBufferData(GLuint bufferId) { android::AutoMutex _lock(m_lock); return m_buffers.valueFor(bufferId); } SharedTextureDataMap* GLSharedGroup::getTextureData() { return &m_textureRecs; } void GLSharedGroup::addBufferData(GLuint bufferId, GLsizeiptr size, void * data) { android::AutoMutex _lock(m_lock); m_buffers.add(bufferId, new BufferData(size, data)); } void GLSharedGroup::updateBufferData(GLuint bufferId, GLsizeiptr size, void * data) { android::AutoMutex _lock(m_lock); ssize_t idx = m_buffers.indexOfKey(bufferId); if (idx >= 0) { delete m_buffers.valueAt(idx); m_buffers.editValueAt(idx) = new BufferData(size, data); } else { m_buffers.add(bufferId, new BufferData(size, data)); } } void GLSharedGroup::setBufferUsage(GLuint bufferId, GLenum usage) { android::AutoMutex _lock(m_lock); ssize_t idx = m_buffers.indexOfKey(bufferId); if (idx >= 0) { m_buffers.editValueAt(idx)->m_usage = usage; } } void GLSharedGroup::setBufferMapped(GLuint bufferId, bool mapped) { BufferData * buf = m_buffers.valueFor(bufferId); if (!buf) return; buf->m_mapped = mapped; } GLenum GLSharedGroup::getBufferUsage(GLuint bufferId) { BufferData * buf = m_buffers.valueFor(bufferId); if (!buf) return 0; return buf->m_usage; } bool GLSharedGroup::isBufferMapped(GLuint bufferId) { BufferData * buf = m_buffers.valueFor(bufferId); if (!buf) return false; return buf->m_mapped; } GLenum GLSharedGroup::subUpdateBufferData(GLuint bufferId, GLintptr offset, GLsizeiptr size, void * data) { android::AutoMutex _lock(m_lock); BufferData * buf = m_buffers.valueFor(bufferId); if ((!buf) || (buf->m_size < offset+size) || (offset < 0) || (size<0)) return GL_INVALID_VALUE; //it's safe to update now memcpy((char*)buf->m_fixedBuffer.ptr() + offset, data, size); buf->m_indexRangeCache.invalidateRange((size_t)offset, (size_t)size); return GL_NO_ERROR; } void GLSharedGroup::deleteBufferData(GLuint bufferId) { android::AutoMutex _lock(m_lock); ssize_t idx = m_buffers.indexOfKey(bufferId); if (idx >= 0) { delete m_buffers.valueAt(idx); m_buffers.removeItemsAt(idx); } } void GLSharedGroup::addProgramData(GLuint program) { android::AutoMutex _lock(m_lock); ProgramData *pData = m_programs.valueFor(program); if (pData) { m_programs.removeItem(program); delete pData; } m_programs.add(program,new ProgramData()); } void GLSharedGroup::initProgramData(GLuint program, GLuint numIndexes) { android::AutoMutex _lock(m_lock); ProgramData *pData = m_programs.valueFor(program); if (pData) { pData->initProgramData(numIndexes); } } bool GLSharedGroup::isProgramInitialized(GLuint program) { android::AutoMutex _lock(m_lock); ProgramData* pData = m_programs.valueFor(program); if (pData) { return pData->isInitialized(); } if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return false; ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); if (spData) { return spData->programData->isInitialized(); } return false; } void GLSharedGroup::deleteProgramData(GLuint program) { android::AutoMutex _lock(m_lock); ProgramData *pData = m_programs.valueFor(program); if (pData) { delete pData; } m_programs.removeItem(program); if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return; ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); if (spData) { delete spData; } m_shaderPrograms.removeItem(m_shaderProgramIdMap[program]); m_shaderProgramIdMap.erase(program); } // No such thing for separable shader programs. void GLSharedGroup::attachShader(GLuint program, GLuint shader) { android::AutoMutex _lock(m_lock); ProgramData* programData = m_programs.valueFor(program); ssize_t idx = m_shaders.indexOfKey(shader); if (programData && idx >= 0) { if (programData->attachShader(shader)) { refShaderDataLocked(idx); } } } void GLSharedGroup::detachShader(GLuint program, GLuint shader) { android::AutoMutex _lock(m_lock); ProgramData* programData = m_programs.valueFor(program); ssize_t idx = m_shaders.indexOfKey(shader); if (programData && idx >= 0) { if (programData->detachShader(shader)) { unrefShaderDataLocked(idx); } } } // Not needed/used for separate shader programs. void GLSharedGroup::setProgramIndexInfo(GLuint program, GLuint index, GLint base, GLint size, GLenum type, const char* name) { android::AutoMutex _lock(m_lock); ProgramData* pData = m_programs.valueFor(program); if (pData) { pData->setIndexInfo(index,base,size,type); if (type == GL_SAMPLER_2D) { size_t n = pData->getNumShaders(); for (size_t i = 0; i < n; i++) { GLuint shaderId = pData->getShader(i); ShaderData* shader = m_shaders.valueFor(shaderId); if (!shader) continue; ShaderData::StringList::iterator nameIter = shader->samplerExternalNames.begin(); ShaderData::StringList::iterator nameEnd = shader->samplerExternalNames.end(); while (nameIter != nameEnd) { if (*nameIter == name) { pData->setIndexFlags(index, ProgramData::INDEX_FLAG_SAMPLER_EXTERNAL); break; } ++nameIter; } } } } } GLenum GLSharedGroup::getProgramUniformType(GLuint program, GLint location) { android::AutoMutex _lock(m_lock); ProgramData* pData = m_programs.valueFor(program); GLenum type=0; if (pData) { type = pData->getTypeForLocation(location); } if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return type; ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); if (spData) { type = spData->programData->getTypeForLocation(location); } return type; } bool GLSharedGroup::isProgram(GLuint program) { android::AutoMutex _lock(m_lock); ProgramData* pData = m_programs.valueFor(program); if (pData) return true; if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return false; ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); if (spData) return true; return false; } void GLSharedGroup::setupLocationShiftWAR(GLuint program) { android::AutoMutex _lock(m_lock); ProgramData* pData = m_programs.valueFor(program); if (pData) pData->setupLocationShiftWAR(); } GLint GLSharedGroup::locationWARHostToApp(GLuint program, GLint hostLoc, GLint arrIndex) { android::AutoMutex _lock(m_lock); ProgramData* pData = m_programs.valueFor(program); if (pData) return pData->locationWARHostToApp(hostLoc, arrIndex); if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return hostLoc; ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); if (spData) return spData->programData->locationWARHostToApp(hostLoc, arrIndex); return hostLoc; } GLint GLSharedGroup::locationWARAppToHost(GLuint program, GLint appLoc) { android::AutoMutex _lock(m_lock); ProgramData* pData = m_programs.valueFor(program); if (pData) return pData->locationWARAppToHost(appLoc); if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return appLoc; ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); if (spData) return spData->programData->locationWARAppToHost(appLoc); return appLoc; } bool GLSharedGroup::needUniformLocationWAR(GLuint program) { android::AutoMutex _lock(m_lock); ProgramData* pData = m_programs.valueFor(program); if (pData) return pData->needUniformLocationWAR(); if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return false; ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); if (spData) return spData->programData->needUniformLocationWAR(); return false; } GLint GLSharedGroup::getNextSamplerUniform(GLuint program, GLint index, GLint* val, GLenum* target) const { android::AutoMutex _lock(m_lock); ProgramData* pData = m_programs.valueFor(program); if (pData) return pData->getNextSamplerUniform(index, val, target); if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return -1; ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap.find(program)->second); if (spData) return spData->programData->getNextSamplerUniform(index, val, target); return -1; } bool GLSharedGroup::setSamplerUniform(GLuint program, GLint appLoc, GLint val, GLenum* target) { android::AutoMutex _lock(m_lock); ProgramData* pData = m_programs.valueFor(program); if (pData) return pData->setSamplerUniform(appLoc, val, target); if (m_shaderProgramIdMap.find(program) == m_shaderProgramIdMap.end()) return false; ShaderProgramData* spData = m_shaderPrograms.valueFor(m_shaderProgramIdMap[program]); if (spData) return spData->programData->setSamplerUniform(appLoc, val, target); return false; } bool GLSharedGroup::isShader(GLuint shader) { android::AutoMutex _lock(m_lock); ShaderData* pData = m_shaders.valueFor(shader); return (pData!=NULL); } bool GLSharedGroup::addShaderData(GLuint shader) { android::AutoMutex _lock(m_lock); ShaderData* data = new ShaderData; if (data) { if (m_shaders.add(shader, data) < 0) { delete data; data = NULL; } data->refcount = 1; } return data != NULL; } ShaderData* GLSharedGroup::getShaderData(GLuint shader) { android::AutoMutex _lock(m_lock); return m_shaders.valueFor(shader); } void GLSharedGroup::unrefShaderData(GLuint shader) { android::AutoMutex _lock(m_lock); ssize_t idx = m_shaders.indexOfKey(shader); if (idx >= 0) { unrefShaderDataLocked(idx); } } void GLSharedGroup::refShaderDataLocked(ssize_t shaderIdx) { assert(shaderIdx >= 0 && shaderIdx <= m_shaders.size()); ShaderData* data = m_shaders.valueAt(shaderIdx); data->refcount++; } void GLSharedGroup::unrefShaderDataLocked(ssize_t shaderIdx) { assert(shaderIdx >= 0 && shaderIdx <= m_shaders.size()); ShaderData* data = m_shaders.valueAt(shaderIdx); if (--data->refcount == 0) { delete data; m_shaders.removeItemsAt(shaderIdx); } } uint32_t GLSharedGroup::addNewShaderProgramData() { android::AutoMutex _lock(m_lock); ShaderProgramData* data = new ShaderProgramData; uint32_t currId = m_shaderProgramId; ALOGD("%s: new data %p id %u", __FUNCTION__, data, currId); m_shaderPrograms.add(currId, data); m_shaderProgramId++; return currId; } void GLSharedGroup::associateGLShaderProgram(GLuint shaderProgramName, uint32_t shaderProgramId) { android::AutoMutex _lock(m_lock); m_shaderProgramIdMap[shaderProgramName] = shaderProgramId; } ShaderProgramData* GLSharedGroup::getShaderProgramDataById(uint32_t id) { android::AutoMutex _lock(m_lock); ShaderProgramData* res = m_shaderPrograms.valueFor(id); ALOGD("%s: id=%u res=%p", __FUNCTION__, id, res); return res; } ShaderProgramData* GLSharedGroup::getShaderProgramData(GLuint shaderProgramName) { android::AutoMutex _lock(m_lock); return m_shaderPrograms.valueFor(m_shaderProgramIdMap[shaderProgramName]); } void GLSharedGroup::deleteShaderProgramDataById(uint32_t id) { android::AutoMutex _lock(m_lock); ShaderProgramData* data = m_shaderPrograms.valueFor(id); delete data; m_shaderPrograms.removeItemsAt(id); } void GLSharedGroup::deleteShaderProgramData(GLuint shaderProgramName) { android::AutoMutex _lock(m_lock); uint32_t id = m_shaderProgramIdMap[shaderProgramName]; ShaderProgramData* data = m_shaderPrograms.valueFor(id); delete data; m_shaderPrograms.removeItemsAt(id); m_shaderProgramIdMap.erase(shaderProgramName); } void GLSharedGroup::initShaderProgramData(GLuint shaderProgram, GLuint numIndices) { ShaderProgramData* spData = getShaderProgramData(shaderProgram); spData->programData->initProgramData(numIndices); } void GLSharedGroup::setShaderProgramIndexInfo(GLuint shaderProgram, GLuint index, GLint base, GLint size, GLenum type, const char* name) { ShaderProgramData* spData = getShaderProgramData(shaderProgram); ProgramData* pData = spData->programData; ShaderData* sData = spData->shaderData; if (pData) { pData->setIndexInfo(index, base, size, type); if (type == GL_SAMPLER_2D) { ShaderData::StringList::iterator nameIter = sData->samplerExternalNames.begin(); ShaderData::StringList::iterator nameEnd = sData->samplerExternalNames.end(); while (nameIter != nameEnd) { if (*nameIter == name) { pData->setIndexFlags(index, ProgramData::INDEX_FLAG_SAMPLER_EXTERNAL); break; } ++nameIter; } } } } void GLSharedGroup::setupShaderProgramLocationShiftWAR(GLuint shaderProgram) { ShaderProgramData* spData = getShaderProgramData(shaderProgram); spData->programData->setupLocationShiftWAR(); }