| /* |
| ** 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 |