/* ** Copyright 2006, 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 <stdio.h> #include <stdlib.h> #include "context.h" #include "TextureObjectManager.h" namespace android { // ---------------------------------------------------------------------------- EGLTextureObject::EGLTextureObject() : mCount(0), mSize(0) { init(); } EGLTextureObject::~EGLTextureObject() { if (!direct) { if (mSize && surface.data) free(surface.data); if (mMipmaps) freeMipmaps(); } } void EGLTextureObject::init() { memset(&surface, 0, sizeof(surface)); surface.version = sizeof(surface); mMipmaps = 0; mNumExtraLod = 0; mIsComplete = false; wraps = GL_REPEAT; wrapt = GL_REPEAT; min_filter = GL_LINEAR; mag_filter = GL_LINEAR; internalformat = 0; memset(crop_rect, 0, sizeof(crop_rect)); generate_mipmap = GL_FALSE; direct = GL_FALSE; } void EGLTextureObject::copyParameters(const sp<EGLTextureObject>& old) { wraps = old->wraps; wrapt = old->wrapt; min_filter = old->min_filter; mag_filter = old->mag_filter; memcpy(crop_rect, old->crop_rect, sizeof(crop_rect)); generate_mipmap = old->generate_mipmap; direct = old->direct; } status_t EGLTextureObject::allocateMipmaps() { // here, by construction, mMipmaps=0 && mNumExtraLod=0 if (!surface.data) return NO_INIT; int w = surface.width; int h = surface.height; const int numLods = 31 - gglClz(max(w,h)); if (numLods <= 0) return NO_ERROR; mMipmaps = (GGLSurface*)malloc(numLods * sizeof(GGLSurface)); if (!mMipmaps) return NO_MEMORY; memset(mMipmaps, 0, numLods * sizeof(GGLSurface)); mNumExtraLod = numLods; return NO_ERROR; } void EGLTextureObject::freeMipmaps() { if (mMipmaps) { for (int i=0 ; i<mNumExtraLod ; i++) { if (mMipmaps[i].data) { free(mMipmaps[i].data); } } free(mMipmaps); mMipmaps = 0; mNumExtraLod = 0; } } const GGLSurface& EGLTextureObject::mip(int lod) const { if (lod<=0 || !mMipmaps) return surface; lod = min(lod-1, mNumExtraLod-1); return mMipmaps[lod]; } GGLSurface& EGLTextureObject::editMip(int lod) { return const_cast<GGLSurface&>(mip(lod)); } status_t EGLTextureObject::setSurface(GGLSurface const* s) { // XXX: glFlush() on 's' if (mSize && surface.data) { free(surface.data); } surface = *s; internalformat = 0; // we should keep the crop_rect, but it's delicate because // the new size of the surface could make it invalid. // so for now, we just loose it. memset(crop_rect, 0, sizeof(crop_rect)); // it would be nice if we could keep the generate_mipmap flag, // we would have to generate them right now though. generate_mipmap = GL_FALSE; direct = GL_TRUE; mSize = 0; // we don't own this surface if (mMipmaps) freeMipmaps(); mIsComplete = true; return NO_ERROR; } status_t EGLTextureObject::reallocate( GLint level, int w, int h, int s, int format, int compressedFormat, int bpr) { const size_t size = h * bpr; if (level == 0) { if (size!=mSize || !surface.data) { if (mSize && surface.data) { free(surface.data); } surface.data = (GGLubyte*)malloc(size); if (!surface.data) { mSize = 0; mIsComplete = false; return NO_MEMORY; } mSize = size; } surface.version = sizeof(GGLSurface); surface.width = w; surface.height = h; surface.stride = s; surface.format = format; surface.compressedFormat = compressedFormat; if (mMipmaps) freeMipmaps(); mIsComplete = true; } else { if (!mMipmaps) { if (allocateMipmaps() != NO_ERROR) return NO_MEMORY; } LOGW_IF(level-1 >= mNumExtraLod, "specifying mipmap level %d, but # of level is %d", level, mNumExtraLod+1); GGLSurface& mipmap = editMip(level); if (mipmap.data) free(mipmap.data); mipmap.data = (GGLubyte*)malloc(size); if (!mipmap.data) { memset(&mipmap, 0, sizeof(GGLSurface)); mIsComplete = false; return NO_MEMORY; } mipmap.version = sizeof(GGLSurface); mipmap.width = w; mipmap.height = h; mipmap.stride = s; mipmap.format = format; mipmap.compressedFormat = compressedFormat; // check if the texture is complete mIsComplete = true; const GGLSurface* prev = &surface; for (int i=0 ; i<mNumExtraLod ; i++) { const GGLSurface* curr = mMipmaps + i; if (curr->format != surface.format) { mIsComplete = false; break; } uint32_t w = (prev->width >> 1) ? : 1; uint32_t h = (prev->height >> 1) ? : 1; if (w != curr->width || h != curr->height) { mIsComplete = false; break; } prev = curr; } } return NO_ERROR; } // ---------------------------------------------------------------------------- EGLSurfaceManager::EGLSurfaceManager() : TokenManager(), mCount(0) { } EGLSurfaceManager::~EGLSurfaceManager() { // everything gets freed automatically here... } sp<EGLTextureObject> EGLSurfaceManager::createTexture(GLuint name) { sp<EGLTextureObject> result; Mutex::Autolock _l(mLock); if (mTextures.indexOfKey(name) >= 0) return result; // already exists! result = new EGLTextureObject(); status_t err = mTextures.add(name, result); if (err < 0) result.clear(); return result; } sp<EGLTextureObject> EGLSurfaceManager::removeTexture(GLuint name) { Mutex::Autolock _l(mLock); const ssize_t index = mTextures.indexOfKey(name); if (index >= 0) { sp<EGLTextureObject> result(mTextures.valueAt(index)); mTextures.removeItemsAt(index); return result; } return 0; } sp<EGLTextureObject> EGLSurfaceManager::replaceTexture(GLuint name) { sp<EGLTextureObject> tex; Mutex::Autolock _l(mLock); const ssize_t index = mTextures.indexOfKey(name); if (index >= 0) { const sp<EGLTextureObject>& old = mTextures.valueAt(index); const uint32_t refs = old->getStrongCount(); if (ggl_likely(refs == 1)) { // we're the only owner tex = old; } else { // keep the texture's parameters tex = new EGLTextureObject(); tex->copyParameters(old); mTextures.removeItemsAt(index); mTextures.add(name, tex); } } return tex; } void EGLSurfaceManager::deleteTextures(GLsizei n, const GLuint *tokens) { // free all textures Mutex::Autolock _l(mLock); for (GLsizei i=0 ; i<n ; i++) { const GLuint t(*tokens++); if (t) { mTextures.removeItem(t); } } } sp<EGLTextureObject> EGLSurfaceManager::texture(GLuint name) { Mutex::Autolock _l(mLock); const ssize_t index = mTextures.indexOfKey(name); if (index >= 0) return mTextures.valueAt(index); return 0; } // ---------------------------------------------------------------------------- }; // namespace android