diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
commit | 9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch) | |
tree | d88beb88001f2482911e3d28e43833b50e4b4e97 /opengl/libagl | |
parent | d83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff) | |
download | frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.zip frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.gz frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'opengl/libagl')
32 files changed, 11463 insertions, 0 deletions
diff --git a/opengl/libagl/Android.mk b/opengl/libagl/Android.mk new file mode 100644 index 0000000..99efe4c --- /dev/null +++ b/opengl/libagl/Android.mk @@ -0,0 +1,39 @@ +LOCAL_PATH:= $(call my-dir) + +# +# Build the software OpenGL ES library +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + egl.cpp \ + state.cpp \ + texture.cpp \ + Tokenizer.cpp \ + TokenManager.cpp \ + TextureObjectManager.cpp \ + BufferObjectManager.cpp \ + array.cpp.arm \ + fp.cpp.arm \ + light.cpp.arm \ + matrix.cpp.arm \ + mipmap.cpp.arm \ + primitives.cpp.arm \ + vertex.cpp.arm + +ifeq ($(TARGET_ARCH),arm) + LOCAL_SRC_FILES += fixed_asm.S iterators.S + LOCAL_CFLAGS += -fstrict-aliasing +endif + +ifneq ($(TARGET_SIMULATOR),true) + # we need to access the private Bionic header <bionic_tls.h> + LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/private +endif + +LOCAL_SHARED_LIBRARIES := libcutils libutils libpixelflinger +LOCAL_LDLIBS := -lpthread -ldl +LOCAL_MODULE:= libagl + +include $(BUILD_SHARED_LIBRARY) diff --git a/opengl/libagl/BufferObjectManager.cpp b/opengl/libagl/BufferObjectManager.cpp new file mode 100644 index 0000000..6bf28ee --- /dev/null +++ b/opengl/libagl/BufferObjectManager.cpp @@ -0,0 +1,103 @@ +/* + ** Copyright 2008, 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 <stdint.h> +#include <stddef.h> +#include <sys/types.h> + +#include <utils/Atomic.h> +#include <utils/RefBase.h> +#include <utils/KeyedVector.h> +#include <utils/Errors.h> + +#include <GLES/gl.h> + +#include "BufferObjectManager.h" + + +namespace android { + +using namespace gl; + +// ---------------------------------------------------------------------------- + +EGLBufferObjectManager::EGLBufferObjectManager() +: TokenManager(), mCount(0) +{ +} + +EGLBufferObjectManager::~EGLBufferObjectManager() +{ + // destroy all the buffer objects and their storage + GLsizei n = mBuffers.size(); + for (GLsizei i=0 ; i<n ; i++) { + buffer_t* bo = mBuffers.valueAt(i); + free(bo->data); + delete bo; + } +} + +buffer_t const* EGLBufferObjectManager::bind(GLuint buffer) +{ + Mutex::Autolock _l(mLock); + int32_t i = mBuffers.indexOfKey(buffer); + if (i >= 0) { + return mBuffers.valueAt(i); + } + buffer_t* bo = new buffer_t; + bo->data = 0; + bo->usage = GL_STATIC_DRAW; + bo->size = 0; + bo->name = buffer; + mBuffers.add(buffer, bo); + return bo; +} + +int EGLBufferObjectManager::allocateStore(buffer_t* bo, + GLsizeiptr size, GLenum usage) +{ + Mutex::Autolock _l(mLock); + if (size != bo->size) { + uint8_t* data = (uint8_t*)malloc(size); + if (data == 0) + return -1; + free(bo->data); + bo->data = data; + bo->size = size; + } + bo->usage = usage; + return 0; +} + +void EGLBufferObjectManager::deleteBuffers(GLsizei n, const GLuint* buffers) +{ + Mutex::Autolock _l(mLock); + while (n--) { + const GLuint t = *buffers++; + if (t) { + int32_t index = mBuffers.indexOfKey(t); + if (index >= 0) { + buffer_t* bo = mBuffers.valueAt(index); + free(bo->data); + mBuffers.removeItemsAt(index); + delete bo; + } + } + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/opengl/libagl/BufferObjectManager.h b/opengl/libagl/BufferObjectManager.h new file mode 100644 index 0000000..9e9340a --- /dev/null +++ b/opengl/libagl/BufferObjectManager.h @@ -0,0 +1,85 @@ +/* + ** + ** 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. + */ + +#ifndef ANDROID_OPENGLES_BUFFER_OBJECT_MANAGER_H +#define ANDROID_OPENGLES_BUFFER_OBJECT_MANAGER_H + +#include <stdint.h> +#include <stddef.h> +#include <sys/types.h> + +#include <utils/Atomic.h> +#include <utils/RefBase.h> +#include <utils/KeyedVector.h> +#include <utils/Errors.h> + +#include <GLES/gl.h> + +#include "Tokenizer.h" +#include "TokenManager.h" + + +namespace android { + +// ---------------------------------------------------------------------------- + +namespace gl { + +struct buffer_t { + GLsizeiptr size; + GLenum usage; + uint8_t* data; + uint32_t name; +}; + +}; + +class EGLBufferObjectManager : public TokenManager +{ +public: + EGLBufferObjectManager(); + ~EGLBufferObjectManager(); + + // protocol for sp<> + inline void incStrong(const void* id) const; + inline void decStrong(const void* id) const; + typedef void weakref_type; + + gl::buffer_t const* bind(GLuint buffer); + int allocateStore(gl::buffer_t* bo, GLsizeiptr size, GLenum usage); + void deleteBuffers(GLsizei n, const GLuint* buffers); + +private: + mutable volatile int32_t mCount; + mutable Mutex mLock; + KeyedVector<GLuint, gl::buffer_t*> mBuffers; +}; + +void EGLBufferObjectManager::incStrong(const void* id) const { + android_atomic_inc(&mCount); +} +void EGLBufferObjectManager::decStrong(const void* id) const { + if (android_atomic_dec(&mCount) == 1) { + delete this; + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_OPENGLES_BUFFER_OBJECT_MANAGER_H + diff --git a/opengl/libagl/TextureObjectManager.cpp b/opengl/libagl/TextureObjectManager.cpp new file mode 100644 index 0000000..ce31854 --- /dev/null +++ b/opengl/libagl/TextureObjectManager.cpp @@ -0,0 +1,309 @@ +/* + ** 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 diff --git a/opengl/libagl/TextureObjectManager.h b/opengl/libagl/TextureObjectManager.h new file mode 100644 index 0000000..74ed1a4 --- /dev/null +++ b/opengl/libagl/TextureObjectManager.h @@ -0,0 +1,140 @@ +/* +** 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. +*/ + +#ifndef ANDROID_OPENGLES_SURFACE_H +#define ANDROID_OPENGLES_SURFACE_H + +#include <stdint.h> +#include <stddef.h> +#include <sys/types.h> + +#include <utils/Atomic.h> +#include <utils/threads.h> +#include <utils/RefBase.h> +#include <utils/KeyedVector.h> +#include <utils/Errors.h> + +#include <private/pixelflinger/ggl_context.h> + +#include <GLES/gl.h> + +#include "Tokenizer.h" +#include "TokenManager.h" + + +namespace android { + +// ---------------------------------------------------------------------------- + +class EGLTextureObject +{ +public: + EGLTextureObject(); + ~EGLTextureObject(); + + // protocol for sp<> + inline void incStrong(const void* id) const; + inline void decStrong(const void* id) const; + inline uint32_t getStrongCount() const; + + status_t setSurface(GGLSurface const* s); + status_t reallocate(GLint level, + int w, int h, int s, + int format, int compressedFormat, int bpr); + inline size_t size() const; + const GGLSurface& mip(int lod) const; + GGLSurface& editMip(int lod); + bool hasMipmaps() const { return mMipmaps!=0; } + bool isComplete() const { return mIsComplete; } + void copyParameters(const sp<EGLTextureObject>& old); + +private: + status_t allocateMipmaps(); + void freeMipmaps(); + void init(); + mutable int32_t mCount; + size_t mSize; + GGLSurface *mMipmaps; + int mNumExtraLod; + bool mIsComplete; + +public: + GGLSurface surface; + GLenum wraps; + GLenum wrapt; + GLenum min_filter; + GLenum mag_filter; + GLenum internalformat; + GLint crop_rect[4]; + GLint generate_mipmap; + GLint direct; +}; + +void EGLTextureObject::incStrong(const void* id) const { + android_atomic_inc(&mCount); +} +void EGLTextureObject::decStrong(const void* id) const { + if (android_atomic_dec(&mCount) == 1) { + delete this; + } +} +uint32_t EGLTextureObject::getStrongCount() const { + return mCount; +} +size_t EGLTextureObject::size() const { + return mSize; +} + +// ---------------------------------------------------------------------------- + +class EGLSurfaceManager : public TokenManager +{ +public: + EGLSurfaceManager(); + ~EGLSurfaceManager(); + + // protocol for sp<> + inline void incStrong(const void* id) const; + inline void decStrong(const void* id) const; + typedef void weakref_type; + + sp<EGLTextureObject> createTexture(GLuint name); + sp<EGLTextureObject> removeTexture(GLuint name); + sp<EGLTextureObject> replaceTexture(GLuint name); + void deleteTextures(GLsizei n, const GLuint *tokens); + sp<EGLTextureObject> texture(GLuint name); + +private: + mutable int32_t mCount; + mutable Mutex mLock; + KeyedVector< GLuint, sp<EGLTextureObject> > mTextures; +}; + +void EGLSurfaceManager::incStrong(const void* id) const { + android_atomic_inc(&mCount); +} +void EGLSurfaceManager::decStrong(const void* id) const { + if (android_atomic_dec(&mCount) == 1) { + delete this; + } +} + + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_OPENGLES_SURFACE_H + diff --git a/opengl/libagl/TokenManager.cpp b/opengl/libagl/TokenManager.cpp new file mode 100644 index 0000000..eea6025 --- /dev/null +++ b/opengl/libagl/TokenManager.cpp @@ -0,0 +1,62 @@ +/* libs/opengles/surface.cpp +** +** 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 "TokenManager.h" + +namespace android { +// ---------------------------------------------------------------------------- + +TokenManager::TokenManager() +{ + // token 0 is always reserved + mTokenizer.reserve(0); +} + +TokenManager::~TokenManager() +{ +} + +status_t TokenManager::getToken(GLsizei n, GLuint *tokens) +{ + Mutex::Autolock _l(mLock); + for (GLsizei i=0 ; i<n ; i++) + *tokens++ = mTokenizer.acquire(); + return NO_ERROR; +} + +void TokenManager::recycleTokens(GLsizei n, const GLuint *tokens) +{ + Mutex::Autolock _l(mLock); + for (int i=0 ; i<n ; i++) { + const GLuint token = *tokens++; + if (token) { + mTokenizer.release(token); + } + } +} + +bool TokenManager::isTokenValid(GLuint token) const +{ + Mutex::Autolock _l(mLock); + return mTokenizer.isAcquired(token); +} + +// ---------------------------------------------------------------------------- +}; // namespace android + diff --git a/opengl/libagl/TokenManager.h b/opengl/libagl/TokenManager.h new file mode 100644 index 0000000..49c1469 --- /dev/null +++ b/opengl/libagl/TokenManager.h @@ -0,0 +1,53 @@ +/* +** 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. +*/ + +#ifndef ANDROID_OPENGLES_TOKEN_MANAGER_H +#define ANDROID_OPENGLES_TOKEN_MANAGER_H + +#include <stdint.h> +#include <stddef.h> +#include <sys/types.h> + +#include <utils/threads.h> + +#include <GLES/gl.h> + +#include "Tokenizer.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +class TokenManager +{ +public: + TokenManager(); + ~TokenManager(); + + status_t getToken(GLsizei n, GLuint *tokens); + void recycleTokens(GLsizei n, const GLuint *tokens); + bool isTokenValid(GLuint token) const; + +private: + mutable Mutex mLock; + Tokenizer mTokenizer; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_OPENGLES_TOKEN_MANAGER_H + diff --git a/opengl/libagl/Tokenizer.cpp b/opengl/libagl/Tokenizer.cpp new file mode 100644 index 0000000..9b3ea1a --- /dev/null +++ b/opengl/libagl/Tokenizer.cpp @@ -0,0 +1,173 @@ +/* libs/opengles/Tokenizer.cpp +** +** 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 "Tokenizer.h" + +// ---------------------------------------------------------------------------- + +namespace android { + +ANDROID_BASIC_TYPES_TRAITS(Tokenizer::run_t) + +Tokenizer::Tokenizer() +{ +} + +Tokenizer::Tokenizer(const Tokenizer& other) + : mRanges(other.mRanges) +{ +} + +Tokenizer::~Tokenizer() +{ +} + +uint32_t Tokenizer::acquire() +{ + if (!mRanges.size() || mRanges[0].first) { + _insertTokenAt(0,0); + return 0; + } + + // just extend the first run + const run_t& run = mRanges[0]; + uint32_t token = run.first + run.length; + _insertTokenAt(token, 1); + return token; +} + +bool Tokenizer::isAcquired(uint32_t token) const +{ + return (_indexOrderOf(token) >= 0); +} + +status_t Tokenizer::reserve(uint32_t token) +{ + size_t o; + const ssize_t i = _indexOrderOf(token, &o); + if (i >= 0) { + return BAD_VALUE; // this token is already taken + } + ssize_t err = _insertTokenAt(token, o); + return (err<0) ? err : status_t(NO_ERROR); +} + +status_t Tokenizer::release(uint32_t token) +{ + const ssize_t i = _indexOrderOf(token); + if (i >= 0) { + const run_t& run = mRanges[i]; + if ((token >= run.first) && (token < run.first+run.length)) { + // token in this range, we need to split + run_t& run = mRanges.editItemAt(i); + if ((token == run.first) || (token == run.first+run.length-1)) { + if (token == run.first) { + run.first += 1; + } + run.length -= 1; + if (run.length == 0) { + // XXX: should we systematically remove a run that's empty? + mRanges.removeItemsAt(i); + } + } else { + // split the run + run_t new_run; + new_run.first = token+1; + new_run.length = run.first+run.length - new_run.first; + run.length = token - run.first; + mRanges.insertAt(new_run, i+1); + } + return NO_ERROR; + } + } + return NAME_NOT_FOUND; +} + +ssize_t Tokenizer::_indexOrderOf(uint32_t token, size_t* order) const +{ + // binary search + ssize_t err = NAME_NOT_FOUND; + ssize_t l = 0; + ssize_t h = mRanges.size()-1; + ssize_t mid; + const run_t* a = mRanges.array(); + while (l <= h) { + mid = l + (h - l)/2; + const run_t* const curr = a + mid; + int c = 0; + if (token < curr->first) c = 1; + else if (token >= curr->first+curr->length) c = -1; + if (c == 0) { + err = l = mid; + break; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + if (order) *order = l; + return err; +} + +ssize_t Tokenizer::_insertTokenAt(uint32_t token, size_t index) +{ + const size_t c = mRanges.size(); + + if (index >= 1) { + // do we need to merge with the previous run? + run_t& p = mRanges.editItemAt(index-1); + if (p.first+p.length == token) { + p.length += 1; + if (index < c) { + const run_t& n = mRanges[index]; + if (token+1 == n.first) { + p.length += n.length; + mRanges.removeItemsAt(index); + } + } + return index; + } + } + + if (index < c) { + // do we need to merge with the next run? + run_t& n = mRanges.editItemAt(index); + if (token+1 == n.first) { + n.first -= 1; + n.length += 1; + return index; + } + } + + return mRanges.insertAt(run_t(token,1), index); +} + +void Tokenizer::dump() const +{ + const run_t* ranges = mRanges.array(); + const size_t c = mRanges.size(); + LOGD("Tokenizer (%p, size = %u)\n", this, c); + for (size_t i=0 ; i<c ; i++) { + LOGD("%u: (%u, %u)\n", i, ranges[i].first, ranges[i].length); + } +} + +}; // namespace android + diff --git a/opengl/libagl/Tokenizer.h b/opengl/libagl/Tokenizer.h new file mode 100644 index 0000000..ac555cb --- /dev/null +++ b/opengl/libagl/Tokenizer.h @@ -0,0 +1,59 @@ +/* libs/opengles/Tokenizer.h +** +** 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. +*/ + + +#ifndef ANDROID_OPENGLES_TOKENIZER_H +#define ANDROID_OPENGLES_TOKENIZER_H + +#include <utils/Vector.h> +#include <utils/Errors.h> + +// ---------------------------------------------------------------------------- + +namespace android { + +class Tokenizer +{ +public: + Tokenizer(); + Tokenizer(const Tokenizer& other); + ~Tokenizer(); + + uint32_t acquire(); + status_t reserve(uint32_t token); + status_t release(uint32_t token); + bool isAcquired(uint32_t token) const; + + void dump() const; + + struct run_t { + run_t() {}; + run_t(uint32_t f, uint32_t l) : first(f), length(l) {} + uint32_t first; + uint32_t length; + }; +private: + ssize_t _indexOrderOf(uint32_t token, size_t* order=0) const; + ssize_t _insertTokenAt(uint32_t token, size_t index); + Vector<run_t> mRanges; +}; + +}; // namespace android + +// ---------------------------------------------------------------------------- + +#endif // ANDROID_OPENGLES_TOKENIZER_H diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp new file mode 100644 index 0000000..8fa7566 --- /dev/null +++ b/opengl/libagl/array.cpp @@ -0,0 +1,1557 @@ +/* +** 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 <stdlib.h> +#include <stdio.h> + +#include "context.h" +#include "fp.h" +#include "state.h" +#include "matrix.h" +#include "vertex.h" +#include "light.h" +#include "primitives.h" +#include "texture.h" +#include "BufferObjectManager.h" + +// ---------------------------------------------------------------------------- + +#define VC_CACHE_STATISTICS 0 +#define VC_CACHE_TYPE_NONE 0 +#define VC_CACHE_TYPE_INDEXED 1 +#define VC_CACHE_TYPE_LRU 2 +#define VC_CACHE_TYPE VC_CACHE_TYPE_INDEXED + +#if VC_CACHE_STATISTICS +#include <utils/Timers.h> +#endif + +// ---------------------------------------------------------------------------- + +namespace android { + +static void validate_arrays(ogles_context_t* c, GLenum mode); + +static void compileElements__generic(ogles_context_t*, + vertex_t*, GLint, GLsizei); +static void compileElement__generic(ogles_context_t*, + vertex_t*, GLint); + +static void drawPrimitivesPoints(ogles_context_t*, GLint, GLsizei); +static void drawPrimitivesLineStrip(ogles_context_t*, GLint, GLsizei); +static void drawPrimitivesLineLoop(ogles_context_t*, GLint, GLsizei); +static void drawPrimitivesLines(ogles_context_t*, GLint, GLsizei); +static void drawPrimitivesTriangleStrip(ogles_context_t*, GLint, GLsizei); +static void drawPrimitivesTriangleFan(ogles_context_t*, GLint, GLsizei); +static void drawPrimitivesTriangles(ogles_context_t*, GLint, GLsizei); + +static void drawIndexedPrimitivesPoints(ogles_context_t*, + GLsizei, const GLvoid*); +static void drawIndexedPrimitivesLineStrip(ogles_context_t*, + GLsizei, const GLvoid*); +static void drawIndexedPrimitivesLineLoop(ogles_context_t*, + GLsizei, const GLvoid*); +static void drawIndexedPrimitivesLines(ogles_context_t*, + GLsizei, const GLvoid*); +static void drawIndexedPrimitivesTriangleStrip(ogles_context_t*, + GLsizei, const GLvoid*); +static void drawIndexedPrimitivesTriangleFan(ogles_context_t*, + GLsizei, const GLvoid*); +static void drawIndexedPrimitivesTriangles(ogles_context_t*, + GLsizei, const GLvoid*); + +// ---------------------------------------------------------------------------- + +typedef void (*arrays_prims_fct_t)(ogles_context_t*, GLint, GLsizei); +static const arrays_prims_fct_t drawArraysPrims[] = { + drawPrimitivesPoints, + drawPrimitivesLines, + drawPrimitivesLineLoop, + drawPrimitivesLineStrip, + drawPrimitivesTriangles, + drawPrimitivesTriangleStrip, + drawPrimitivesTriangleFan +}; + +typedef void (*elements_prims_fct_t)(ogles_context_t*, GLsizei, const GLvoid*); +static const elements_prims_fct_t drawElementsPrims[] = { + drawIndexedPrimitivesPoints, + drawIndexedPrimitivesLines, + drawIndexedPrimitivesLineLoop, + drawIndexedPrimitivesLineStrip, + drawIndexedPrimitivesTriangles, + drawIndexedPrimitivesTriangleStrip, + drawIndexedPrimitivesTriangleFan +}; + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +void ogles_init_array(ogles_context_t* c) +{ + c->arrays.vertex.size = 4; + c->arrays.vertex.type = GL_FLOAT; + c->arrays.color.size = 4; + c->arrays.color.type = GL_FLOAT; + c->arrays.normal.size = 4; + c->arrays.normal.type = GL_FLOAT; + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + c->arrays.texture[i].size = 4; + c->arrays.texture[i].type = GL_FLOAT; + } + c->vc.init(); + + if (!c->vc.vBuffer) { + // this could have failed + ogles_error(c, GL_OUT_OF_MEMORY); + } +} + +void ogles_uninit_array(ogles_context_t* c) +{ + c->vc.uninit(); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Array fetchers +#endif + +static void currentColor(ogles_context_t* c, GLfixed* v, const GLvoid*) { + memcpy(v, c->current.color.v, sizeof(vec4_t)); +} +static void currentColor_clamp(ogles_context_t* c, GLfixed* v, const GLvoid*) { + memcpy(v, c->currentColorClamped.v, sizeof(vec4_t)); +} +static void currentNormal(ogles_context_t* c, GLfixed* v, const GLvoid*) { + memcpy(v, c->currentNormal.v, sizeof(vec3_t)); +} +static void currentTexCoord(ogles_context_t* c, GLfixed* v, const GLvoid*) { + memcpy(v, c->current.texture[c->arrays.tmu].v, sizeof(vec4_t)); +} + + +static void fetchNop(ogles_context_t*, GLfixed*, const GLvoid*) { +} +static void fetch2b(ogles_context_t*, GLfixed* v, const GLbyte* p) { + v[0] = gglIntToFixed(p[0]); + v[1] = gglIntToFixed(p[1]); +} +static void fetch2s(ogles_context_t*, GLfixed* v, const GLshort* p) { + v[0] = gglIntToFixed(p[0]); + v[1] = gglIntToFixed(p[1]); +} +static void fetch2x(ogles_context_t*, GLfixed* v, const GLfixed* p) { + memcpy(v, p, 2*sizeof(GLfixed)); +} +static void fetch2f(ogles_context_t*, GLfixed* v, const GLfloat* p) { + v[0] = gglFloatToFixed(p[0]); + v[1] = gglFloatToFixed(p[1]); +} +static void fetch3b(ogles_context_t*, GLfixed* v, const GLbyte* p) { + v[0] = gglIntToFixed(p[0]); + v[1] = gglIntToFixed(p[1]); + v[2] = gglIntToFixed(p[2]); +} +static void fetch3s(ogles_context_t*, GLfixed* v, const GLshort* p) { + v[0] = gglIntToFixed(p[0]); + v[1] = gglIntToFixed(p[1]); + v[2] = gglIntToFixed(p[2]); +} +static void fetch3x(ogles_context_t*, GLfixed* v, const GLfixed* p) { + memcpy(v, p, 3*sizeof(GLfixed)); +} +static void fetch3f(ogles_context_t*, GLfixed* v, const GLfloat* p) { + v[0] = gglFloatToFixed(p[0]); + v[1] = gglFloatToFixed(p[1]); + v[2] = gglFloatToFixed(p[2]); +} +static void fetch4b(ogles_context_t*, GLfixed* v, const GLbyte* p) { + v[0] = gglIntToFixed(p[0]); + v[1] = gglIntToFixed(p[1]); + v[2] = gglIntToFixed(p[2]); + v[3] = gglIntToFixed(p[3]); +} +static void fetch4s(ogles_context_t*, GLfixed* v, const GLshort* p) { + v[0] = gglIntToFixed(p[0]); + v[1] = gglIntToFixed(p[1]); + v[2] = gglIntToFixed(p[2]); + v[3] = gglIntToFixed(p[3]); +} +static void fetch4x(ogles_context_t*, GLfixed* v, const GLfixed* p) { + memcpy(v, p, 4*sizeof(GLfixed)); +} +static void fetch4f(ogles_context_t*, GLfixed* v, const GLfloat* p) { + v[0] = gglFloatToFixed(p[0]); + v[1] = gglFloatToFixed(p[1]); + v[2] = gglFloatToFixed(p[2]); + v[3] = gglFloatToFixed(p[3]); +} +static void fetchExpand4ub(ogles_context_t*, GLfixed* v, const GLubyte* p) { + v[0] = GGL_UB_TO_X(p[0]); + v[1] = GGL_UB_TO_X(p[1]); + v[2] = GGL_UB_TO_X(p[2]); + v[3] = GGL_UB_TO_X(p[3]); +} +static void fetchClamp4x(ogles_context_t*, GLfixed* v, const GLfixed* p) { + v[0] = gglClampx(p[0]); + v[1] = gglClampx(p[1]); + v[2] = gglClampx(p[2]); + v[3] = gglClampx(p[3]); +} +static void fetchClamp4f(ogles_context_t*, GLfixed* v, const GLfloat* p) { + v[0] = gglClampx(gglFloatToFixed(p[0])); + v[1] = gglClampx(gglFloatToFixed(p[1])); + v[2] = gglClampx(gglFloatToFixed(p[2])); + v[3] = gglClampx(gglFloatToFixed(p[3])); +} +static void fetchExpand3ub(ogles_context_t*, GLfixed* v, const GLubyte* p) { + v[0] = GGL_UB_TO_X(p[0]); + v[1] = GGL_UB_TO_X(p[1]); + v[2] = GGL_UB_TO_X(p[2]); + v[3] = 0x10000; +} +static void fetchClamp3x(ogles_context_t*, GLfixed* v, const GLfixed* p) { + v[0] = gglClampx(p[0]); + v[1] = gglClampx(p[1]); + v[2] = gglClampx(p[2]); + v[3] = 0x10000; +} +static void fetchClamp3f(ogles_context_t*, GLfixed* v, const GLfloat* p) { + v[0] = gglClampx(gglFloatToFixed(p[0])); + v[1] = gglClampx(gglFloatToFixed(p[1])); + v[2] = gglClampx(gglFloatToFixed(p[2])); + v[3] = 0x10000; +} +static void fetchExpand3b(ogles_context_t*, GLfixed* v, const GLbyte* p) { + v[0] = GGL_B_TO_X(p[0]); + v[1] = GGL_B_TO_X(p[1]); + v[2] = GGL_B_TO_X(p[2]); +} +static void fetchExpand3s(ogles_context_t*, GLfixed* v, const GLshort* p) { + v[0] = GGL_S_TO_X(p[0]); + v[1] = GGL_S_TO_X(p[1]); + v[2] = GGL_S_TO_X(p[2]); +} + +typedef array_t::fetcher_t fn_t; + +static const fn_t color_fct[2][16] = { // size={3,4}, type={ub,f,x} + { 0, (fn_t)fetchExpand3ub, 0, 0, 0, 0, + (fn_t)fetch3f, 0, 0, 0, 0, 0, + (fn_t)fetch3x }, + { 0, (fn_t)fetchExpand4ub, 0, 0, 0, 0, + (fn_t)fetch4f, 0, 0, 0, 0, 0, + (fn_t)fetch4x }, +}; +static const fn_t color_clamp_fct[2][16] = { // size={3,4}, type={ub,f,x} + { 0, (fn_t)fetchExpand3ub, 0, 0, 0, 0, + (fn_t)fetchClamp3f, 0, 0, 0, 0, 0, + (fn_t)fetchClamp3x }, + { 0, (fn_t)fetchExpand4ub, 0, 0, 0, 0, + (fn_t)fetchClamp4f, 0, 0, 0, 0, 0, + (fn_t)fetchClamp4x }, +}; +static const fn_t normal_fct[1][16] = { // size={3}, type={b,s,f,x} + { (fn_t)fetchExpand3b, 0, + (fn_t)fetchExpand3s, 0, 0, 0, + (fn_t)fetch3f, 0, 0, 0, 0, 0, + (fn_t)fetch3x }, +}; +static const fn_t vertex_fct[3][16] = { // size={2,3,4}, type={b,s,f,x} + { (fn_t)fetch2b, 0, + (fn_t)fetch2s, 0, 0, 0, + (fn_t)fetch2f, 0, 0, 0, 0, 0, + (fn_t)fetch3x }, + { (fn_t)fetch3b, 0, + (fn_t)fetch3s, 0, 0, 0, + (fn_t)fetch3f, 0, 0, 0, 0, 0, + (fn_t)fetch3x }, + { (fn_t)fetch4b, 0, + (fn_t)fetch4s, 0, 0, 0, + (fn_t)fetch4f, 0, 0, 0, 0, 0, + (fn_t)fetch4x } +}; +static const fn_t texture_fct[3][16] = { // size={2,3,4}, type={b,s,f,x} + { (fn_t)fetch2b, 0, + (fn_t)fetch2s, 0, 0, 0, + (fn_t)fetch2f, 0, 0, 0, 0, 0, + (fn_t)fetch2x }, + { (fn_t)fetch3b, 0, + (fn_t)fetch3s, 0, 0, 0, + (fn_t)fetch3f, 0, 0, 0, 0, 0, + (fn_t)fetch3x }, + { (fn_t)fetch4b, 0, + (fn_t)fetch4s, 0, 0, 0, + (fn_t)fetch4f, 0, 0, 0, 0, 0, + (fn_t)fetch4x } +}; + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark array_t +#endif + +void array_t::init( + GLint size, GLenum type, GLsizei stride, + const GLvoid *pointer, const buffer_t* bo, GLsizei count) +{ + if (!stride) { + stride = size; + switch (type) { + case GL_SHORT: + case GL_UNSIGNED_SHORT: + stride *= 2; + break; + case GL_FLOAT: + case GL_FIXED: + stride *= 4; + break; + } + } + this->size = size; + this->type = type; + this->stride = stride; + this->pointer = pointer; + this->bo = bo; + this->bounds = count; +} + +inline void array_t::resolve() +{ + physical_pointer = (bo) ? (bo->data + uintptr_t(pointer)) : pointer; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark vertex_cache_t +#endif + +void vertex_cache_t::init() +{ + // make sure the size of vertex_t allows cache-line alignment + CTA<(sizeof(vertex_t) & 0x1F) == 0> assertAlignedSize; + + const int align = 32; + const size_t s = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE; + const size_t size = s*sizeof(vertex_t) + align; + base = malloc(size); + if (base) { + memset(base, 0, size); + vBuffer = (vertex_t*)((size_t(base) + align - 1) & ~(align-1)); + vCache = vBuffer + VERTEX_BUFFER_SIZE; + sequence = 0; + } +} + +void vertex_cache_t::uninit() +{ + free(base); + base = vBuffer = vCache = 0; +} + +void vertex_cache_t::clear() +{ +#if VC_CACHE_STATISTICS + startTime = systemTime(SYSTEM_TIME_THREAD); + total = 0; + misses = 0; +#endif + +#if VC_CACHE_TYPE == VC_CACHE_TYPE_LRU + vertex_t* v = vBuffer; + size_t count = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE; + do { + v->mru = 0; + v++; + } while (--count); +#endif + + sequence += INDEX_SEQ; + if (sequence >= 0x80000000LU) { + sequence = INDEX_SEQ; + vertex_t* v = vBuffer; + size_t count = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE; + do { + v->index = 0; + v++; + } while (--count); + } +} + +void vertex_cache_t::dump_stats(GLenum mode) +{ +#if VC_CACHE_STATISTICS + nsecs_t time = systemTime(SYSTEM_TIME_THREAD) - startTime; + uint32_t hits = total - misses; + uint32_t prim_count; + switch (mode) { + case GL_POINTS: prim_count = total; break; + case GL_LINE_STRIP: prim_count = total - 1; break; + case GL_LINE_LOOP: prim_count = total - 1; break; + case GL_LINES: prim_count = total / 2; break; + case GL_TRIANGLE_STRIP: prim_count = total - 2; break; + case GL_TRIANGLE_FAN: prim_count = total - 2; break; + case GL_TRIANGLES: prim_count = total / 3; break; + default: return; + } + printf( "total=%5u, hits=%5u, miss=%5u, hitrate=%3u%%," + " prims=%5u, time=%6u us, prims/s=%d, v/t=%f\n", + total, hits, misses, (hits*100)/total, + prim_count, int(ns2us(time)), int(prim_count*float(seconds(1))/time), + float(misses) / prim_count); +#endif +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +static __attribute__((noinline)) +void enableDisableClientState(ogles_context_t* c, GLenum array, bool enable) +{ + const int tmu = c->arrays.activeTexture; + array_t* a; + switch (array) { + case GL_COLOR_ARRAY: a = &c->arrays.color; break; + case GL_NORMAL_ARRAY: a = &c->arrays.normal; break; + case GL_TEXTURE_COORD_ARRAY: a = &c->arrays.texture[tmu]; break; + case GL_VERTEX_ARRAY: a = &c->arrays.vertex; break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + a->enable = enable ? GL_TRUE : GL_FALSE; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Vertex Cache +#endif + +static __attribute__((noinline)) +vertex_t* cache_vertex(ogles_context_t* c, vertex_t* v, uint32_t index) +{ + #if VC_CACHE_STATISTICS + c->vc.misses++; + #endif + if (ggl_unlikely(v->locked)) { + // we're just looking for an entry in the cache that is not locked. + // and we know that there cannot be more than 2 locked entries + // because a triangle needs at most 3 vertices. + // We never use the first and second entries because they might be in + // use by the striper or faner. Any other entry will do as long as + // it's not locked. + // We compute directly the index of a "free" entry from the locked + // state of v[2] and v[3]. + v = c->vc.vBuffer + 2; + v += v[0].locked | (v[1].locked<<1); + } + // note: compileElement clears v->flags + c->arrays.compileElement(c, v, index); + v->locked = 1; + return v; +} + +static __attribute__((noinline)) +vertex_t* fetch_vertex(ogles_context_t* c, size_t index) +{ + index |= c->vc.sequence; + +#if VC_CACHE_TYPE == VC_CACHE_TYPE_INDEXED + + vertex_t* const v = c->vc.vCache + + (index & (vertex_cache_t::VERTEX_CACHE_SIZE-1)); + + if (ggl_likely(v->index == index)) { + v->locked = 1; + return v; + } + return cache_vertex(c, v, index); + +#elif VC_CACHE_TYPE == VC_CACHE_TYPE_LRU + + vertex_t* v = c->vc.vCache + + (index & ((vertex_cache_t::VERTEX_CACHE_SIZE-1)>>1))*2; + + // always record LRU in v[0] + if (ggl_likely(v[0].index == index)) { + v[0].locked = 1; + v[0].mru = 0; + return &v[0]; + } + + if (ggl_likely(v[1].index == index)) { + v[1].locked = 1; + v[0].mru = 1; + return &v[1]; + } + + const int lru = 1 - v[0].mru; + v[0].mru = lru; + return cache_vertex(c, &v[lru], index); + +#elif VC_CACHE_TYPE == VC_CACHE_TYPE_NONE + + // just for debugging... + vertex_t* v = c->vc.vBuffer + 2; + return cache_vertex(c, v, index); + +#endif +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Primitive Assembly +#endif + +void drawPrimitivesPoints(ogles_context_t* c, GLint first, GLsizei count) +{ + if (ggl_unlikely(count < 1)) + return; + + // vertex cache size must be multiple of 1 + const GLsizei vcs = + (vertex_cache_t::VERTEX_BUFFER_SIZE + + vertex_cache_t::VERTEX_CACHE_SIZE); + do { + vertex_t* v = c->vc.vBuffer; + GLsizei num = count > vcs ? vcs : count; + c->arrays.cull = vertex_t::CLIP_ALL; + c->arrays.compileElements(c, v, first, num); + first += num; + count -= num; + if (!c->arrays.cull) { + // quick/trivial reject of the whole batch + do { + const uint32_t cc = v[0].flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderPoint(c, v); + v++; + num--; + } while (num); + } + } while (count); +} + +// ---------------------------------------------------------------------------- + +void drawPrimitivesLineStrip(ogles_context_t* c, GLint first, GLsizei count) +{ + if (ggl_unlikely(count < 2)) + return; + + vertex_t *v, *v0, *v1; + c->arrays.cull = vertex_t::CLIP_ALL; + c->arrays.compileElement(c, c->vc.vBuffer, first); + first += 1; + count -= 1; + + // vertex cache size must be multiple of 1 + const GLsizei vcs = + (vertex_cache_t::VERTEX_BUFFER_SIZE + + vertex_cache_t::VERTEX_CACHE_SIZE - 1); + do { + v0 = c->vc.vBuffer + 0; + v = c->vc.vBuffer + 1; + GLsizei num = count > vcs ? vcs : count; + c->arrays.compileElements(c, v, first, num); + first += num; + count -= num; + if (!c->arrays.cull) { + // quick/trivial reject of the whole batch + do { + v1 = v++; + const uint32_t cc = v0->flags & v1->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderLine(c, v0, v1); + v0 = v1; + num--; + } while (num); + } + // copy back the last processed vertex + c->vc.vBuffer[0] = *v0; + c->arrays.cull = v0->flags & vertex_t::CLIP_ALL; + } while (count); +} + +void drawPrimitivesLineLoop(ogles_context_t* c, GLint first, GLsizei count) +{ + if (ggl_unlikely(count < 2)) + return; + drawPrimitivesLineStrip(c, first, count); + if (ggl_likely(count >= 3)) { + vertex_t* v0 = c->vc.vBuffer; + vertex_t* v1 = c->vc.vBuffer + 1; + c->arrays.compileElement(c, v1, first); + const uint32_t cc = v0->flags & v1->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderLine(c, v0, v1); + } +} + +void drawPrimitivesLines(ogles_context_t* c, GLint first, GLsizei count) +{ + if (ggl_unlikely(count < 2)) + return; + + // vertex cache size must be multiple of 2 + const GLsizei vcs = + ((vertex_cache_t::VERTEX_BUFFER_SIZE + + vertex_cache_t::VERTEX_CACHE_SIZE) / 2) * 2; + do { + vertex_t* v = c->vc.vBuffer; + GLsizei num = count > vcs ? vcs : count; + c->arrays.cull = vertex_t::CLIP_ALL; + c->arrays.compileElements(c, v, first, num); + first += num; + count -= num; + if (!c->arrays.cull) { + // quick/trivial reject of the whole batch + num -= 2; + do { + const uint32_t cc = v[0].flags & v[1].flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderLine(c, v, v+1); + v += 2; + num -= 2; + } while (num >= 0); + } + } while (count >= 2); +} + +// ---------------------------------------------------------------------------- + +static void drawPrimitivesTriangleFanOrStrip(ogles_context_t* c, + GLint first, GLsizei count, int winding) +{ + // winding == 2 : fan + // winding == 1 : strip + + if (ggl_unlikely(count < 3)) + return; + + vertex_t *v, *v0, *v1, *v2; + c->arrays.cull = vertex_t::CLIP_ALL; + c->arrays.compileElements(c, c->vc.vBuffer, first, 2); + first += 2; + count -= 2; + + // vertex cache size must be multiple of 2. This is extremely important + // because it allows us to preserve the same winding when the whole + // batch is culled. We also need 2 extra vertices in the array, because + // we always keep the two first ones. + const GLsizei vcs = + ((vertex_cache_t::VERTEX_BUFFER_SIZE + + vertex_cache_t::VERTEX_CACHE_SIZE - 2) / 2) * 2; + do { + v0 = c->vc.vBuffer + 0; + v1 = c->vc.vBuffer + 1; + v = c->vc.vBuffer + 2; + GLsizei num = count > vcs ? vcs : count; + c->arrays.compileElements(c, v, first, num); + first += num; + count -= num; + if (!c->arrays.cull) { + // quick/trivial reject of the whole batch + do { + v2 = v++; + const uint32_t cc = v0->flags & v1->flags & v2->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderTriangle(c, v0, v1, v2); + swap(((winding^=1) ? v1 : v0), v2); + num--; + } while (num); + } + if (count) { + v0 = c->vc.vBuffer + 2 + num - 2; + v1 = c->vc.vBuffer + 2 + num - 1; + if ((winding&2) == 0) { + // for strips copy back the two last compiled vertices + c->vc.vBuffer[0] = *v0; + } + c->vc.vBuffer[1] = *v1; + c->arrays.cull = v0->flags & v1->flags & vertex_t::CLIP_ALL; + } + } while (count > 0); +} + +void drawPrimitivesTriangleStrip(ogles_context_t* c, + GLint first, GLsizei count) { + drawPrimitivesTriangleFanOrStrip(c, first, count, 1); +} + +void drawPrimitivesTriangleFan(ogles_context_t* c, + GLint first, GLsizei count) { + drawPrimitivesTriangleFanOrStrip(c, first, count, 2); +} + +void drawPrimitivesTriangles(ogles_context_t* c, GLint first, GLsizei count) +{ + if (ggl_unlikely(count < 3)) + return; + + // vertex cache size must be multiple of 3 + const GLsizei vcs = + ((vertex_cache_t::VERTEX_BUFFER_SIZE + + vertex_cache_t::VERTEX_CACHE_SIZE) / 3) * 3; + do { + vertex_t* v = c->vc.vBuffer; + GLsizei num = count > vcs ? vcs : count; + c->arrays.cull = vertex_t::CLIP_ALL; + c->arrays.compileElements(c, v, first, num); + first += num; + count -= num; + if (!c->arrays.cull) { + // quick/trivial reject of the whole batch + num -= 3; + do { + const uint32_t cc = v[0].flags & v[1].flags & v[2].flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderTriangle(c, v, v+1, v+2); + v += 3; + num -= 3; + } while (num >= 0); + } + } while (count >= 3); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +// this looks goofy, but gcc does a great job with this... +static inline unsigned int read_index(int type, const GLvoid*& p) { + unsigned int r; + if (type) { + r = *(const GLubyte*)p; + p = (const GLubyte*)p + 1; + } else { + r = *(const GLushort*)p; + p = (const GLushort*)p + 1; + } + return r; +} + +// ---------------------------------------------------------------------------- + +void drawIndexedPrimitivesPoints(ogles_context_t* c, + GLsizei count, const GLvoid *indices) +{ + if (ggl_unlikely(count < 1)) + return; + const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); + do { + vertex_t * v = fetch_vertex(c, read_index(type, indices)); + if (ggl_likely(!(v->flags & vertex_t::CLIP_ALL))) + c->prims.renderPoint(c, v); + v->locked = 0; + count--; + } while(count); +} + +// ---------------------------------------------------------------------------- + +void drawIndexedPrimitivesLineStrip(ogles_context_t* c, + GLsizei count, const GLvoid *indices) +{ + if (ggl_unlikely(count < 2)) + return; + + vertex_t * const v = c->vc.vBuffer; + vertex_t* v0 = v; + vertex_t* v1; + + const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); + c->arrays.compileElement(c, v0, read_index(type, indices)); + count -= 1; + do { + v1 = fetch_vertex(c, read_index(type, indices)); + const uint32_t cc = v0->flags & v1->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderLine(c, v0, v1); + v0->locked = 0; + v0 = v1; + count--; + } while (count); + v1->locked = 0; +} + +void drawIndexedPrimitivesLineLoop(ogles_context_t* c, + GLsizei count, const GLvoid *indices) +{ + if (ggl_unlikely(count <= 2)) { + drawIndexedPrimitivesLines(c, count, indices); + return; + } + + vertex_t * const v = c->vc.vBuffer; + vertex_t* v0 = v; + vertex_t* v1; + + const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); + c->arrays.compileElement(c, v0, read_index(type, indices)); + count -= 1; + do { + v1 = fetch_vertex(c, read_index(type, indices)); + const uint32_t cc = v0->flags & v1->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderLine(c, v0, v1); + v0->locked = 0; + v0 = v1; + count--; + } while (count); + v1->locked = 0; + + v1 = c->vc.vBuffer; + const uint32_t cc = v0->flags & v1->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderLine(c, v0, v1); +} + +void drawIndexedPrimitivesLines(ogles_context_t* c, + GLsizei count, const GLvoid *indices) +{ + if (ggl_unlikely(count < 2)) + return; + + count -= 2; + const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); + do { + vertex_t* const v0 = fetch_vertex(c, read_index(type, indices)); + vertex_t* const v1 = fetch_vertex(c, read_index(type, indices)); + const uint32_t cc = v0->flags & v1->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderLine(c, v0, v1); + v0->locked = 0; + v1->locked = 0; + count -= 2; + } while (count >= 0); +} + +// ---------------------------------------------------------------------------- + +static void drawIndexedPrimitivesTriangleFanOrStrip(ogles_context_t* c, + GLsizei count, const GLvoid *indices, int winding) +{ + // winding == 2 : fan + // winding == 1 : strip + + if (ggl_unlikely(count < 3)) + return; + + vertex_t * const v = c->vc.vBuffer; + vertex_t* v0 = v; + vertex_t* v1 = v+1; + vertex_t* v2; + + const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); + c->arrays.compileElement(c, v0, read_index(type, indices)); + c->arrays.compileElement(c, v1, read_index(type, indices)); + count -= 2; + + // note: GCC 4.1.1 here makes a prety interesting optimization + // where it duplicates the loop below based on c->arrays.indicesType + + do { + v2 = fetch_vertex(c, read_index(type, indices)); + const uint32_t cc = v0->flags & v1->flags & v2->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderTriangle(c, v0, v1, v2); + vertex_t* & consumed = ((winding^=1) ? v1 : v0); + consumed->locked = 0; + consumed = v2; + count--; + } while (count); + v0->locked = v1->locked = 0; + v2->locked = 0; +} + +void drawIndexedPrimitivesTriangleStrip(ogles_context_t* c, + GLsizei count, const GLvoid *indices) { + drawIndexedPrimitivesTriangleFanOrStrip(c, count, indices, 1); +} + +void drawIndexedPrimitivesTriangleFan(ogles_context_t* c, + GLsizei count, const GLvoid *indices) { + drawIndexedPrimitivesTriangleFanOrStrip(c, count, indices, 2); +} + +void drawIndexedPrimitivesTriangles(ogles_context_t* c, + GLsizei count, const GLvoid *indices) +{ + if (ggl_unlikely(count < 3)) + return; + + count -= 3; + if (ggl_likely(c->arrays.indicesType == GL_UNSIGNED_SHORT)) { + // This case is probably our most common case... + uint16_t const * p = (uint16_t const *)indices; + do { + vertex_t* const v0 = fetch_vertex(c, *p++); + vertex_t* const v1 = fetch_vertex(c, *p++); + vertex_t* const v2 = fetch_vertex(c, *p++); + const uint32_t cc = v0->flags & v1->flags & v2->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderTriangle(c, v0, v1, v2); + v0->locked = 0; + v1->locked = 0; + v2->locked = 0; + count -= 3; + } while (count >= 0); + } else { + uint8_t const * p = (uint8_t const *)indices; + do { + vertex_t* const v0 = fetch_vertex(c, *p++); + vertex_t* const v1 = fetch_vertex(c, *p++); + vertex_t* const v2 = fetch_vertex(c, *p++); + const uint32_t cc = v0->flags & v1->flags & v2->flags; + if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) + c->prims.renderTriangle(c, v0, v1, v2); + v0->locked = 0; + v1->locked = 0; + v2->locked = 0; + count -= 3; + } while (count >= 0); + } +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Array compilers +#endif + +void compileElement__generic(ogles_context_t* c, + vertex_t* v, GLint first) +{ + v->flags = 0; + v->index = first; + first &= vertex_cache_t::INDEX_MASK; + const GLubyte* vp = c->arrays.vertex.element(first); + c->arrays.vertex.fetch(c, v->obj.v, vp); + c->arrays.mvp_transform(&c->transforms.mvp, &v->clip, &v->obj); + c->arrays.perspective(c, v); +} + +void compileElements__generic(ogles_context_t* c, + vertex_t* v, GLint first, GLsizei count) +{ + const GLubyte* vp = c->arrays.vertex.element( + first & vertex_cache_t::INDEX_MASK); + const size_t stride = c->arrays.vertex.stride; + transform_t const* const mvp = &c->transforms.mvp; + do { + v->flags = 0; + v->index = first++; + c->arrays.vertex.fetch(c, v->obj.v, vp); + c->arrays.mvp_transform(mvp, &v->clip, &v->obj); + c->arrays.perspective(c, v); + vp += stride; + v++; + } while (--count); +} + +/* +void compileElements__3x_full(ogles_context_t* c, + vertex_t* v, GLint first, GLsizei count) +{ + const GLfixed* vp = (const GLfixed*)c->arrays.vertex.element(first); + const size_t stride = c->arrays.vertex.stride / 4; +// const GLfixed* const& m = c->transforms.mvp.matrix.m; + + GLfixed m[16]; + memcpy(&m, c->transforms.mvp.matrix.m, sizeof(m)); + + do { + const GLfixed rx = vp[0]; + const GLfixed ry = vp[1]; + const GLfixed rz = vp[2]; + vp += stride; + v->index = first++; + v->clip.x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); + v->clip.y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]); + v->clip.z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]); + v->clip.w = mla3a(rx, m[ 3], ry, m[ 7], rz, m[11], m[15]); + + const GLfixed w = v->clip.w; + uint32_t clip = 0; + if (v->clip.x < -w) clip |= vertex_t::CLIP_L; + if (v->clip.x > w) clip |= vertex_t::CLIP_R; + if (v->clip.y < -w) clip |= vertex_t::CLIP_B; + if (v->clip.y > w) clip |= vertex_t::CLIP_T; + if (v->clip.z < -w) clip |= vertex_t::CLIP_N; + if (v->clip.z > w) clip |= vertex_t::CLIP_F; + v->flags = clip; + c->arrays.cull &= clip; + + //c->arrays.perspective(c, v); + v++; + } while (--count); +} +*/ + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark clippers +#endif + +static void clipVec4(vec4_t& nv, + GLfixed t, const vec4_t& s, const vec4_t& p) +{ + for (int i=0; i<4 ; i++) + nv.v[i] = gglMulAddx(t, s.v[i] - p.v[i], p.v[i], 28); +} + +static void clipVertex(ogles_context_t* c, vertex_t* nv, + GLfixed t, const vertex_t* s, const vertex_t* p) +{ + clipVec4(nv->clip, t, s->clip, p->clip); + nv->fog = gglMulAddx(t, s->fog - p->fog, p->fog, 28); + ogles_vertex_project(c, nv); + nv->flags |= vertex_t::LIT | vertex_t::EYE | vertex_t::TT; + nv->flags &= ~vertex_t::CLIP_ALL; +} + +static void clipVertexC(ogles_context_t* c, vertex_t* nv, + GLfixed t, const vertex_t* s, const vertex_t* p) +{ + clipVec4(nv->color, t, s->color, p->color); + clipVertex(c, nv, t, s, p); +} + +static void clipVertexT(ogles_context_t* c, vertex_t* nv, + GLfixed t, const vertex_t* s, const vertex_t* p) +{ + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + if (c->rasterizer.state.texture[i].enable) + clipVec4(nv->texture[i], t, s->texture[i], p->texture[i]); + } + clipVertex(c, nv, t, s, p); +} + +static void clipVertexAll(ogles_context_t* c, vertex_t* nv, + GLfixed t, const vertex_t* s, const vertex_t* p) +{ + clipVec4(nv->color, t, s->color, p->color); + clipVertexT(c, nv, t, s, p); +} + +static void clipEye(ogles_context_t* c, vertex_t* nv, + GLfixed t, const vertex_t* s, const vertex_t* p) +{ + nv->clear(); + c->arrays.clipVertex(c, nv, t, p, s); + clipVec4(nv->eye, t, s->eye, p->eye); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +void validate_arrays(ogles_context_t* c, GLenum mode) +{ + uint32_t enables = c->rasterizer.state.enables; + + // Perspective correction is not need if Ortho transform, but + // the user can still provide the w coordinate manually, so we can't + // automatically turn it off (in fact we could when the 4th coordinate + // is not spcified in the vertex array). + // W interpolation is never needed for points. + GLboolean perspective = + c->perspective && mode!=GL_POINTS && (enables & GGL_ENABLE_TMUS); + c->rasterizer.procs.enableDisable(c, GGL_W_LERP, perspective); + + // set anti-aliasing + GLboolean smooth = GL_FALSE; + switch (mode) { + case GL_POINTS: + smooth = c->point.smooth; + break; + case GL_LINES: + case GL_LINE_LOOP: + case GL_LINE_STRIP: + smooth = c->line.smooth; + break; + } + if (((enables & GGL_ENABLE_AA)?1:0) != smooth) + c->rasterizer.procs.enableDisable(c, GGL_AA, smooth); + + // set the shade model for this primitive + c->rasterizer.procs.shadeModel(c, + (mode == GL_POINTS) ? GL_FLAT : c->lighting.shadeModel); + + // compute all the matrices we'll need... + uint32_t want = + transform_state_t::MVP | + transform_state_t::VIEWPORT; + if (c->lighting.enable) { // needs normal transforms and eye coords + want |= transform_state_t::MVUI; + want |= transform_state_t::MODELVIEW; + } + if (enables & GGL_ENABLE_TMUS) { // needs texture transforms + want |= transform_state_t::TEXTURE; + } + if (c->clipPlanes.enable || (enables & GGL_ENABLE_FOG)) { + want |= transform_state_t::MODELVIEW; // needs eye coords + } + ogles_validate_transform(c, want); + + // textures... + if (enables & GGL_ENABLE_TMUS) + ogles_validate_texture(c); + + // vertex compilers + c->arrays.compileElement = compileElement__generic; + c->arrays.compileElements = compileElements__generic; + + // vertex transform + c->arrays.mvp_transform = + c->transforms.mvp.pointv[c->arrays.vertex.size - 2]; + + c->arrays.mv_transform = + c->transforms.modelview.transform.pointv[c->arrays.vertex.size - 2]; + + /* + * *********************************************************************** + * pick fetchers + * *********************************************************************** + */ + + array_machine_t& am = c->arrays; + am.vertex.fetch = fetchNop; + am.normal.fetch = currentNormal; + am.color.fetch = currentColor; + + if (am.vertex.enable) { + am.vertex.resolve(); + if (am.vertex.bo || am.vertex.pointer) { + am.vertex.fetch = vertex_fct[am.vertex.size-2][am.vertex.type & 0xF]; + } + } + + if (am.normal.enable) { + am.normal.resolve(); + if (am.normal.bo || am.normal.pointer) { + am.normal.fetch = normal_fct[am.normal.size-3][am.normal.type & 0xF]; + } + } + + if (am.color.enable) { + am.color.resolve(); + if (c->lighting.enable) { + if (am.color.bo || am.color.pointer) { + am.color.fetch = color_fct[am.color.size-3][am.color.type & 0xF]; + } + } else { + if (am.color.bo || am.color.pointer) { + am.color.fetch = color_clamp_fct[am.color.size-3][am.color.type & 0xF]; + } + } + } + + int activeTmuCount = 0; + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + am.texture[i].fetch = currentTexCoord; + if (c->rasterizer.state.texture[i].enable) { + + // texture fetchers... + if (am.texture[i].enable) { + am.texture[i].resolve(); + if (am.texture[i].bo || am.texture[i].pointer) { + am.texture[i].fetch = texture_fct[am.texture[i].size-2][am.texture[i].type & 0xF]; + } + } + + // texture transform... + const int index = c->arrays.texture[i].size - 2; + c->arrays.tex_transform[i] = + c->transforms.texture[i].transform.pointv[index]; + + am.tmu = i; + activeTmuCount++; + } + } + + // pick the vertex-clipper + uint32_t clipper = 0; + // we must reload 'enables' here + enables = c->rasterizer.state.enables; + if (enables & GGL_ENABLE_SMOOTH) + clipper |= 1; // we need to interpolate colors + if (enables & GGL_ENABLE_TMUS) + clipper |= 2; // we need to interpolate textures + switch (clipper) { + case 0: c->arrays.clipVertex = clipVertex; break; + case 1: c->arrays.clipVertex = clipVertexC; break; + case 2: c->arrays.clipVertex = clipVertexT; break; + case 3: c->arrays.clipVertex = clipVertexAll; break; + } + c->arrays.clipEye = clipEye; + + // pick the primitive rasterizer + ogles_validate_primitives(c); +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + +using namespace android; + +#if 0 +#pragma mark - +#pragma mark array API +#endif + +void glVertexPointer( + GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) +{ + ogles_context_t* c = ogles_context_t::get(); + if (size<2 || size>4 || stride<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + switch (type) { + case GL_BYTE: + case GL_SHORT: + case GL_FIXED: + case GL_FLOAT: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->arrays.vertex.init(size, type, stride, pointer, c->arrays.array_buffer, 0); +} + +void glColorPointer( + GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) +{ + ogles_context_t* c = ogles_context_t::get(); + // in theory ogles doesn't allow color arrays of size 3 + // but it is very useful to 'visualize' the normal array. + if (size<3 || size>4 || stride<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + switch (type) { + case GL_UNSIGNED_BYTE: + case GL_FIXED: + case GL_FLOAT: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->arrays.color.init(size, type, stride, pointer, c->arrays.array_buffer, 0); +} + +void glNormalPointer( + GLenum type, GLsizei stride, const GLvoid *pointer) +{ + ogles_context_t* c = ogles_context_t::get(); + if (stride<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + switch (type) { + case GL_BYTE: + case GL_SHORT: + case GL_FIXED: + case GL_FLOAT: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->arrays.normal.init(3, type, stride, pointer, c->arrays.array_buffer, 0); +} + +void glTexCoordPointer( + GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) +{ + ogles_context_t* c = ogles_context_t::get(); + if (size<2 || size>4 || stride<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + switch (type) { + case GL_BYTE: + case GL_SHORT: + case GL_FIXED: + case GL_FLOAT: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + const int tmu = c->arrays.activeTexture; + c->arrays.texture[tmu].init(size, type, stride, pointer, + c->arrays.array_buffer, 0); +} + + +void glEnableClientState(GLenum array) { + ogles_context_t* c = ogles_context_t::get(); + enableDisableClientState(c, array, true); +} + +void glDisableClientState(GLenum array) { + ogles_context_t* c = ogles_context_t::get(); + enableDisableClientState(c, array, false); +} + +void glClientActiveTexture(GLenum texture) +{ + ogles_context_t* c = ogles_context_t::get(); + if (texture<GL_TEXTURE0 || texture>=GL_TEXTURE0+GGL_TEXTURE_UNIT_COUNT) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->arrays.activeTexture = texture - GL_TEXTURE0; +} + +void glDrawArrays(GLenum mode, GLint first, GLsizei count) +{ + ogles_context_t* c = ogles_context_t::get(); + if (count<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + switch (mode) { + case GL_POINTS: + case GL_LINE_STRIP: + case GL_LINE_LOOP: + case GL_LINES: + case GL_TRIANGLE_STRIP: + case GL_TRIANGLE_FAN: + case GL_TRIANGLES: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + + if (count == 0 || !c->arrays.vertex.enable) + return; + if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK)) + return; // all triangles are culled + + validate_arrays(c, mode); + drawArraysPrims[mode](c, first, count); + +#if VC_CACHE_STATISTICS + c->vc.total = count; + c->vc.dump_stats(mode); +#endif +} + +void glDrawElements( + GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) +{ + ogles_context_t* c = ogles_context_t::get(); + if (count<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + switch (mode) { + case GL_POINTS: + case GL_LINE_STRIP: + case GL_LINE_LOOP: + case GL_LINES: + case GL_TRIANGLE_STRIP: + case GL_TRIANGLE_FAN: + case GL_TRIANGLES: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + switch (type) { + case GL_UNSIGNED_BYTE: + case GL_UNSIGNED_SHORT: + c->arrays.indicesType = type; + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (count == 0 || !c->arrays.vertex.enable) + return; + if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK)) + return; // all triangles are culled + + // clear the vertex-cache + c->vc.clear(); + validate_arrays(c, mode); + + // if indices are in a buffer object, the pointer is treated as an + // offset in that buffer. + if (c->arrays.element_array_buffer) { + indices = c->arrays.element_array_buffer->data + uintptr_t(indices); + } + + drawElementsPrims[mode](c, count, indices); + +#if VC_CACHE_STATISTICS + c->vc.total = count; + c->vc.dump_stats(mode); +#endif +} + +// ---------------------------------------------------------------------------- +// buffers +// ---------------------------------------------------------------------------- + +void glBindBuffer(GLenum target, GLuint buffer) +{ + ogles_context_t* c = ogles_context_t::get(); + if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + // create a buffer object, or bind an existing one + buffer_t const* bo = 0; + if (buffer) { + bo = c->bufferObjectManager->bind(buffer); + if (!bo) { + ogles_error(c, GL_OUT_OF_MEMORY); + return; + } + } + ((target == GL_ARRAY_BUFFER) ? + c->arrays.array_buffer : c->arrays.element_array_buffer) = bo; +} + +void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) +{ + ogles_context_t* c = ogles_context_t::get(); + if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (size<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + if ((usage!=GL_STATIC_DRAW) && (usage!=GL_DYNAMIC_DRAW)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ? + c->arrays.array_buffer : c->arrays.element_array_buffer); + + if (bo == 0) { + // can't modify buffer 0 + ogles_error(c, GL_INVALID_OPERATION); + return; + } + + buffer_t* edit_bo = const_cast<buffer_t*>(bo); + if (c->bufferObjectManager->allocateStore(edit_bo, size, usage) != 0) { + ogles_error(c, GL_OUT_OF_MEMORY); + return; + } + if (data) { + memcpy(bo->data, data, size); + } +} + +void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) +{ + ogles_context_t* c = ogles_context_t::get(); + if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (offset<0 || size<0 || data==0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ? + c->arrays.array_buffer : c->arrays.element_array_buffer); + + if (bo == 0) { + // can't modify buffer 0 + ogles_error(c, GL_INVALID_OPERATION); + return; + } + if (offset+size > bo->size) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + memcpy(bo->data + offset, data, size); +} + +void glDeleteBuffers(GLsizei n, const GLuint* buffers) +{ + ogles_context_t* c = ogles_context_t::get(); + if (n<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + for (int i=0 ; i<n ; i++) { + GLuint name = buffers[i]; + if (name) { + // unbind bound deleted buffers... + if (c->arrays.element_array_buffer->name == name) { + c->arrays.element_array_buffer = 0; + } + if (c->arrays.array_buffer->name == name) { + c->arrays.array_buffer = 0; + } + if (c->arrays.vertex.bo->name == name) { + c->arrays.vertex.bo = 0; + } + if (c->arrays.normal.bo->name == name) { + c->arrays.normal.bo = 0; + } + if (c->arrays.color.bo->name == name) { + c->arrays.color.bo = 0; + } + for (int t=0 ; t<GGL_TEXTURE_UNIT_COUNT ; t++) { + if (c->arrays.texture[t].bo->name == name) { + c->arrays.texture[t].bo = 0; + } + } + } + } + c->bufferObjectManager->deleteBuffers(n, buffers); + c->bufferObjectManager->recycleTokens(n, buffers); +} + +void glGenBuffers(GLsizei n, GLuint* buffers) +{ + ogles_context_t* c = ogles_context_t::get(); + if (n<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + c->bufferObjectManager->getToken(n, buffers); +} diff --git a/opengl/libagl/array.h b/opengl/libagl/array.h new file mode 100644 index 0000000..e156978 --- /dev/null +++ b/opengl/libagl/array.h @@ -0,0 +1,37 @@ +/* libs/opengles/array.h +** +** 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. +*/ + +#ifndef ANDROID_OPENGLES_ARRAY_H +#define ANDROID_OPENGLES_ARRAY_H + +#include <stdint.h> +#include <stddef.h> +#include <sys/types.h> + +namespace android { + +namespace gl { +struct ogles_context_t; +}; + +void ogles_init_array(ogles_context_t* c); +void ogles_uninit_array(ogles_context_t* c); + +}; // namespace android + +#endif // ANDROID_OPENGLES_ARRAY_H + diff --git a/opengl/libagl/context.h b/opengl/libagl/context.h new file mode 100644 index 0000000..ef36b56 --- /dev/null +++ b/opengl/libagl/context.h @@ -0,0 +1,20 @@ +/* libs/opengles/context.h +** +** 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 <private/opengles/gl_context.h> + +using namespace android::gl; diff --git a/opengl/libagl/dxt.cpp b/opengl/libagl/dxt.cpp new file mode 100644 index 0000000..238c81f --- /dev/null +++ b/opengl/libagl/dxt.cpp @@ -0,0 +1,636 @@ +/* libs/opengles/dxt.cpp +** +** Copyright 2007, 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. +*/ + +#define TIMING 0 + +#if TIMING +#include <sys/time.h> // for optimization timing +#include <stdio.h> +#include <stdlib.h> +#endif + +#include <GLES/gl.h> +#include <utils/Endian.h> + +#include "context.h" + +#define TIMING 0 + +namespace android { + +static uint8_t avg23tab[64*64]; +static volatile int tables_initialized = 0; + +// Definitions below are equivalent to these over the valid range of arguments +// #define div5(x) ((x)/5) +// #define div7(x) ((x)/7) + +// Use fixed-point to divide by 5 and 7 +// 3277 = 2^14/5 + 1 +// 2341 = 2^14/7 + 1 +#define div5(x) (((x)*3277) >> 14) +#define div7(x) (((x)*2341) >> 14) + +// Table with entry [a << 6 | b] = (2*a + b)/3 for 0 <= a,b < 64 +#define avg23(x0,x1) avg23tab[((x0) << 6) | (x1)] + +// Extract 5/6/5 RGB +#define red(x) (((x) >> 11) & 0x1f) +#define green(x) (((x) >> 5) & 0x3f) +#define blue(x) ( (x) & 0x1f) + +/* + * Convert 5/6/5 RGB (as 3 ints) to 8/8/8 + * + * Operation count: 8 <<, 0 &, 5 | + */ +inline static int rgb565SepTo888(int r, int g, int b) + +{ + return ((((r << 3) | (r >> 2)) << 16) | + (((g << 2) | (g >> 4)) << 8) | + ((b << 3) | (b >> 2))); +} + +/* + * Convert 5/6/5 RGB (as a single 16-bit word) to 8/8/8 + * + * r4r3r2r1 r0g5g4g3 g2g1g0b4 b3b2b1b0 rgb + * r4r3r2 r1r0g5g4 g3g2g1g0 b4b3b2b1 b0 0 0 0 rgb << 3 + * r4r3r2r1 r0r4r3r2 g5g4g3g2 g1g0g5g4 b4b3b2b1 b0b4b3b2 desired result + * + * Construct the 24-bit RGB word as: + * + * r4r3r2r1 r0------ -------- -------- -------- -------- (rgb << 8) & 0xf80000 + * r4r3r2 -------- -------- -------- -------- (rgb << 3) & 0x070000 + * g5g4g3g2 g1g0---- -------- -------- (rgb << 5) & 0x00fc00 + * g5g4 -------- -------- (rgb >> 1) & 0x000300 + * b4b3b2b1 b0------ (rgb << 3) & 0x0000f8 + * b4b3b2 (rgb >> 2) & 0x000007 + * + * Operation count: 5 <<, 6 &, 5 | (n.b. rgb >> 3 is used twice) + */ +inline static int rgb565To888(int rgb) + +{ + int rgb3 = rgb >> 3; + return (((rgb << 8) & 0xf80000) | + ( rgb3 & 0x070000) | + ((rgb << 5) & 0x00fc00) | + ((rgb >> 1) & 0x000300) | + ( rgb3 & 0x0000f8) | + ((rgb >> 2) & 0x000007)); +} + +#if __BYTE_ORDER == __BIG_ENDIAN +static uint32_t swap(uint32_t x) { + int b0 = (x >> 24) & 0xff; + int b1 = (x >> 16) & 0xff; + int b2 = (x >> 8) & 0xff; + int b3 = (x ) & 0xff; + + return (uint32_t)((b3 << 24) | (b2 << 16) | (b1 << 8) | b0); +} +#endif + +static void +init_tables() +{ + if (tables_initialized) { + return; + } + + for (int i = 0; i < 64; i++) { + for (int j = 0; j < 64; j++) { + int avg = (2*i + j)/3; + avg23tab[(i << 6) | j] = avg; + } + } + + asm volatile ("" : : : "memory"); + tables_initialized = 1; +} + +/* + * Utility to scan a DXT1 compressed texture to determine whether it + * contains a transparent pixel (color0 < color1, code == 3). This + * may be useful if the application lacks information as to whether + * the true format is GL_COMPRESSED_RGB_S3TC_DXT1_EXT or + * GL_COMPRESSED_RGBA_S3TC_DXT1_EXT. + */ +bool +DXT1HasAlpha(const GLvoid *data, int width, int height) { +#if TIMING + struct timeval start_t, end_t; + struct timezone tz; + + gettimeofday(&start_t, &tz); +#endif + + bool hasAlpha = false; + + int xblocks = (width + 3)/4; + int yblocks = (height + 3)/4; + int numblocks = xblocks*yblocks; + + uint32_t const *d32 = (uint32_t *)data; + for (int b = 0; b < numblocks; b++) { + uint32_t colors = *d32++; + +#if __BYTE_ORDER == __BIG_ENDIAN + colors = swap(colors); +#endif + + uint16_t color0 = colors & 0xffff; + uint16_t color1 = colors >> 16; + + if (color0 < color1) { + // There's no need to endian-swap within 'bits' + // since we don't care which pixel is the transparent one + uint32_t bits = *d32++; + + // Detect if any (odd, even) pair of bits are '11' + // bits: b31 b30 b29 ... b3 b2 b1 b0 + // bits >> 1: b31 b31 b30 ... b4 b3 b2 b1 + // &: b31 (b31 & b30) (b29 & b28) ... (b2 & b1) (b1 & b0) + // & 0x55..: 0 (b31 & b30) 0 ... 0 (b1 & b0) + if (((bits & (bits >> 1)) & 0x55555555) != 0) { + hasAlpha = true; + goto done; + } + } else { + // Skip 4 bytes + ++d32; + } + } + + done: +#if TIMING + gettimeofday(&end_t, &tz); + long usec = (end_t.tv_sec - start_t.tv_sec)*1000000 + + (end_t.tv_usec - start_t.tv_usec); + + printf("Scanned w=%d h=%d in %ld usec\n", width, height, usec); +#endif + + return hasAlpha; +} + +static void +decodeDXT1(const GLvoid *data, int width, int height, + void *surface, int stride, + bool hasAlpha) + +{ + init_tables(); + + uint32_t const *d32 = (uint32_t *)data; + + // Color table for the current block + uint16_t c[4]; + c[0] = c[1] = c[2] = c[3] = 0; + + // Specified colors from the previous block + uint16_t prev_color0 = 0x0000; + uint16_t prev_color1 = 0x0000; + + uint16_t* rowPtr = (uint16_t*)surface; + for (int base_y = 0; base_y < height; base_y += 4, rowPtr += 4*stride) { + uint16_t *blockPtr = rowPtr; + for (int base_x = 0; base_x < width; base_x += 4, blockPtr += 4) { + uint32_t colors = *d32++; + uint32_t bits = *d32++; + +#if __BYTE_ORDER == __BIG_ENDIAN + colors = swap(colors); + bits = swap(bits); +#endif + + // Raw colors + uint16_t color0 = colors & 0xffff; + uint16_t color1 = colors >> 16; + + // If the new block has the same base colors as the + // previous one, we don't need to recompute the color + // table c[] + if (color0 != prev_color0 || color1 != prev_color1) { + // Store raw colors for comparison with next block + prev_color0 = color0; + prev_color1 = color1; + + int r0 = red(color0); + int g0 = green(color0); + int b0 = blue(color0); + + int r1 = red(color1); + int g1 = green(color1); + int b1 = blue(color1); + + if (hasAlpha) { + c[0] = (r0 << 11) | ((g0 >> 1) << 6) | (b0 << 1) | 0x1; + c[1] = (r1 << 11) | ((g1 >> 1) << 6) | (b1 << 1) | 0x1; + } else { + c[0] = color0; + c[1] = color1; + } + + int r2, g2, b2, r3, g3, b3, a3; + + int bbits = bits >> 1; + bool has2 = ((bbits & ~bits) & 0x55555555) != 0; + bool has3 = ((bbits & bits) & 0x55555555) != 0; + + if (has2 || has3) { + if (color0 > color1) { + r2 = avg23(r0, r1); + g2 = avg23(g0, g1); + b2 = avg23(b0, b1); + + r3 = avg23(r1, r0); + g3 = avg23(g1, g0); + b3 = avg23(b1, b0); + a3 = 1; + } else { + r2 = (r0 + r1) >> 1; + g2 = (g0 + g1) >> 1; + b2 = (b0 + b1) >> 1; + + r3 = g3 = b3 = a3 = 0; + } + if (hasAlpha) { + c[2] = (r2 << 11) | ((g2 >> 1) << 6) | + (b2 << 1) | 0x1; + c[3] = (r3 << 11) | ((g3 >> 1) << 6) | + (b3 << 1) | a3; + } else { + c[2] = (r2 << 11) | (g2 << 5) | b2; + c[3] = (r3 << 11) | (g3 << 5) | b3; + } + } + } + + uint16_t* blockRowPtr = blockPtr; + for (int y = 0; y < 4; y++, blockRowPtr += stride) { + // Don't process rows past the botom + if (base_y + y >= height) { + break; + } + + int w = min(width - base_x, 4); + for (int x = 0; x < w; x++) { + int code = bits & 0x3; + bits >>= 2; + + blockRowPtr[x] = c[code]; + } + } + } + } +} + +// Output data as internalformat=GL_RGBA, type=GL_UNSIGNED_BYTE +static void +decodeDXT3(const GLvoid *data, int width, int height, + void *surface, int stride) + +{ + init_tables(); + + uint32_t const *d32 = (uint32_t *)data; + + // Specified colors from the previous block + uint16_t prev_color0 = 0x0000; + uint16_t prev_color1 = 0x0000; + + // Color table for the current block + uint32_t c[4]; + c[0] = c[1] = c[2] = c[3] = 0; + + uint32_t* rowPtr = (uint32_t*)surface; + for (int base_y = 0; base_y < height; base_y += 4, rowPtr += 4*stride) { + uint32_t *blockPtr = rowPtr; + for (int base_x = 0; base_x < width; base_x += 4, blockPtr += 4) { + +#if __BYTE_ORDER == __BIG_ENDIAN + uint32_t alphahi = *d32++; + uint32_t alphalo = *d32++; + alphahi = swap(alphahi); + alphalo = swap(alphalo); +#else + uint32_t alphalo = *d32++; + uint32_t alphahi = *d32++; +#endif + + uint32_t colors = *d32++; + uint32_t bits = *d32++; + +#if __BYTE_ORDER == __BIG_ENDIAN + colors = swap(colors); + bits = swap(bits); +#endif + + uint64_t alpha = ((uint64_t)alphahi << 32) | alphalo; + + // Raw colors + uint16_t color0 = colors & 0xffff; + uint16_t color1 = colors >> 16; + + // If the new block has the same base colors as the + // previous one, we don't need to recompute the color + // table c[] + if (color0 != prev_color0 || color1 != prev_color1) { + // Store raw colors for comparison with next block + prev_color0 = color0; + prev_color1 = color1; + + int bbits = bits >> 1; + bool has2 = ((bbits & ~bits) & 0x55555555) != 0; + bool has3 = ((bbits & bits) & 0x55555555) != 0; + + if (has2 || has3) { + int r0 = red(color0); + int g0 = green(color0); + int b0 = blue(color0); + + int r1 = red(color1); + int g1 = green(color1); + int b1 = blue(color1); + + int r2 = avg23(r0, r1); + int g2 = avg23(g0, g1); + int b2 = avg23(b0, b1); + + int r3 = avg23(r1, r0); + int g3 = avg23(g1, g0); + int b3 = avg23(b1, b0); + + c[0] = rgb565SepTo888(r0, g0, b0); + c[1] = rgb565SepTo888(r1, g1, b1); + c[2] = rgb565SepTo888(r2, g2, b2); + c[3] = rgb565SepTo888(r3, g3, b3); + } else { + // Convert to 8 bits + c[0] = rgb565To888(color0); + c[1] = rgb565To888(color1); + } + } + + uint32_t* blockRowPtr = blockPtr; + for (int y = 0; y < 4; y++, blockRowPtr += stride) { + // Don't process rows past the botom + if (base_y + y >= height) { + break; + } + + int w = min(width - base_x, 4); + for (int x = 0; x < w; x++) { + int a = alpha & 0xf; + alpha >>= 4; + + int code = bits & 0x3; + bits >>= 2; + + blockRowPtr[x] = c[code] | (a << 28) | (a << 24); + } + } + } + } +} + +// Output data as internalformat=GL_RGBA, type=GL_UNSIGNED_BYTE +static void +decodeDXT5(const GLvoid *data, int width, int height, + void *surface, int stride) + +{ + init_tables(); + + uint32_t const *d32 = (uint32_t *)data; + + // Specified alphas from the previous block + uint8_t prev_alpha0 = 0x00; + uint8_t prev_alpha1 = 0x00; + + // Specified colors from the previous block + uint16_t prev_color0 = 0x0000; + uint16_t prev_color1 = 0x0000; + + // Alpha table for the current block + uint8_t a[8]; + a[0] = a[1] = a[2] = a[3] = a[4] = a[5] = a[6] = a[7] = 0; + + // Color table for the current block + uint32_t c[4]; + c[0] = c[1] = c[2] = c[3] = 0; + + int good_a5 = 0; + int bad_a5 = 0; + int good_a6 = 0; + int bad_a6 = 0; + int good_a7 = 0; + int bad_a7 = 0; + + uint32_t* rowPtr = (uint32_t*)surface; + for (int base_y = 0; base_y < height; base_y += 4, rowPtr += 4*stride) { + uint32_t *blockPtr = rowPtr; + for (int base_x = 0; base_x < width; base_x += 4, blockPtr += 4) { + +#if __BYTE_ORDER == __BIG_ENDIAN + uint32_t alphahi = *d32++; + uint32_t alphalo = *d32++; + alphahi = swap(alphahi); + alphalo = swap(alphalo); +#else + uint32_t alphalo = *d32++; + uint32_t alphahi = *d32++; +#endif + + uint32_t colors = *d32++; + uint32_t bits = *d32++; + +#if __BYTE_ORDER == __BIG_ENDIANx + colors = swap(colors); + bits = swap(bits); +#endif + + uint64_t alpha = ((uint64_t)alphahi << 32) | alphalo; + uint64_t alpha0 = alpha & 0xff; + alpha >>= 8; + uint64_t alpha1 = alpha & 0xff; + alpha >>= 8; + + if (alpha0 != prev_alpha0 || alpha1 != prev_alpha1) { + prev_alpha0 = alpha0; + prev_alpha1 = alpha1; + + a[0] = alpha0; + a[1] = alpha1; + int a01 = alpha0 + alpha1 - 1; + if (alpha0 > alpha1) { + a[2] = div7(6*alpha0 + alpha1); + a[4] = div7(4*alpha0 + 3*alpha1); + a[6] = div7(2*alpha0 + 5*alpha1); + + // Use symmetry to derive half of the values + // A few values will be off by 1 (~.5%) + // Alternate which values are computed directly + // and which are derived to try to reduce bias + a[3] = a01 - a[6]; + a[5] = a01 - a[4]; + a[7] = a01 - a[2]; + } else { + a[2] = div5(4*alpha0 + alpha1); + a[4] = div5(2*alpha0 + 3*alpha1); + a[3] = a01 - a[4]; + a[5] = a01 - a[2]; + a[6] = 0x00; + a[7] = 0xff; + } + } + + // Raw colors + uint16_t color0 = colors & 0xffff; + uint16_t color1 = colors >> 16; + + // If the new block has the same base colors as the + // previous one, we don't need to recompute the color + // table c[] + if (color0 != prev_color0 || color1 != prev_color1) { + // Store raw colors for comparison with next block + prev_color0 = color0; + prev_color1 = color1; + + int bbits = bits >> 1; + bool has2 = ((bbits & ~bits) & 0x55555555) != 0; + bool has3 = ((bbits & bits) & 0x55555555) != 0; + + if (has2 || has3) { + int r0 = red(color0); + int g0 = green(color0); + int b0 = blue(color0); + + int r1 = red(color1); + int g1 = green(color1); + int b1 = blue(color1); + + int r2 = avg23(r0, r1); + int g2 = avg23(g0, g1); + int b2 = avg23(b0, b1); + + int r3 = avg23(r1, r0); + int g3 = avg23(g1, g0); + int b3 = avg23(b1, b0); + + c[0] = rgb565SepTo888(r0, g0, b0); + c[1] = rgb565SepTo888(r1, g1, b1); + c[2] = rgb565SepTo888(r2, g2, b2); + c[3] = rgb565SepTo888(r3, g3, b3); + } else { + // Convert to 8 bits + c[0] = rgb565To888(color0); + c[1] = rgb565To888(color1); + } + } + + uint32_t* blockRowPtr = blockPtr; + for (int y = 0; y < 4; y++, blockRowPtr += stride) { + // Don't process rows past the botom + if (base_y + y >= height) { + break; + } + + int w = min(width - base_x, 4); + for (int x = 0; x < w; x++) { + int acode = alpha & 0x7; + alpha >>= 3; + + int code = bits & 0x3; + bits >>= 2; + + blockRowPtr[x] = c[code] | (a[acode] << 24); + } + } + } + } +} + +/* + * Decode a DXT-compressed texture into memory. DXT textures consist of + * a series of 4x4 pixel blocks in left-to-right, top-down order. + * The number of blocks is given by ceil(width/4)*ceil(height/4). + * + * 'data' points to the texture data. 'width' and 'height' indicate the + * dimensions of the texture. We assume width and height are >= 0 but + * do not require them to be powers of 2 or divisible by any factor. + * + * The output is written to 'surface' with each scanline separated by + * 'stride' 2- or 4-byte words. + * + * 'format' indicates the type of compression and must be one of the following: + * + * GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + * The output is written as 5/6/5 opaque RGB (16 bit words). + * 8 bytes are read from 'data' for each block. + * + * GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + * The output is written as 5/5/5/1 RGBA (16 bit words) + * 8 bytes are read from 'data' for each block. + * + * GL_COMPRESSED_RGBA_S3TC_DXT3_EXT + * GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + * The output is written as 8/8/8/8 ARGB (32 bit words) + * 16 bytes are read from 'data' for each block. + */ +void +decodeDXT(const GLvoid *data, int width, int height, + void *surface, int stride, int format) +{ +#if TIMING + struct timeval start_t, end_t; + struct timezone tz; + + gettimeofday(&start_t, &tz); +#endif + + switch (format) { + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + decodeDXT1(data, width, height, surface, stride, false); + break; + + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + decodeDXT1(data, width, height, surface, stride, true); + break; + + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + decodeDXT3(data, width, height, surface, stride); + break; + + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + decodeDXT5(data, width, height, surface, stride); + break; + } + +#if TIMING + gettimeofday(&end_t, &tz); + long usec = (end_t.tv_sec - start_t.tv_sec)*1000000 + + (end_t.tv_usec - start_t.tv_usec); + + printf("Loaded w=%d h=%d in %ld usec\n", width, height, usec); +#endif +} + +} // namespace android diff --git a/opengl/libagl/dxt.h b/opengl/libagl/dxt.h new file mode 100644 index 0000000..d95a36c --- /dev/null +++ b/opengl/libagl/dxt.h @@ -0,0 +1,33 @@ +/* libs/opengles/dxt.h +** +** Copyright 2007, 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. +*/ + +#ifndef ANDROID_OPENGLES_TEXTURE_H +#define ANDROID_OPENGLES_TEXTURE_H + +#include <stdlib.h> + +#include <GLES/gl.h> + +namespace android { + + bool DXT1HasAlpha(const GLvoid *data, int width, int height); + void decodeDXT(const GLvoid *data, int width, int height, + void *surface, int stride, int format); + +} // namespace android + +#endif // ANDROID_OPENGLES_TEXTURE_H diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp new file mode 100644 index 0000000..5b90bf0 --- /dev/null +++ b/opengl/libagl/egl.cpp @@ -0,0 +1,1543 @@ +/* +** +** Copyright 2007 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. +*/ + +#define LOG_TAG "EGL" + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/mman.h> + +#include <cutils/log.h> +#include <cutils/atomic.h> + +#include <utils/threads.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include <pixelflinger/format.h> +#include <pixelflinger/pixelflinger.h> + +#include "context.h" +#include "state.h" +#include "texture.h" +#include "matrix.h" + +#undef NELEM +#define NELEM(x) (sizeof(x)/sizeof(*(x))) + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +const unsigned int NUM_DISPLAYS = 1; + +static pthread_mutex_t gInitMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t gErrorKeyMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_key_t gEGLErrorKey = -1; +#ifndef HAVE_ANDROID_OS +namespace gl { +pthread_key_t gGLKey = -1; +}; // namespace gl +#endif + +template<typename T> +static T setError(GLint error, T returnValue) { + if (ggl_unlikely(gEGLErrorKey == -1)) { + pthread_mutex_lock(&gErrorKeyMutex); + if (gEGLErrorKey == -1) + pthread_key_create(&gEGLErrorKey, NULL); + pthread_mutex_unlock(&gErrorKeyMutex); + } + pthread_setspecific(gEGLErrorKey, (void*)error); + return returnValue; +} + +static GLint getError() { + if (ggl_unlikely(gEGLErrorKey == -1)) + return EGL_SUCCESS; + GLint error = (GLint)pthread_getspecific(gEGLErrorKey); + pthread_setspecific(gEGLErrorKey, (void*)EGL_SUCCESS); + return error; +} + +// ---------------------------------------------------------------------------- + +struct egl_display_t +{ + egl_display_t() : type(0), initialized(0) { } + + static egl_display_t& get_display(EGLDisplay dpy); + + static EGLBoolean is_valid(EGLDisplay dpy) { + return ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS) ? EGL_FALSE : EGL_TRUE; + } + + NativeDisplayType type; + volatile int32_t initialized; +}; + +static egl_display_t gDisplays[NUM_DISPLAYS]; + +egl_display_t& egl_display_t::get_display(EGLDisplay dpy) { + return gDisplays[uintptr_t(dpy)-1U]; +} + +struct egl_context_t { + enum { + IS_CURRENT = 0x00010000, + NEVER_CURRENT = 0x00020000 + }; + uint32_t flags; + EGLDisplay dpy; + EGLConfig config; + EGLSurface read; + EGLSurface draw; + + static inline egl_context_t* context(EGLContext ctx) { + ogles_context_t* const gl = static_cast<ogles_context_t*>(ctx); + return static_cast<egl_context_t*>(gl->rasterizer.base); + } +}; + +// ---------------------------------------------------------------------------- + +struct egl_surface_t +{ + enum { + PAGE_FLIP = 0x00000001, + MAGIC = 0x31415265 + }; + + uint32_t magic; + EGLDisplay dpy; + EGLConfig config; + EGLContext ctx; + + egl_surface_t(EGLDisplay dpy, EGLConfig config, int32_t depthFormat); + virtual ~egl_surface_t(); + virtual bool isValid() const = 0; + + virtual EGLBoolean bindDrawSurface(ogles_context_t* gl) = 0; + virtual EGLBoolean bindReadSurface(ogles_context_t* gl) = 0; + virtual EGLint getWidth() const = 0; + virtual EGLint getHeight() const = 0; + virtual void* getBits() const = 0; + + virtual EGLint getHorizontalResolution() const; + virtual EGLint getVerticalResolution() const; + virtual EGLint getRefreshRate() const; + virtual EGLint getSwapBehavior() const; + virtual EGLBoolean swapBuffers(); +protected: + GGLSurface depth; +}; + +egl_surface_t::egl_surface_t(EGLDisplay dpy, + EGLConfig config, + int32_t depthFormat) + : magic(MAGIC), dpy(dpy), config(config), ctx(0) +{ + depth.version = sizeof(GGLSurface); + depth.data = 0; + depth.format = depthFormat; +} +egl_surface_t::~egl_surface_t() +{ + magic = 0; + free(depth.data); +} +EGLBoolean egl_surface_t::swapBuffers() { + return EGL_FALSE; +} +EGLint egl_surface_t::getHorizontalResolution() const { + return (0 * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); +} +EGLint egl_surface_t::getVerticalResolution() const { + return (0 * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); +} +EGLint egl_surface_t::getRefreshRate() const { + return (60 * EGL_DISPLAY_SCALING); +} +EGLint egl_surface_t::getSwapBehavior() const { + return EGL_BUFFER_PRESERVED; +} + +// ---------------------------------------------------------------------------- + +struct egl_window_surface_t : public egl_surface_t +{ + egl_window_surface_t( + EGLDisplay dpy, EGLConfig config, + int32_t depthFormat, + egl_native_window_t* window); + + ~egl_window_surface_t(); + + virtual bool isValid() const { return nativeWindow->magic == 0x600913; } + virtual EGLBoolean swapBuffers(); + virtual EGLBoolean bindDrawSurface(ogles_context_t* gl); + virtual EGLBoolean bindReadSurface(ogles_context_t* gl); + virtual EGLint getWidth() const { return nativeWindow->width; } + virtual EGLint getHeight() const { return nativeWindow->height; } + virtual void* getBits() const; + virtual EGLint getHorizontalResolution() const; + virtual EGLint getVerticalResolution() const; + virtual EGLint getRefreshRate() const; + virtual EGLint getSwapBehavior() const; +private: + egl_native_window_t* nativeWindow; +}; + +egl_window_surface_t::egl_window_surface_t(EGLDisplay dpy, + EGLConfig config, + int32_t depthFormat, + egl_native_window_t* window) + : egl_surface_t(dpy, config, depthFormat), nativeWindow(window) +{ + if (depthFormat) { + depth.width = window->width; + depth.height = window->height; + depth.stride = depth.width; // use the width here + depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2); + if (depth.data == 0) { + setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); + return; + } + } + nativeWindow->incRef(nativeWindow); +} +egl_window_surface_t::~egl_window_surface_t() { + nativeWindow->decRef(nativeWindow); +} + +EGLBoolean egl_window_surface_t::swapBuffers() +{ + uint32_t flags = nativeWindow->swapBuffers(nativeWindow); + if (flags & EGL_NATIVES_FLAG_SIZE_CHANGED) { + // TODO: we probably should reset the swap rect here + // if the window size has changed + if (depth.data) { + free(depth.data); + depth.width = nativeWindow->width; + depth.height = nativeWindow->height; + depth.stride = nativeWindow->stride; + depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2); + if (depth.data == 0) { + setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); + return EGL_FALSE; + } + } + } + return EGL_TRUE; +} + +EGLBoolean egl_window_surface_t::bindDrawSurface(ogles_context_t* gl) +{ + GGLSurface buffer; + buffer.version = sizeof(GGLSurface); + buffer.width = nativeWindow->width; + buffer.height = nativeWindow->height; + buffer.stride = nativeWindow->stride; + buffer.data = (GGLubyte*)nativeWindow->base + nativeWindow->offset; + buffer.format = nativeWindow->format; + gl->rasterizer.procs.colorBuffer(gl, &buffer); + if (depth.data != gl->rasterizer.state.buffers.depth.data) + gl->rasterizer.procs.depthBuffer(gl, &depth); + return EGL_TRUE; +} +EGLBoolean egl_window_surface_t::bindReadSurface(ogles_context_t* gl) +{ + GGLSurface buffer; + buffer.version = sizeof(GGLSurface); + buffer.width = nativeWindow->width; + buffer.height = nativeWindow->height; + buffer.stride = nativeWindow->stride; + buffer.data = (GGLubyte*)nativeWindow->base + nativeWindow->offset; + buffer.format = nativeWindow->format; + gl->rasterizer.procs.readBuffer(gl, &buffer); + return EGL_TRUE; +} +void* egl_window_surface_t::getBits() const { + return (GGLubyte*)nativeWindow->base + nativeWindow->offset; +} +EGLint egl_window_surface_t::getHorizontalResolution() const { + return (nativeWindow->xdpi * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); +} +EGLint egl_window_surface_t::getVerticalResolution() const { + return (nativeWindow->ydpi * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); +} +EGLint egl_window_surface_t::getRefreshRate() const { + return (nativeWindow->fps * EGL_DISPLAY_SCALING); +} +EGLint egl_window_surface_t::getSwapBehavior() const { + uint32_t flags = nativeWindow->flags; + if (flags & EGL_NATIVES_FLAG_DESTROY_BACKBUFFER) + return EGL_BUFFER_DESTROYED; + return EGL_BUFFER_PRESERVED; +} + +// ---------------------------------------------------------------------------- + +struct egl_pixmap_surface_t : public egl_surface_t +{ + egl_pixmap_surface_t( + EGLDisplay dpy, EGLConfig config, + int32_t depthFormat, + egl_native_pixmap_t const * pixmap); + + virtual ~egl_pixmap_surface_t() { } + + virtual bool isValid() const { return nativePixmap.version == sizeof(egl_native_pixmap_t); } + virtual EGLBoolean bindDrawSurface(ogles_context_t* gl); + virtual EGLBoolean bindReadSurface(ogles_context_t* gl); + virtual EGLint getWidth() const { return nativePixmap.width; } + virtual EGLint getHeight() const { return nativePixmap.height; } + virtual void* getBits() const { return nativePixmap.data; } +private: + egl_native_pixmap_t nativePixmap; +}; + +egl_pixmap_surface_t::egl_pixmap_surface_t(EGLDisplay dpy, + EGLConfig config, + int32_t depthFormat, + egl_native_pixmap_t const * pixmap) + : egl_surface_t(dpy, config, depthFormat), nativePixmap(*pixmap) +{ + if (depthFormat) { + depth.width = pixmap->width; + depth.height = pixmap->height; + depth.stride = depth.width; // use the width here + depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2); + if (depth.data == 0) { + setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); + return; + } + } +} +EGLBoolean egl_pixmap_surface_t::bindDrawSurface(ogles_context_t* gl) +{ + GGLSurface buffer; + buffer.version = sizeof(GGLSurface); + buffer.width = nativePixmap.width; + buffer.height = nativePixmap.height; + buffer.stride = nativePixmap.stride; + buffer.data = nativePixmap.data; + buffer.format = nativePixmap.format; + + gl->rasterizer.procs.colorBuffer(gl, &buffer); + if (depth.data != gl->rasterizer.state.buffers.depth.data) + gl->rasterizer.procs.depthBuffer(gl, &depth); + return EGL_TRUE; +} +EGLBoolean egl_pixmap_surface_t::bindReadSurface(ogles_context_t* gl) +{ + GGLSurface buffer; + buffer.version = sizeof(GGLSurface); + buffer.width = nativePixmap.width; + buffer.height = nativePixmap.height; + buffer.stride = nativePixmap.stride; + buffer.data = nativePixmap.data; + buffer.format = nativePixmap.format; + gl->rasterizer.procs.readBuffer(gl, &buffer); + return EGL_TRUE; +} + +// ---------------------------------------------------------------------------- + +struct egl_pbuffer_surface_t : public egl_surface_t +{ + egl_pbuffer_surface_t( + EGLDisplay dpy, EGLConfig config, int32_t depthFormat, + int32_t w, int32_t h, int32_t f); + + virtual ~egl_pbuffer_surface_t(); + + virtual bool isValid() const { return pbuffer.data != 0; } + virtual EGLBoolean bindDrawSurface(ogles_context_t* gl); + virtual EGLBoolean bindReadSurface(ogles_context_t* gl); + virtual EGLint getWidth() const { return pbuffer.width; } + virtual EGLint getHeight() const { return pbuffer.height; } + virtual void* getBits() const { return pbuffer.data; } +private: + GGLSurface pbuffer; +}; + +egl_pbuffer_surface_t::egl_pbuffer_surface_t(EGLDisplay dpy, + EGLConfig config, int32_t depthFormat, + int32_t w, int32_t h, int32_t f) + : egl_surface_t(dpy, config, depthFormat) +{ + size_t size = w*h; + switch (f) { + case GGL_PIXEL_FORMAT_A_8: size *= 1; break; + case GGL_PIXEL_FORMAT_RGB_565: size *= 2; break; + case GGL_PIXEL_FORMAT_RGBA_8888: size *= 4; break; + default: + LOGE("incompatible pixel format for pbuffer (format=%d)", f); + pbuffer.data = 0; + break; + } + pbuffer.version = sizeof(GGLSurface); + pbuffer.width = w; + pbuffer.height = h; + pbuffer.stride = w; + pbuffer.data = (GGLubyte*)malloc(size); + pbuffer.format = f; + + if (depthFormat) { + depth.width = pbuffer.width; + depth.height = pbuffer.height; + depth.stride = depth.width; // use the width here + depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2); + if (depth.data == 0) { + setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); + return; + } + } +} +egl_pbuffer_surface_t::~egl_pbuffer_surface_t() { + free(pbuffer.data); +} +EGLBoolean egl_pbuffer_surface_t::bindDrawSurface(ogles_context_t* gl) +{ + gl->rasterizer.procs.colorBuffer(gl, &pbuffer); + if (depth.data != gl->rasterizer.state.buffers.depth.data) + gl->rasterizer.procs.depthBuffer(gl, &depth); + return EGL_TRUE; +} +EGLBoolean egl_pbuffer_surface_t::bindReadSurface(ogles_context_t* gl) +{ + gl->rasterizer.procs.readBuffer(gl, &pbuffer); + return EGL_TRUE; +} + +// ---------------------------------------------------------------------------- + +struct config_pair_t { + GLint key; + GLint value; +}; + +struct configs_t { + const config_pair_t* array; + int size; +}; + +struct config_management_t { + GLint key; + bool (*match)(GLint reqValue, GLint confValue); + static bool atLeast(GLint reqValue, GLint confValue) { + return (reqValue == EGL_DONT_CARE) || (confValue >= reqValue); + } + static bool exact(GLint reqValue, GLint confValue) { + return (reqValue == EGL_DONT_CARE) || (confValue == reqValue); + } + static bool mask(GLint reqValue, GLint confValue) { + return (confValue & reqValue) == reqValue; + } +}; + +// ---------------------------------------------------------------------------- + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 2 +static char const * const gVendorString = "Google Inc."; +static char const * const gVersionString = "1.2 Android Driver"; +static char const * const gClientApiString = "OpenGL ES"; +static char const * const gExtensionsString = ""; + +// ---------------------------------------------------------------------------- + +struct extention_map_t { + const char * const name; + __eglMustCastToProperFunctionPointerType address; +}; + +static const extention_map_t gExtentionMap[] = { + { "glDrawTexsOES", (void(*)())&glDrawTexsOES }, + { "glDrawTexiOES", (void(*)())&glDrawTexiOES }, + { "glDrawTexfOES", (void(*)())&glDrawTexfOES }, + { "glDrawTexxOES", (void(*)())&glDrawTexxOES }, + { "glDrawTexsvOES", (void(*)())&glDrawTexsvOES }, + { "glDrawTexivOES", (void(*)())&glDrawTexivOES }, + { "glDrawTexfvOES", (void(*)())&glDrawTexfvOES }, + { "glDrawTexxvOES", (void(*)())&glDrawTexxvOES }, + { "glQueryMatrixxOES", (void(*)())&glQueryMatrixxOES }, + { "glClipPlanef", (void(*)())&glClipPlanef }, + { "glClipPlanex", (void(*)())&glClipPlanex }, + { "glBindBuffer", (void(*)())&glBindBuffer }, + { "glBufferData", (void(*)())&glBufferData }, + { "glBufferSubData", (void(*)())&glBufferSubData }, + { "glDeleteBuffers", (void(*)())&glDeleteBuffers }, + { "glGenBuffers", (void(*)())&glGenBuffers }, +}; + +/* + * In the lists below, attributes names MUST be sorted. + * Additionally, all configs must be sorted according to + * the EGL specification. + */ + +static config_pair_t const config_base_attribute_list[] = { + { EGL_STENCIL_SIZE, 0 }, + { EGL_CONFIG_CAVEAT, EGL_SLOW_CONFIG }, + { EGL_LEVEL, 0 }, + { EGL_MAX_PBUFFER_HEIGHT, GGL_MAX_VIEWPORT_DIMS }, + { EGL_MAX_PBUFFER_PIXELS, + GGL_MAX_VIEWPORT_DIMS*GGL_MAX_VIEWPORT_DIMS }, + { EGL_MAX_PBUFFER_WIDTH, GGL_MAX_VIEWPORT_DIMS }, + { EGL_NATIVE_RENDERABLE, EGL_TRUE }, + { EGL_NATIVE_VISUAL_ID, 0 }, + { EGL_NATIVE_VISUAL_TYPE, GGL_PIXEL_FORMAT_RGB_565 }, + { EGL_SAMPLES, 0 }, + { EGL_SAMPLE_BUFFERS, 0 }, + { EGL_TRANSPARENT_TYPE, EGL_NONE }, + { EGL_TRANSPARENT_BLUE_VALUE, 0 }, + { EGL_TRANSPARENT_GREEN_VALUE, 0 }, + { EGL_TRANSPARENT_RED_VALUE, 0 }, + { EGL_BIND_TO_TEXTURE_RGBA, EGL_FALSE }, + { EGL_BIND_TO_TEXTURE_RGB, EGL_FALSE }, + { EGL_MIN_SWAP_INTERVAL, 1 }, + { EGL_MAX_SWAP_INTERVAL, 4 }, +}; + +// These configs can override the base attribute list +// NOTE: when adding a config here, don't forget to update eglCreate*Surface() + +static config_pair_t const config_0_attribute_list[] = { + { EGL_BUFFER_SIZE, 16 }, + { EGL_ALPHA_SIZE, 0 }, + { EGL_BLUE_SIZE, 5 }, + { EGL_GREEN_SIZE, 6 }, + { EGL_RED_SIZE, 5 }, + { EGL_DEPTH_SIZE, 0 }, + { EGL_CONFIG_ID, 0 }, + { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, +}; + +static config_pair_t const config_1_attribute_list[] = { + { EGL_BUFFER_SIZE, 16 }, + { EGL_ALPHA_SIZE, 0 }, + { EGL_BLUE_SIZE, 5 }, + { EGL_GREEN_SIZE, 6 }, + { EGL_RED_SIZE, 5 }, + { EGL_DEPTH_SIZE, 16 }, + { EGL_CONFIG_ID, 1 }, + { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, +}; + +static config_pair_t const config_2_attribute_list[] = { + { EGL_BUFFER_SIZE, 32 }, + { EGL_ALPHA_SIZE, 8 }, + { EGL_BLUE_SIZE, 8 }, + { EGL_GREEN_SIZE, 8 }, + { EGL_RED_SIZE, 8 }, + { EGL_DEPTH_SIZE, 0 }, + { EGL_CONFIG_ID, 2 }, + { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, +}; + +static config_pair_t const config_3_attribute_list[] = { + { EGL_BUFFER_SIZE, 32 }, + { EGL_ALPHA_SIZE, 8 }, + { EGL_BLUE_SIZE, 8 }, + { EGL_GREEN_SIZE, 8 }, + { EGL_RED_SIZE, 8 }, + { EGL_DEPTH_SIZE, 16 }, + { EGL_CONFIG_ID, 3 }, + { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, +}; + +static config_pair_t const config_4_attribute_list[] = { + { EGL_BUFFER_SIZE, 8 }, + { EGL_ALPHA_SIZE, 8 }, + { EGL_BLUE_SIZE, 0 }, + { EGL_GREEN_SIZE, 0 }, + { EGL_RED_SIZE, 0 }, + { EGL_DEPTH_SIZE, 0 }, + { EGL_CONFIG_ID, 4 }, + { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, +}; + +static config_pair_t const config_5_attribute_list[] = { + { EGL_BUFFER_SIZE, 8 }, + { EGL_ALPHA_SIZE, 8 }, + { EGL_BLUE_SIZE, 0 }, + { EGL_GREEN_SIZE, 0 }, + { EGL_RED_SIZE, 0 }, + { EGL_DEPTH_SIZE, 16 }, + { EGL_CONFIG_ID, 5 }, + { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, +}; + +static configs_t const gConfigs[] = { + { config_0_attribute_list, NELEM(config_0_attribute_list) }, + { config_1_attribute_list, NELEM(config_1_attribute_list) }, + { config_2_attribute_list, NELEM(config_2_attribute_list) }, + { config_3_attribute_list, NELEM(config_3_attribute_list) }, + { config_4_attribute_list, NELEM(config_4_attribute_list) }, + { config_5_attribute_list, NELEM(config_5_attribute_list) }, +}; + +static config_management_t const gConfigManagement[] = { + { EGL_BUFFER_SIZE, config_management_t::atLeast }, + { EGL_ALPHA_SIZE, config_management_t::atLeast }, + { EGL_BLUE_SIZE, config_management_t::atLeast }, + { EGL_GREEN_SIZE, config_management_t::atLeast }, + { EGL_RED_SIZE, config_management_t::atLeast }, + { EGL_DEPTH_SIZE, config_management_t::atLeast }, + { EGL_STENCIL_SIZE, config_management_t::atLeast }, + { EGL_CONFIG_CAVEAT, config_management_t::exact }, + { EGL_CONFIG_ID, config_management_t::exact }, + { EGL_LEVEL, config_management_t::exact }, + { EGL_MAX_PBUFFER_HEIGHT, config_management_t::exact }, + { EGL_MAX_PBUFFER_PIXELS, config_management_t::exact }, + { EGL_MAX_PBUFFER_WIDTH, config_management_t::exact }, + { EGL_NATIVE_RENDERABLE, config_management_t::exact }, + { EGL_NATIVE_VISUAL_ID, config_management_t::exact }, + { EGL_NATIVE_VISUAL_TYPE, config_management_t::exact }, + { EGL_SAMPLES, config_management_t::exact }, + { EGL_SAMPLE_BUFFERS, config_management_t::exact }, + { EGL_SURFACE_TYPE, config_management_t::mask }, + { EGL_TRANSPARENT_TYPE, config_management_t::exact }, + { EGL_TRANSPARENT_BLUE_VALUE, config_management_t::exact }, + { EGL_TRANSPARENT_GREEN_VALUE, config_management_t::exact }, + { EGL_TRANSPARENT_RED_VALUE, config_management_t::exact }, + { EGL_BIND_TO_TEXTURE_RGBA, config_management_t::exact }, + { EGL_BIND_TO_TEXTURE_RGB, config_management_t::exact }, + { EGL_MIN_SWAP_INTERVAL, config_management_t::exact }, + { EGL_MAX_SWAP_INTERVAL, config_management_t::exact }, +}; + +static config_pair_t const config_defaults[] = { + { EGL_SURFACE_TYPE, EGL_WINDOW_BIT }, +}; + +// ---------------------------------------------------------------------------- + +template<typename T> +static int binarySearch(T const sortedArray[], int first, int last, EGLint key) +{ + while (first <= last) { + int mid = (first + last) / 2; + if (key > sortedArray[mid].key) { + first = mid + 1; + } else if (key < sortedArray[mid].key) { + last = mid - 1; + } else { + return mid; + } + } + return -1; +} + +static int isAttributeMatching(int i, EGLint attr, EGLint val) +{ + // look for the attribute in all of our configs + config_pair_t const* configFound = gConfigs[i].array; + int index = binarySearch<config_pair_t>( + gConfigs[i].array, + 0, gConfigs[i].size-1, + attr); + if (index < 0) { + configFound = config_base_attribute_list; + index = binarySearch<config_pair_t>( + config_base_attribute_list, + 0, NELEM(config_base_attribute_list)-1, + attr); + } + if (index >= 0) { + // attribute found, check if this config could match + int cfgMgtIndex = binarySearch<config_management_t>( + gConfigManagement, + 0, NELEM(gConfigManagement)-1, + attr); + if (index >= 0) { + bool match = gConfigManagement[cfgMgtIndex].match( + val, configFound[index].value); + if (match) { + // this config matches + return 1; + } + } else { + // attribute not found. this should NEVER happen. + } + } else { + // error, this attribute doesn't exist + } + return 0; +} + +static int makeCurrent(ogles_context_t* gl) +{ + ogles_context_t* current = (ogles_context_t*)getGlThreadSpecific(); + if (gl) { + egl_context_t* c = egl_context_t::context(gl); + if (c->flags & egl_context_t::IS_CURRENT) { + if (current != gl) { + // it is an error to set a context current, if it's already + // current to another thread + return -1; + } + } else { + if (current) { + // mark the current context as not current, and flush + glFlush(); + egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT; + } + } + if (!(c->flags & egl_context_t::IS_CURRENT)) { + // The context is not current, make it current! + setGlThreadSpecific(gl); + c->flags |= egl_context_t::IS_CURRENT; + } + } else { + if (current) { + // mark the current context as not current, and flush + glFlush(); + egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT; + } + // this thread has no context attached to it + setGlThreadSpecific(0); + } + return 0; +} + +static EGLBoolean getConfigAttrib(EGLDisplay dpy, EGLConfig config, + EGLint attribute, EGLint *value) +{ + size_t numConfigs = NELEM(gConfigs); + int index = (int)config; + if (uint32_t(index) >= numConfigs) + return setError(EGL_BAD_CONFIG, EGL_FALSE); + + int attrIndex; + attrIndex = binarySearch<config_pair_t>( + gConfigs[index].array, + 0, gConfigs[index].size-1, + attribute); + if (attrIndex>=0) { + *value = gConfigs[index].array[attrIndex].value; + return EGL_TRUE; + } + + attrIndex = binarySearch<config_pair_t>( + config_base_attribute_list, + 0, NELEM(config_base_attribute_list)-1, + attribute); + if (attrIndex>=0) { + *value = config_base_attribute_list[attrIndex].value; + return EGL_TRUE; + } + return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); +} + +static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config, + NativeWindowType window, const EGLint *attrib_list) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); + if (window == 0) + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + + EGLint surfaceType; + if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) + return EGL_FALSE; + + if (!(surfaceType & EGL_WINDOW_BIT)) + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + + EGLint configID; + if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE) + return EGL_FALSE; + + int32_t depthFormat; + int32_t pixelFormat; + switch(configID) { + case 0: + pixelFormat = GGL_PIXEL_FORMAT_RGB_565; + depthFormat = 0; + break; + case 1: + pixelFormat = GGL_PIXEL_FORMAT_RGB_565; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + case 2: + pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; + depthFormat = 0; + break; + case 3: + pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + case 4: + pixelFormat = GGL_PIXEL_FORMAT_A_8; + depthFormat = 0; + break; + case 5: + pixelFormat = GGL_PIXEL_FORMAT_A_8; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + default: + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + } + + // FIXME: we don't have access to the pixelFormat here just yet. + // (it's possible that the surface is not fully initialized) + // maybe this should be done after the page-flip + //if (EGLint(info.format) != pixelFormat) + // return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + + egl_surface_t* surface = + new egl_window_surface_t(dpy, config, depthFormat, + static_cast<egl_native_window_t*>(window)); + + if (!surface->isValid()) { + // there was a problem in the ctor, the error + // flag has been set. + delete surface; + surface = 0; + } + return surface; +} + +static EGLSurface createPixmapSurface(EGLDisplay dpy, EGLConfig config, + NativePixmapType pixmap, const EGLint *attrib_list) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); + if (pixmap == 0) + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + + EGLint surfaceType; + if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) + return EGL_FALSE; + + if (!(surfaceType & EGL_PIXMAP_BIT)) + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + + EGLint configID; + if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE) + return EGL_FALSE; + + int32_t depthFormat; + int32_t pixelFormat; + switch(configID) { + case 0: + pixelFormat = GGL_PIXEL_FORMAT_RGB_565; + depthFormat = 0; + break; + case 1: + pixelFormat = GGL_PIXEL_FORMAT_RGB_565; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + case 2: + pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; + depthFormat = 0; + break; + case 3: + pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + case 4: + pixelFormat = GGL_PIXEL_FORMAT_A_8; + depthFormat = 0; + break; + case 5: + pixelFormat = GGL_PIXEL_FORMAT_A_8; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + default: + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + } + + if (pixmap->format != pixelFormat) + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + + egl_surface_t* surface = + new egl_pixmap_surface_t(dpy, config, depthFormat, + static_cast<egl_native_pixmap_t*>(pixmap)); + + if (!surface->isValid()) { + // there was a problem in the ctor, the error + // flag has been set. + delete surface; + surface = 0; + } + return surface; +} + +static EGLSurface createPbufferSurface(EGLDisplay dpy, EGLConfig config, + const EGLint *attrib_list) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); + + EGLint surfaceType; + if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) + return EGL_FALSE; + + if (!(surfaceType & EGL_PBUFFER_BIT)) + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + + EGLint configID; + if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE) + return EGL_FALSE; + + int32_t depthFormat; + int32_t pixelFormat; + switch(configID) { + case 0: + pixelFormat = GGL_PIXEL_FORMAT_RGB_565; + depthFormat = 0; + break; + case 1: + pixelFormat = GGL_PIXEL_FORMAT_RGB_565; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + case 2: + pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; + depthFormat = 0; + break; + case 3: + pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + case 4: + pixelFormat = GGL_PIXEL_FORMAT_A_8; + depthFormat = 0; + break; + case 5: + pixelFormat = GGL_PIXEL_FORMAT_A_8; + depthFormat = GGL_PIXEL_FORMAT_Z_16; + break; + default: + return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); + } + + int32_t w = 0; + int32_t h = 0; + while (attrib_list[0]) { + if (attrib_list[0] == EGL_WIDTH) w = attrib_list[1]; + if (attrib_list[0] == EGL_HEIGHT) h = attrib_list[1]; + attrib_list+=2; + } + + egl_surface_t* surface = + new egl_pbuffer_surface_t(dpy, config, depthFormat, w, h, pixelFormat); + + if (!surface->isValid()) { + // there was a problem in the ctor, the error + // flag has been set. + delete surface; + surface = 0; + } + return surface; +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + +using namespace android; + +// ---------------------------------------------------------------------------- +// Initialization +// ---------------------------------------------------------------------------- + +EGLDisplay eglGetDisplay(NativeDisplayType display) +{ +#ifndef HAVE_ANDROID_OS + // this just needs to be done once + if (gGLKey == -1) { + pthread_mutex_lock(&gInitMutex); + if (gGLKey == -1) + pthread_key_create(&gGLKey, NULL); + pthread_mutex_unlock(&gInitMutex); + } +#endif + if (display == EGL_DEFAULT_DISPLAY) { + EGLDisplay dpy = (EGLDisplay)1; + egl_display_t& d = egl_display_t::get_display(dpy); + d.type = display; + return dpy; + } + return EGL_NO_DISPLAY; +} + +EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + EGLBoolean res = EGL_TRUE; + egl_display_t& d = egl_display_t::get_display(dpy); + + if (android_atomic_inc(&d.initialized) == 0) { + // initialize stuff here if needed + //pthread_mutex_lock(&gInitMutex); + //pthread_mutex_unlock(&gInitMutex); + } + + if (res == EGL_TRUE) { + if (major != NULL) *major = VERSION_MAJOR; + if (minor != NULL) *minor = VERSION_MINOR; + } + return res; +} + +EGLBoolean eglTerminate(EGLDisplay dpy) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + EGLBoolean res = EGL_TRUE; + egl_display_t& d = egl_display_t::get_display(dpy); + if (android_atomic_dec(&d.initialized) == 1) { + // TODO: destroy all resources (surfaces, contexts, etc...) + //pthread_mutex_lock(&gInitMutex); + //pthread_mutex_unlock(&gInitMutex); + } + return res; +} + +// ---------------------------------------------------------------------------- +// configuration +// ---------------------------------------------------------------------------- + +EGLBoolean eglGetConfigs( EGLDisplay dpy, + EGLConfig *configs, + EGLint config_size, EGLint *num_config) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + GLint numConfigs = NELEM(gConfigs); + if (!configs) { + *num_config = numConfigs; + return EGL_TRUE; + } + GLint i; + for (i=0 ; i<numConfigs && i<config_size ; i++) { + *configs++ = (EGLConfig)i; + } + *num_config = i; + return EGL_TRUE; +} + +EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, + EGLConfig *configs, EGLint config_size, + EGLint *num_config) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + if (ggl_unlikely(configs==0 || attrib_list==0)) { + *num_config = 0; + return EGL_TRUE; + } + + int numAttributes = 0; + int numConfigs = NELEM(gConfigs); + uint32_t possibleMatch = (1<<numConfigs)-1; + while(possibleMatch && *attrib_list != EGL_NONE) { + numAttributes++; + EGLint attr = *attrib_list++; + EGLint val = *attrib_list++; + for (int i=0 ; i<numConfigs ; i++) { + if (!(possibleMatch & (1<<i))) + continue; + if (isAttributeMatching(i, attr, val) == 0) { + possibleMatch &= ~(1<<i); + } + } + } + + // now, handle the attributes which have a useful default value + for (size_t j=0 ; j<NELEM(config_defaults) ; j++) { + // see if this attribute was specified, if not apply its + // default value + if (binarySearch<config_pair_t>( + (config_pair_t const*)attrib_list, + 0, numAttributes, + config_defaults[j].key) < 0) + { + for (int i=0 ; i<numConfigs ; i++) { + if (!(possibleMatch & (1<<i))) + continue; + if (isAttributeMatching(i, + config_defaults[j].key, + config_defaults[j].value) == 0) + { + possibleMatch &= ~(1<<i); + } + } + } + } + + // return the configurations found + int n=0; + if (possibleMatch) { + for (int i=0 ; config_size && i<numConfigs ; i++) { + if (possibleMatch & (1<<i)) { + *configs++ = (EGLConfig)i; + config_size--; + n++; + } + } + } + *num_config = n; + return EGL_TRUE; +} + +EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, + EGLint attribute, EGLint *value) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + return getConfigAttrib(dpy, config, attribute, value); +} + +// ---------------------------------------------------------------------------- +// surfaces +// ---------------------------------------------------------------------------- + +EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, + NativeWindowType window, + const EGLint *attrib_list) +{ + return createWindowSurface(dpy, config, window, attrib_list); +} + +EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config, + NativePixmapType pixmap, + const EGLint *attrib_list) +{ + return createPixmapSurface(dpy, config, pixmap, attrib_list); +} + +EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config, + const EGLint *attrib_list) +{ + return createPbufferSurface(dpy, config, attrib_list); +} + +EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface eglSurface) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + if (eglSurface != EGL_NO_SURFACE) { + egl_surface_t* surface( static_cast<egl_surface_t*>(eglSurface) ); + if (surface->magic != egl_surface_t::MAGIC) + return setError(EGL_BAD_SURFACE, EGL_FALSE); + if (surface->dpy != dpy) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + delete surface; + } + return EGL_TRUE; +} + +EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface eglSurface, + EGLint attribute, EGLint *value) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + egl_surface_t* surface = static_cast<egl_surface_t*>(eglSurface); + if (surface->dpy != dpy) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + EGLBoolean ret = EGL_TRUE; + switch (attribute) { + case EGL_CONFIG_ID: + ret = getConfigAttrib(dpy, surface->config, EGL_CONFIG_ID, value); + break; + case EGL_WIDTH: + *value = surface->getWidth(); + break; + case EGL_HEIGHT: + *value = surface->getHeight(); + break; + case EGL_LARGEST_PBUFFER: + // not modified for a window or pixmap surface + break; + case EGL_TEXTURE_FORMAT: + *value = EGL_NO_TEXTURE; + break; + case EGL_TEXTURE_TARGET: + *value = EGL_NO_TEXTURE; + break; + case EGL_MIPMAP_TEXTURE: + *value = EGL_FALSE; + break; + case EGL_MIPMAP_LEVEL: + *value = 0; + break; + case EGL_RENDER_BUFFER: + // TODO: return the real RENDER_BUFFER here + *value = EGL_BACK_BUFFER; + break; + case EGL_HORIZONTAL_RESOLUTION: + // pixel/mm * EGL_DISPLAY_SCALING + *value = surface->getHorizontalResolution(); + break; + case EGL_VERTICAL_RESOLUTION: + // pixel/mm * EGL_DISPLAY_SCALING + *value = surface->getVerticalResolution(); + break; + case EGL_PIXEL_ASPECT_RATIO: { + // w/h * EGL_DISPLAY_SCALING + int wr = surface->getHorizontalResolution(); + int hr = surface->getVerticalResolution(); + *value = (wr * EGL_DISPLAY_SCALING) / hr; + } break; + case EGL_SWAP_BEHAVIOR: + *value = surface->getSwapBehavior(); + break; + default: + ret = setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); + } + return ret; +} + +EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, + EGLContext share_list, const EGLint *attrib_list) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); + + ogles_context_t* gl = ogles_init(sizeof(egl_context_t)); + if (!gl) return setError(EGL_BAD_ALLOC, EGL_NO_CONTEXT); + + egl_context_t* c = static_cast<egl_context_t*>(gl->rasterizer.base); + c->flags = egl_context_t::NEVER_CURRENT; + c->dpy = dpy; + c->config = config; + c->read = 0; + c->draw = 0; + return (EGLContext)gl; +} + +EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + egl_context_t* c = egl_context_t::context(ctx); + if (c->flags & egl_context_t::IS_CURRENT) + setGlThreadSpecific(0); + ogles_uninit((ogles_context_t*)ctx); + return EGL_TRUE; +} + +EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, + EGLSurface read, EGLContext ctx) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + if (draw) { + egl_surface_t* s = (egl_surface_t*)draw; + if (s->dpy != dpy) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + // TODO: check that draw and read are compatible with the context + } + + EGLContext current_ctx = EGL_NO_CONTEXT; + + if ((read == EGL_NO_SURFACE && draw == EGL_NO_SURFACE) && (ctx != EGL_NO_CONTEXT)) + return setError(EGL_BAD_MATCH, EGL_FALSE); + + if ((read != EGL_NO_SURFACE || draw != EGL_NO_SURFACE) && (ctx == EGL_NO_CONTEXT)) + return setError(EGL_BAD_MATCH, EGL_FALSE); + + if (ctx == EGL_NO_CONTEXT) { + // if we're detaching, we need the current context + current_ctx = (EGLContext)getGlThreadSpecific(); + } else { + egl_context_t* c = egl_context_t::context(ctx); + egl_surface_t* d = (egl_surface_t*)draw; + egl_surface_t* r = (egl_surface_t*)read; + if ((d && d->ctx && d->ctx != ctx) || + (r && r->ctx && r->ctx != ctx)) { + // once of the surface is bound to a context in another thread + return setError(EGL_BAD_ACCESS, EGL_FALSE); + } + } + + ogles_context_t* gl = (ogles_context_t*)ctx; + if (makeCurrent(gl) == 0) { + if (ctx) { + egl_context_t* c = egl_context_t::context(ctx); + egl_surface_t* d = (egl_surface_t*)draw; + egl_surface_t* r = (egl_surface_t*)read; + c->read = read; + c->draw = draw; + if (c->flags & egl_context_t::NEVER_CURRENT) { + c->flags &= ~egl_context_t::NEVER_CURRENT; + GLint w = 0; + GLint h = 0; + if (draw) { + w = d->getWidth(); + h = d->getHeight(); + } + ogles_surfaceport(gl, 0, 0); + ogles_viewport(gl, 0, 0, w, h); + ogles_scissor(gl, 0, 0, w, h); + } + if (d) { + d->ctx = ctx; + d->bindDrawSurface(gl); + } + if (r) { + r->ctx = ctx; + r->bindReadSurface(gl); + } + } else { + // if surfaces were bound to the context bound to this thread + // mark then as unbound. + if (current_ctx) { + egl_context_t* c = egl_context_t::context(current_ctx); + egl_surface_t* d = (egl_surface_t*)c->draw; + egl_surface_t* r = (egl_surface_t*)c->read; + if (d) d->ctx = EGL_NO_CONTEXT; + if (r) r->ctx = EGL_NO_CONTEXT; + } + } + return EGL_TRUE; + } + return setError(EGL_BAD_ACCESS, EGL_FALSE); +} + +EGLContext eglGetCurrentContext(void) +{ + // eglGetCurrentContext returns the current EGL rendering context, + // as specified by eglMakeCurrent. If no context is current, + // EGL_NO_CONTEXT is returned. + return (EGLContext)getGlThreadSpecific(); +} + +EGLSurface eglGetCurrentSurface(EGLint readdraw) +{ + // eglGetCurrentSurface returns the read or draw surface attached + // to the current EGL rendering context, as specified by eglMakeCurrent. + // If no context is current, EGL_NO_SURFACE is returned. + EGLContext ctx = (EGLContext)getGlThreadSpecific(); + if (ctx == EGL_NO_CONTEXT) return EGL_NO_SURFACE; + egl_context_t* c = egl_context_t::context(ctx); + if (readdraw == EGL_READ) { + return c->read; + } else if (readdraw == EGL_DRAW) { + return c->draw; + } + return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); +} + +EGLDisplay eglGetCurrentDisplay(void) +{ + // eglGetCurrentDisplay returns the current EGL display connection + // for the current EGL rendering context, as specified by eglMakeCurrent. + // If no context is current, EGL_NO_DISPLAY is returned. + EGLContext ctx = (EGLContext)getGlThreadSpecific(); + if (ctx == EGL_NO_CONTEXT) return EGL_NO_DISPLAY; + egl_context_t* c = egl_context_t::context(ctx); + return c->dpy; +} + +EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx, + EGLint attribute, EGLint *value) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + egl_context_t* c = egl_context_t::context(ctx); + switch (attribute) { + case EGL_CONFIG_ID: + // Returns the ID of the EGL frame buffer configuration with + // respect to which the context was created + return getConfigAttrib(dpy, c->config, EGL_CONFIG_ID, value); + } + return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); +} + +EGLBoolean eglWaitGL(void) +{ + return EGL_TRUE; +} + +EGLBoolean eglWaitNative(EGLint engine) +{ + return EGL_TRUE; +} + +EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + egl_surface_t* d = static_cast<egl_surface_t*>(draw); + if (d->dpy != dpy) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + + // post the surface + d->swapBuffers(); + + // if it's bound to a context, update the buffer + if (d->ctx != EGL_NO_CONTEXT) { + d->bindDrawSurface((ogles_context_t*)d->ctx); + // if this surface is also the read surface of the context + // it is bound to, make sure to update the read buffer as well. + // The EGL spec is a little unclear about this. + egl_context_t* c = egl_context_t::context(d->ctx); + if (c->read == draw) { + d->bindReadSurface((ogles_context_t*)d->ctx); + } + } + + return EGL_TRUE; +} + +EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface, + NativePixmapType target) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + // TODO: eglCopyBuffers() + return EGL_FALSE; +} + +EGLint eglGetError(void) +{ + return getError(); +} + +const char* eglQueryString(EGLDisplay dpy, EGLint name) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, (const char*)0); + + switch (name) { + case EGL_VENDOR: + return gVendorString; + case EGL_VERSION: + return gVersionString; + case EGL_EXTENSIONS: + return gExtensionsString; + case EGL_CLIENT_APIS: + return gClientApiString; + } + return setError(EGL_BAD_PARAMETER, (const char *)0); +} + +// ---------------------------------------------------------------------------- +// EGL 1.1 +// ---------------------------------------------------------------------------- + +EGLBoolean eglSurfaceAttrib( + EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + // TODO: eglSurfaceAttrib() + return setError(EGL_BAD_PARAMETER, EGL_FALSE); +} + +EGLBoolean eglBindTexImage( + EGLDisplay dpy, EGLSurface surface, EGLint buffer) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + // TODO: eglBindTexImage() + return setError(EGL_BAD_PARAMETER, EGL_FALSE); +} + +EGLBoolean eglReleaseTexImage( + EGLDisplay dpy, EGLSurface surface, EGLint buffer) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + // TODO: eglReleaseTexImage() + return setError(EGL_BAD_PARAMETER, EGL_FALSE); +} + +EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + // TODO: eglSwapInterval() + return setError(EGL_BAD_PARAMETER, EGL_FALSE); +} + +// ---------------------------------------------------------------------------- +// EGL 1.2 +// ---------------------------------------------------------------------------- + +EGLBoolean eglBindAPI(EGLenum api) +{ + if (api != EGL_OPENGL_ES_API) + return setError(EGL_BAD_PARAMETER, EGL_FALSE); + return EGL_TRUE; +} + +EGLenum eglQueryAPI(void) +{ + return EGL_OPENGL_ES_API; +} + +EGLBoolean eglWaitClient(void) +{ + glFinish(); + return EGL_TRUE; +} + +EGLBoolean eglReleaseThread(void) +{ + // TODO: eglReleaseThread() + return EGL_TRUE; +} + +EGLSurface eglCreatePbufferFromClientBuffer( + EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, + EGLConfig config, const EGLint *attrib_list) +{ + if (egl_display_t::is_valid(dpy) == EGL_FALSE) + return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); + // TODO: eglCreatePbufferFromClientBuffer() + return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); +} + +// ---------------------------------------------------------------------------- +// Android extensions +// ---------------------------------------------------------------------------- + +void (*eglGetProcAddress (const char *procname))() +{ + extention_map_t const * const map = gExtentionMap; + for (uint32_t i=0 ; i<NELEM(gExtentionMap) ; i++) { + if (!strcmp(procname, map[i].name)) { + return map[i].address; + } + } + return NULL; +} diff --git a/opengl/libagl/fixed_asm.S b/opengl/libagl/fixed_asm.S new file mode 100644 index 0000000..6cbc56f --- /dev/null +++ b/opengl/libagl/fixed_asm.S @@ -0,0 +1,65 @@ +/* libs/opengles/fixed_asm.S +** +** 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. +*/ + + + .text + .align + + .global gglFloatToFixed + .global gglFloatToFixedFast + + +/* + * Converts a float to a s15.16 fixed-point number. + * this doesn't handle floats out of the [-32768, +32768[ range + * and doesn't performs round-to-nearest. + * however, it's very fast :-) + */ + +gglFloatToFixedFast: + movs r1, r0, lsl #1 /* remove bit sign */ + mov r2, #0x8E /* 127 + 15 */ + sub r1, r2, r1, lsr #24 /* compute shift */ + mov r2, r0, lsl #8 /* mantissa<<8 */ + orr r2, r2, #0x80000000 /* add the missing 1 */ + mov r0, r2, lsr r1 /* scale to 16.16 */ + rsbcs r0, r0, #0 /* negate if needed */ + bx lr + +/* + * this version rounds-to-nearest and saturates numbers + * outside the range (but not NaNs). + */ + +gglFloatToFixed: + mov r1, r0, lsl #1 /* remove bit sign */ + mov r2, #0x8E /* 127 + 15 */ + subs r1, r2, r1, lsr #24 /* compute shift */ + bls 0f /* too big */ + mov r2, r0, lsl #8 /* mantissa<<8 */ + orr r2, r2, #0x80000000 /* add the missing 1 */ + mov r3, r0 + movs r0, r2, lsr r1 /* scale to 16.16 */ + addcs r0, r0, #1 /* round-to-nearest */ + tst r3, #0x80000000 /* negative? */ + rsbne r0, r0, #0 /* negate if needed */ + bx lr + +0: ands r0, r0, #0x80000000 /* keep only the sign bit */ + moveq r0, #0x7fffffff /* positive, maximum value */ + bx lr + diff --git a/opengl/libagl/fp.cpp b/opengl/libagl/fp.cpp new file mode 100644 index 0000000..ae5f1fe --- /dev/null +++ b/opengl/libagl/fp.cpp @@ -0,0 +1,87 @@ +/* libs/opengles/fp.cpp +** +** 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 "fp.h" + +// ---------------------------------------------------------------------------- + +#if !defined(__arm__) +GGLfixed gglFloatToFixed(float v) { + return GGLfixed(floorf(v * 65536.0f + 0.5f)); +} +#endif + +// ---------------------------------------------------------------------------- + +namespace android { + +namespace gl { + +GLfloat fixedToFloat(GLfixed x) +{ +#if DEBUG_USE_FLOATS + return x / 65536.0f; +#else + if (!x) return 0; + const uint32_t s = x & 0x80000000; + union { + uint32_t i; + float f; + }; + i = s ? -x : x; + const int c = gglClz(i) - 8; + i = (c>=0) ? (i<<c) : (i>>-c); + const uint32_t e = 134 - c; + i &= ~0x800000; + i |= e<<23; + i |= s; + return f; +#endif +} + +float sinef(float x) +{ + const float A = 1.0f / (2.0f*M_PI); + const float B = -16.0f; + const float C = 8.0f; + + // scale angle for easy argument reduction + x *= A; + + if (fabsf(x) >= 0.5f) { + // Argument reduction + x = x - ceilf(x + 0.5f) + 1.0f; + } + + const float y = B*x*fabsf(x) + C*x; + return 0.2215f * (y*fabsf(y) - y) + y; +} + +float cosinef(float x) +{ + return sinef(x + float(M_PI/2)); +} + +void sincosf(GLfloat angle, GLfloat* s, GLfloat* c) { + *s = sinef(angle); + *c = cosinef(angle); +} + +}; // namespace fp_utils + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/opengl/libagl/fp.h b/opengl/libagl/fp.h new file mode 100644 index 0000000..6d0c183 --- /dev/null +++ b/opengl/libagl/fp.h @@ -0,0 +1,243 @@ +/* libs/opengles/fp.h +** +** 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. +*/ + +#ifndef ANDROID_OPENGLES_FP_H +#define ANDROID_OPENGLES_FP_H + +#include <stdint.h> +#include <stddef.h> +#include <sys/types.h> +#include <math.h> + +#include <private/pixelflinger/ggl_context.h> + +#include <GLES/gl.h> + +#define DEBUG_USE_FLOATS 0 + +// ---------------------------------------------------------------------------- + +extern "C" GLfixed gglFloatToFixed(float f) __attribute__((const)); + +// ---------------------------------------------------------------------------- +namespace android { + +namespace gl { + + GLfloat fixedToFloat(GLfixed) CONST; + + void sincosf(GLfloat angle, GLfloat* s, GLfloat* c); + float sinef(GLfloat x) CONST; + float cosinef(GLfloat x) CONST; + +inline bool cmpf(GLfloat a, GLfloat b) CONST; +inline bool isZerof(GLfloat) CONST; +inline bool isOnef(GLfloat) CONST; + +inline int isZeroOrNegativef(GLfloat) CONST; + +inline int exponent(GLfloat) CONST; +inline int32_t mantissa(GLfloat) CONST; +inline GLfloat clampToZerof(GLfloat) CONST; +inline GLfloat reciprocalf(GLfloat) CONST; +inline GLfloat rsqrtf(GLfloat) CONST; +inline GLfloat sqrf(GLfloat) CONST; +inline GLfloat addExpf(GLfloat v, int e) CONST; +inline GLfloat mul2f(GLfloat v) CONST; +inline GLfloat div2f(GLfloat v) CONST; +inline GLfloat absf(GLfloat v) CONST; + + +/* + * float fastexpf(float) : a fast approximation of expf(x) + * give somewhat accurate results for -88 <= x <= 88 + * + * exp(x) = 2^(x/ln(2)) + * we use the properties of float encoding + * to get a fast 2^ and linear interpolation + * + */ + +inline float fastexpf(float y) __attribute__((const)); + +inline float fastexpf(float y) +{ + union { + float r; + int32_t i; + } u; + + // 127*ln(2) = 88 + if (y < -88.0f) { + u.r = 0.0f; + } else if (y > 88.0f) { + u.r = INFINITY; + } else { + const float kOneOverLogTwo = (1L<<23) / M_LN2; + const int32_t kExponentBias = 127L<<23; + const int32_t e = int32_t(y*kOneOverLogTwo); + u.i = e + kExponentBias; + } + + return u.r; +} + + +bool cmpf(GLfloat a, GLfloat b) { +#if DEBUG_USE_FLOATS + return a == b; +#else + union { + float f; + uint32_t i; + } ua, ub; + ua.f = a; + ub.f = b; + return ua.i == ub.i; +#endif +} + +bool isZerof(GLfloat v) { +#if DEBUG_USE_FLOATS + return v == 0; +#else + union { + float f; + int32_t i; + }; + f = v; + return (i<<1) == 0; +#endif +} + +bool isOnef(GLfloat v) { + return cmpf(v, 1.0f); +} + +int isZeroOrNegativef(GLfloat v) { +#if DEBUG_USE_FLOATS + return v <= 0; +#else + union { + float f; + int32_t i; + }; + f = v; + return isZerof(v) | (i>>31); +#endif +} + +int exponent(GLfloat v) { + union { + float f; + uint32_t i; + }; + f = v; + return ((i << 1) >> 24) - 127; +} + +int32_t mantissa(GLfloat v) { + union { + float f; + uint32_t i; + }; + f = v; + if (!(i&0x7F800000)) return 0; + const int s = i >> 31; + i |= (1L<<23); + i &= ~0xFF000000; + return s ? -i : i; +} + +GLfloat clampToZerof(GLfloat v) { +#if DEBUG_USE_FLOATS + return v<0 ? 0 : (v>1 ? 1 : v); +#else + union { + float f; + int32_t i; + }; + f = v; + i &= ~(i>>31); + return f; +#endif +} + +GLfloat reciprocalf(GLfloat v) { + // XXX: do better + return 1.0f / v; +} + +GLfloat rsqrtf(GLfloat v) { + // XXX: do better + return 1.0f / sqrtf(v); +} + +GLfloat sqrf(GLfloat v) { + // XXX: do better + return v*v; +} + +GLfloat addExpf(GLfloat v, int e) { + union { + float f; + int32_t i; + }; + f = v; + if (i<<1) { // XXX: deal with over/underflow + i += int32_t(e)<<23; + } + return f; +} + +GLfloat mul2f(GLfloat v) { +#if DEBUG_USE_FLOATS + return v*2; +#else + return addExpf(v, 1); +#endif +} + +GLfloat div2f(GLfloat v) { +#if DEBUG_USE_FLOATS + return v*0.5f; +#else + return addExpf(v, -1); +#endif +} + +GLfloat absf(GLfloat v) { +#if DEBUG_USE_FLOATS + return v<0 ? -v : v; +#else + union { + float f; + int32_t i; + }; + f = v; + i &= ~0x80000000; + return f; +#endif +} + +}; // namespace gl + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_OPENGLES_FP_H + diff --git a/opengl/libagl/iterators.S b/opengl/libagl/iterators.S new file mode 100644 index 0000000..daf2937 --- /dev/null +++ b/opengl/libagl/iterators.S @@ -0,0 +1,88 @@ +/* libs/opengles/iterators.S +** +** 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. +*/ + + + .text + .align + .arm + + .global iterators0032 + +/* + * iterators0032 + * + * MUST BE CALLED FROM ARM CODE + * + * r0: const compute_iterators_t* (this) + * r0 + 0: m_dx01 + * r0 + 4: m_dy10 + * r0 + 8: m_dx20 + * r0 +12: m_dy02 + * r0 +16: m_x0 + * r0 +20: m_y0 + * r0 +24: m_area + * r0 +28: m_scale + * r0 +29: m_area_scale; + * r1: int32_t* (out) + * r1 + 0: c + * r1 + 4: dcdx + * r1 + 8: dcdy + * r2: c0 + * r3: c1 + * [sp]: c2 + */ + +iterators0032: + stmfd sp!, {r4, r5, r6, r7, r8, lr} + ldr r4, [sp, #4*6] + + ldrb r12, [r0, #29] + sub r3, r3, r2 + sub r4, r4, r2 + sub r12, r12, #16 + mov r3, r3, asr r12 + mov r4, r4, asr r12 + + ldr r5, [r0, #0] + ldr r12, [r0, #4] + smull r8, lr, r4, r5 + ldr r5, [r0, #8] + smull r6, r7, r4, r12 + ldr r12, [r0, #12] + smlal r8, lr, r3, r5 + smlal r6, r7, r3, r12 + + ldr r3, [r0, #16] // m_x0 + ldr r4, [r0, #20] // m_x1 + + str r6, [r1, #4] + str r8, [r1, #8] + + umull r6, r5, r3, r6 + umull r8, r0, r4, r8 + mla r7, r3, r7, r5 + mla lr, r4, lr, r0 + adds r6, r6, r8 + adc r7, r7, lr + + movs r6, r6, lsr #4 + adc r6, r6, r7, lsl #28 + rsb r6, r6, r2, lsl #16 + str r6, [r1, #0] + + ldmfd sp!, {r4, r5, r6, r7, r8, pc} + diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp new file mode 100644 index 0000000..25c41d0 --- /dev/null +++ b/opengl/libagl/light.cpp @@ -0,0 +1,852 @@ +/* libs/opengles/light.cpp +** +** 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 "context.h" +#include "fp.h" +#include "light.h" +#include "state.h" +#include "matrix.h" + + +#if defined(__arm__) && defined(__thumb__) +#warning "light.cpp should not be compiled in thumb on ARM." +#endif + +namespace android { + +// ---------------------------------------------------------------------------- + +static void invalidate_lighting(ogles_context_t* c); +static void lightVertexValidate(ogles_context_t* c, vertex_t* v); +static void lightVertexNop(ogles_context_t* c, vertex_t* v); +static void lightVertex(ogles_context_t* c, vertex_t* v); +static void lightVertexMaterial(ogles_context_t* c, vertex_t* v); + +static inline void vscale3(GLfixed* d, const GLfixed* m, GLfixed s); +static inline void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b); + +static __attribute__((noinline)) +void vnorm3(GLfixed* d, const GLfixed* a); + +static inline void vsa3(GLfixed* d, + const GLfixed* m, GLfixed s, const GLfixed* a); +static inline void vmla3(GLfixed* d, + const GLfixed* m0, const GLfixed* m1, const GLfixed* a); +static inline void vmul3(GLfixed* d, + const GLfixed* m0, const GLfixed* m1); + +static GLfixed fog_linear(ogles_context_t* c, GLfixed z); +static GLfixed fog_exp(ogles_context_t* c, GLfixed z); +static GLfixed fog_exp2(ogles_context_t* c, GLfixed z); + + +// ---------------------------------------------------------------------------- + +static void init_white(vec4_t& c) { + c.r = c.g = c.b = c.a = 0x10000; +} + +void ogles_init_light(ogles_context_t* c) +{ + for (unsigned int i=0 ; i<OGLES_MAX_LIGHTS ; i++) { + c->lighting.lights[i].ambient.a = 0x10000; + c->lighting.lights[i].position.z = 0x10000; + c->lighting.lights[i].spotDir.z = -0x10000; + c->lighting.lights[i].spotCutoff = gglIntToFixed(180); + c->lighting.lights[i].attenuation[0] = 0x10000; + } + init_white(c->lighting.lights[0].diffuse); + init_white(c->lighting.lights[0].specular); + + c->lighting.front.ambient.r = + c->lighting.front.ambient.g = + c->lighting.front.ambient.b = gglFloatToFixed(0.2f); + c->lighting.front.ambient.a = 0x10000; + c->lighting.front.diffuse.r = + c->lighting.front.diffuse.g = + c->lighting.front.diffuse.b = gglFloatToFixed(0.8f); + c->lighting.front.diffuse.a = 0x10000; + c->lighting.front.specular.a = 0x10000; + c->lighting.front.emission.a = 0x10000; + + c->lighting.lightModel.ambient.r = + c->lighting.lightModel.ambient.g = + c->lighting.lightModel.ambient.b = gglFloatToFixed(0.2f); + c->lighting.lightModel.ambient.a = 0x10000; + + c->lighting.colorMaterial.face = GL_FRONT_AND_BACK; + c->lighting.colorMaterial.mode = GL_AMBIENT_AND_DIFFUSE; + + c->fog.mode = GL_EXP; + c->fog.fog = fog_exp; + c->fog.density = 0x10000; + c->fog.end = 0x10000; + c->fog.invEndMinusStart = 0x10000; + + invalidate_lighting(c); + + c->rasterizer.procs.shadeModel(c, GL_SMOOTH); + c->lighting.shadeModel = GL_SMOOTH; +} + +void ogles_uninit_light(ogles_context_t* c) +{ +} + +static inline int32_t clampF(GLfixed f) CONST; +int32_t clampF(GLfixed f) { + f = (f & ~(f>>31)); + if (f >= 0x10000) + f = 0x10000; + return f; +} + +static GLfixed fog_linear(ogles_context_t* c, GLfixed z) { + return clampF(gglMulx((c->fog.end - ((z<0)?-z:z)), c->fog.invEndMinusStart)); +} + +static GLfixed fog_exp(ogles_context_t* c, GLfixed z) { + const float e = fixedToFloat(gglMulx(c->fog.density, ((z<0)?-z:z))); + return clampF(gglFloatToFixed(fastexpf(-e))); +} + +static GLfixed fog_exp2(ogles_context_t* c, GLfixed z) { + const float e = fixedToFloat(gglMulx(c->fog.density, z)); + return clampF(gglFloatToFixed(fastexpf(-e*e))); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark math helpers +#endif + +static inline +void vscale3(GLfixed* d, const GLfixed* m, GLfixed s) { + d[0] = gglMulx(m[0], s); + d[1] = gglMulx(m[1], s); + d[2] = gglMulx(m[2], s); +} + +static inline +void vsa3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) { + d[0] = gglMulAddx(m[0], s, a[0]); + d[1] = gglMulAddx(m[1], s, a[1]); + d[2] = gglMulAddx(m[2], s, a[2]); +} + +static inline +void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b) { + const GLfixed wa = a[3]; + const GLfixed wb = b[3]; + if (ggl_likely(wa == wb)) { + d[0] = a[0] - b[0]; + d[1] = a[1] - b[1]; + d[2] = a[2] - b[2]; + } else { + d[0] = gglMulSubx(a[0], wb, gglMulx(b[0], wa)); + d[1] = gglMulSubx(a[1], wb, gglMulx(b[1], wa)); + d[2] = gglMulSubx(a[2], wb, gglMulx(b[2], wa)); + } +} + +static inline +void vmla3(GLfixed* d, + const GLfixed* m0, const GLfixed* m1, const GLfixed* a) +{ + d[0] = gglMulAddx(m0[0], m1[0], a[0]); + d[1] = gglMulAddx(m0[1], m1[1], a[1]); + d[2] = gglMulAddx(m0[2], m1[2], a[2]); +} + +static inline +void vmul3(GLfixed* d, const GLfixed* m0, const GLfixed* m1) { + d[0] = gglMulx(m0[0], m1[0]); + d[1] = gglMulx(m0[1], m1[1]); + d[2] = gglMulx(m0[2], m1[2]); +} + +void vnorm3(GLfixed* d, const GLfixed* a) +{ + // we must take care of overflows when normalizing a vector + GLfixed n; + int32_t x = a[0]; x = x>=0 ? x : -x; + int32_t y = a[1]; y = y>=0 ? y : -y; + int32_t z = a[2]; z = z>=0 ? z : -z; + if (ggl_likely(x<=0x6800 && y<=0x6800 && z<= 0x6800)) { + // in this case this will all fit on 32 bits + n = x*x + y*y + z*z; + n = gglSqrtRecipx(n); + n <<= 8; + } else { + // here norm^2 is at least 0x7EC00000 (>>32 == 0.495117) + n = vsquare3(x, y, z); + n = gglSqrtRecipx(n); + } + vscale3(d, a, n); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark lighting equations +#endif + +static inline void light_picker(ogles_context_t* c) +{ + if (ggl_likely(!c->lighting.enable)) { + c->lighting.lightVertex = lightVertexNop; + return; + } + if (c->lighting.colorMaterial.enable) { + c->lighting.lightVertex = lightVertexMaterial; + } else { + c->lighting.lightVertex = lightVertex; + } +} + +static inline void validate_light_mvi(ogles_context_t* c) +{ + uint32_t en = c->lighting.enabledLights; + while (en) { + const int i = 31 - gglClz(en); + en &= ~(1<<i); + light_t& l = c->lighting.lights[i]; + c->transforms.mvui.point3(&c->transforms.mvui, + &l.objPosition, &l.position); + vnorm3(l.normalizedObjPosition.v, l.objPosition.v); + } +} + +static inline void validate_light(ogles_context_t* c) +{ + // if colorMaterial is enabled, we get the color from the vertex + if (!c->lighting.colorMaterial.enable) { + material_t& material = c->lighting.front; + uint32_t en = c->lighting.enabledLights; + while (en) { + const int i = 31 - gglClz(en); + en &= ~(1<<i); + light_t& l = c->lighting.lights[i]; + vmul3(l.implicitAmbient.v, material.ambient.v, l.ambient.v); + vmul3(l.implicitDiffuse.v, material.diffuse.v, l.diffuse.v); + vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v); + + // this is just a flag to tell if we have a specular component + l.implicitSpecular.v[3] = + l.implicitSpecular.r | + l.implicitSpecular.g | + l.implicitSpecular.b; + + l.rConstAttenuation = (l.attenuation[1] | l.attenuation[2])==0; + if (l.rConstAttenuation) + l.rConstAttenuation = gglRecipFast(l.attenuation[0]); + } + // emission and ambient for the whole scene + vmla3( c->lighting.implicitSceneEmissionAndAmbient.v, + c->lighting.lightModel.ambient.v, + material.ambient.v, + material.emission.v); + c->lighting.implicitSceneEmissionAndAmbient.a = material.diffuse.a; + } + validate_light_mvi(c); +} + +void invalidate_lighting(ogles_context_t* c) +{ + // TODO: pick lightVertexValidate or lightVertexValidateMVI + // instead of systematically the heavier lightVertexValidate() + c->lighting.lightVertex = lightVertexValidate; +} + +void ogles_invalidate_lighting_mvui(ogles_context_t* c) +{ + invalidate_lighting(c); +} + +void lightVertexNop(ogles_context_t*, vertex_t* v) +{ + // we should never end-up here +} + +void lightVertexValidateMVI(ogles_context_t* c, vertex_t* v) +{ + validate_light_mvi(c); + light_picker(c); + c->lighting.lightVertex(c, v); +} + +void lightVertexValidate(ogles_context_t* c, vertex_t* v) +{ + validate_light(c); + light_picker(c); + c->lighting.lightVertex(c, v); +} + +void lightVertexMaterial(ogles_context_t* c, vertex_t* v) +{ + // fetch the material color + const GLvoid* cp = c->arrays.color.element( + v->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v->color.v, cp); + + // acquire the color-material from the vertex + material_t& material = c->lighting.front; + material.ambient = + material.diffuse = v->color; + // implicit arguments need to be computed per/vertex + uint32_t en = c->lighting.enabledLights; + while (en) { + const int i = 31 - gglClz(en); + en &= ~(1<<i); + light_t& l = c->lighting.lights[i]; + vmul3(l.implicitAmbient.v, material.ambient.v, l.ambient.v); + vmul3(l.implicitDiffuse.v, material.diffuse.v, l.diffuse.v); + vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v); + } + // emission and ambient for the whole scene + vmla3( c->lighting.implicitSceneEmissionAndAmbient.v, + c->lighting.lightModel.ambient.v, + material.ambient.v, + material.emission.v); + c->lighting.implicitSceneEmissionAndAmbient.a = material.diffuse.a; + + // now we can light our vertex as usual + lightVertex(c, v); +} + +void lightVertex(ogles_context_t* c, vertex_t* v) +{ + // emission and ambient for the whole scene + vec4_t r = c->lighting.implicitSceneEmissionAndAmbient; + + uint32_t en = c->lighting.enabledLights; + if (ggl_likely(en)) { + // since we do the lighting in object-space, we don't need to + // transform each normal. However, we might still have to normalize + // it if GL_NORMALIZE is enabled. + vec4_t n; + c->arrays.normal.fetch(c, n.v, + c->arrays.normal.element(v->index & vertex_cache_t::INDEX_MASK)); + if (c->transforms.rescaleNormals == GL_NORMALIZE) + vnorm3(n.v, n.v); + + const material_t& material = c->lighting.front; + const int twoSide = c->lighting.lightModel.twoSide; + + while (en) { + const int i = 31 - gglClz(en); + en &= ~(1<<i); + const light_t& l = c->lighting.lights[i]; + + vec4_t d, t; + GLfixed s; + GLfixed sqDist = 0x10000; + + // compute vertex-to-light vector + if (ggl_unlikely(l.position.w)) { + vsub3w(d.v, l.objPosition.v, v->obj.v); + sqDist = dot3(d.v, d.v); + vscale3(d.v, d.v, gglSqrtRecipx(sqDist)); + } else { + // TODO: avoid copy here + d = l.normalizedObjPosition; + } + + // ambient & diffuse + s = dot3(n.v, d.v); + s = (s<0) ? (twoSide?(-s):0) : s; + vsa3(t.v, l.implicitDiffuse.v, s, l.implicitAmbient.v); + + // specular + if (ggl_unlikely(s && l.implicitSpecular.v[3])) { + vec4_t h; + h.x = d.x; + h.y = d.y; + h.z = d.z + 0x10000; + vnorm3(h.v, h.v); + s = dot3(n.v, h.v); + s = (s<0) ? (twoSide?(-s):0) : s; + if (s > 0) { + s = gglPowx(s, material.shininess); + vsa3(t.v, l.implicitSpecular.v, s, t.v); + } + } + + // spot + if (ggl_unlikely(l.spotCutoff != gglIntToFixed(180))) { + GLfixed spotAtt = -dot3(l.normalizedSpotDir.v, d.v); + if (spotAtt >= l.spotCutoffCosine) { + vscale3(t.v, t.v, gglPowx(spotAtt, l.spotExp)); + } + } + + // attenuation + if (ggl_unlikely(l.position.w)) { + if (l.rConstAttenuation) { + s = l.rConstAttenuation; + } else { + s = gglMulAddx(sqDist, l.attenuation[2], l.attenuation[0]); + if (l.attenuation[1]) + s = gglMulAddx(gglSqrtx(sqDist), l.attenuation[1], s); + s = gglRecipFast(s); + } + vscale3(t.v, t.v, s); + } + + r.r += t.r; + r.g += t.g; + r.b += t.b; + } + } + v->color.r = gglClampx(r.r); + v->color.g = gglClampx(r.g); + v->color.b = gglClampx(r.b); + v->color.a = gglClampx(r.a); + v->flags |= vertex_t::LIT; +} + +static void lightModelx(GLenum pname, GLfixed param, ogles_context_t* c) +{ + if (ggl_unlikely(pname != GL_LIGHT_MODEL_TWO_SIDE)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->lighting.lightModel.twoSide = param ? GL_TRUE : GL_FALSE; + invalidate_lighting(c); +} + +static void lightx(GLenum i, GLenum pname, GLfixed param, ogles_context_t* c) +{ + if (ggl_unlikely(uint32_t(i-GL_LIGHT0) >= OGLES_MAX_LIGHTS)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + light_t& light = c->lighting.lights[i-GL_LIGHT0]; + const GLfixed kDegToRad = GLfixed((M_PI * gglIntToFixed(1)) / 180.0f); + switch (pname) { + case GL_SPOT_EXPONENT: + if (GGLfixed(param) >= gglIntToFixed(128)) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + light.spotExp = param; + break; + case GL_SPOT_CUTOFF: + if (param!=gglIntToFixed(180) && GGLfixed(param)>=gglIntToFixed(90)) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + light.spotCutoff = param; + light.spotCutoffCosine = + gglFloatToFixed(cosinef((M_PI/(180.0f*65536.0f))*param)); + break; + case GL_CONSTANT_ATTENUATION: + if (param < 0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + light.attenuation[0] = param; + break; + case GL_LINEAR_ATTENUATION: + if (param < 0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + light.attenuation[1] = param; + break; + case GL_QUADRATIC_ATTENUATION: + if (param < 0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + light.attenuation[2] = param; + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + invalidate_lighting(c); +} + +static void lightxv(GLenum i, GLenum pname, const GLfixed *params, ogles_context_t* c) +{ + if (ggl_unlikely(uint32_t(i-GL_LIGHT0) >= OGLES_MAX_LIGHTS)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + GLfixed* what; + light_t& light = c->lighting.lights[i-GL_LIGHT0]; + switch (pname) { + case GL_AMBIENT: + what = light.ambient.v; + break; + case GL_DIFFUSE: + what = light.diffuse.v; + break; + case GL_SPECULAR: + what = light.specular.v; + break; + case GL_POSITION: { + ogles_validate_transform(c, transform_state_t::MODELVIEW); + transform_t& mv = c->transforms.modelview.transform; + memcpy(light.position.v, params, sizeof(light.position.v)); + mv.point4(&mv, &light.position, &light.position); + invalidate_lighting(c); + return; + } + case GL_SPOT_DIRECTION: { + ogles_validate_transform(c, transform_state_t::MVUI); + transform_t& mvui = c->transforms.mvui; + mvui.point3(&mvui, &light.spotDir, (vec4_t*)params); + vnorm3(light.normalizedSpotDir.v, light.spotDir.v); + invalidate_lighting(c); + return; + } + default: + lightx(i, pname, params[0], c); + return; + } + what[0] = params[0]; + what[1] = params[1]; + what[2] = params[2]; + what[3] = params[3]; + invalidate_lighting(c); +} + +static void materialx(GLenum face, GLenum pname, GLfixed param, ogles_context_t* c) +{ + if (ggl_unlikely(face != GL_FRONT_AND_BACK)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (ggl_unlikely(pname != GL_SHININESS)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->lighting.front.shininess = param; + invalidate_lighting(c); +} + +static void fogx(GLenum pname, GLfixed param, ogles_context_t* c) +{ + switch (pname) { + case GL_FOG_DENSITY: + if (param >= 0) { + c->fog.density = param; + break; + } + ogles_error(c, GL_INVALID_VALUE); + break; + case GL_FOG_START: + c->fog.start = param; + c->fog.invEndMinusStart = gglRecip(c->fog.end - c->fog.start); + break; + case GL_FOG_END: + c->fog.end = param; + c->fog.invEndMinusStart = gglRecip(c->fog.end - c->fog.start); + break; + case GL_FOG_MODE: + switch (param) { + case GL_LINEAR: + c->fog.mode = param; + c->fog.fog = fog_linear; + break; + case GL_EXP: + c->fog.mode = param; + c->fog.fog = fog_exp; + break; + case GL_EXP2: + c->fog.mode = param; + c->fog.fog = fog_exp2; + break; + default: + ogles_error(c, GL_INVALID_ENUM); + break; + } + break; + default: + ogles_error(c, GL_INVALID_ENUM); + break; + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + +using namespace android; + +#if 0 +#pragma mark - +#pragma mark lighting APIs +#endif + +void glShadeModel(GLenum mode) +{ + ogles_context_t* c = ogles_context_t::get(); + if (ggl_unlikely(mode != GL_SMOOTH && mode != GL_FLAT)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->lighting.shadeModel = mode; +} + +void glLightModelf(GLenum pname, GLfloat param) +{ + ogles_context_t* c = ogles_context_t::get(); + lightModelx(pname, gglFloatToFixed(param), c); +} + +void glLightModelx(GLenum pname, GLfixed param) +{ + ogles_context_t* c = ogles_context_t::get(); + lightModelx(pname, param, c); +} + +void glLightModelfv(GLenum pname, const GLfloat *params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (pname == GL_LIGHT_MODEL_TWO_SIDE) { + lightModelx(pname, gglFloatToFixed(params[0]), c); + return; + } + + if (ggl_unlikely(pname != GL_LIGHT_MODEL_AMBIENT)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + c->lighting.lightModel.ambient.r = gglFloatToFixed(params[0]); + c->lighting.lightModel.ambient.g = gglFloatToFixed(params[1]); + c->lighting.lightModel.ambient.b = gglFloatToFixed(params[2]); + c->lighting.lightModel.ambient.a = gglFloatToFixed(params[3]); + invalidate_lighting(c); +} + +void glLightModelxv(GLenum pname, const GLfixed *params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (pname == GL_LIGHT_MODEL_TWO_SIDE) { + lightModelx(pname, params[0], c); + return; + } + + if (ggl_unlikely(pname != GL_LIGHT_MODEL_AMBIENT)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + c->lighting.lightModel.ambient.r = params[0]; + c->lighting.lightModel.ambient.g = params[1]; + c->lighting.lightModel.ambient.b = params[2]; + c->lighting.lightModel.ambient.a = params[3]; + invalidate_lighting(c); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +void glLightf(GLenum i, GLenum pname, GLfloat param) +{ + ogles_context_t* c = ogles_context_t::get(); + lightx(i, pname, gglFloatToFixed(param), c); +} + +void glLightx(GLenum i, GLenum pname, GLfixed param) +{ + ogles_context_t* c = ogles_context_t::get(); + lightx(i, pname, param, c); +} + +void glLightfv(GLenum i, GLenum pname, const GLfloat *params) +{ + ogles_context_t* c = ogles_context_t::get(); + switch (pname) { + case GL_SPOT_EXPONENT: + case GL_SPOT_CUTOFF: + case GL_CONSTANT_ATTENUATION: + case GL_LINEAR_ATTENUATION: + case GL_QUADRATIC_ATTENUATION: + lightx(i, pname, gglFloatToFixed(params[0]), c); + return; + } + + GLfixed paramsx[4]; + paramsx[0] = gglFloatToFixed(params[0]); + paramsx[1] = gglFloatToFixed(params[1]); + paramsx[2] = gglFloatToFixed(params[2]); + if (pname != GL_SPOT_DIRECTION) + paramsx[3] = gglFloatToFixed(params[3]); + + lightxv(i, pname, paramsx, c); +} + +void glLightxv(GLenum i, GLenum pname, const GLfixed *params) +{ + ogles_context_t* c = ogles_context_t::get(); + lightxv(i, pname, params, c); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +void glMaterialf(GLenum face, GLenum pname, GLfloat param) +{ + ogles_context_t* c = ogles_context_t::get(); + materialx(face, pname, gglFloatToFixed(param), c); +} + +void glMaterialx(GLenum face, GLenum pname, GLfixed param) +{ + ogles_context_t* c = ogles_context_t::get(); + materialx(face, pname, param, c); +} + +void glMaterialfv( + GLenum face, GLenum pname, const GLfloat *params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (ggl_unlikely(face != GL_FRONT_AND_BACK)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + GLfixed* what=0; + GLfixed* other=0; + switch (pname) { + case GL_AMBIENT: what = c->lighting.front.ambient.v; break; + case GL_DIFFUSE: what = c->lighting.front.diffuse.v; break; + case GL_SPECULAR: what = c->lighting.front.specular.v; break; + case GL_EMISSION: what = c->lighting.front.emission.v; break; + case GL_AMBIENT_AND_DIFFUSE: + what = c->lighting.front.ambient.v; break; + other = c->lighting.front.diffuse.v; break; + break; + case GL_SHININESS: + c->lighting.front.shininess = gglFloatToFixed(params[0]); + invalidate_lighting(c); + return; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + what[0] = gglFloatToFixed(params[0]); + what[1] = gglFloatToFixed(params[1]); + what[2] = gglFloatToFixed(params[2]); + what[3] = gglFloatToFixed(params[3]); + if (other) { + other[0] = what[0]; + other[1] = what[1]; + other[2] = what[2]; + other[3] = what[3]; + } + invalidate_lighting(c); +} + +void glMaterialxv( + GLenum face, GLenum pname, const GLfixed *params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (ggl_unlikely(face != GL_FRONT_AND_BACK)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + GLfixed* what=0; + GLfixed* other=0; + switch (pname) { + case GL_AMBIENT: what = c->lighting.front.ambient.v; break; + case GL_DIFFUSE: what = c->lighting.front.diffuse.v; break; + case GL_SPECULAR: what = c->lighting.front.specular.v; break; + case GL_EMISSION: what = c->lighting.front.emission.v; break; + case GL_AMBIENT_AND_DIFFUSE: + what = c->lighting.front.ambient.v; break; + other= c->lighting.front.diffuse.v; break; + break; + case GL_SHININESS: + c->lighting.front.shininess = gglFloatToFixed(params[0]); + invalidate_lighting(c); + return; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + what[0] = params[0]; + what[1] = params[1]; + what[2] = params[2]; + what[3] = params[3]; + if (other) { + other[0] = what[0]; + other[1] = what[1]; + other[2] = what[2]; + other[3] = what[3]; + } + invalidate_lighting(c); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark fog +#endif + +void glFogf(GLenum pname, GLfloat param) { + ogles_context_t* c = ogles_context_t::get(); + GLfixed paramx = (GLfixed)param; + if (pname != GL_FOG_MODE) + paramx = gglFloatToFixed(param); + fogx(pname, paramx, c); +} + +void glFogx(GLenum pname, GLfixed param) { + ogles_context_t* c = ogles_context_t::get(); + fogx(pname, param, c); +} + +void glFogfv(GLenum pname, const GLfloat *params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (pname != GL_FOG_COLOR) { + GLfixed paramx = (GLfixed)params[0]; + if (pname != GL_FOG_MODE) + paramx = gglFloatToFixed(params[0]); + fogx(pname, paramx, c); + return; + } + GLfixed paramsx[4]; + paramsx[0] = gglFloatToFixed(params[0]); + paramsx[1] = gglFloatToFixed(params[1]); + paramsx[2] = gglFloatToFixed(params[2]); + paramsx[3] = gglFloatToFixed(params[3]); + c->rasterizer.procs.fogColor3xv(c, paramsx); +} + +void glFogxv(GLenum pname, const GLfixed *params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (pname != GL_FOG_COLOR) { + fogx(pname, params[0], c); + return; + } + c->rasterizer.procs.fogColor3xv(c, params); +} diff --git a/opengl/libagl/light.h b/opengl/libagl/light.h new file mode 100644 index 0000000..6dae25f --- /dev/null +++ b/opengl/libagl/light.h @@ -0,0 +1,38 @@ +/* libs/opengles/light.h +** +** 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. +*/ + +#ifndef ANDROID_OPENGLES_LIGHT_H +#define ANDROID_OPENGLES_LIGHT_H + +#include <stdint.h> +#include <stddef.h> +#include <sys/types.h> + +namespace android { + +namespace gl { +struct ogles_context_t; +}; + +void ogles_init_light(ogles_context_t* c); +void ogles_uninit_light(ogles_context_t* c); +void ogles_invalidate_lighting_mvui(ogles_context_t* c); + +}; // namespace android + +#endif // ANDROID_OPENGLES_LIGHT_H + diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp new file mode 100644 index 0000000..f175cda --- /dev/null +++ b/opengl/libagl/matrix.cpp @@ -0,0 +1,1145 @@ +/* libs/opengles/matrix.cpp +** +** 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 <stdlib.h> +#include <stdio.h> + +#include "context.h" +#include "fp.h" +#include "state.h" +#include "matrix.h" +#include "vertex.h" +#include "light.h" + +#if defined(__arm__) && defined(__thumb__) +#warning "matrix.cpp should not be compiled in thumb on ARM." +#endif + +#define I(_i, _j) ((_j)+ 4*(_i)) + +namespace android { + +// ---------------------------------------------------------------------------- + +static const GLfloat gIdentityf[16] = { 1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1 }; + +static const matrixx_t gIdentityx = { + { 0x10000,0,0,0, + 0,0x10000,0,0, + 0,0,0x10000,0, + 0,0,0,0x10000 + } + }; + +static void point2__nop(transform_t const*, vec4_t* c, vec4_t const* o); +static void point3__nop(transform_t const*, vec4_t* c, vec4_t const* o); +static void point4__nop(transform_t const*, vec4_t* c, vec4_t const* o); +static void normal__nop(transform_t const*, vec4_t* c, vec4_t const* o); +static void point2__generic(transform_t const*, vec4_t* c, vec4_t const* o); +static void point3__generic(transform_t const*, vec4_t* c, vec4_t const* o); +static void point4__generic(transform_t const*, vec4_t* c, vec4_t const* o); +static void normal__generic(transform_t const*, vec4_t* c, vec4_t const* o); + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +void ogles_init_matrix(ogles_context_t* c) +{ + c->transforms.modelview.init(OGLES_MODELVIEW_STACK_DEPTH); + c->transforms.projection.init(OGLES_PROJECTION_STACK_DEPTH); + for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) + c->transforms.texture[i].init(OGLES_TEXTURE_STACK_DEPTH); + + c->transforms.current = &c->transforms.modelview; + c->transforms.matrixMode = GL_MODELVIEW; + c->transforms.dirty = transform_state_t::VIEWPORT | + transform_state_t::MVUI | + transform_state_t::MVIT | + transform_state_t::MVP; + c->transforms.mvp.loadIdentity(); + c->transforms.mvp4.loadIdentity(); + c->transforms.mvit4.loadIdentity(); + c->transforms.mvui.loadIdentity(); + c->transforms.vpt.loadIdentity(); + c->transforms.vpt.zNear = 0.0f; + c->transforms.vpt.zFar = 1.0f; +} + +void ogles_uninit_matrix(ogles_context_t* c) +{ + c->transforms.modelview.uninit(); + c->transforms.projection.uninit(); + for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) + c->transforms.texture[i].uninit(); +} + +static void validate_perspective(ogles_context_t* c, vertex_t* v) +{ + const uint32_t enables = c->rasterizer.state.enables; + c->arrays.perspective = (c->clipPlanes.enable) ? + ogles_vertex_clipAllPerspective3D : ogles_vertex_perspective3D; + if (enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)) { + c->arrays.perspective = ogles_vertex_perspective3DZ; + if (c->clipPlanes.enable || (enables&GGL_ENABLE_FOG)) + c->arrays.perspective = ogles_vertex_clipAllPerspective3DZ; + } + if ((c->arrays.vertex.size != 4) && + (c->transforms.mvp4.flags & transform_t::FLAGS_2D_PROJECTION)) { + c->arrays.perspective = ogles_vertex_perspective2D; + } + c->arrays.perspective(c, v); +} + +void ogles_invalidate_perspective(ogles_context_t* c) +{ + c->arrays.perspective = validate_perspective; +} + +void ogles_validate_transform_impl(ogles_context_t* c, uint32_t want) +{ + int dirty = c->transforms.dirty & want; + + // Validate the modelview + if (dirty & transform_state_t::MODELVIEW) { + c->transforms.modelview.validate(); + } + + // Validate the projection stack (in fact, it's never needed) + if (dirty & transform_state_t::PROJECTION) { + c->transforms.projection.validate(); + } + + // Validate the viewport transformation + if (dirty & transform_state_t::VIEWPORT) { + vp_transform_t& vpt = c->transforms.vpt; + vpt.transform.matrix.load(vpt.matrix); + vpt.transform.picker(); + } + + // We need to update the mvp (used to transform each vertex) + if (dirty & transform_state_t::MVP) { + c->transforms.update_mvp(); + // invalidate perspective (divide by W) and view volume clipping + ogles_invalidate_perspective(c); + } + + // Validate the mvui (for normal transformation) + if (dirty & transform_state_t::MVUI) { + c->transforms.update_mvui(); + ogles_invalidate_lighting_mvui(c); + } + + // Validate the texture stack + if (dirty & transform_state_t::TEXTURE) { + for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) + c->transforms.texture[i].validate(); + } + + // Validate the mvit4 (user-clip planes) + if (dirty & transform_state_t::MVIT) { + c->transforms.update_mvit(); + } + + c->transforms.dirty &= ~want; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark transform_t +#endif + +void transform_t::loadIdentity() { + matrix = gIdentityx; + flags = 0; + ops = OP_IDENTITY; + point2 = point2__nop; + point3 = point3__nop; + point4 = point4__nop; +} + + +static inline +int notZero(GLfixed v) { + return abs(v) & ~0x3; +} + +static inline +int notOne(GLfixed v) { + return notZero(v - 0x10000); +} + +void transform_t::picker() +{ + const GLfixed* const m = matrix.m; + + // XXX: picker needs to be smarter + flags = 0; + ops = OP_ALL; + point2 = point2__generic; + point3 = point3__generic; + point4 = point4__generic; + + // find out if this is a 2D projection + if (!(notZero(m[3]) | notZero(m[7]) | notZero(m[11]) | notOne(m[15]))) { + flags |= FLAGS_2D_PROJECTION; + } +} + +void mvui_transform_t::picker() +{ + flags = 0; + ops = OP_ALL; + point3 = normal__generic; +} + +void transform_t::dump(const char* what) +{ + GLfixed const * const m = matrix.m; + LOGD("%s:", what); + for (int i=0 ; i<4 ; i++) + LOGD("[%08x %08x %08x %08x] [%f %f %f %f]\n", + m[I(0,i)], m[I(1,i)], m[I(2,i)], m[I(3,i)], + fixedToFloat(m[I(0,i)]), + fixedToFloat(m[I(1,i)]), + fixedToFloat(m[I(2,i)]), + fixedToFloat(m[I(3,i)])); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark matrixx_t +#endif + +void matrixx_t::load(const matrixf_t& rhs) { + GLfixed* xp = m; + GLfloat const* fp = rhs.elements(); + unsigned int i = 16; + do { + const GLfloat f = *fp++; + *xp++ = isZerof(f) ? 0 : gglFloatToFixed(f); + } while (--i); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark matrixf_t +#endif + +void matrixf_t::multiply(matrixf_t& r, const matrixf_t& lhs, const matrixf_t& rhs) +{ + GLfloat const* const m = lhs.m; + for (int i=0 ; i<4 ; i++) { + register const float rhs_i0 = rhs.m[ I(i,0) ]; + register float ri0 = m[ I(0,0) ] * rhs_i0; + register float ri1 = m[ I(0,1) ] * rhs_i0; + register float ri2 = m[ I(0,2) ] * rhs_i0; + register float ri3 = m[ I(0,3) ] * rhs_i0; + for (int j=1 ; j<4 ; j++) { + register const float rhs_ij = rhs.m[ I(i,j) ]; + ri0 += m[ I(j,0) ] * rhs_ij; + ri1 += m[ I(j,1) ] * rhs_ij; + ri2 += m[ I(j,2) ] * rhs_ij; + ri3 += m[ I(j,3) ] * rhs_ij; + } + r.m[ I(i,0) ] = ri0; + r.m[ I(i,1) ] = ri1; + r.m[ I(i,2) ] = ri2; + r.m[ I(i,3) ] = ri3; + } +} + +void matrixf_t::dump(const char* what) { + LOGD("%s", what); + LOGD("[ %9f %9f %9f %9f ]", m[I(0,0)], m[I(1,0)], m[I(2,0)], m[I(3,0)]); + LOGD("[ %9f %9f %9f %9f ]", m[I(0,1)], m[I(1,1)], m[I(2,1)], m[I(3,1)]); + LOGD("[ %9f %9f %9f %9f ]", m[I(0,2)], m[I(1,2)], m[I(2,2)], m[I(3,2)]); + LOGD("[ %9f %9f %9f %9f ]", m[I(0,3)], m[I(1,3)], m[I(2,3)], m[I(3,3)]); +} + +void matrixf_t::loadIdentity() { + memcpy(m, gIdentityf, sizeof(m)); +} + +void matrixf_t::set(const GLfixed* rhs) { + load(rhs); +} + +void matrixf_t::set(const GLfloat* rhs) { + load(rhs); +} + +void matrixf_t::load(const GLfixed* rhs) { + GLfloat* fp = m; + unsigned int i = 16; + do { + *fp++ = fixedToFloat(*rhs++); + } while (--i); +} + +void matrixf_t::load(const GLfloat* rhs) { + memcpy(m, rhs, sizeof(m)); +} + +void matrixf_t::load(const matrixf_t& rhs) { + operator = (rhs); +} + +void matrixf_t::multiply(const matrixf_t& rhs) { + matrixf_t r; + multiply(r, *this, rhs); + operator = (r); +} + +void matrixf_t::translate(GLfloat x, GLfloat y, GLfloat z) { + for (int i=0 ; i<4 ; i++) { + m[12+i] += m[i]*x + m[4+i]*y + m[8+i]*z; + } +} + +void matrixf_t::scale(GLfloat x, GLfloat y, GLfloat z) { + for (int i=0 ; i<4 ; i++) { + m[ i] *= x; + m[4+i] *= y; + m[8+i] *= z; + } +} + +void matrixf_t::rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z) +{ + matrixf_t rotation; + GLfloat* r = rotation.m; + GLfloat c, s; + r[3] = 0; r[7] = 0; r[11]= 0; + r[12]= 0; r[13]= 0; r[14]= 0; r[15]= 1; + a *= GLfloat(M_PI / 180.0f); + sincosf(a, &s, &c); + if (isOnef(x) && isZerof(y) && isZerof(z)) { + r[5] = c; r[10]= c; + r[6] = s; r[9] = -s; + r[1] = 0; r[2] = 0; + r[4] = 0; r[8] = 0; + r[0] = 1; + } else if (isZerof(x) && isOnef(y) && isZerof(z)) { + r[0] = c; r[10]= c; + r[8] = s; r[2] = -s; + r[1] = 0; r[4] = 0; + r[6] = 0; r[9] = 0; + r[5] = 1; + } else if (isZerof(x) && isZerof(y) && isOnef(z)) { + r[0] = c; r[5] = c; + r[1] = s; r[4] = -s; + r[2] = 0; r[6] = 0; + r[8] = 0; r[9] = 0; + r[10]= 1; + } else { + const GLfloat len = sqrtf(x*x + y*y + z*z); + if (!isOnef(len)) { + const GLfloat recipLen = reciprocalf(len); + x *= recipLen; + y *= recipLen; + z *= recipLen; + } + const GLfloat nc = 1.0f - c; + const GLfloat xy = x * y; + const GLfloat yz = y * z; + const GLfloat zx = z * x; + const GLfloat xs = x * s; + const GLfloat ys = y * s; + const GLfloat zs = z * s; + r[ 0] = x*x*nc + c; r[ 4] = xy*nc - zs; r[ 8] = zx*nc + ys; + r[ 1] = xy*nc + zs; r[ 5] = y*y*nc + c; r[ 9] = yz*nc - xs; + r[ 2] = zx*nc - ys; r[ 6] = yz*nc + xs; r[10] = z*z*nc + c; + } + multiply(rotation); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark matrix_stack_t +#endif + +void matrix_stack_t::init(int depth) { + stack = new matrixf_t[depth]; + ops = new uint8_t[depth]; + maxDepth = depth; + depth = 0; + dirty = 0; + loadIdentity(); +} + +void matrix_stack_t::uninit() { + delete [] stack; + delete [] ops; +} + +void matrix_stack_t::loadIdentity() { + transform.loadIdentity(); + stack[depth].loadIdentity(); + ops[depth] = OP_IDENTITY; +} + +void matrix_stack_t::load(const GLfixed* rhs) +{ + memcpy(transform.matrix.m, rhs, sizeof(transform.matrix.m)); + stack[depth].load(rhs); + ops[depth] = OP_ALL; // TODO: we should look at the matrix +} + +void matrix_stack_t::load(const GLfloat* rhs) +{ + stack[depth].load(rhs); + ops[depth] = OP_ALL; // TODO: we should look at the matrix +} + +void matrix_stack_t::multiply(const matrixf_t& rhs) +{ + stack[depth].multiply(rhs); + ops[depth] = OP_ALL; // TODO: we should look at the matrix +} + +void matrix_stack_t::translate(GLfloat x, GLfloat y, GLfloat z) +{ + stack[depth].translate(x,y,z); + ops[depth] |= OP_TRANSLATE; +} + +void matrix_stack_t::scale(GLfloat x, GLfloat y, GLfloat z) +{ + stack[depth].scale(x,y,z); + if (x==y && y==z) { + ops[depth] |= OP_UNIFORM_SCALE; + } else { + ops[depth] |= OP_SCALE; + } +} + +void matrix_stack_t::rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z) +{ + stack[depth].rotate(a,x,y,z); + ops[depth] |= OP_ROTATE; +} + +void matrix_stack_t::validate() +{ + if (dirty & DO_FLOAT_TO_FIXED) { + transform.matrix.load(top()); + } + if (dirty & DO_PICKER) { + transform.picker(); + } + dirty = 0; +} + +GLint matrix_stack_t::push() +{ + if (depth >= (maxDepth-1)) { + return GL_STACK_OVERFLOW; + } + stack[depth+1] = stack[depth]; + ops[depth+1] = ops[depth]; + depth++; + return 0; +} + +GLint matrix_stack_t::pop() +{ + if (depth == 0) { + return GL_STACK_UNDERFLOW; + } + depth--; + return 0; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark vp_transform_t +#endif + +void vp_transform_t::loadIdentity() { + transform.loadIdentity(); + matrix.loadIdentity(); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark transform_state_t +#endif + +void transform_state_t::invalidate() +{ + switch (matrixMode) { + case GL_MODELVIEW: dirty |= MODELVIEW | MVP | MVUI | MVIT; break; + case GL_PROJECTION: dirty |= PROJECTION | MVP; break; + case GL_TEXTURE: dirty |= TEXTURE | MVP; break; + } + current->dirty = matrix_stack_t::DO_PICKER | + matrix_stack_t::DO_FLOAT_TO_FIXED; +} + +void transform_state_t::update_mvp() +{ + matrixf_t temp_mvp; + matrixf_t::multiply(temp_mvp, projection.top(), modelview.top()); + mvp4.matrix.load(temp_mvp); + mvp4.picker(); + + if (mvp4.flags & transform_t::FLAGS_2D_PROJECTION) { + // the mvp matrix doesn't transform W, in this case we can + // premultiply it with the viewport transformation. In addition to + // being more efficient, this is also much more accurate and in fact + // is needed for 2D drawing with a resulting 1:1 mapping. + matrixf_t mvpv; + matrixf_t::multiply(mvpv, vpt.matrix, temp_mvp); + mvp.matrix.load(mvpv); + mvp.picker(); + } else { + mvp = mvp4; + } +} + +static inline +GLfloat det22(GLfloat a, GLfloat b, GLfloat c, GLfloat d) { + return a*d - b*c; +} + +static inline +GLfloat ndet22(GLfloat a, GLfloat b, GLfloat c, GLfloat d) { + return b*c - a*d; +} + +static __attribute__((noinline)) +void invert(GLfloat* inverse, const GLfloat* src) +{ + double t; + int i, j, k, swap; + GLfloat tmp[4][4]; + + memcpy(inverse, gIdentityf, sizeof(gIdentityf)); + memcpy(tmp, src, sizeof(GLfloat)*16); + + for (i = 0; i < 4; i++) { + // look for largest element in column + swap = i; + for (j = i + 1; j < 4; j++) { + if (fabs(tmp[j][i]) > fabs(tmp[i][i])) { + swap = j; + } + } + + if (swap != i) { + /* swap rows. */ + for (k = 0; k < 4; k++) { + t = tmp[i][k]; + tmp[i][k] = tmp[swap][k]; + tmp[swap][k] = t; + + t = inverse[i*4+k]; + inverse[i*4+k] = inverse[swap*4+k]; + inverse[swap*4+k] = t; + } + } + + t = 1.0f / tmp[i][i]; + for (k = 0; k < 4; k++) { + tmp[i][k] *= t; + inverse[i*4+k] *= t; + } + for (j = 0; j < 4; j++) { + if (j != i) { + t = tmp[j][i]; + for (k = 0; k < 4; k++) { + tmp[j][k] -= tmp[i][k]*t; + inverse[j*4+k] -= inverse[i*4+k]*t; + } + } + } + } +} + +void transform_state_t::update_mvit() +{ + GLfloat r[16]; + const GLfloat* const mv = modelview.top().elements(); + invert(r, mv); + // convert to fixed-point and transpose + GLfixed* const x = mvit4.matrix.m; + for (int i=0 ; i<4 ; i++) + for (int j=0 ; j<4 ; j++) + x[I(i,j)] = gglFloatToFixed(r[I(j,i)]); + mvit4.picker(); +} + +void transform_state_t::update_mvui() +{ + const GLfloat* const mv = modelview.top().elements(); + + /* + When transforming normals, we can use the upper 3x3 matrix, see: + http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node26.html + */ + + // Also note that: + // l(obj) = tr(M).l(eye) for infinite light + // l(obj) = inv(M).l(eye) for local light + + const uint32_t ops = modelview.top_ops() & ~OP_TRANSLATE; + if (ggl_likely((!(ops & ~OP_ROTATE)) || + (rescaleNormals && modelview.isRigidBody()))) { + // if the modelview matrix is a rigid body transformation + // (translation, rotation, uniform scaling), then we can bypass + // the inverse by transposing the matrix. + GLfloat rescale = 1.0f; + if (rescaleNormals == GL_RESCALE_NORMAL) { + if (!(ops & ~OP_UNIFORM_SCALE)) { + rescale = reciprocalf(mv[I(0,0)]); + } else { + rescale = rsqrtf( + sqrf(mv[I(2,0)]) + sqrf(mv[I(2,1)]) + sqrf(mv[I(2,2)])); + } + } + GLfixed* const x = mvui.matrix.m; + for (int i=0 ; i<3 ; i++) { + x[I(i,0)] = gglFloatToFixed(mv[I(0,i)] * rescale); + x[I(i,1)] = gglFloatToFixed(mv[I(1,i)] * rescale); + x[I(i,2)] = gglFloatToFixed(mv[I(2,i)] * rescale); + } + mvui.picker(); + return; + } + + GLfloat r[3][3]; + r[0][0] = det22(mv[I(1,1)], mv[I(2,1)], mv[I(1,2)], mv[I(2,2)]); + r[0][1] =ndet22(mv[I(0,1)], mv[I(2,1)], mv[I(0,2)], mv[I(2,2)]); + r[0][2] = det22(mv[I(0,1)], mv[I(1,1)], mv[I(0,2)], mv[I(1,2)]); + r[1][0] =ndet22(mv[I(1,0)], mv[I(2,0)], mv[I(1,2)], mv[I(2,2)]); + r[1][1] = det22(mv[I(0,0)], mv[I(2,0)], mv[I(0,2)], mv[I(2,2)]); + r[1][2] =ndet22(mv[I(0,0)], mv[I(1,0)], mv[I(0,2)], mv[I(1,2)]); + r[2][0] = det22(mv[I(1,0)], mv[I(2,0)], mv[I(1,1)], mv[I(2,1)]); + r[2][1] =ndet22(mv[I(0,0)], mv[I(2,0)], mv[I(0,1)], mv[I(2,1)]); + r[2][2] = det22(mv[I(0,0)], mv[I(1,0)], mv[I(0,1)], mv[I(1,1)]); + + GLfloat rdet; + if (rescaleNormals == GL_RESCALE_NORMAL) { + rdet = rsqrtf(sqrf(r[0][2]) + sqrf(r[1][2]) + sqrf(r[2][2])); + } else { + rdet = reciprocalf( + r[0][0]*mv[I(0,0)] + r[0][1]*mv[I(1,0)] + r[0][2]*mv[I(2,0)]); + } + + GLfixed* const x = mvui.matrix.m; + for (int i=0 ; i<3 ; i++) { + x[I(i,0)] = gglFloatToFixed(r[i][0] * rdet); + x[I(i,1)] = gglFloatToFixed(r[i][1] * rdet); + x[I(i,2)] = gglFloatToFixed(r[i][2] * rdet); + } + mvui.picker(); +} + + +// ---------------------------------------------------------------------------- +// transformation and matrices API +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark transformation and matrices API +#endif + +int ogles_surfaceport(ogles_context_t* c, GLint x, GLint y) +{ + c->viewport.surfaceport.x = x; + c->viewport.surfaceport.y = y; + + ogles_viewport(c, + c->viewport.x, + c->viewport.y, + c->viewport.w, + c->viewport.h); + + ogles_scissor(c, + c->viewport.scissor.x, + c->viewport.scissor.y, + c->viewport.scissor.w, + c->viewport.scissor.h); + + return 0; +} + +void ogles_scissor(ogles_context_t* c, + GLint x, GLint y, GLsizei w, GLsizei h) +{ + if ((w|h) < 0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + c->viewport.scissor.x = x; + c->viewport.scissor.y = y; + c->viewport.scissor.w = w; + c->viewport.scissor.h = h; + + x += c->viewport.surfaceport.x; + y += c->viewport.surfaceport.y; + + y = c->rasterizer.state.buffers.color.height - (y + h); + c->rasterizer.procs.scissor(c, x, y, w, h); +} + +void ogles_viewport(ogles_context_t* c, + GLint x, GLint y, GLsizei w, GLsizei h) +{ + if ((w|h)<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + c->viewport.x = x; + c->viewport.y = y; + c->viewport.w = w; + c->viewport.h = h; + + x += c->viewport.surfaceport.x; + y += c->viewport.surfaceport.y; + + GLint H = c->rasterizer.state.buffers.color.height; + GLfloat sx = div2f(w); + GLfloat ox = sx + x; + GLfloat sy = div2f(h); + GLfloat oy = sy - y + (H - h); + + GLfloat near = c->transforms.vpt.zNear; + GLfloat far = c->transforms.vpt.zFar; + GLfloat A = div2f(far - near); + GLfloat B = div2f(far + near); + + // compute viewport matrix + GLfloat* const f = c->transforms.vpt.matrix.editElements(); + f[0] = sx; f[4] = 0; f[ 8] = 0; f[12] = ox; + f[1] = 0; f[5] =-sy; f[ 9] = 0; f[13] = oy; + f[2] = 0; f[6] = 0; f[10] = A; f[14] = B; + f[3] = 0; f[7] = 0; f[11] = 0; f[15] = 1; + c->transforms.dirty |= transform_state_t::VIEWPORT; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark matrix * vertex +#endif + +void point2__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { + const GLfixed* const m = mx->matrix.m; + const GLfixed rx = rhs->x; + const GLfixed ry = rhs->y; + lhs->x = mla2a(rx, m[ 0], ry, m[ 4], m[12]); + lhs->y = mla2a(rx, m[ 1], ry, m[ 5], m[13]); + lhs->z = mla2a(rx, m[ 2], ry, m[ 6], m[14]); + lhs->w = mla2a(rx, m[ 3], ry, m[ 7], m[15]); +} + +void point3__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { + const GLfixed* const m = mx->matrix.m; + const GLfixed rx = rhs->x; + const GLfixed ry = rhs->y; + const GLfixed rz = rhs->z; + lhs->x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); + lhs->y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]); + lhs->z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]); + lhs->w = mla3a(rx, m[ 3], ry, m[ 7], rz, m[11], m[15]); +} + +void point4__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { + const GLfixed* const m = mx->matrix.m; + const GLfixed rx = rhs->x; + const GLfixed ry = rhs->y; + const GLfixed rz = rhs->z; + const GLfixed rw = rhs->w; + lhs->x = mla4(rx, m[ 0], ry, m[ 4], rz, m[ 8], rw, m[12]); + lhs->y = mla4(rx, m[ 1], ry, m[ 5], rz, m[ 9], rw, m[13]); + lhs->z = mla4(rx, m[ 2], ry, m[ 6], rz, m[10], rw, m[14]); + lhs->w = mla4(rx, m[ 3], ry, m[ 7], rz, m[11], rw, m[15]); +} + +void normal__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { + const GLfixed* const m = mx->matrix.m; + const GLfixed rx = rhs->x; + const GLfixed ry = rhs->y; + const GLfixed rz = rhs->z; + lhs->x = mla3(rx, m[ 0], ry, m[ 4], rz, m[ 8]); + lhs->y = mla3(rx, m[ 1], ry, m[ 5], rz, m[ 9]); + lhs->z = mla3(rx, m[ 2], ry, m[ 6], rz, m[10]); +} + + +void point2__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) { + lhs->z = 0; + lhs->w = 0x10000; + if (lhs != rhs) { + lhs->x = rhs->x; + lhs->y = rhs->y; + } +} + +void point3__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) { + lhs->w = 0x10000; + if (lhs != rhs) { + lhs->x = rhs->x; + lhs->y = rhs->y; + lhs->z = rhs->z; + } +} + +void point4__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) { + if (lhs != rhs) + *lhs = *rhs; +} + + +static void frustumf( + GLfloat left, GLfloat right, + GLfloat bottom, GLfloat top, + GLfloat zNear, GLfloat zFar, + ogles_context_t* c) + { + if (cmpf(left,right) || + cmpf(top, bottom) || + cmpf(zNear, zFar) || + isZeroOrNegativef(zNear) || + isZeroOrNegativef(zFar)) + { + ogles_error(c, GL_INVALID_VALUE); + return; + } + const GLfloat r_width = reciprocalf(right - left); + const GLfloat r_height = reciprocalf(top - bottom); + const GLfloat r_depth = reciprocalf(zNear - zFar); + const GLfloat x = mul2f(zNear * r_width); + const GLfloat y = mul2f(zNear * r_height); + const GLfloat A = mul2f((right + left) * r_width); + const GLfloat B = (top + bottom) * r_height; + const GLfloat C = (zFar + zNear) * r_depth; + const GLfloat D = mul2f(zFar * zNear * r_depth); + GLfloat f[16]; + f[ 0] = x; + f[ 5] = y; + f[ 8] = A; + f[ 9] = B; + f[10] = C; + f[14] = D; + f[11] = -1.0f; + f[ 1] = f[ 2] = f[ 3] = + f[ 4] = f[ 6] = f[ 7] = + f[12] = f[13] = f[15] = 0.0f; + + matrixf_t rhs; + rhs.set(f); + c->transforms.current->multiply(rhs); + c->transforms.invalidate(); +} + +static void orthof( + GLfloat left, GLfloat right, + GLfloat bottom, GLfloat top, + GLfloat zNear, GLfloat zFar, + ogles_context_t* c) +{ + if (cmpf(left,right) || + cmpf(top, bottom) || + cmpf(zNear, zFar)) + { + ogles_error(c, GL_INVALID_VALUE); + return; + } + const GLfloat r_width = reciprocalf(right - left); + const GLfloat r_height = reciprocalf(top - bottom); + const GLfloat r_depth = reciprocalf(zFar - zNear); + const GLfloat x = mul2f(r_width); + const GLfloat y = mul2f(r_height); + const GLfloat z = -mul2f(r_depth); + const GLfloat tx = -(right + left) * r_width; + const GLfloat ty = -(top + bottom) * r_height; + const GLfloat tz = -(zFar + zNear) * r_depth; + GLfloat f[16]; + f[ 0] = x; + f[ 5] = y; + f[10] = z; + f[12] = tx; + f[13] = ty; + f[14] = tz; + f[15] = 1.0f; + f[ 1] = f[ 2] = f[ 3] = + f[ 4] = f[ 6] = f[ 7] = + f[ 8] = f[ 9] = f[11] = 0.0f; + matrixf_t rhs; + rhs.set(f); + c->transforms.current->multiply(rhs); + c->transforms.invalidate(); +} + +static void depthRangef(GLclampf zNear, GLclampf zFar, ogles_context_t* c) +{ + zNear = clampToZerof(zNear > 1 ? 1 : zNear); + zFar = clampToZerof(zFar > 1 ? 1 : zFar); + GLfloat* const f = c->transforms.vpt.matrix.editElements(); + f[10] = div2f(zFar - zNear); + f[14] = div2f(zFar + zNear); + c->transforms.dirty |= transform_state_t::VIEWPORT; + c->transforms.vpt.zNear = zNear; + c->transforms.vpt.zFar = zFar; +} + + +// ---------------------------------------------------------------------------- +}; // namespace android + +using namespace android; + +void glMatrixMode(GLenum mode) +{ + ogles_context_t* c = ogles_context_t::get(); + matrix_stack_t* stack = 0; + switch (mode) { + case GL_MODELVIEW: + stack = &c->transforms.modelview; + break; + case GL_PROJECTION: + stack = &c->transforms.projection; + break; + case GL_TEXTURE: + stack = &c->transforms.texture[c->textures.active]; + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->transforms.matrixMode = mode; + c->transforms.current = stack; +} + +void glLoadIdentity() +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->loadIdentity(); // also loads the GLfixed transform + c->transforms.invalidate(); + c->transforms.current->dirty = 0; +} + +void glLoadMatrixf(const GLfloat* m) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->load(m); + c->transforms.invalidate(); +} + +void glLoadMatrixx(const GLfixed* m) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->load(m); // also loads the GLfixed transform + c->transforms.invalidate(); + c->transforms.current->dirty &= ~matrix_stack_t::DO_FLOAT_TO_FIXED; +} + +void glMultMatrixf(const GLfloat* m) +{ + ogles_context_t* c = ogles_context_t::get(); + matrixf_t rhs; + rhs.set(m); + c->transforms.current->multiply(rhs); + c->transforms.invalidate(); +} + +void glMultMatrixx(const GLfixed* m) +{ + ogles_context_t* c = ogles_context_t::get(); + matrixf_t rhs; + rhs.set(m); + c->transforms.current->multiply(rhs); + c->transforms.invalidate(); +} + +void glPopMatrix() +{ + ogles_context_t* c = ogles_context_t::get(); + GLint err = c->transforms.current->pop(); + if (ggl_unlikely(err)) { + ogles_error(c, err); + return; + } + c->transforms.invalidate(); +} + +void glPushMatrix() +{ + ogles_context_t* c = ogles_context_t::get(); + GLint err = c->transforms.current->push(); + if (ggl_unlikely(err)) { + ogles_error(c, err); + return; + } + c->transforms.invalidate(); +} + +void glFrustumf( + GLfloat left, GLfloat right, + GLfloat bottom, GLfloat top, + GLfloat zNear, GLfloat zFar) +{ + ogles_context_t* c = ogles_context_t::get(); + frustumf(left, right, bottom, top, zNear, zFar, c); +} + +void glFrustumx( + GLfixed left, GLfixed right, + GLfixed bottom, GLfixed top, + GLfixed zNear, GLfixed zFar) +{ + ogles_context_t* c = ogles_context_t::get(); + frustumf( fixedToFloat(left), fixedToFloat(right), + fixedToFloat(bottom), fixedToFloat(top), + fixedToFloat(zNear), fixedToFloat(zFar), + c); +} + +void glOrthof( + GLfloat left, GLfloat right, + GLfloat bottom, GLfloat top, + GLfloat zNear, GLfloat zFar) +{ + ogles_context_t* c = ogles_context_t::get(); + orthof(left, right, bottom, top, zNear, zFar, c); +} + +void glOrthox( + GLfixed left, GLfixed right, + GLfixed bottom, GLfixed top, + GLfixed zNear, GLfixed zFar) +{ + ogles_context_t* c = ogles_context_t::get(); + orthof( fixedToFloat(left), fixedToFloat(right), + fixedToFloat(bottom), fixedToFloat(top), + fixedToFloat(zNear), fixedToFloat(zFar), + c); +} + +void glRotatef(GLfloat a, GLfloat x, GLfloat y, GLfloat z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->rotate(a, x, y, z); + c->transforms.invalidate(); +} + +void glRotatex(GLfixed a, GLfixed x, GLfixed y, GLfixed z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->rotate( + fixedToFloat(a), fixedToFloat(x), + fixedToFloat(y), fixedToFloat(z)); + c->transforms.invalidate(); +} + +void glScalef(GLfloat x, GLfloat y, GLfloat z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->scale(x, y, z); + c->transforms.invalidate(); +} + +void glScalex(GLfixed x, GLfixed y, GLfixed z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->scale( + fixedToFloat(x), fixedToFloat(y), fixedToFloat(z)); + c->transforms.invalidate(); +} + +void glTranslatef(GLfloat x, GLfloat y, GLfloat z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->translate(x, y, z); + c->transforms.invalidate(); +} + +void glTranslatex(GLfixed x, GLfixed y, GLfixed z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->transforms.current->translate( + fixedToFloat(x), fixedToFloat(y), fixedToFloat(z)); + c->transforms.invalidate(); +} + +void glScissor(GLint x, GLint y, GLsizei w, GLsizei h) +{ + ogles_context_t* c = ogles_context_t::get(); + ogles_scissor(c, x, y, w, h); +} + +void glViewport(GLint x, GLint y, GLsizei w, GLsizei h) +{ + ogles_context_t* c = ogles_context_t::get(); + ogles_viewport(c, x, y, w, h); +} + +void glDepthRangef(GLclampf zNear, GLclampf zFar) +{ + ogles_context_t* c = ogles_context_t::get(); + depthRangef(zNear, zFar, c); +} + +void glDepthRangex(GLclampx zNear, GLclampx zFar) +{ + ogles_context_t* c = ogles_context_t::get(); + depthRangef(fixedToFloat(zNear), fixedToFloat(zFar), c); +} + +void glPolygonOffsetx(GLfixed factor, GLfixed units) +{ + ogles_context_t* c = ogles_context_t::get(); + c->polygonOffset.factor = factor; + c->polygonOffset.units = units; +} + +void glPolygonOffset(GLfloat factor, GLfloat units) +{ + ogles_context_t* c = ogles_context_t::get(); + c->polygonOffset.factor = gglFloatToFixed(factor); + c->polygonOffset.units = gglFloatToFixed(units); +} + +GLbitfield glQueryMatrixxOES(GLfixed* m, GLint* e) +{ + ogles_context_t* c = ogles_context_t::get(); + GLbitfield status = 0; + GLfloat const* f = c->transforms.current->top().elements(); + for (int i=0 ; i<16 ; i++) { + if (isnan(f[i]) || isinf(f[i])) { + status |= 1<<i; + continue; + } + e[i] = exponent(f[i]) - 7; + m[i] = mantissa(f[i]); + } + return status; +} diff --git a/opengl/libagl/matrix.h b/opengl/libagl/matrix.h new file mode 100644 index 0000000..c9a38a9 --- /dev/null +++ b/opengl/libagl/matrix.h @@ -0,0 +1,355 @@ +/* libs/opengles/matrix.h +** +** 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. +*/ + +#ifndef ANDROID_OPENGLES_MATRIX_H +#define ANDROID_OPENGLES_MATRIX_H + +#include <stdint.h> +#include <stddef.h> +#include <sys/types.h> +#include <utils/Log.h> + +#include <private/pixelflinger/ggl_context.h> + +#include <GLES/gl.h> + +namespace android { + +const int OGLES_MODELVIEW_STACK_DEPTH = 16; +const int OGLES_PROJECTION_STACK_DEPTH = 2; +const int OGLES_TEXTURE_STACK_DEPTH = 2; + +void ogles_init_matrix(ogles_context_t*); +void ogles_uninit_matrix(ogles_context_t*); +void ogles_invalidate_perspective(ogles_context_t* c); +void ogles_validate_transform_impl(ogles_context_t* c, uint32_t want); + +int ogles_surfaceport(ogles_context_t* c, GLint x, GLint y); + +void ogles_scissor(ogles_context_t* c, + GLint x, GLint y, GLsizei w, GLsizei h); + +void ogles_viewport(ogles_context_t* c, + GLint x, GLint y, GLsizei w, GLsizei h); + +inline void ogles_validate_transform( + ogles_context_t* c, uint32_t want) +{ + if (c->transforms.dirty & want) + ogles_validate_transform_impl(c, want); +} + +// ---------------------------------------------------------------------------- + +inline +GLfixed vsquare3(GLfixed a, GLfixed b, GLfixed c) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + int32_t t; + asm( + "smull %0, %1, %2, %2 \n" + "smlal %0, %1, %3, %3 \n" + "smlal %0, %1, %4, %4 \n" + "movs %0, %0, lsr #16 \n" + "adc %0, %0, %1, lsl #16 \n" + : "=&r"(r), "=&r"(t) + : "%r"(a), "r"(b), "r"(c) + : "cc" + ); + return r; + +#else + + return (( int64_t(a)*a + + int64_t(b)*b + + int64_t(c)*c + 0x8000)>>16); + +#endif +} + +static inline GLfixed mla2a( GLfixed a0, GLfixed b0, + GLfixed a1, GLfixed b1, + GLfixed c) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + int32_t t; + asm( + "smull %0, %1, %2, %3 \n" + "smlal %0, %1, %4, %5 \n" + "add %0, %6, %0, lsr #16 \n" + "add %0, %0, %1, lsl #16 \n" + : "=&r"(r), "=&r"(t) + : "%r"(a0), "r"(b0), + "%r"(a1), "r"(b1), + "r"(c) + : + ); + return r; + +#else + + return (( int64_t(a0)*b0 + + int64_t(a1)*b1)>>16) + c; + +#endif +} + +static inline GLfixed mla3a( GLfixed a0, GLfixed b0, + GLfixed a1, GLfixed b1, + GLfixed a2, GLfixed b2, + GLfixed c) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + int32_t t; + asm( + "smull %0, %1, %2, %3 \n" + "smlal %0, %1, %4, %5 \n" + "smlal %0, %1, %6, %7 \n" + "add %0, %8, %0, lsr #16 \n" + "add %0, %0, %1, lsl #16 \n" + : "=&r"(r), "=&r"(t) + : "%r"(a0), "r"(b0), + "%r"(a1), "r"(b1), + "%r"(a2), "r"(b2), + "r"(c) + : + ); + return r; + +#else + + return (( int64_t(a0)*b0 + + int64_t(a1)*b1 + + int64_t(a2)*b2)>>16) + c; + +#endif +} + +// b0, b1, b2 are signed 16-bit quanities +// that have been shifted right by 'shift' bits relative to normal +// S16.16 fixed point +static inline GLfixed mla3a16( GLfixed a0, int32_t b1b0, + GLfixed a1, + GLfixed a2, int32_t b2, + GLint shift, + GLfixed c) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + asm( + "smulwb %0, %1, %2 \n" + "smlawt %0, %3, %2, %0 \n" + "smlawb %0, %4, %5, %0 \n" + "add %0, %7, %0, lsl %6 \n" + : "=&r"(r) + : "r"(a0), "r"(b1b0), + "r"(a1), + "r"(a2), "r"(b2), + "r"(shift), + "r"(c) + : + ); + return r; + +#else + + int32_t accum; + int16_t b0 = b1b0 & 0xffff; + int16_t b1 = (b1b0 >> 16) & 0xffff; + accum = int64_t(a0)*int16_t(b0) >> 16; + accum += int64_t(a1)*int16_t(b1) >> 16; + accum += int64_t(a2)*int16_t(b2) >> 16; + accum = (accum << shift) + c; + return accum; + +#endif +} + + +static inline GLfixed mla3a16_btb( GLfixed a0, + GLfixed a1, + GLfixed a2, + int32_t b1b0, int32_t xxb2, + GLint shift, + GLfixed c) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + asm( + "smulwb %0, %1, %4 \n" + "smlawt %0, %2, %4, %0 \n" + "smlawb %0, %3, %5, %0 \n" + "add %0, %7, %0, lsl %6 \n" + : "=&r"(r) + : "r"(a0), + "r"(a1), + "r"(a2), + "r"(b1b0), "r"(xxb2), + "r"(shift), + "r"(c) + : + ); + return r; + +#else + + int32_t accum; + int16_t b0 = b1b0 & 0xffff; + int16_t b1 = (b1b0 >> 16) & 0xffff; + int16_t b2 = xxb2 & 0xffff; + accum = int64_t(a0)*int16_t(b0) >> 16; + accum += int64_t(a1)*int16_t(b1) >> 16; + accum += int64_t(a2)*int16_t(b2) >> 16; + accum = (accum << shift) + c; + return accum; + +#endif +} + +static inline GLfixed mla3a16_btt( GLfixed a0, + GLfixed a1, + GLfixed a2, + int32_t b1b0, int32_t b2xx, + GLint shift, + GLfixed c) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + asm( + "smulwb %0, %1, %4 \n" + "smlawt %0, %2, %4, %0 \n" + "smlawt %0, %3, %5, %0 \n" + "add %0, %7, %0, lsl %6 \n" + : "=&r"(r) + : "r"(a0), + "r"(a1), + "r"(a2), + "r"(b1b0), "r"(b2xx), + "r"(shift), + "r"(c) + : + ); + return r; + +#else + + int32_t accum; + int16_t b0 = b1b0 & 0xffff; + int16_t b1 = (b1b0 >> 16) & 0xffff; + int16_t b2 = (b2xx >> 16) & 0xffff; + accum = int64_t(a0)*int16_t(b0) >> 16; + accum += int64_t(a1)*int16_t(b1) >> 16; + accum += int64_t(a2)*int16_t(b2) >> 16; + accum = (accum << shift) + c; + return accum; + +#endif +} + +static inline GLfixed mla3( GLfixed a0, GLfixed b0, + GLfixed a1, GLfixed b1, + GLfixed a2, GLfixed b2) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + int32_t t; + asm( + "smull %0, %1, %2, %3 \n" + "smlal %0, %1, %4, %5 \n" + "smlal %0, %1, %6, %7 \n" + "movs %0, %0, lsr #16 \n" + "adc %0, %0, %1, lsl #16 \n" + : "=&r"(r), "=&r"(t) + : "%r"(a0), "r"(b0), + "%r"(a1), "r"(b1), + "%r"(a2), "r"(b2) + : "cc" + ); + return r; + +#else + + return (( int64_t(a0)*b0 + + int64_t(a1)*b1 + + int64_t(a2)*b2 + 0x8000)>>16); + +#endif +} + +static inline GLfixed mla4( GLfixed a0, GLfixed b0, + GLfixed a1, GLfixed b1, + GLfixed a2, GLfixed b2, + GLfixed a3, GLfixed b3) +{ +#if defined(__arm__) && !defined(__thumb__) + + GLfixed r; + int32_t t; + asm( + "smull %0, %1, %2, %3 \n" + "smlal %0, %1, %4, %5 \n" + "smlal %0, %1, %6, %7 \n" + "smlal %0, %1, %8, %9 \n" + "movs %0, %0, lsr #16 \n" + "adc %0, %0, %1, lsl #16 \n" + : "=&r"(r), "=&r"(t) + : "%r"(a0), "r"(b0), + "%r"(a1), "r"(b1), + "%r"(a2), "r"(b2), + "%r"(a3), "r"(b3) + : "cc" + ); + return r; + +#else + + return (( int64_t(a0)*b0 + + int64_t(a1)*b1 + + int64_t(a2)*b2 + + int64_t(a3)*b3 + 0x8000)>>16); + +#endif +} + +inline +GLfixed dot4(const GLfixed* a, const GLfixed* b) +{ + return mla4(a[0], b[0], a[1], b[1], a[2], b[2], a[3], b[3]); +} + + +inline +GLfixed dot3(const GLfixed* a, const GLfixed* b) +{ + return mla3(a[0], b[0], a[1], b[1], a[2], b[2]); +} + + +}; // namespace android + +#endif // ANDROID_OPENGLES_MATRIX_H + diff --git a/opengl/libagl/mipmap.cpp b/opengl/libagl/mipmap.cpp new file mode 100644 index 0000000..ccd77b7 --- /dev/null +++ b/opengl/libagl/mipmap.cpp @@ -0,0 +1,192 @@ +/* libs/opengles/mipmap.cpp +** +** 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 "state.h" +#include "texture.h" +#include "TextureObjectManager.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +status_t buildAPyramid(ogles_context_t* c, EGLTextureObject* tex) +{ + int level = 0; + const GGLSurface* base = &tex->surface; + const GGLFormat& pixelFormat(c->rasterizer.formats[base->format]); + + int w = base->width; + int h = base->height; + if ((w&h) == 1) + return NO_ERROR; + + w = (w>>1) ? : 1; + h = (h>>1) ? : 1; + + while(true) { + ++level; + const int bpr = w * pixelFormat.size; + if (tex->reallocate(level, w, h, w, + base->format, base->compressedFormat, bpr) != NO_ERROR) { + return NO_MEMORY; + } + + int stride = w; + int bs = base->stride; + GGLSurface& cur = tex->editMip(level); + + if (base->format == GGL_PIXEL_FORMAT_RGB_565) + { + uint16_t const * src = (uint16_t const *)base->data; + uint16_t* dst = (uint16_t*)cur.data; + const uint32_t mask = 0x07E0F81F; + for (int y=0 ; y<h ; y++) { + size_t offset = (y*2) * bs; + for (int x=0 ; x<w ; x++) { + uint32_t p00 = src[offset]; + uint32_t p10 = src[offset+1]; + uint32_t p01 = src[offset+bs]; + uint32_t p11 = src[offset+bs+1]; + p00 = (p00 | (p00 << 16)) & mask; + p01 = (p01 | (p01 << 16)) & mask; + p10 = (p10 | (p10 << 16)) & mask; + p11 = (p11 | (p11 << 16)) & mask; + uint32_t grb = ((p00 + p10 + p01 + p11) >> 2) & mask; + uint32_t rgb = (grb & 0xFFFF) | (grb >> 16); + dst[x + y*stride] = rgb; + offset += 2; + } + } + } + else if (base->format == GGL_PIXEL_FORMAT_RGBA_5551) + { + uint16_t const * src = (uint16_t const *)base->data; + uint16_t* dst = (uint16_t*)cur.data; + for (int y=0 ; y<h ; y++) { + size_t offset = (y*2) * bs; + for (int x=0 ; x<w ; x++) { + uint32_t p00 = src[offset]; + uint32_t p10 = src[offset+1]; + uint32_t p01 = src[offset+bs]; + uint32_t p11 = src[offset+bs+1]; + uint32_t r = ((p00>>11)+(p10>>11)+(p01>>11)+(p11>>11)+2)>>2; + uint32_t g = (((p00>>6)+(p10>>6)+(p01>>6)+(p11>>6)+2)>>2)&0x3F; + uint32_t b = ((p00&0x3E)+(p10&0x3E)+(p01&0x3E)+(p11&0x3E)+4)>>3; + uint32_t a = ((p00&1)+(p10&1)+(p01&1)+(p11&1)+2)>>2; + dst[x + y*stride] = (r<<11)|(g<<6)|(b<<1)|a; + offset += 2; + } + } + } + else if (base->format == GGL_PIXEL_FORMAT_RGBA_8888) + { + uint32_t const * src = (uint32_t const *)base->data; + uint32_t* dst = (uint32_t*)cur.data; + for (int y=0 ; y<h ; y++) { + size_t offset = (y*2) * bs; + for (int x=0 ; x<w ; x++) { + uint32_t p00 = src[offset]; + uint32_t p10 = src[offset+1]; + uint32_t p01 = src[offset+bs]; + uint32_t p11 = src[offset+bs+1]; + uint32_t rb00 = p00 & 0x00FF00FF; + uint32_t rb01 = p01 & 0x00FF00FF; + uint32_t rb10 = p10 & 0x00FF00FF; + uint32_t rb11 = p11 & 0x00FF00FF; + uint32_t ga00 = (p00 >> 8) & 0x00FF00FF; + uint32_t ga01 = (p01 >> 8) & 0x00FF00FF; + uint32_t ga10 = (p10 >> 8) & 0x00FF00FF; + uint32_t ga11 = (p11 >> 8) & 0x00FF00FF; + uint32_t rb = (rb00 + rb01 + rb10 + rb11)>>2; + uint32_t ga = (ga00 + ga01 + ga10 + ga11)>>2; + uint32_t rgba = (rb & 0x00FF00FF) | ((ga & 0x00FF00FF)<<8); + dst[x + y*stride] = rgba; + offset += 2; + } + } + } + else if ((base->format == GGL_PIXEL_FORMAT_RGB_888) || + (base->format == GGL_PIXEL_FORMAT_LA_88) || + (base->format == GGL_PIXEL_FORMAT_A_8) || + (base->format == GGL_PIXEL_FORMAT_L_8)) + { + int skip; + switch (base->format) { + case GGL_PIXEL_FORMAT_RGB_888: skip = 3; break; + case GGL_PIXEL_FORMAT_LA_88: skip = 2; break; + default: skip = 1; break; + } + uint8_t const * src = (uint8_t const *)base->data; + uint8_t* dst = (uint8_t*)cur.data; + bs *= skip; + stride *= skip; + for (int y=0 ; y<h ; y++) { + size_t offset = (y*2) * bs; + for (int x=0 ; x<w ; x++) { + for (int c=0 ; c<skip ; c++) { + uint32_t p00 = src[c+offset]; + uint32_t p10 = src[c+offset+skip]; + uint32_t p01 = src[c+offset+bs]; + uint32_t p11 = src[c+offset+bs+skip]; + dst[x + y*stride + c] = (p00 + p10 + p01 + p11) >> 2; + } + offset += 2*skip; + } + } + } + else if (base->format == GGL_PIXEL_FORMAT_RGBA_4444) + { + uint16_t const * src = (uint16_t const *)base->data; + uint16_t* dst = (uint16_t*)cur.data; + for (int y=0 ; y<h ; y++) { + size_t offset = (y*2) * bs; + for (int x=0 ; x<w ; x++) { + uint32_t p00 = src[offset]; + uint32_t p10 = src[offset+1]; + uint32_t p01 = src[offset+bs]; + uint32_t p11 = src[offset+bs+1]; + p00 = ((p00 << 12) & 0x0F0F0000) | (p00 & 0x0F0F); + p10 = ((p10 << 12) & 0x0F0F0000) | (p10 & 0x0F0F); + p01 = ((p01 << 12) & 0x0F0F0000) | (p01 & 0x0F0F); + p11 = ((p11 << 12) & 0x0F0F0000) | (p11 & 0x0F0F); + uint32_t rbga = (p00 + p10 + p01 + p11) >> 2; + uint32_t rgba = (rbga & 0x0F0F) | ((rbga>>12) & 0xF0F0); + dst[x + y*stride] = rgba; + offset += 2; + } + } + } else { + LOGE("Unsupported format (%d)", base->format); + return BAD_TYPE; + } + + // exit condition: we just processed the 1x1 LODs + if ((w&h) == 1) + break; + + base = &cur; + w = (w>>1) ? : 1; + h = (h>>1) ? : 1; + } + return NO_ERROR; +} + +}; // namespace android diff --git a/opengl/libagl/primitives.cpp b/opengl/libagl/primitives.cpp new file mode 100644 index 0000000..f164c02 --- /dev/null +++ b/opengl/libagl/primitives.cpp @@ -0,0 +1,1111 @@ +/* libs/opengles/primitives.cpp +** +** 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 <math.h> + +#include "context.h" +#include "primitives.h" +#include "light.h" +#include "matrix.h" +#include "vertex.h" +#include "fp.h" +#include "TextureObjectManager.h" + +extern "C" void iterators0032(const void* that, + int32_t* it, int32_t c0, int32_t c1, int32_t c2); + +namespace android { + +// ---------------------------------------------------------------------------- + +static void primitive_point(ogles_context_t* c, vertex_t* v); +static void primitive_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1); +static void primitive_clip_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void primitive_nop_point(ogles_context_t* c, vertex_t* v); +static void primitive_nop_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1); +static void primitive_nop_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static inline bool cull_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void lerp_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void lerp_texcoords(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void lerp_texcoords_w(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static void clip_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2); + +static unsigned int clip_line(ogles_context_t* c, + vertex_t* s, vertex_t* p); + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +static void lightTriangleDarkSmooth(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + if (!(v0->flags & vertex_t::LIT)) { + v0->flags |= vertex_t::LIT; + const GLvoid* cp = c->arrays.color.element( + v0->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v0->color.v, cp); + } + if (!(v1->flags & vertex_t::LIT)) { + v1->flags |= vertex_t::LIT; + const GLvoid* cp = c->arrays.color.element( + v1->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v1->color.v, cp); + } + if(!(v2->flags & vertex_t::LIT)) { + v2->flags |= vertex_t::LIT; + const GLvoid* cp = c->arrays.color.element( + v2->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v2->color.v, cp); + } +} + +static void lightTriangleDarkFlat(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + if (!(v2->flags & vertex_t::LIT)) { + v2->flags |= vertex_t::LIT; + const GLvoid* cp = c->arrays.color.element( + v2->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v2->color.v, cp); + } + // configure the rasterizer here, before we clip + c->rasterizer.procs.color4xv(c, v2->color.v); +} + +static void lightTriangleSmooth(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + if (!(v0->flags & vertex_t::LIT)) + c->lighting.lightVertex(c, v0); + if (!(v1->flags & vertex_t::LIT)) + c->lighting.lightVertex(c, v1); + if(!(v2->flags & vertex_t::LIT)) + c->lighting.lightVertex(c, v2); +} + +static void lightTriangleFlat(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + if (!(v2->flags & vertex_t::LIT)) + c->lighting.lightVertex(c, v2); + // configure the rasterizer here, before we clip + c->rasterizer.procs.color4xv(c, v2->color.v); +} + +// The fog versions... + +static inline +void lightVertexDarkSmoothFog(ogles_context_t* c, vertex_t* v) +{ + if (!(v->flags & vertex_t::LIT)) { + v->flags |= vertex_t::LIT; + v->fog = c->fog.fog(c, v->eye.z); + const GLvoid* cp = c->arrays.color.element( + v->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v->color.v, cp); + } +} +static inline +void lightVertexDarkFlatFog(ogles_context_t* c, vertex_t* v) +{ + if (!(v->flags & vertex_t::LIT)) { + v->flags |= vertex_t::LIT; + v->fog = c->fog.fog(c, v->eye.z); + } +} +static inline +void lightVertexSmoothFog(ogles_context_t* c, vertex_t* v) +{ + if (!(v->flags & vertex_t::LIT)) { + v->fog = c->fog.fog(c, v->eye.z); + c->lighting.lightVertex(c, v); + } +} + +static void lightTriangleDarkSmoothFog(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + lightVertexDarkSmoothFog(c, v0); + lightVertexDarkSmoothFog(c, v1); + lightVertexDarkSmoothFog(c, v2); +} + +static void lightTriangleDarkFlatFog(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + lightVertexDarkFlatFog(c, v0); + lightVertexDarkFlatFog(c, v1); + lightVertexDarkSmoothFog(c, v2); + // configure the rasterizer here, before we clip + c->rasterizer.procs.color4xv(c, v2->color.v); +} + +static void lightTriangleSmoothFog(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + lightVertexSmoothFog(c, v0); + lightVertexSmoothFog(c, v1); + lightVertexSmoothFog(c, v2); +} + +static void lightTriangleFlatFog(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + lightVertexDarkFlatFog(c, v0); + lightVertexDarkFlatFog(c, v1); + lightVertexSmoothFog(c, v2); + // configure the rasterizer here, before we clip + c->rasterizer.procs.color4xv(c, v2->color.v); +} + + + +typedef void (*light_primitive_t)(ogles_context_t*, + vertex_t*, vertex_t*, vertex_t*); + +// fog 0x4, light 0x2, smooth 0x1 +static const light_primitive_t lightPrimitive[8] = { + lightTriangleDarkFlat, // no fog | dark | flat + lightTriangleDarkSmooth, // no fog | dark | smooth + lightTriangleFlat, // no fog | light | flat + lightTriangleSmooth, // no fog | light | smooth + lightTriangleDarkFlatFog, // fog | dark | flat + lightTriangleDarkSmoothFog, // fog | dark | smooth + lightTriangleFlatFog, // fog | light | flat + lightTriangleSmoothFog // fog | light | smooth +}; + +void ogles_validate_primitives(ogles_context_t* c) +{ + const uint32_t enables = c->rasterizer.state.enables; + + // set up the lighting/shading/smoothing/fogging function + int index = enables & GGL_ENABLE_SMOOTH ? 0x1 : 0; + index |= c->lighting.enable ? 0x2 : 0; + index |= enables & GGL_ENABLE_FOG ? 0x4 : 0; + c->lighting.lightTriangle = lightPrimitive[index]; + + // set up the primitive renderers + if (ggl_likely(c->arrays.vertex.enable)) { + c->prims.renderPoint = primitive_point; + c->prims.renderLine = primitive_line; + c->prims.renderTriangle = primitive_clip_triangle; + } else { + c->prims.renderPoint = primitive_nop_point; + c->prims.renderLine = primitive_nop_line; + c->prims.renderTriangle = primitive_nop_triangle; + } +} + +// ---------------------------------------------------------------------------- + +void compute_iterators_t::initTriangle( + vertex_t const* v0, vertex_t const* v1, vertex_t const* v2) +{ + m_dx01 = v1->window.x - v0->window.x; + m_dy10 = v0->window.y - v1->window.y; + m_dx20 = v0->window.x - v2->window.x; + m_dy02 = v2->window.y - v0->window.y; + m_area = m_dx01*m_dy02 + (-m_dy10)*m_dx20; +} + +void compute_iterators_t::initLine( + vertex_t const* v0, vertex_t const* v1) +{ + m_dx01 = m_dy02 = v1->window.x - v0->window.x; + m_dy10 = m_dx20 = v0->window.y - v1->window.y; + m_area = m_dx01*m_dy02 + (-m_dy10)*m_dx20; +} + +void compute_iterators_t::initLerp(vertex_t const* v0, uint32_t enables) +{ + m_x0 = v0->window.x; + m_y0 = v0->window.y; + const GGLcoord area = (m_area + TRI_HALF) >> TRI_FRACTION_BITS; + const GGLcoord minArea = 2; // cannot be inverted + // triangles with an area smaller than 1.0 are not smooth-shaded + + int q=0, s=0, d=0; + if (abs(area) >= minArea) { + // Here we do some voodoo magic, to compute a suitable scale + // factor for deltas/area: + + // First compute the 1/area with full 32-bits precision, + // gglRecipQNormalized returns a number [-0.5, 0.5[ and an exponent. + d = gglRecipQNormalized(area, &q); + + // Then compute the minimum left-shift to not overflow the muls + // below. + s = 32 - gglClz(abs(m_dy02)|abs(m_dy10)|abs(m_dx01)|abs(m_dx20)); + + // We'll keep 16-bits of precision for deltas/area. So we need + // to shift everything left an extra 15 bits. + s += 15; + + // make sure all final shifts are not > 32, because gglMulx + // can't handle it. + if (s < q) s = q; + if (s > 32) { + d >>= 32-s; + s = 32; + } + } + + m_dx01 = gglMulx(m_dx01, d, s); + m_dy10 = gglMulx(m_dy10, d, s); + m_dx20 = gglMulx(m_dx20, d, s); + m_dy02 = gglMulx(m_dy02, d, s); + m_area_scale = 32 + q - s; + m_scale = 0; + + if (enables & GGL_ENABLE_TMUS) { + const int A = gglClz(abs(m_dy02)|abs(m_dy10)|abs(m_dx01)|abs(m_dx20)); + const int B = gglClz(abs(m_x0)|abs(m_y0)); + m_scale = max(0, 32 - (A + 16)) + + max(0, 32 - (B + TRI_FRACTION_BITS)) + 1; + } +} + +int compute_iterators_t::iteratorsScale(GGLfixed* it, + int32_t c0, int32_t c1, int32_t c2) const +{ + int32_t dc01 = c1 - c0; + int32_t dc02 = c2 - c0; + const int A = gglClz(abs(c0)); + const int B = gglClz(abs(dc01)|abs(dc02)); + const int scale = min(A, B - m_scale) - 2; + if (scale >= 0) { + c0 <<= scale; + dc01 <<= scale; + dc02 <<= scale; + } else { + c0 >>= -scale; + dc01 >>= -scale; + dc02 >>= -scale; + } + const int s = m_area_scale; + int32_t dcdx = gglMulAddx(dc01, m_dy02, gglMulx(dc02, m_dy10, s), s); + int32_t dcdy = gglMulAddx(dc02, m_dx01, gglMulx(dc01, m_dx20, s), s); + int32_t c = c0 - (gglMulAddx(dcdx, m_x0, + gglMulx(dcdy, m_y0, TRI_FRACTION_BITS), TRI_FRACTION_BITS)); + it[0] = c; + it[1] = dcdx; + it[2] = dcdy; + return scale; +} + +void compute_iterators_t::iterators1616(GGLfixed* it, + GGLfixed c0, GGLfixed c1, GGLfixed c2) const +{ + const GGLfixed dc01 = c1 - c0; + const GGLfixed dc02 = c2 - c0; + // 16.16 x 16.16 == 32.32 --> 16.16 + const int s = m_area_scale; + int32_t dcdx = gglMulAddx(dc01, m_dy02, gglMulx(dc02, m_dy10, s), s); + int32_t dcdy = gglMulAddx(dc02, m_dx01, gglMulx(dc01, m_dx20, s), s); + int32_t c = c0 - (gglMulAddx(dcdx, m_x0, + gglMulx(dcdy, m_y0, TRI_FRACTION_BITS), TRI_FRACTION_BITS)); + it[0] = c; + it[1] = dcdx; + it[2] = dcdy; +} + +void compute_iterators_t::iterators0032(int64_t* it, + int32_t c0, int32_t c1, int32_t c2) const +{ + const int s = m_area_scale - 16; + int32_t dc01 = (c1 - c0)>>s; + int32_t dc02 = (c2 - c0)>>s; + // 16.16 x 16.16 == 32.32 + int64_t dcdx = gglMulii(dc01, m_dy02) + gglMulii(dc02, m_dy10); + int64_t dcdy = gglMulii(dc02, m_dx01) + gglMulii(dc01, m_dx20); + it[ 0] = (c0<<16) - ((dcdx*m_x0 + dcdy*m_y0)>>4); + it[ 1] = dcdx; + it[ 2] = dcdy; +} + +#if defined(__arm__) && !defined(__thumb__) +inline void compute_iterators_t::iterators0032(int32_t* it, + int32_t c0, int32_t c1, int32_t c2) const +{ + ::iterators0032(this, it, c0, c1, c2); +} +#else +void compute_iterators_t::iterators0032(int32_t* it, + int32_t c0, int32_t c1, int32_t c2) const +{ + int64_t it64[3]; + iterators0032(it, c0, c1, c2); + it[0] = it64[0]; + it[1] = it64[1]; + it[2] = it64[2]; +} +#endif + +// ---------------------------------------------------------------------------- + +static inline int32_t clampZ(GLfixed z) CONST; +int32_t clampZ(GLfixed z) { + z = (z & ~(z>>31)); + if (z >= 0x10000) + z = 0xFFFF; + return z; +} + +static __attribute__((noinline)) +void fetch_texcoord_impl(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + vertex_t* const vtx[3] = { v0, v1, v2 }; + array_t const * const texcoordArray = c->arrays.texture; + + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + if (!(c->rasterizer.state.texture[i].enable)) + continue; + + for (int j=0 ; j<3 ; j++) { + vertex_t* const v = vtx[j]; + if (v->flags & vertex_t::TT) + continue; + + // NOTE: here we could compute automatic texgen + // such as sphere/cube maps, instead of fetching them + // from the textcoord array. + + vec4_t& coords = v->texture[i]; + const GLubyte* tp = texcoordArray[i].element( + v->index & vertex_cache_t::INDEX_MASK); + texcoordArray[i].fetch(c, coords.v, tp); + + // transform texture coordinates... + coords.Q = 0x10000; + const transform_t& tr = c->transforms.texture[i].transform; + if (ggl_unlikely(tr.ops)) { + c->arrays.tex_transform[i](&tr, &coords, &coords); + } + + // divide by Q + const GGLfixed q = coords.Q; + if (ggl_unlikely(q != 0x10000)) { + const int32_t qinv = gglRecip28(q); + coords.S = gglMulx(coords.S, qinv, 28); + coords.T = gglMulx(coords.T, qinv, 28); + } + } + } + v0->flags |= vertex_t::TT; + v1->flags |= vertex_t::TT; + v2->flags |= vertex_t::TT; +} + +inline void fetch_texcoord(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + const uint32_t enables = c->rasterizer.state.enables; + if (!(enables & GGL_ENABLE_TMUS)) + return; + + // Fetch & transform texture coordinates... + if (ggl_likely(v0->flags & v1->flags & v2->flags & vertex_t::TT)) { + // already done for all three vertices, bail... + return; + } + fetch_texcoord_impl(c, v0, v1, v2); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Point +#endif + +void primitive_nop_point(ogles_context_t*, vertex_t*) { +} + +void primitive_point(ogles_context_t* c, vertex_t* v) +{ + // lighting & clamping... + const uint32_t enables = c->rasterizer.state.enables; + + if (ggl_unlikely(!(v->flags & vertex_t::LIT))) { + if (c->lighting.enable) { + c->lighting.lightVertex(c, v); + } else { + v->flags |= vertex_t::LIT; + const GLvoid* cp = c->arrays.color.element( + v->index & vertex_cache_t::INDEX_MASK); + c->arrays.color.fetch(c, v->color.v, cp); + } + if (enables & GGL_ENABLE_FOG) { + v->fog = c->fog.fog(c, v->eye.z); + } + } + + // XXX: we don't need to do that each-time + // if color array and lighting not enabled + c->rasterizer.procs.color4xv(c, v->color.v); + + // XXX: look into ES point-sprite extension + if (enables & GGL_ENABLE_TMUS) { + fetch_texcoord(c, v,v,v); + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + if (!c->rasterizer.state.texture[i].enable) + continue; + int32_t itt[8]; + itt[1] = itt[2] = itt[4] = itt[5] = 0; + itt[6] = itt[7] = 16; // XXX: check that + if (c->rasterizer.state.texture[i].s_wrap == GGL_CLAMP) { + int width = c->textures.tmu[i].texture->surface.width; + itt[0] = v->texture[i].S * width; + itt[6] = 0; + } + if (c->rasterizer.state.texture[i].t_wrap == GGL_CLAMP) { + int height = c->textures.tmu[i].texture->surface.height; + itt[3] = v->texture[i].T * height; + itt[7] = 0; + } + c->rasterizer.procs.texCoordGradScale8xv(c, i, itt); + } + } + + if (enables & GGL_ENABLE_DEPTH_TEST) { + int32_t itz[3]; + itz[0] = clampZ(v->window.z) * 0x00010001; + itz[1] = itz[2] = 0; + c->rasterizer.procs.zGrad3xv(c, itz); + } + + if (enables & GGL_ENABLE_FOG) { + GLfixed itf[3]; + itf[0] = v->fog; + itf[1] = itf[2] = 0; + c->rasterizer.procs.fogGrad3xv(c, itf); + } + + // Render our point... + c->rasterizer.procs.pointx(c, v->window.v, c->point.size); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Line +#endif + +void primitive_nop_line(ogles_context_t*, vertex_t*, vertex_t*) { +} + +void primitive_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1) +{ + // get texture coordinates + fetch_texcoord(c, v0, v1, v1); + + // light/shade the vertices first (they're copied below) + c->lighting.lightTriangle(c, v0, v1, v1); + + // clip the line if needed + if (ggl_unlikely((v0->flags | v1->flags) & vertex_t::CLIP_ALL)) { + unsigned int count = clip_line(c, v0, v1); + if (ggl_unlikely(count == 0)) + return; + } + + // compute iterators... + const uint32_t enables = c->rasterizer.state.enables; + const uint32_t mask = GGL_ENABLE_TMUS | + GGL_ENABLE_SMOOTH | + GGL_ENABLE_W | + GGL_ENABLE_FOG | + GGL_ENABLE_DEPTH_TEST; + + if (ggl_unlikely(enables & mask)) { + c->lerp.initLine(v0, v1); + lerp_triangle(c, v0, v1, v0); + } + + // render our line + c->rasterizer.procs.linex(c, v0->window.v, v1->window.v, c->line.width); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Triangle +#endif + +void primitive_nop_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) { +} + +void primitive_clip_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + uint32_t cc = (v0->flags | v1->flags | v2->flags) & vertex_t::CLIP_ALL; + if (ggl_likely(!cc)) { + // code below must be as optimized as possible, this is the + // common code path. + + // This triangle is not clipped, test if it's culled + // unclipped triangle... + c->lerp.initTriangle(v0, v1, v2); + if (cull_triangle(c, v0, v1, v2)) + return; // culled! + + // Fetch all texture coordinates if needed + fetch_texcoord(c, v0, v1, v2); + + // light (or shade) our triangle! + c->lighting.lightTriangle(c, v0, v1, v2); + + triangle(c, v0, v1, v2); + return; + } + + // The assumption here is that we're not going to clip very often, + // and even more rarely will we clip a triangle that ends up + // being culled out. So it's okay to light the vertices here, even though + // in a few cases we won't render the triangle (if culled). + + // Fetch texture coordinates... + fetch_texcoord(c, v0, v1, v2); + + // light (or shade) our triangle! + c->lighting.lightTriangle(c, v0, v1, v2); + + clip_triangle(c, v0, v1, v2); +} + +// ----------------------------------------------------------------------- + +void triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + // compute iterators... + const uint32_t enables = c->rasterizer.state.enables; + const uint32_t mask = GGL_ENABLE_TMUS | + GGL_ENABLE_SMOOTH | + GGL_ENABLE_W | + GGL_ENABLE_FOG | + GGL_ENABLE_DEPTH_TEST; + + if (ggl_likely(enables & mask)) + lerp_triangle(c, v0, v1, v2); + + c->rasterizer.procs.trianglex(c, v0->window.v, v1->window.v, v2->window.v); +} + +void lerp_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + const uint32_t enables = c->rasterizer.state.enables; + c->lerp.initLerp(v0, enables); + + // set up texture iterators + if (enables & GGL_ENABLE_TMUS) { + if (enables & GGL_ENABLE_W) { + lerp_texcoords_w(c, v0, v1, v2); + } else { + lerp_texcoords(c, v0, v1, v2); + } + } + + // set up the color iterators + const compute_iterators_t& lerp = c->lerp; + if (enables & GGL_ENABLE_SMOOTH) { + GLfixed itc[12]; + for (int i=0 ; i<4 ; i++) { + const GGLcolor c0 = v0->color.v[i] * 255; + const GGLcolor c1 = v1->color.v[i] * 255; + const GGLcolor c2 = v2->color.v[i] * 255; + lerp.iterators1616(&itc[i*3], c0, c1, c2); + } + c->rasterizer.procs.colorGrad12xv(c, itc); + } + + if (enables & GGL_ENABLE_DEPTH_TEST) { + int32_t itz[3]; + const int32_t v0z = clampZ(v0->window.z); + const int32_t v1z = clampZ(v1->window.z); + const int32_t v2z = clampZ(v2->window.z); + if (ggl_unlikely(c->polygonOffset.enable)) { + const int32_t units = (c->polygonOffset.units << 16); + const GLfixed factor = c->polygonOffset.factor; + if (factor) { + int64_t itz64[3]; + lerp.iterators0032(itz64, v0z, v1z, v2z); + int64_t maxDepthSlope = max(itz64[1], itz64[2]); + itz[0] = uint32_t(itz64[0]) + + uint32_t((maxDepthSlope*factor)>>16) + units; + itz[1] = uint32_t(itz64[1]); + itz[2] = uint32_t(itz64[2]); + } else { + lerp.iterators0032(itz, v0z, v1z, v2z); + itz[0] += units; + } + } else { + lerp.iterators0032(itz, v0z, v1z, v2z); + } + c->rasterizer.procs.zGrad3xv(c, itz); + } + + if (ggl_unlikely(enables & GGL_ENABLE_FOG)) { + GLfixed itf[3]; + lerp.iterators1616(itf, v0->fog, v1->fog, v2->fog); + c->rasterizer.procs.fogGrad3xv(c, itf); + } +} + + +static inline +int compute_lod(ogles_context_t* c, int i, + int32_t s0, int32_t t0, int32_t s1, int32_t t1, int32_t s2, int32_t t2) +{ + // Compute mipmap level / primitive + // rho = sqrt( texelArea / area ) + // lod = log2( rho ) + // lod = log2( texelArea / area ) / 2 + // lod = (log2( texelArea ) - log2( area )) / 2 + const compute_iterators_t& lerp = c->lerp; + const GGLcoord area = abs(lerp.area()); + const int w = c->textures.tmu[i].texture->surface.width; + const int h = c->textures.tmu[i].texture->surface.height; + const int shift = 16 + (16 - TRI_FRACTION_BITS); + int32_t texelArea = abs( gglMulx(s1-s0, t2-t0, shift) - + gglMulx(s2-s0, t1-t0, shift) )*w*h; + int log2TArea = (32-TRI_FRACTION_BITS -1) - gglClz(texelArea); + int log2Area = (32-TRI_FRACTION_BITS*2-1) - gglClz(area); + int lod = (log2TArea - log2Area + 1) >> 1; + return lod; +} + +void lerp_texcoords(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + const compute_iterators_t& lerp = c->lerp; + int32_t itt[8] __attribute__((aligned(16))); + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + const texture_t& tmu = c->rasterizer.state.texture[i]; + if (!tmu.enable) + continue; + + // compute the jacobians using block floating-point + int32_t s0 = v0->texture[i].S; + int32_t t0 = v0->texture[i].T; + int32_t s1 = v1->texture[i].S; + int32_t t1 = v1->texture[i].T; + int32_t s2 = v2->texture[i].S; + int32_t t2 = v2->texture[i].T; + + const GLenum min_filter = c->textures.tmu[i].texture->min_filter; + if (ggl_unlikely(min_filter >= GL_NEAREST_MIPMAP_NEAREST)) { + int lod = compute_lod(c, i, s0, t0, s1, t1, s2, t2); + c->rasterizer.procs.bindTextureLod(c, i, + &c->textures.tmu[i].texture->mip(lod)); + } + + // premultiply (s,t) when clampling + if (tmu.s_wrap == GGL_CLAMP) { + const int width = tmu.surface.width; + s0 *= width; + s1 *= width; + s2 *= width; + } + if (tmu.t_wrap == GGL_CLAMP) { + const int height = tmu.surface.height; + t0 *= height; + t1 *= height; + t2 *= height; + } + itt[6] = -lerp.iteratorsScale(itt+0, s0, s1, s2); + itt[7] = -lerp.iteratorsScale(itt+3, t0, t1, t2); + c->rasterizer.procs.texCoordGradScale8xv(c, i, itt); + } +} + +void lerp_texcoords_w(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + const compute_iterators_t& lerp = c->lerp; + int32_t itt[8] __attribute__((aligned(16))); + int32_t itw[3]; + + // compute W's scale to 2.30 + int32_t w0 = v0->window.w; + int32_t w1 = v1->window.w; + int32_t w2 = v2->window.w; + int wscale = 32 - gglClz(w0|w1|w2); + + // compute the jacobian using block floating-point + int sc = lerp.iteratorsScale(itw, w0, w1, w2); + sc += wscale - 16; + c->rasterizer.procs.wGrad3xv(c, itw); + + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + const texture_t& tmu = c->rasterizer.state.texture[i]; + if (!tmu.enable) + continue; + + // compute the jacobians using block floating-point + int32_t s0 = v0->texture[i].S; + int32_t t0 = v0->texture[i].T; + int32_t s1 = v1->texture[i].S; + int32_t t1 = v1->texture[i].T; + int32_t s2 = v2->texture[i].S; + int32_t t2 = v2->texture[i].T; + + const GLenum min_filter = c->textures.tmu[i].texture->min_filter; + if (ggl_unlikely(min_filter >= GL_NEAREST_MIPMAP_NEAREST)) { + int lod = compute_lod(c, i, s0, t0, s1, t1, s2, t2); + c->rasterizer.procs.bindTextureLod(c, i, + &c->textures.tmu[i].texture->mip(lod)); + } + + // premultiply (s,t) when clampling + if (tmu.s_wrap == GGL_CLAMP) { + const int width = tmu.surface.width; + s0 *= width; + s1 *= width; + s2 *= width; + } + if (tmu.t_wrap == GGL_CLAMP) { + const int height = tmu.surface.height; + t0 *= height; + t1 *= height; + t2 *= height; + } + + s0 = gglMulx(s0, w0, wscale); + t0 = gglMulx(t0, w0, wscale); + s1 = gglMulx(s1, w1, wscale); + t1 = gglMulx(t1, w1, wscale); + s2 = gglMulx(s2, w2, wscale); + t2 = gglMulx(t2, w2, wscale); + + itt[6] = sc - lerp.iteratorsScale(itt+0, s0, s1, s2); + itt[7] = sc - lerp.iteratorsScale(itt+3, t0, t1, t2); + c->rasterizer.procs.texCoordGradScale8xv(c, i, itt); + } +} + + +static inline +bool cull_triangle(ogles_context_t* c, vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + if (ggl_likely(c->cull.enable)) { + const GLenum winding = (c->lerp.area() > 0) ? GL_CW : GL_CCW; + const GLenum face = (winding == c->cull.frontFace) ? GL_FRONT : GL_BACK; + if (face == c->cull.cullFace) + return true; // culled! + } + return false; +} + +static inline +GLfixed frustumPlaneDist(int plane, const vec4_t& s) +{ + const GLfixed d = s.v[ plane >> 1 ]; + return ((plane & 1) ? (s.w - d) : (s.w + d)); +} + +static inline +int32_t clipDivide(GLfixed a, GLfixed b) { + // returns a 4.28 fixed-point + return gglMulDivi(1LU<<28, a, b); +} + +void clip_triangle(ogles_context_t* c, + vertex_t* v0, vertex_t* v1, vertex_t* v2) +{ + uint32_t all_cc = (v0->flags | v1->flags | v2->flags) & vertex_t::CLIP_ALL; + + vertex_t *p0, *p1, *p2; + const int MAX_CLIPPING_PLANES = 6 + OGLES_MAX_CLIP_PLANES; + const int MAX_VERTICES = 3; + + // Temporary buffer to hold the new vertices. Each plane can add up to + // two new vertices (because the polygon is convex). + // We need one extra element, to handle an overflow case when + // the polygon degenerates into something non convex. + vertex_t buffer[MAX_CLIPPING_PLANES * 2 + 1]; // ~3KB + vertex_t* buf = buffer; + + // original list of vertices (polygon to clip, in fact this + // function works with an arbitrary polygon). + vertex_t* in[3] = { v0, v1, v2 }; + + // output lists (we need 2, which we use back and forth) + // (maximum outpout list's size is MAX_CLIPPING_PLANES + MAX_VERTICES) + // 2 more elements for overflow when non convex polygons. + vertex_t* out[2][MAX_CLIPPING_PLANES + MAX_VERTICES + 2]; + unsigned int outi = 0; + + // current input list + vertex_t** ivl = in; + + // 3 input vertices, 0 in the output list, first plane + unsigned int ic = 3; + + // User clip-planes first, the clipping is always done in eye-coordinate + // this is basically the same algorithm than for the view-volume + // clipping, except for the computation of the distance (vertex, plane) + // and the fact that we need to compute the eye-coordinates of each + // new vertex we create. + + if (ggl_unlikely(all_cc & vertex_t::USER_CLIP_ALL)) + { + unsigned int plane = 0; + uint32_t cc = (all_cc & vertex_t::USER_CLIP_ALL) >> 8; + do { + if (cc & 1) { + // pointers to our output list (head and current) + vertex_t** const ovl = &out[outi][0]; + vertex_t** output = ovl; + unsigned int oc = 0; + unsigned int sentinel = 0; + // previous vertex, compute distance to the plane + vertex_t* s = ivl[ic-1]; + const vec4_t& equation = c->clipPlanes.plane[plane].equation; + GLfixed sd = dot4(equation.v, s->eye.v); + // clip each vertex against this plane... + for (unsigned int i=0 ; i<ic ; i++) { + vertex_t* p = ivl[i]; + const GLfixed pd = dot4(equation.v, p->eye.v); + if (sd >= 0) { + if (pd >= 0) { + // both inside + *output++ = p; + oc++; + } else { + // s inside, p outside (exiting) + const GLfixed t = clipDivide(sd, sd-pd); + c->arrays.clipEye(c, buf, t, p, s); + *output++ = buf++; + oc++; + if (++sentinel >= 3) + return; // non-convex polygon! + } + } else { + if (pd >= 0) { + // s outside (entering) + if (pd) { + const GLfixed t = clipDivide(pd, pd-sd); + c->arrays.clipEye(c, buf, t, s, p); + *output++ = buf++; + oc++; + if (++sentinel >= 3) + return; // non-convex polygon! + } + *output++ = p; + oc++; + } else { + // both outside + } + } + s = p; + sd = pd; + } + // output list become the new input list + if (oc<3) + return; // less than 3 vertices left? we're done! + ivl = ovl; + ic = oc; + outi = 1-outi; + } + cc >>= 1; + plane++; + } while (cc); + } + + // frustum clip-planes + if (all_cc & vertex_t::FRUSTUM_CLIP_ALL) + { + unsigned int plane = 0; + uint32_t cc = all_cc & vertex_t::FRUSTUM_CLIP_ALL; + do { + if (cc & 1) { + // pointers to our output list (head and current) + vertex_t** const ovl = &out[outi][0]; + vertex_t** output = ovl; + unsigned int oc = 0; + unsigned int sentinel = 0; + // previous vertex, compute distance to the plane + vertex_t* s = ivl[ic-1]; + GLfixed sd = frustumPlaneDist(plane, s->clip); + // clip each vertex against this plane... + for (unsigned int i=0 ; i<ic ; i++) { + vertex_t* p = ivl[i]; + const GLfixed pd = frustumPlaneDist(plane, p->clip); + if (sd >= 0) { + if (pd >= 0) { + // both inside + *output++ = p; + oc++; + } else { + // s inside, p outside (exiting) + const GLfixed t = clipDivide(sd, sd-pd); + c->arrays.clipVertex(c, buf, t, p, s); + *output++ = buf++; + oc++; + if (++sentinel >= 3) + return; // non-convex polygon! + } + } else { + if (pd >= 0) { + // s outside (entering) + if (pd) { + const GLfixed t = clipDivide(pd, pd-sd); + c->arrays.clipVertex(c, buf, t, s, p); + *output++ = buf++; + oc++; + if (++sentinel >= 3) + return; // non-convex polygon! + } + *output++ = p; + oc++; + } else { + // both outside + } + } + s = p; + sd = pd; + } + // output list become the new input list + if (oc<3) + return; // less than 3 vertices left? we're done! + ivl = ovl; + ic = oc; + outi = 1-outi; + } + cc >>= 1; + plane++; + } while (cc); + } + + // finally we can render our triangles... + p0 = ivl[0]; + p1 = ivl[1]; + for (unsigned int i=2 ; i<ic ; i++) { + p2 = ivl[i]; + c->lerp.initTriangle(p0, p1, p2); + if (cull_triangle(c, p0, p1, p2)) { + p1 = p2; + continue; // culled! + } + triangle(c, p0, p1, p2); + p1 = p2; + } +} + +unsigned int clip_line(ogles_context_t* c, vertex_t* s, vertex_t* p) +{ + const uint32_t all_cc = (s->flags | p->flags) & vertex_t::CLIP_ALL; + + if (ggl_unlikely(all_cc & vertex_t::USER_CLIP_ALL)) + { + unsigned int plane = 0; + uint32_t cc = (all_cc & vertex_t::USER_CLIP_ALL) >> 8; + do { + if (cc & 1) { + const vec4_t& equation = c->clipPlanes.plane[plane].equation; + const GLfixed sd = dot4(equation.v, s->eye.v); + const GLfixed pd = dot4(equation.v, p->eye.v); + if (sd >= 0) { + if (pd >= 0) { + // both inside + } else { + // s inside, p outside (exiting) + const GLfixed t = clipDivide(sd, sd-pd); + c->arrays.clipEye(c, p, t, p, s); + } + } else { + if (pd >= 0) { + // s outside (entering) + if (pd) { + const GLfixed t = clipDivide(pd, pd-sd); + c->arrays.clipEye(c, s, t, s, p); + } + } else { + // both outside + return 0; + } + } + } + cc >>= 1; + plane++; + } while (cc); + } + + // frustum clip-planes + if (all_cc & vertex_t::FRUSTUM_CLIP_ALL) + { + unsigned int plane = 0; + uint32_t cc = all_cc & vertex_t::FRUSTUM_CLIP_ALL; + do { + if (cc & 1) { + const GLfixed sd = frustumPlaneDist(plane, s->clip); + const GLfixed pd = frustumPlaneDist(plane, p->clip); + if (sd >= 0) { + if (pd >= 0) { + // both inside + } else { + // s inside, p outside (exiting) + const GLfixed t = clipDivide(sd, sd-pd); + c->arrays.clipVertex(c, p, t, p, s); + } + } else { + if (pd >= 0) { + // s outside (entering) + if (pd) { + const GLfixed t = clipDivide(pd, pd-sd); + c->arrays.clipVertex(c, s, t, s, p); + } + } else { + // both outside + return 0; + } + } + } + cc >>= 1; + plane++; + } while (cc); + } + + return 2; +} + + +}; // namespace android diff --git a/opengl/libagl/primitives.h b/opengl/libagl/primitives.h new file mode 100644 index 0000000..1bef604 --- /dev/null +++ b/opengl/libagl/primitives.h @@ -0,0 +1,37 @@ +/* libs/opengles/primitives.h +** +** 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. +*/ + +#ifndef ANDROID_OPENGLES_PRIMITIVES_H +#define ANDROID_OPENGLES_PRIMITIVES_H + +#include <stdint.h> +#include <stddef.h> +#include <sys/types.h> + + +namespace android { + +namespace gl { +struct ogles_context_t; +}; + +void ogles_validate_primitives(ogles_context_t* c); + +}; // namespace android + +#endif // ANDROID_OPENGLES_PRIMITIVES_H + diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp new file mode 100644 index 0000000..5cbabea --- /dev/null +++ b/opengl/libagl/state.cpp @@ -0,0 +1,586 @@ +/* libs/opengles/state.cpp +** +** 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 <stdlib.h> + +#include "context.h" +#include "fp.h" +#include "state.h" +#include "array.h" +#include "matrix.h" +#include "vertex.h" +#include "light.h" +#include "texture.h" +#include "BufferObjectManager.h" +#include "TextureObjectManager.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +static char const * const gVendorString = "Android"; +static char const * const gRendererString = "Android PixelFlinger 1.0"; +static char const * const gVersionString = "OpenGL ES-CM 1.0"; +static char const * const gExtensionsString = + "GL_OES_byte_coordinates " // OK + "GL_OES_fixed_point " // OK + "GL_OES_single_precision " // OK + "GL_OES_read_format " // OK + "GL_OES_compressed_paletted_texture " // OK + "GL_OES_draw_texture " // OK + "GL_OES_matrix_get " // OK + "GL_OES_query_matrix " // OK + // "GL_OES_point_size_array " // TODO + // "GL_OES_point_sprite " // TODO + "GL_ARB_texture_compression " // OK + "GL_ARB_texture_non_power_of_two " // OK + "GL_ANDROID_direct_texture " // OK + "GL_ANDROID_user_clip_plane " // OK + "GL_ANDROID_vertex_buffer_object " // OK + "GL_ANDROID_generate_mipmap " // OK + ; + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +ogles_context_t *ogles_init(size_t extra) +{ + void* const base = malloc(extra + sizeof(ogles_context_t) + 32); + if (!base) return 0; + + ogles_context_t *c = + (ogles_context_t *)((ptrdiff_t(base) + extra + 31) & ~0x1FL); + memset(c, 0, sizeof(ogles_context_t)); + ggl_init_context(&(c->rasterizer)); + + // XXX: this should be passed as an argument + sp<EGLSurfaceManager> smgr(new EGLSurfaceManager()); + c->surfaceManager = smgr.get(); + c->surfaceManager->incStrong(c); + + sp<EGLBufferObjectManager> bomgr(new EGLBufferObjectManager()); + c->bufferObjectManager = bomgr.get(); + c->bufferObjectManager->incStrong(c); + + ogles_init_array(c); + ogles_init_matrix(c); + ogles_init_vertex(c); + ogles_init_light(c); + ogles_init_texture(c); + + c->rasterizer.base = base; + c->point.size = TRI_ONE; + c->line.width = TRI_ONE; + + // in OpenGL, writing to the depth buffer is enabled by default. + c->rasterizer.procs.depthMask(c, 1); + + // OpenGL enables dithering by default + c->rasterizer.procs.enable(c, GL_DITHER); + + return c; +} + +void ogles_uninit(ogles_context_t* c) +{ + ogles_uninit_array(c); + ogles_uninit_matrix(c); + ogles_uninit_vertex(c); + ogles_uninit_light(c); + ogles_uninit_texture(c); + c->surfaceManager->decStrong(c); + c->bufferObjectManager->decStrong(c); + ggl_uninit_context(&(c->rasterizer)); + free(c->rasterizer.base); +} + +void _ogles_error(ogles_context_t* c, GLenum error) +{ + if (c->error == GL_NO_ERROR) + c->error = error; +} + +static bool stencilop_valid(GLenum op) { + switch (op) { + case GL_KEEP: + case GL_ZERO: + case GL_REPLACE: + case GL_INCR: + case GL_DECR: + case GL_INVERT: + return true; + } + return false; +} + +static void enable_disable(ogles_context_t* c, GLenum cap, int enabled) +{ + if ((cap >= GL_LIGHT0) && (cap<GL_LIGHT0+OGLES_MAX_LIGHTS)) { + c->lighting.lights[cap-GL_LIGHT0].enable = enabled; + c->lighting.enabledLights &= ~(1<<(cap-GL_LIGHT0)); + c->lighting.enabledLights |= (enabled<<(cap-GL_LIGHT0)); + return; + } + + switch (cap) { + case GL_POINT_SMOOTH: + c->point.smooth = enabled; + break; + case GL_LINE_SMOOTH: + c->line.smooth = enabled; + break; + case GL_POLYGON_OFFSET_FILL: + c->polygonOffset.enable = enabled; + break; + case GL_CULL_FACE: + c->cull.enable = enabled; + break; + case GL_LIGHTING: + c->lighting.enable = enabled; + break; + case GL_COLOR_MATERIAL: + c->lighting.colorMaterial.enable = enabled; + break; + case GL_NORMALIZE: + case GL_RESCALE_NORMAL: + c->transforms.rescaleNormals = enabled ? cap : 0; + // XXX: invalidate mvit + break; + + case GL_CLIP_PLANE0: + case GL_CLIP_PLANE1: + case GL_CLIP_PLANE2: + case GL_CLIP_PLANE3: + case GL_CLIP_PLANE4: + case GL_CLIP_PLANE5: + c->clipPlanes.enable &= ~(1<<(cap-GL_CLIP_PLANE0)); + c->clipPlanes.enable |= (enabled<<(cap-GL_CLIP_PLANE0)); + ogles_invalidate_perspective(c); + break; + + case GL_FOG: + case GL_DEPTH_TEST: + ogles_invalidate_perspective(c); + // fall-through... + case GL_BLEND: + case GL_SCISSOR_TEST: + case GL_ALPHA_TEST: + case GL_COLOR_LOGIC_OP: + case GL_DITHER: + case GL_STENCIL_TEST: + case GL_TEXTURE_2D: + // these need to fall through into the rasterizer + c->rasterizer.procs.enableDisable(c, cap, enabled); + break; + + case GL_MULTISAMPLE: + case GL_SAMPLE_ALPHA_TO_COVERAGE: + case GL_SAMPLE_ALPHA_TO_ONE: + case GL_SAMPLE_COVERAGE: + // not supported in this implementation + break; + + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- +using namespace android; + +#if 0 +#pragma mark - +#endif + +// These ones are super-easy, we're not supporting those features! +void glSampleCoverage(GLclampf value, GLboolean invert) { +} +void glSampleCoveragex(GLclampx value, GLboolean invert) { +} +void glStencilFunc(GLenum func, GLint ref, GLuint mask) { + ogles_context_t* c = ogles_context_t::get(); + if (func < GL_NEVER || func > GL_ALWAYS) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + // from OpenGL|ES 1.0 sepcification: + // If there is no stencil buffer, no stencil modification can occur + // and it is as if the stencil test always passes. +} + +void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass) { + ogles_context_t* c = ogles_context_t::get(); + if ((stencilop_valid(fail) & + stencilop_valid(zfail) & + stencilop_valid(zpass)) == 0) { + ogles_error(c, GL_INVALID_ENUM); + return; + } +} + +// ---------------------------------------------------------------------------- + +void glAlphaFunc(GLenum func, GLclampf ref) +{ + glAlphaFuncx(func, gglFloatToFixed(ref)); +} + +void glCullFace(GLenum mode) +{ + ogles_context_t* c = ogles_context_t::get(); + switch (mode) { + case GL_FRONT: + case GL_BACK: + case GL_FRONT_AND_BACK: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + } + c->cull.cullFace = mode; +} + +void glFrontFace(GLenum mode) +{ + ogles_context_t* c = ogles_context_t::get(); + switch (mode) { + case GL_CW: + case GL_CCW: + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->cull.frontFace = mode; +} + +void glHint(GLenum target, GLenum mode) +{ + ogles_context_t* c = ogles_context_t::get(); + switch (target) { + case GL_FOG_HINT: + case GL_GENERATE_MIPMAP_HINT: + case GL_LINE_SMOOTH_HINT: + break; + case GL_POINT_SMOOTH_HINT: + c->rasterizer.procs.enableDisable(c, + GGL_POINT_SMOOTH_NICE, mode==GL_NICEST); + break; + case GL_PERSPECTIVE_CORRECTION_HINT: + c->perspective = (mode == GL_NICEST) ? 1 : 0; + break; + default: + ogles_error(c, GL_INVALID_ENUM); + } +} + +void glEnable(GLenum cap) { + ogles_context_t* c = ogles_context_t::get(); + enable_disable(c, cap, 1); +} +void glDisable(GLenum cap) { + ogles_context_t* c = ogles_context_t::get(); + enable_disable(c, cap, 0); +} + +void glFinish() +{ // nothing to do for our software implementation +} + +void glFlush() +{ // nothing to do for our software implementation +} + +GLenum glGetError() +{ + // From OpenGL|ES 1.0 specification: + // If more than one flag has recorded an error, glGetError returns + // and clears an arbitrary error flag value. Thus, glGetError should + // always be called in a loop, until it returns GL_NO_ERROR, + // if all error flags are to be reset. + + ogles_context_t* c = ogles_context_t::get(); + if (c->error) { + const GLenum ret(c->error); + c->error = 0; + return ret; + } + + if (c->rasterizer.error) { + const GLenum ret(c->rasterizer.error); + c->rasterizer.error = 0; + return ret; + } + + return GL_NO_ERROR; +} + +const GLubyte* glGetString(GLenum string) +{ + switch (string) { + case GL_VENDOR: return (const GLubyte*)gVendorString; + case GL_RENDERER: return (const GLubyte*)gRendererString; + case GL_VERSION: return (const GLubyte*)gVersionString; + case GL_EXTENSIONS: return (const GLubyte*)gExtensionsString; + } + ogles_context_t* c = ogles_context_t::get(); + ogles_error(c, GL_INVALID_ENUM); + return 0; +} + +void glGetIntegerv(GLenum pname, GLint *params) +{ + ogles_context_t* c = ogles_context_t::get(); + switch (pname) { + case GL_ALIASED_POINT_SIZE_RANGE: + params[0] = 0; + params[1] = GGL_MAX_ALIASED_POINT_SIZE; + break; + case GL_ALIASED_LINE_WIDTH_RANGE: + params[0] = 0; + params[1] = GGL_MAX_ALIASED_POINT_SIZE; + break; + case GL_ALPHA_BITS: { + int index = c->rasterizer.state.buffers.color.format; + GGLFormat const * formats = gglGetPixelFormatTable(); + params[0] = formats[index].ah - formats[index].al; + break; + } + case GL_RED_BITS: { + int index = c->rasterizer.state.buffers.color.format; + GGLFormat const * formats = gglGetPixelFormatTable(); + params[0] = formats[index].rh - formats[index].rl; + break; + } + case GL_GREEN_BITS: { + int index = c->rasterizer.state.buffers.color.format; + GGLFormat const * formats = gglGetPixelFormatTable(); + params[0] = formats[index].gh - formats[index].gl; + break; + } + case GL_BLUE_BITS: { + int index = c->rasterizer.state.buffers.color.format; + GGLFormat const * formats = gglGetPixelFormatTable(); + params[0] = formats[index].bh - formats[index].bl; + break; + } + case GL_COMPRESSED_TEXTURE_FORMATS: + params[ 0] = GL_PALETTE4_RGB8_OES; + params[ 1] = GL_PALETTE4_RGBA8_OES; + params[ 2] = GL_PALETTE4_R5_G6_B5_OES; + params[ 3] = GL_PALETTE4_RGBA4_OES; + params[ 4] = GL_PALETTE4_RGB5_A1_OES; + params[ 5] = GL_PALETTE8_RGB8_OES; + params[ 6] = GL_PALETTE8_RGBA8_OES; + params[ 7] = GL_PALETTE8_R5_G6_B5_OES; + params[ 8] = GL_PALETTE8_RGBA4_OES; + params[ 9] = GL_PALETTE8_RGB5_A1_OES; + break; + case GL_DEPTH_BITS: + params[0] = c->rasterizer.state.buffers.depth.format ? 0 : 16; + break; + case GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES: + params[0] = GL_RGB; + break; + case GL_IMPLEMENTATION_COLOR_READ_TYPE_OES: + params[0] = GL_UNSIGNED_SHORT_5_6_5; + break; + case GL_MAX_LIGHTS: + params[0] = OGLES_MAX_LIGHTS; + break; + case GL_MAX_CLIP_PLANES: + params[0] = OGLES_MAX_CLIP_PLANES; + break; + case GL_MAX_MODELVIEW_STACK_DEPTH: + params[0] = OGLES_MODELVIEW_STACK_DEPTH; + break; + case GL_MAX_PROJECTION_STACK_DEPTH: + params[0] = OGLES_PROJECTION_STACK_DEPTH; + break; + case GL_MAX_TEXTURE_STACK_DEPTH: + params[0] = OGLES_TEXTURE_STACK_DEPTH; + break; + case GL_MAX_TEXTURE_SIZE: + params[0] = GGL_MAX_TEXTURE_SIZE; + break; + case GL_MAX_TEXTURE_UNITS: + params[0] = GGL_TEXTURE_UNIT_COUNT; + break; + case GL_MAX_VIEWPORT_DIMS: + params[0] = GGL_MAX_VIEWPORT_DIMS; + params[1] = GGL_MAX_VIEWPORT_DIMS; + break; + case GL_NUM_COMPRESSED_TEXTURE_FORMATS: + params[0] = OGLES_NUM_COMPRESSED_TEXTURE_FORMATS; + break; + case GL_SMOOTH_LINE_WIDTH_RANGE: + params[0] = 0; + params[1] = GGL_MAX_SMOOTH_LINE_WIDTH; + break; + case GL_SMOOTH_POINT_SIZE_RANGE: + params[0] = 0; + params[1] = GGL_MAX_SMOOTH_POINT_SIZE; + break; + case GL_STENCIL_BITS: + params[0] = 0; + break; + case GL_SUBPIXEL_BITS: + params[0] = GGL_SUBPIXEL_BITS; + break; + + case GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES: + memcpy( params, + c->transforms.modelview.top().elements(), + 16*sizeof(GLint)); + break; + case GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES: + memcpy( params, + c->transforms.projection.top().elements(), + 16*sizeof(GLint)); + break; + case GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES: + memcpy( params, + c->transforms.texture[c->textures.active].top().elements(), + 16*sizeof(GLint)); + break; + + default: + ogles_error(c, GL_INVALID_ENUM); + break; + } +} + +// ---------------------------------------------------------------------------- + +void glPointSize(GLfloat size) +{ + ogles_context_t* c = ogles_context_t::get(); + if (size <= 0) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->point.size = TRI_FROM_FIXED(gglFloatToFixed(size)); +} + +void glPointSizex(GLfixed size) +{ + ogles_context_t* c = ogles_context_t::get(); + if (size <= 0) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->point.size = TRI_FROM_FIXED(size); +} + +// ---------------------------------------------------------------------------- + +void glLineWidth(GLfloat width) +{ + ogles_context_t* c = ogles_context_t::get(); + if (width <= 0) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->line.width = TRI_FROM_FIXED(gglFloatToFixed(width)); +} + +void glLineWidthx(GLfixed width) +{ + ogles_context_t* c = ogles_context_t::get(); + if (width <= 0) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->line.width = TRI_FROM_FIXED(width); +} + +// ---------------------------------------------------------------------------- + +void glColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.colorMask(c, r, g, b, a); +} + +void glDepthMask(GLboolean flag) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.depthMask(c, flag); +} + +void glStencilMask(GLuint mask) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.stencilMask(c, mask); +} + +void glDepthFunc(GLenum func) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.depthFunc(c, func); +} + +void glLogicOp(GLenum opcode) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.logicOp(c, opcode); +} + +void glAlphaFuncx(GLenum func, GLclampx ref) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.alphaFuncx(c, func, ref); +} + +void glBlendFunc(GLenum sfactor, GLenum dfactor) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.blendFunc(c, sfactor, dfactor); +} + +void glClear(GLbitfield mask) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.clear(c, mask); +} + +void glClearColorx(GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.clearColorx(c, red, green, blue, alpha); +} + +void glClearColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a) +{ + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.clearColorx(c, + gglFloatToFixed(r), + gglFloatToFixed(g), + gglFloatToFixed(b), + gglFloatToFixed(a)); +} + +void glClearDepthx(GLclampx depth) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.clearDepthx(c, depth); +} + +void glClearDepthf(GLclampf depth) +{ + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.clearDepthx(c, gglFloatToFixed(depth)); +} + +void glClearStencil(GLint s) { + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.clearStencil(c, s); +} diff --git a/opengl/libagl/state.h b/opengl/libagl/state.h new file mode 100644 index 0000000..55a5ccb --- /dev/null +++ b/opengl/libagl/state.h @@ -0,0 +1,54 @@ +/* libs/opengles/state.h +** +** 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. +*/ + +#ifndef ANDROID_OPENGLES_STATE_H +#define ANDROID_OPENGLES_STATE_H + +#include <stdint.h> +#include <stddef.h> +#include <sys/types.h> + +#include <private/pixelflinger/ggl_context.h> + +#include <GLES/gl.h> + +#include <stdio.h> + +namespace android { + +ogles_context_t *ogles_init(size_t extra); +void ogles_uninit(ogles_context_t* c); +void _ogles_error(ogles_context_t* c, GLenum error); + +#ifndef TRACE_GL_ERRORS +#define TRACE_GL_ERRORS 0 +#endif + +#if TRACE_GL_ERRORS +#define ogles_error(c, error) \ +do { \ + printf("ogles_error at file %s line %d\n", __FILE__, __LINE__); \ + _ogles_error(c, error); \ +} while (0) +#else /* !TRACE_GL_ERRORS */ +#define ogles_error(c, error) _ogles_error((c), (error)) +#endif + +}; // namespace android + +#endif // ANDROID_OPENGLES_STATE_H + diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp new file mode 100644 index 0000000..b6f534b --- /dev/null +++ b/opengl/libagl/texture.cpp @@ -0,0 +1,1421 @@ +/* libs/opengles/texture.cpp +** +** 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 "fp.h" +#include "state.h" +#include "texture.h" +#include "TextureObjectManager.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +static void bindTextureTmu( + ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex); + +static __attribute__((noinline)) +void generateMipmap(ogles_context_t* c, GLint level); + +// ---------------------------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Init +#endif + +void ogles_init_texture(ogles_context_t* c) +{ + c->textures.packAlignment = 4; + c->textures.unpackAlignment = 4; + + // each context has a default named (0) texture (not shared) + c->textures.defaultTexture = new EGLTextureObject(); + c->textures.defaultTexture->incStrong(c); + + // bind the default texture to each texture unit + for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + bindTextureTmu(c, i, 0, c->textures.defaultTexture); + memset(c->current.texture[i].v, 0, sizeof(vec4_t)); + c->current.texture[i].Q = 0x10000; + } +} + +void ogles_uninit_texture(ogles_context_t* c) +{ + if (c->textures.ggl) + gglUninit(c->textures.ggl); + c->textures.defaultTexture->decStrong(c); + for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + if (c->textures.tmu[i].texture) + c->textures.tmu[i].texture->decStrong(c); + } +} + +static __attribute__((noinline)) +void validate_tmu(ogles_context_t* c, int i) +{ + texture_unit_t& u(c->textures.tmu[i]); + if (u.dirty) { + u.dirty = 0; + c->rasterizer.procs.activeTexture(c, i); + c->rasterizer.procs.bindTexture(c, &(u.texture->surface)); + c->rasterizer.procs.texGeni(c, GGL_S, + GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC); + c->rasterizer.procs.texGeni(c, GGL_T, + GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC); + c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, + GGL_TEXTURE_WRAP_S, u.texture->wraps); + c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, + GGL_TEXTURE_WRAP_T, u.texture->wrapt); + c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, + GGL_TEXTURE_MIN_FILTER, u.texture->min_filter); + c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, + GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter); + + // disable this texture unit if it's not complete + if (!u.texture->isComplete()) { + c->rasterizer.procs.disable(c, GGL_TEXTURE_2D); + } + } +} + +void ogles_validate_texture_impl(ogles_context_t* c) +{ + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + if (c->rasterizer.state.texture[i].enable) + validate_tmu(c, i); + } + c->rasterizer.procs.activeTexture(c, c->textures.active); +} + +static +void invalidate_texture(ogles_context_t* c, int tmu, uint8_t flags = 0xFF) { + c->textures.tmu[tmu].dirty = flags; +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Format conversion +#endif + +static uint32_t gl2format_table[6][4] = { + // BYTE, 565, 4444, 5551 + { GGL_PIXEL_FORMAT_A_8, + 0, 0, 0 }, // GL_ALPHA + { GGL_PIXEL_FORMAT_RGB_888, + GGL_PIXEL_FORMAT_RGB_565, + 0, 0 }, // GL_RGB + { GGL_PIXEL_FORMAT_RGBA_8888, + 0, + GGL_PIXEL_FORMAT_RGBA_4444, + GGL_PIXEL_FORMAT_RGBA_5551 }, // GL_RGBA + { GGL_PIXEL_FORMAT_L_8, + 0, 0, 0 }, // GL_LUMINANCE + { GGL_PIXEL_FORMAT_LA_88, + 0, 0, 0 }, // GL_LUMINANCE_ALPHA +}; + +static int32_t convertGLPixelFormat(GLint format, GLenum type) +{ + int32_t fi = -1; + int32_t ti = -1; + switch (format) { + case GL_ALPHA: fi = 0; break; + case GL_RGB: fi = 1; break; + case GL_RGBA: fi = 2; break; + case GL_LUMINANCE: fi = 3; break; + case GL_LUMINANCE_ALPHA: fi = 4; break; + } + switch (type) { + case GL_UNSIGNED_BYTE: ti = 0; break; + case GL_UNSIGNED_SHORT_5_6_5: ti = 1; break; + case GL_UNSIGNED_SHORT_4_4_4_4: ti = 2; break; + case GL_UNSIGNED_SHORT_5_5_5_1: ti = 3; break; + } + if (fi==-1 || ti==-1) + return 0; + return gl2format_table[fi][ti]; +} + +// ---------------------------------------------------------------------------- + +static GLenum validFormatType(ogles_context_t* c, GLenum format, GLenum type) +{ + GLenum error = 0; + if (format<GL_ALPHA || format>GL_LUMINANCE_ALPHA) { + error = GL_INVALID_ENUM; + } + if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT_4_4_4_4 && + type != GL_UNSIGNED_SHORT_5_5_5_1 && type != GL_UNSIGNED_SHORT_5_6_5) { + error = GL_INVALID_ENUM; + } + if (type == GL_UNSIGNED_SHORT_5_6_5 && format != GL_RGB) { + error = GL_INVALID_OPERATION; + } + if ((type == GL_UNSIGNED_SHORT_4_4_4_4 || + type == GL_UNSIGNED_SHORT_5_5_5_1) && format != GL_RGBA) { + error = GL_INVALID_OPERATION; + } + if (error) { + ogles_error(c, error); + } + return error; +} + +// ---------------------------------------------------------------------------- + +GGLContext* getRasterizer(ogles_context_t* c) +{ + GGLContext* ggl = c->textures.ggl; + if (ggl_unlikely(!ggl)) { + // this is quite heavy the first time... + gglInit(&ggl); + if (!ggl) { + return 0; + } + GGLfixed colors[4] = { 0, 0, 0, 0x10000 }; + c->textures.ggl = ggl; + ggl->activeTexture(ggl, 0); + ggl->enable(ggl, GGL_TEXTURE_2D); + ggl->texEnvi(ggl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); + ggl->disable(ggl, GGL_DITHER); + ggl->shadeModel(ggl, GGL_FLAT); + ggl->color4xv(ggl, colors); + } + return ggl; +} + +static __attribute__((noinline)) +int copyPixels( + ogles_context_t* c, + const GGLSurface& dst, + GLint xoffset, GLint yoffset, + const GGLSurface& src, + GLint x, GLint y, GLsizei w, GLsizei h) +{ + if ((dst.format == src.format) && + (dst.stride == src.stride) && + (dst.width == src.width) && + (dst.height == src.height) && + (dst.stride > 0) && + ((x|y) == 0) && + ((xoffset|yoffset) == 0)) + { + // this is a common case... + const GGLFormat& pixelFormat(c->rasterizer.formats[src.format]); + const size_t size = src.height * src.stride * pixelFormat.size; + memcpy(dst.data, src.data, size); + return 0; + } + + // use pixel-flinger to handle all the conversions + GGLContext* ggl = getRasterizer(c); + if (!ggl) { + // the only reason this would fail is because we ran out of memory + return GL_OUT_OF_MEMORY; + } + + ggl->colorBuffer(ggl, &dst); + ggl->bindTexture(ggl, &src); + ggl->texCoord2i(ggl, x-xoffset, y-yoffset); + ggl->recti(ggl, xoffset, yoffset, xoffset+w, yoffset+h); + return 0; +} + +// ---------------------------------------------------------------------------- + +static __attribute__((noinline)) +sp<EGLTextureObject> getAndBindActiveTextureObject(ogles_context_t* c) +{ + sp<EGLTextureObject> tex; + const int active = c->textures.active; + const GLuint name = c->textures.tmu[active].name; + + // free the reference to the previously bound object + texture_unit_t& u(c->textures.tmu[active]); + if (u.texture) + u.texture->decStrong(c); + + if (name == 0) { + // 0 is our local texture object, not shared with anyone. + // But it affects all bound TMUs immediately. + // (we need to invalidate all units bound to this texture object) + tex = c->textures.defaultTexture; + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + if (c->textures.tmu[i].texture == tex.get()) + invalidate_texture(c, i); + } + } else { + // get a new texture object for that name + tex = c->surfaceManager->replaceTexture(name); + } + + // bind this texture to the current active texture unit + // and add a reference to this texture object + u.texture = tex.get(); + u.texture->incStrong(c); + u.name = name; + invalidate_texture(c, active); + return tex; +} + +void bindTextureTmu( + ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex) +{ + if (tex.get() == c->textures.tmu[tmu].texture) + return; + + // free the reference to the previously bound object + texture_unit_t& u(c->textures.tmu[tmu]); + if (u.texture) + u.texture->decStrong(c); + + // bind this texture to the current active texture unit + // and add a reference to this texture object + u.texture = tex.get(); + u.texture->incStrong(c); + u.name = texture; + invalidate_texture(c, tmu); +} + +int createTextureSurface(ogles_context_t* c, + GGLSurface** outSurface, int32_t* outSize, GLint level, + GLenum format, GLenum type, GLsizei width, GLsizei height, + GLenum compressedFormat = 0) +{ + // find out which texture is bound to the current unit + const int active = c->textures.active; + const GLuint name = c->textures.tmu[active].name; + + // convert the pixelformat to one we can handle + const int32_t formatIdx = convertGLPixelFormat(format, type); + if (formatIdx == 0) { // we don't know what to do with this + return GL_INVALID_OPERATION; + } + + // figure out the size we need as well as the stride + const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); + const int32_t align = c->textures.unpackAlignment-1; + const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; + const size_t size = bpr * height; + const int32_t stride = bpr / pixelFormat.size; + + if (level > 0) { + const int active = c->textures.active; + EGLTextureObject* tex = c->textures.tmu[active].texture; + status_t err = tex->reallocate(level, + width, height, stride, formatIdx, compressedFormat, bpr); + if (err != NO_ERROR) + return GL_OUT_OF_MEMORY; + GGLSurface& surface = tex->editMip(level); + *outSurface = &surface; + *outSize = size; + return 0; + } + + sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c); + status_t err = tex->reallocate(level, + width, height, stride, formatIdx, compressedFormat, bpr); + if (err != NO_ERROR) + return GL_OUT_OF_MEMORY; + + tex->internalformat = format; + *outSurface = &tex->surface; + *outSize = size; + return 0; +} + +static void decodePalette4(const GLvoid *data, int level, int width, int height, + void *surface, int stride, int format) + +{ + int indexBits = 8; + int entrySize = 0; + switch (format) { + case GL_PALETTE4_RGB8_OES: + indexBits = 4; + /* FALLTHROUGH */ + case GL_PALETTE8_RGB8_OES: + entrySize = 3; + break; + + case GL_PALETTE4_RGBA8_OES: + indexBits = 4; + /* FALLTHROUGH */ + case GL_PALETTE8_RGBA8_OES: + entrySize = 4; + break; + + case GL_PALETTE4_R5_G6_B5_OES: + case GL_PALETTE4_RGBA4_OES: + case GL_PALETTE4_RGB5_A1_OES: + indexBits = 4; + /* FALLTHROUGH */ + case GL_PALETTE8_R5_G6_B5_OES: + case GL_PALETTE8_RGBA4_OES: + case GL_PALETTE8_RGB5_A1_OES: + entrySize = 2; + break; + } + + const int paletteSize = (1 << indexBits) * entrySize; + uint8_t const* pixels = (uint8_t *)data + paletteSize; + for (int i=0 ; i<level ; i++) { + int w = (width >> i) ? : 1; + int h = (height >> i) ? : 1; + pixels += h * ((w * indexBits) / 8); + } + width = (width >> level) ? : 1; + height = (height >> level) ? : 1; + + if (entrySize == 2) { + uint8_t const* const palette = (uint8_t*)data; + for (int y=0 ; y<height ; y++) { + uint8_t* p = (uint8_t*)surface + y*stride*2; + if (indexBits == 8) { + for (int x=0 ; x<width ; x++) { + int index = 2 * (*pixels++); + *p++ = palette[index + 0]; + *p++ = palette[index + 1]; + } + } else { + for (int x=0 ; x<width ; x+=2) { + int v = *pixels++; + int index = 2 * (v >> 4); + *p++ = palette[index + 0]; + *p++ = palette[index + 1]; + if (x+1 < width) { + index = 2 * (v & 0xF); + *p++ = palette[index + 0]; + *p++ = palette[index + 1]; + } + } + } + } + } else if (entrySize == 3) { + uint8_t const* const palette = (uint8_t*)data; + for (int y=0 ; y<height ; y++) { + uint8_t* p = (uint8_t*)surface + y*stride*3; + if (indexBits == 8) { + for (int x=0 ; x<width ; x++) { + int index = 3 * (*pixels++); + *p++ = palette[index + 0]; + *p++ = palette[index + 1]; + *p++ = palette[index + 2]; + } + } else { + for (int x=0 ; x<width ; x+=2) { + int v = *pixels++; + int index = 3 * (v >> 4); + *p++ = palette[index + 0]; + *p++ = palette[index + 1]; + *p++ = palette[index + 2]; + if (x+1 < width) { + index = 3 * (v & 0xF); + *p++ = palette[index + 0]; + *p++ = palette[index + 1]; + *p++ = palette[index + 2]; + } + } + } + } + } else if (entrySize == 4) { + uint8_t const* const palette = (uint8_t*)data; + for (int y=0 ; y<height ; y++) { + uint8_t* p = (uint8_t*)surface + y*stride*4; + if (indexBits == 8) { + for (int x=0 ; x<width ; x++) { + int index = 4 * (*pixels++); + *p++ = palette[index + 0]; + *p++ = palette[index + 1]; + *p++ = palette[index + 2]; + *p++ = palette[index + 3]; + } + } else { + for (int x=0 ; x<width ; x+=2) { + int v = *pixels++; + int index = 4 * (v >> 4); + *p++ = palette[index + 0]; + *p++ = palette[index + 1]; + *p++ = palette[index + 2]; + *p++ = palette[index + 3]; + if (x+1 < width) { + index = 4 * (v & 0xF); + *p++ = palette[index + 0]; + *p++ = palette[index + 1]; + *p++ = palette[index + 2]; + *p++ = palette[index + 3]; + } + } + } + } + } +} + + + +static __attribute__((noinline)) +void set_depth_and_fog(ogles_context_t* c, GLint z) +{ + const uint32_t enables = c->rasterizer.state.enables; + // we need to compute Zw + int32_t iterators[3]; + iterators[1] = iterators[2] = 0; + GGLfixed Zw; + GGLfixed n = gglFloatToFixed(c->transforms.vpt.zNear); + GGLfixed f = gglFloatToFixed(c->transforms.vpt.zFar); + if (z<=0) Zw = n; + else if (z>=1) Zw = f; + else Zw = gglMulAddx(z, (f-n), n); + if (enables & GGL_ENABLE_FOG) { + // set up fog if needed... + iterators[0] = c->fog.fog(c, Zw); + c->rasterizer.procs.fogGrad3xv(c, iterators); + } + if (enables & GGL_ENABLE_DEPTH_TEST) { + // set up z-test if needed... + int32_t z = (Zw & ~(Zw>>31)); + if (z >= 0x10000) + z = 0xFFFF; + iterators[0] = (z << 16) | z; + c->rasterizer.procs.zGrad3xv(c, iterators); + } +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark Generate mimaps +#endif + +extern status_t buildAPyramid(ogles_context_t* c, EGLTextureObject* tex); + +void generateMipmap(ogles_context_t* c, GLint level) +{ + if (level == 0) { + const int active = c->textures.active; + EGLTextureObject* tex = c->textures.tmu[active].texture; + if (tex->generate_mipmap) { + if (buildAPyramid(c, tex) != NO_ERROR) { + ogles_error(c, GL_OUT_OF_MEMORY); + return; + } + } + } +} + + +static void texParameterx( + GLenum target, GLenum pname, GLfixed param, ogles_context_t* c) +{ + if (target != GL_TEXTURE_2D) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture; + switch (pname) { + case GL_TEXTURE_WRAP_S: + if ((param == GL_REPEAT) || + (param == GL_CLAMP_TO_EDGE)) { + textureObject->wraps = param; + } else { + goto invalid_enum; + } + break; + case GL_TEXTURE_WRAP_T: + if ((param == GL_REPEAT) || + (param == GL_CLAMP_TO_EDGE)) { + textureObject->wrapt = param; + } else { + goto invalid_enum; + } + break; + case GL_TEXTURE_MIN_FILTER: + if ((param == GL_NEAREST) || + (param == GL_LINEAR) || + (param == GL_NEAREST_MIPMAP_NEAREST) || + (param == GL_LINEAR_MIPMAP_NEAREST) || + (param == GL_NEAREST_MIPMAP_LINEAR) || + (param == GL_LINEAR_MIPMAP_LINEAR)) { + textureObject->min_filter = param; + } else { + goto invalid_enum; + } + break; + case GL_TEXTURE_MAG_FILTER: + if ((param == GL_NEAREST) || + (param == GL_LINEAR)) { + textureObject->mag_filter = param; + } else { + goto invalid_enum; + } + break; + case GL_GENERATE_MIPMAP: + textureObject->generate_mipmap = param; + break; + default: +invalid_enum: + ogles_error(c, GL_INVALID_ENUM); + return; + } + invalidate_texture(c, c->textures.active); +} + + +static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h, + ogles_context_t* c) +{ + // quickly reject empty rects + if ((w|h) <= 0) + return; + + const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; + y = gglIntToFixed(cbSurface.height) - (y + h); + w >>= FIXED_BITS; + h >>= FIXED_BITS; + + // set up all texture units + for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { + if (!c->rasterizer.state.texture[i].enable) + continue; + + int32_t texcoords[8]; + texture_unit_t& u(c->textures.tmu[i]); + + // validate this tmu (bind, wrap, filter) + validate_tmu(c, i); + // we CLAMP here, which works with premultiplied (s,t) + c->rasterizer.procs.texParameteri(c, + GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_S, GGL_CLAMP); + c->rasterizer.procs.texParameteri(c, + GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP); + u.dirty = 0xFF; // XXX: should be more subtle + + EGLTextureObject* textureObject = u.texture; + const GLint Ucr = textureObject->crop_rect[0] << 16; + const GLint Vcr = textureObject->crop_rect[1] << 16; + const GLint Wcr = textureObject->crop_rect[2] << 16; + const GLint Hcr = textureObject->crop_rect[3] << 16; + + // computes texture coordinates (pre-multiplied) + int32_t dsdx = Wcr / w; // dsdx = ((Wcr/w)/Wt)*Wt + int32_t dtdy =-Hcr / h; // dtdy = -((Hcr/h)/Ht)*Ht + int32_t s0 = Ucr - gglMulx(dsdx, x); // s0 = Ucr - x * dsdx + int32_t t0 = (Vcr+Hcr) - gglMulx(dtdy, y); // t0 = (Vcr+Hcr) - y*dtdy + texcoords[0] = s0; + texcoords[1] = dsdx; + texcoords[2] = 0; + texcoords[3] = t0; + texcoords[4] = 0; + texcoords[5] = dtdy; + texcoords[6] = 0; + texcoords[7] = 0; + c->rasterizer.procs.texCoordGradScale8xv(c, i, texcoords); + } + + const uint32_t enables = c->rasterizer.state.enables; + if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG))) + set_depth_and_fog(c, z); + + c->rasterizer.procs.activeTexture(c, c->textures.active); + c->rasterizer.procs.color4xv(c, c->currentColorClamped.v); + c->rasterizer.procs.disable(c, GGL_W_LERP); + c->rasterizer.procs.disable(c, GGL_AA); + c->rasterizer.procs.shadeModel(c, GL_FLAT); + c->rasterizer.procs.recti(c, + gglFixedToIntRound(x), + gglFixedToIntRound(y), + gglFixedToIntRound(x)+w, + gglFixedToIntRound(y)+h); +} + +static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_context_t* c) +{ + // All coordinates are integer, so if we have only one + // texture unit active and no scaling is required + // THEN, we can use our special 1:1 mapping + // which is a lot faster. + + if (ggl_likely(c->rasterizer.state.enabled_tmu == 1)) { + const int tmu = 0; + texture_unit_t& u(c->textures.tmu[tmu]); + EGLTextureObject* textureObject = u.texture; + const GLint Wcr = textureObject->crop_rect[2]; + const GLint Hcr = textureObject->crop_rect[3]; + + if ((w == Wcr) && (h == -Hcr)) { + if ((w|h) <= 0) return; // quickly reject empty rects + + if (u.dirty) { + c->rasterizer.procs.activeTexture(c, tmu); + c->rasterizer.procs.bindTexture(c, &(u.texture->surface)); + c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, + GGL_TEXTURE_MIN_FILTER, u.texture->min_filter); + c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, + GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter); + } + c->rasterizer.procs.texGeni(c, GGL_S, + GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + c->rasterizer.procs.texGeni(c, GGL_T, + GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + u.dirty = 0xFF; // XXX: should be more subtle + c->rasterizer.procs.activeTexture(c, c->textures.active); + + const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; + y = cbSurface.height - (y + h); + const GLint Ucr = textureObject->crop_rect[0]; + const GLint Vcr = textureObject->crop_rect[1]; + const GLint s0 = Ucr - x; + const GLint t0 = (Vcr + Hcr) - y; + + const GLuint tw = textureObject->surface.width; + const GLuint th = textureObject->surface.height; + if ((uint32_t(s0+x+w) > tw) || (uint32_t(t0+y+h) > th)) { + // The GL spec is unclear about what should happen + // in this case, so we just use the slow case, which + // at least won't crash + goto slow_case; + } + + c->rasterizer.procs.texCoord2i(c, s0, t0); + const uint32_t enables = c->rasterizer.state.enables; + if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG))) + set_depth_and_fog(c, z); + + c->rasterizer.procs.color4xv(c, c->currentColorClamped.v); + c->rasterizer.procs.disable(c, GGL_W_LERP); + c->rasterizer.procs.disable(c, GGL_AA); + c->rasterizer.procs.shadeModel(c, GL_FLAT); + c->rasterizer.procs.recti(c, x, y, x+w, y+h); + return; + } + } + +slow_case: + drawTexxOES( + gglIntToFixed(x), gglIntToFixed(y), gglIntToFixed(z), + gglIntToFixed(w), gglIntToFixed(h), + c); +} + + +}; // namespace android +// ---------------------------------------------------------------------------- + +using namespace android; + + +#if 0 +#pragma mark - +#pragma mark Texture API +#endif + +void glActiveTexture(GLenum texture) +{ + ogles_context_t* c = ogles_context_t::get(); + if (uint32_t(texture-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + c->textures.active = texture - GL_TEXTURE0; + c->rasterizer.procs.activeTexture(c, c->textures.active); +} + +void glBindTexture(GLenum target, GLuint texture) +{ + ogles_context_t* c = ogles_context_t::get(); + if (target != GL_TEXTURE_2D) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + // Bind or create a texture + sp<EGLTextureObject> tex; + if (texture == 0) { + // 0 is our local texture object + tex = c->textures.defaultTexture; + } else { + tex = c->surfaceManager->texture(texture); + if (ggl_unlikely(tex == 0)) { + tex = c->surfaceManager->createTexture(texture); + if (tex == 0) { + ogles_error(c, GL_OUT_OF_MEMORY); + return; + } + } + } + bindTextureTmu(c, c->textures.active, texture, tex); +} + +void glGenTextures(GLsizei n, GLuint *textures) +{ + ogles_context_t* c = ogles_context_t::get(); + if (n<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + // generate unique (shared) texture names + c->surfaceManager->getToken(n, textures); +} + +void glDeleteTextures(GLsizei n, const GLuint *textures) +{ + ogles_context_t* c = ogles_context_t::get(); + if (n<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + // If deleting a bound texture, bind this unit to 0 + for (int t=0 ; t<GGL_TEXTURE_UNIT_COUNT ; t++) { + if (c->textures.tmu[t].name == 0) + continue; + for (int i=0 ; i<n ; i++) { + if (textures[i] && (textures[i] == c->textures.tmu[t].name)) { + // bind this tmu to texture 0 + sp<EGLTextureObject> tex(c->textures.defaultTexture); + bindTextureTmu(c, t, 0, tex); + } + } + } + c->surfaceManager->deleteTextures(n, textures); + c->surfaceManager->recycleTokens(n, textures); +} + +void glMultiTexCoord4f( + GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) +{ + ogles_context_t* c = ogles_context_t::get(); + if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + const int tmu = target-GL_TEXTURE0; + c->current.texture[tmu].S = gglFloatToFixed(s); + c->current.texture[tmu].T = gglFloatToFixed(t); + c->current.texture[tmu].R = gglFloatToFixed(r); + c->current.texture[tmu].Q = gglFloatToFixed(q); +} + +void glMultiTexCoord4x( + GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q) +{ + ogles_context_t* c = ogles_context_t::get(); + if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + const int tmu = target-GL_TEXTURE0; + c->current.texture[tmu].S = s; + c->current.texture[tmu].T = t; + c->current.texture[tmu].R = r; + c->current.texture[tmu].Q = q; +} + +void glPixelStorei(GLenum pname, GLint param) +{ + ogles_context_t* c = ogles_context_t::get(); + if ((pname != GL_PACK_ALIGNMENT) && (pname != GL_UNPACK_ALIGNMENT)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if ((param<=0 || param>8) || (param & (param-1))) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + if (pname == GL_PACK_ALIGNMENT) + c->textures.packAlignment = param; + if (pname == GL_UNPACK_ALIGNMENT) + c->textures.unpackAlignment = param; +} + +void glTexEnvf(GLenum target, GLenum pname, GLfloat param) +{ + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.texEnvi(c, target, pname, GLint(param)); +} + +void glTexEnvfv( + GLenum target, GLenum pname, const GLfloat *params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (pname == GL_TEXTURE_ENV_MODE) { + c->rasterizer.procs.texEnvi(c, target, pname, GLint(*params)); + return; + } + if (pname == GL_TEXTURE_ENV_COLOR) { + GGLfixed fixed[4]; + for (int i=0 ; i<4 ; i++) + fixed[i] = gglFloatToFixed(params[i]); + c->rasterizer.procs.texEnvxv(c, target, pname, fixed); + return; + } + ogles_error(c, GL_INVALID_ENUM); +} + +void glTexEnvx(GLenum target, GLenum pname, GLfixed param) +{ + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.texEnvi(c, target, pname, param); +} + +void glTexEnvxv( + GLenum target, GLenum pname, const GLfixed *params) +{ + ogles_context_t* c = ogles_context_t::get(); + c->rasterizer.procs.texEnvxv(c, target, pname, params); +} + +void glTexParameteriv( + GLenum target, GLenum pname, const GLint* params) +{ + ogles_context_t* c = ogles_context_t::get(); + if (target != GGL_TEXTURE_2D) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture; + switch (pname) { + case GL_TEXTURE_CROP_RECT_OES: + memcpy(textureObject->crop_rect, params, 4*sizeof(GLint)); + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } +} + +void glTexParameterf( + GLenum target, GLenum pname, GLfloat param) +{ + ogles_context_t* c = ogles_context_t::get(); + texParameterx(target, pname, GLfixed(param), c); +} + +void glTexParameterx( + GLenum target, GLenum pname, GLfixed param) +{ + ogles_context_t* c = ogles_context_t::get(); + texParameterx(target, pname, param, c); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#endif + +void glCompressedTexImage2D( + GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, + GLsizei imageSize, const GLvoid *data) +{ + ogles_context_t* c = ogles_context_t::get(); + if (target != GL_TEXTURE_2D) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if ((internalformat < GL_PALETTE4_RGB8_OES || + internalformat > GL_PALETTE8_RGB5_A1_OES)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (width<0 || height<0 || border!=0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + // "uncompress" the texture since pixelflinger doesn't support + // any compressed texture format natively. + GLenum format; + GLenum type; + switch (internalformat) { + case GL_PALETTE8_RGB8_OES: + case GL_PALETTE4_RGB8_OES: + format = GL_RGB; + type = GL_UNSIGNED_BYTE; + break; + case GL_PALETTE8_RGBA8_OES: + case GL_PALETTE4_RGBA8_OES: + format = GL_RGBA; + type = GL_UNSIGNED_BYTE; + break; + case GL_PALETTE8_R5_G6_B5_OES: + case GL_PALETTE4_R5_G6_B5_OES: + format = GL_RGB; + type = GL_UNSIGNED_SHORT_5_6_5; + break; + case GL_PALETTE8_RGBA4_OES: + case GL_PALETTE4_RGBA4_OES: + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case GL_PALETTE8_RGB5_A1_OES: + case GL_PALETTE4_RGB5_A1_OES: + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_5_5_5_1; + break; + default: + ogles_error(c, GL_INVALID_ENUM); + return; + } + + if (!data || !width || !height) { + // unclear if this is an error or not... + return; + } + + int32_t size; + GGLSurface* surface; + // all mipmap levels are specified at once. + const int numLevels = level<0 ? -level : 1; + for (int i=0 ; i<numLevels ; i++) { + int lod_w = (width >> i) ? : 1; + int lod_h = (height >> i) ? : 1; + int error = createTextureSurface(c, &surface, &size, + i, format, type, lod_w, lod_h); + if (error) { + ogles_error(c, error); + return; + } + decodePalette4(data, i, width, height, + surface->data, surface->stride, internalformat); + } +} + + +void glTexImage2D( + GLenum target, GLint level, GLint internalformat, + GLsizei width, GLsizei height, GLint border, + GLenum format, GLenum type, const GLvoid *pixels) +{ + ogles_context_t* c = ogles_context_t::get(); + if (target != GL_TEXTURE_2D && target != GL_DIRECT_TEXTURE_2D_QUALCOMM) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (width<0 || height<0 || border!=0 || level < 0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + if (format != internalformat) { + ogles_error(c, GL_INVALID_OPERATION); + return; + } + if (validFormatType(c, format, type)) { + return; + } + + int32_t size = 0; + GGLSurface* surface = 0; + if (target != GL_DIRECT_TEXTURE_2D_QUALCOMM) { + int error = createTextureSurface(c, &surface, &size, + level, format, type, width, height); + if (error) { + ogles_error(c, error); + return; + } + } else if (pixels == 0 || level != 0) { + // pixel can't be null for direct texture + ogles_error(c, GL_INVALID_OPERATION); + return; + } + + if (pixels) { + const int32_t formatIdx = convertGLPixelFormat(format, type); + const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); + const int32_t align = c->textures.unpackAlignment-1; + const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; + const size_t size = bpr * height; + const int32_t stride = bpr / pixelFormat.size; + + GGLSurface userSurface; + userSurface.version = sizeof(userSurface); + userSurface.width = width; + userSurface.height = height; + userSurface.stride = stride; + userSurface.format = formatIdx; + userSurface.compressedFormat = 0; + userSurface.data = (GLubyte*)pixels; + + if (target != GL_DIRECT_TEXTURE_2D_QUALCOMM) { + int err = copyPixels(c, *surface, 0, 0, userSurface, 0, 0, width, height); + if (err) { + ogles_error(c, err); + return; + } + generateMipmap(c, level); + } else { + // bind it to the texture unit + sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c); + tex->setSurface(&userSurface); + } + } +} + +// ---------------------------------------------------------------------------- + +void glCompressedTexSubImage2D( + GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, + GLenum format, GLsizei imageSize, + const GLvoid *data) +{ + ogles_context_t* c = ogles_context_t::get(); + ogles_error(c, GL_INVALID_ENUM); +} + +void glTexSubImage2D( + GLenum target, GLint level, GLint xoffset, + GLint yoffset, GLsizei width, GLsizei height, + GLenum format, GLenum type, const GLvoid *pixels) +{ + ogles_context_t* c = ogles_context_t::get(); + if (target != GL_TEXTURE_2D) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + if (validFormatType(c, format, type)) { + return; + } + + // find out which texture is bound to the current unit + const int active = c->textures.active; + EGLTextureObject* tex = c->textures.tmu[active].texture; + const GGLSurface& surface(tex->mip(level)); + + if (!tex->internalformat || tex->direct) { + ogles_error(c, GL_INVALID_OPERATION); + return; + } + if ((xoffset + width > GLsizei(surface.width)) || + (yoffset + height > GLsizei(surface.height))) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + if (!width || !height) { + return; // okay, but no-op. + } + + // figure out the size we need as well as the stride + const int32_t formatIdx = convertGLPixelFormat(format, type); + if (formatIdx == 0) { // we don't know what to do with this + ogles_error(c, GL_INVALID_OPERATION); + return; + } + + const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); + const int32_t align = c->textures.unpackAlignment-1; + const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; + const size_t size = bpr * height; + const int32_t stride = bpr / pixelFormat.size; + GGLSurface userSurface; + userSurface.version = sizeof(userSurface); + userSurface.width = width; + userSurface.height = height; + userSurface.stride = stride; + userSurface.format = formatIdx; + userSurface.compressedFormat = 0; + userSurface.data = (GLubyte*)pixels; + + int err = copyPixels(c, + surface, xoffset, yoffset, + userSurface, 0, 0, width, height); + if (err) { + ogles_error(c, err); + return; + } + + generateMipmap(c, level); + + // since we only changed the content of the texture, we don't need + // to call bindTexture on the main rasterizer. +} + +// ---------------------------------------------------------------------------- + +void glCopyTexImage2D( + GLenum target, GLint level, GLenum internalformat, + GLint x, GLint y, GLsizei width, GLsizei height, + GLint border) +{ + ogles_context_t* c = ogles_context_t::get(); + if (target != GL_TEXTURE_2D) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (internalformat<GL_ALPHA || internalformat>GL_LUMINANCE_ALPHA) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (width<0 || height<0 || border!=0 || level<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + GLenum format = 0; + GLenum type = GL_UNSIGNED_BYTE; + const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; + const int cbFormatIdx = cbSurface.format; + switch (cbFormatIdx) { + case GGL_PIXEL_FORMAT_RGB_565: + type = GL_UNSIGNED_SHORT_5_6_5; + break; + case GGL_PIXEL_FORMAT_RGBA_5551: + type = GL_UNSIGNED_SHORT_5_5_5_1; + break; + case GGL_PIXEL_FORMAT_RGBA_4444: + type = GL_UNSIGNED_SHORT_4_4_4_4; + break; + } + switch (internalformat) { + case GL_ALPHA: + case GL_LUMINANCE_ALPHA: + case GL_LUMINANCE: + type = GL_UNSIGNED_BYTE; + break; + } + + // figure out the format to use for the new texture + switch (cbFormatIdx) { + case GGL_PIXEL_FORMAT_RGBA_8888: + case GGL_PIXEL_FORMAT_A_8: + case GGL_PIXEL_FORMAT_RGBA_5551: + case GGL_PIXEL_FORMAT_RGBA_4444: + format = internalformat; + break; + case GGL_PIXEL_FORMAT_RGBX_8888: + case GGL_PIXEL_FORMAT_RGB_888: + case GGL_PIXEL_FORMAT_RGB_565: + case GGL_PIXEL_FORMAT_L_8: + switch (internalformat) { + case GL_LUMINANCE: + case GL_RGB: + format = internalformat; + break; + } + break; + } + + if (format == 0) { + // invalid combination + ogles_error(c, GL_INVALID_ENUM); + return; + } + + // create the new texture... + int32_t size; + GGLSurface* surface; + int error = createTextureSurface(c, &surface, &size, + level, format, type, width, height); + if (error) { + ogles_error(c, error); + return; + } + + // The bottom row is stored first in textures + GGLSurface txSurface(*surface); + txSurface.stride = -txSurface.stride; + + // (x,y) is the lower-left corner of colorBuffer + y = cbSurface.height - (y + height); + + int err = copyPixels(c, + txSurface, 0, 0, + cbSurface, x, y, cbSurface.width, cbSurface.height); + if (err) { + ogles_error(c, err); + } + + generateMipmap(c, level); +} + +void glCopyTexSubImage2D( + GLenum target, GLint level, GLint xoffset, GLint yoffset, + GLint x, GLint y, GLsizei width, GLsizei height) +{ + ogles_context_t* c = ogles_context_t::get(); + if (target != GL_TEXTURE_2D) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + if (!width || !height) { + return; // okay, but no-op. + } + + // find out which texture is bound to the current unit + const int active = c->textures.active; + EGLTextureObject* tex = c->textures.tmu[active].texture; + const GGLSurface& surface(tex->mip(level)); + + if (!tex->internalformat) { + ogles_error(c, GL_INVALID_OPERATION); + return; + } + if ((xoffset + width > GLsizei(surface.width)) || + (yoffset + height > GLsizei(surface.height))) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + // The bottom row is stored first in textures + GGLSurface txSurface(surface); + txSurface.stride = -txSurface.stride; + + // (x,y) is the lower-left corner of colorBuffer + const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; + y = cbSurface.height - (y + height); + + int err = copyPixels(c, + surface, xoffset, yoffset, + cbSurface, x, y, width, height); + if (err) { + ogles_error(c, err); + return; + } + + generateMipmap(c, level); +} + +void glReadPixels( + GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, GLvoid *pixels) +{ + ogles_context_t* c = ogles_context_t::get(); + if ((format != GL_RGBA) && (format != GL_RGB)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if ((type != GL_UNSIGNED_BYTE) && (type != GL_UNSIGNED_SHORT_5_6_5)) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + if (width<0 || height<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + if (x<0 || x<0) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + int32_t formatIdx = GGL_PIXEL_FORMAT_NONE; + if ((format == GL_RGBA) && (type == GL_UNSIGNED_BYTE)) { + formatIdx = GGL_PIXEL_FORMAT_RGBA_8888; + } else if ((format == GL_RGB) && (type == GL_UNSIGNED_SHORT_5_6_5)) { + formatIdx = GGL_PIXEL_FORMAT_RGB_565; + } else { + ogles_error(c, GL_INVALID_OPERATION); + return; + } + + const GGLSurface& readSurface = c->rasterizer.state.buffers.read.s; + if ((x+width > GLint(readSurface.width)) || + (y+height > GLint(readSurface.height))) { + ogles_error(c, GL_INVALID_VALUE); + return; + } + + const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); + const int32_t align = c->textures.packAlignment-1; + const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; + const int32_t stride = bpr / pixelFormat.size; + + GGLSurface userSurface; + userSurface.version = sizeof(userSurface); + userSurface.width = width; + userSurface.height = height; + userSurface.stride = -stride; // bottom row is transfered first + userSurface.format = formatIdx; + userSurface.compressedFormat = 0; + userSurface.data = (GLubyte*)pixels; + + // use pixel-flinger to handle all the conversions + GGLContext* ggl = getRasterizer(c); + if (!ggl) { + // the only reason this would fail is because we ran out of memory + ogles_error(c, GL_OUT_OF_MEMORY); + return; + } + + ggl->colorBuffer(ggl, &userSurface); // destination is user buffer + ggl->bindTexture(ggl, &readSurface); // source is read-buffer + ggl->texCoord2i(ggl, x, readSurface.height - (y + height)); + ggl->recti(ggl, 0, 0, width, height); +} + +// ---------------------------------------------------------------------------- +#if 0 +#pragma mark - +#pragma mark DrawTexture Extension +#endif + +void glDrawTexsvOES(const GLshort* coords) { + ogles_context_t* c = ogles_context_t::get(); + drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c); +} +void glDrawTexivOES(const GLint* coords) { + ogles_context_t* c = ogles_context_t::get(); + drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c); +} +void glDrawTexsOES(GLshort x , GLshort y, GLshort z, GLshort w, GLshort h) { + ogles_context_t* c = ogles_context_t::get(); + drawTexiOES(x, y, z, w, h, c); +} +void glDrawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h) { + ogles_context_t* c = ogles_context_t::get(); + drawTexiOES(x, y, z, w, h, c); +} + +void glDrawTexfvOES(const GLfloat* coords) { + ogles_context_t* c = ogles_context_t::get(); + drawTexxOES( + gglFloatToFixed(coords[0]), + gglFloatToFixed(coords[1]), + gglFloatToFixed(coords[2]), + gglFloatToFixed(coords[3]), + gglFloatToFixed(coords[4]), + c); +} +void glDrawTexxvOES(const GLfixed* coords) { + ogles_context_t* c = ogles_context_t::get(); + drawTexxOES(coords[0], coords[1], coords[2], coords[3], coords[4], c); +} +void glDrawTexfOES(GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h){ + ogles_context_t* c = ogles_context_t::get(); + drawTexxOES( + gglFloatToFixed(x), gglFloatToFixed(y), gglFloatToFixed(z), + gglFloatToFixed(w), gglFloatToFixed(h), + c); +} +void glDrawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h) { + ogles_context_t* c = ogles_context_t::get(); + drawTexxOES(x, y, z, w, h, c); +} diff --git a/opengl/libagl/texture.h b/opengl/libagl/texture.h new file mode 100644 index 0000000..5c57948 --- /dev/null +++ b/opengl/libagl/texture.h @@ -0,0 +1,45 @@ +/* libs/opengles/texture.h +** +** 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. +*/ + +#ifndef ANDROID_OPENGLES_TEXTURE_H +#define ANDROID_OPENGLES_TEXTURE_H + +#include <stdint.h> +#include <stddef.h> +#include <sys/types.h> + +#include <private/pixelflinger/ggl_context.h> + +#include <GLES/gl.h> + +#include "context.h" + +namespace android { + +void ogles_init_texture(ogles_context_t* c); +void ogles_uninit_texture(ogles_context_t* c); +void ogles_validate_texture_impl(ogles_context_t* c); + +inline void ogles_validate_texture(ogles_context_t* c) { + if (c->rasterizer.state.enables & GGL_ENABLE_TMUS) + ogles_validate_texture_impl(c); +} + + +}; // namespace android + +#endif // ANDROID_OPENGLES_TEXTURE_H diff --git a/opengl/libagl/vertex.cpp b/opengl/libagl/vertex.cpp new file mode 100644 index 0000000..dad04d6 --- /dev/null +++ b/opengl/libagl/vertex.cpp @@ -0,0 +1,247 @@ +/* libs/opengles/vertex.cpp +** +** 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 "fp.h" +#include "vertex.h" +#include "state.h" +#include "matrix.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +void ogles_init_vertex(ogles_context_t* c) +{ + c->cull.enable = GL_FALSE; + c->cull.cullFace = GL_BACK; + c->cull.frontFace = GL_CCW; + + c->current.color.r = 0x10000; + c->current.color.g = 0x10000; + c->current.color.b = 0x10000; + c->current.color.a = 0x10000; + + c->currentNormal.z = 0x10000; +} + +void ogles_uninit_vertex(ogles_context_t* c) +{ +} + +// ---------------------------------------------------------------------------- +// vertex processing +// ---------------------------------------------------------------------------- + +// Divides a vertex clip coordinates by W +static inline +void perspective(ogles_context_t* c, vertex_t* v, uint32_t enables) +{ + // [x,y,z]window = vpt * ([x,y,z]clip / clip.w) + // [w]window = 1/w + + // With a regular projection generated by glFrustum(), + // we have w=-z, therefore, w is in [zNear, zFar]. + // Also, zNear and zFar are stricly positive, + // and 1/w (window.w) is in [1/zFar, 1/zNear], usually this + // means ]0, +inf[ -- however, it is always recommended + // to use as large values as possible for zNear. + // All in all, w is usually smaller than 1.0 (assuming + // zNear is at least 1.0); and even if zNear is smaller than 1.0 + // values of w won't be too big. + + const int32_t rw = gglRecip28(v->clip.w); + const GLfixed* const m = c->transforms.vpt.transform.matrix.m; + v->window.w = rw; + v->window.x = gglMulAddx(gglMulx(v->clip.x, rw, 16), m[ 0], m[12], 28); + v->window.y = gglMulAddx(gglMulx(v->clip.y, rw, 16), m[ 5], m[13], 28); + v->window.x = TRI_FROM_FIXED(v->window.x); + v->window.y = TRI_FROM_FIXED(v->window.y); + if (enables & GGL_ENABLE_DEPTH_TEST) { + v->window.z = gglMulAddx(gglMulx(v->clip.z, rw, 16), m[10], m[14], 28); + } +} + +// frustum clipping and W-divide +static inline +void clipFrustumPerspective(ogles_context_t* c, vertex_t* v, uint32_t enables) +{ + // ndc = clip / W + // window = ncd * viewport + + // clip to the view-volume + uint32_t clip = v->flags & vertex_t::CLIP_ALL; + const GLfixed w = v->clip.w; + if (v->clip.x < -w) clip |= vertex_t::CLIP_L; + if (v->clip.x > w) clip |= vertex_t::CLIP_R; + if (v->clip.y < -w) clip |= vertex_t::CLIP_B; + if (v->clip.y > w) clip |= vertex_t::CLIP_T; + if (v->clip.z < -w) clip |= vertex_t::CLIP_N; + if (v->clip.z > w) clip |= vertex_t::CLIP_F; + + v->flags |= clip; + c->arrays.cull &= clip; + + if (ggl_likely(!clip)) { + // if the vertex is clipped, we don't do the perspective + // divide, since we don't need its window coordinates. + perspective(c, v, enables); + } +} + +// frustum clipping, user clipping and W-divide +static inline +void clipAllPerspective(ogles_context_t* c, vertex_t* v, uint32_t enables) +{ + // compute eye coordinates + c->arrays.mv_transform( + &c->transforms.modelview.transform, &v->eye, &v->obj); + v->flags |= vertex_t::EYE; + + // clip this vertex against each user clip plane + uint32_t clip = 0; + int planes = c->clipPlanes.enable; + while (planes) { + const int i = 31 - gglClz(planes); + planes &= ~(1<<i); + // XXX: we should have a special dot() for 2,3,4 coords vertices + GLfixed d = dot4(c->clipPlanes.plane[i].equation.v, v->eye.v); + if (d < 0) { + clip |= 0x100<<i; + } + } + v->flags |= clip; + + clipFrustumPerspective(c, v, enables); +} + +// ---------------------------------------------------------------------------- + +void ogles_vertex_project(ogles_context_t* c, vertex_t* v) { + perspective(c, v, c->rasterizer.state.enables); +} + +void ogles_vertex_perspective2D(ogles_context_t* c, vertex_t* v) +{ + // here we assume w=1.0 and the viewport transformation + // has been applied already. + c->arrays.cull = 0; + v->window.x = TRI_FROM_FIXED(v->clip.x); + v->window.y = TRI_FROM_FIXED(v->clip.y); + v->window.z = v->clip.z; + v->window.w = v->clip.w << 12; +} + +void ogles_vertex_perspective3DZ(ogles_context_t* c, vertex_t* v) { + clipFrustumPerspective(c, v, GGL_ENABLE_DEPTH_TEST); +} +void ogles_vertex_perspective3D(ogles_context_t* c, vertex_t* v) { + clipFrustumPerspective(c, v, 0); +} +void ogles_vertex_clipAllPerspective3DZ(ogles_context_t* c, vertex_t* v) { + clipAllPerspective(c, v, GGL_ENABLE_DEPTH_TEST); +} +void ogles_vertex_clipAllPerspective3D(ogles_context_t* c, vertex_t* v) { + clipAllPerspective(c, v, 0); +} + +static void clipPlanex(GLenum plane, const GLfixed* equ, ogles_context_t* c) +{ + const int p = plane - GL_CLIP_PLANE0; + if (ggl_unlikely(uint32_t(p) > (GL_CLIP_PLANE5 - GL_CLIP_PLANE0))) { + ogles_error(c, GL_INVALID_ENUM); + return; + } + + vec4_t& equation = c->clipPlanes.plane[p].equation; + memcpy(equation.v, equ, sizeof(vec4_t)); + + ogles_validate_transform(c, transform_state_t::MVIT); + transform_t& mvit = c->transforms.mvit4; + mvit.point4(&mvit, &equation, &equation); +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + +using namespace android; + + +void glColor4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a) +{ + ogles_context_t* c = ogles_context_t::get(); + c->current.color.r = gglFloatToFixed(r); + c->currentColorClamped.r = gglClampx(c->current.color.r); + c->current.color.g = gglFloatToFixed(g); + c->currentColorClamped.g = gglClampx(c->current.color.g); + c->current.color.b = gglFloatToFixed(b); + c->currentColorClamped.b = gglClampx(c->current.color.b); + c->current.color.a = gglFloatToFixed(a); + c->currentColorClamped.a = gglClampx(c->current.color.a); +} + +void glColor4x(GLfixed r, GLfixed g, GLfixed b, GLfixed a) +{ + ogles_context_t* c = ogles_context_t::get(); + c->current.color.r = r; + c->current.color.g = g; + c->current.color.b = b; + c->current.color.a = a; + c->currentColorClamped.r = gglClampx(r); + c->currentColorClamped.g = gglClampx(g); + c->currentColorClamped.b = gglClampx(b); + c->currentColorClamped.a = gglClampx(a); +} + +void glNormal3f(GLfloat x, GLfloat y, GLfloat z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->currentNormal.x = gglFloatToFixed(x); + c->currentNormal.y = gglFloatToFixed(y); + c->currentNormal.z = gglFloatToFixed(z); +} + +void glNormal3x(GLfixed x, GLfixed y, GLfixed z) +{ + ogles_context_t* c = ogles_context_t::get(); + c->currentNormal.x = x; + c->currentNormal.y = y; + c->currentNormal.z = z; +} + +// ---------------------------------------------------------------------------- + +void glClipPlanef(GLenum plane, const GLfloat* equ) +{ + const GLfixed equx[4] = { + gglFloatToFixed(equ[0]), + gglFloatToFixed(equ[1]), + gglFloatToFixed(equ[2]), + gglFloatToFixed(equ[3]) + }; + ogles_context_t* c = ogles_context_t::get(); + clipPlanex(plane, equx, c); +} + +void glClipPlanex(GLenum plane, const GLfixed* equ) +{ + ogles_context_t* c = ogles_context_t::get(); + clipPlanex(plane, equ, c); +} diff --git a/opengl/libagl/vertex.h b/opengl/libagl/vertex.h new file mode 100644 index 0000000..55e6213 --- /dev/null +++ b/opengl/libagl/vertex.h @@ -0,0 +1,48 @@ +/* libs/opengles/vertex.h +** +** 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. +*/ + +#ifndef ANDROID_OPENGLES_VERTEX_H +#define ANDROID_OPENGLES_VERTEX_H + +#include <stdint.h> +#include <stddef.h> +#include <sys/types.h> + +namespace android { + +namespace gl { +struct vertex_t; +struct ogles_context_t; +}; + +void ogles_init_vertex(ogles_context_t* c); +void ogles_uninit_vertex(ogles_context_t* c); + +void ogles_vertex_perspective2D(ogles_context_t*, vertex_t*); + +void ogles_vertex_perspective3D(ogles_context_t*, vertex_t*); +void ogles_vertex_perspective3DZ(ogles_context_t*, vertex_t*); +void ogles_vertex_clipAllPerspective3D(ogles_context_t*, vertex_t*); +void ogles_vertex_clipAllPerspective3DZ(ogles_context_t*, vertex_t*); + + +void ogles_vertex_project(ogles_context_t* c, vertex_t*); + +}; // namespace android + +#endif // ANDROID_OPENGLES_VERTEX_H + |