diff options
Diffstat (limited to 'libs')
85 files changed, 4411 insertions, 1874 deletions
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 629b899..b578a6c 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -371,6 +371,11 @@ int IPCThreadState::getCallingUid() return mCallingUid; } +int IPCThreadState::getOrigCallingUid() +{ + return mOrigCallingUid; +} + int64_t IPCThreadState::clearCallingIdentity() { int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid; @@ -641,6 +646,7 @@ IPCThreadState::IPCThreadState() { pthread_setspecific(gTLS, this); clearCaller(); + mOrigCallingUid = mCallingUid; mIn.setDataCapacity(256); mOut.setDataCapacity(256); } @@ -987,6 +993,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd) mCallingPid = tr.sender_pid; mCallingUid = tr.sender_euid; + mOrigCallingUid = tr.sender_euid; int curPrio = getpriority(PRIO_PROCESS, mMyThreadId); if (gDisableBackgroundScheduling) { @@ -1045,6 +1052,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd) mCallingPid = origPid; mCallingUid = origUid; + mOrigCallingUid = origUid; IF_LOG_TRANSACTIONS() { TextOutput::Bundle _b(alog); diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp index ff5e6bd..8d0e0a7 100644 --- a/libs/binder/MemoryDealer.cpp +++ b/libs/binder/MemoryDealer.cpp @@ -180,7 +180,6 @@ Allocation::~Allocation() /* NOTE: it's VERY important to not free allocations of size 0 because * they're special as they don't have any record in the allocator * and could alias some real allocation (their offset is zero). */ - mDealer->deallocate(freedOffset); // keep the size to unmap in excess size_t pagesize = getpagesize(); @@ -216,6 +215,11 @@ Allocation::~Allocation() } #endif } + + // This should be done after madvise(MADV_REMOVE), otherwise madvise() + // might kick out the memory region that's allocated and/or written + // right after the deallocation. + mDealer->deallocate(freedOffset); } } diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 3400e97..dea14bb 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -722,7 +722,15 @@ status_t Parcel::writeFileDescriptor(int fd, bool takeOwnership) status_t Parcel::writeDupFileDescriptor(int fd) { - return writeFileDescriptor(dup(fd), true /*takeOwnership*/); + int dupFd = dup(fd); + if (dupFd < 0) { + return -errno; + } + status_t err = writeFileDescriptor(dupFd, true /*takeOwnership*/); + if (err) { + close(dupFd); + } + return err; } status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob) diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index 9767568..b6f5b9e 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -2,11 +2,13 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + BitTube.cpp \ + DisplayEventReceiver.cpp \ + IDisplayEventConnection.cpp \ ISensorEventConnection.cpp \ ISensorServer.cpp \ ISurfaceTexture.cpp \ Sensor.cpp \ - SensorChannel.cpp \ SensorEventQueue.cpp \ SensorManager.cpp \ SurfaceTexture.cpp \ @@ -32,6 +34,13 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= libgui +ifeq ($(TARGET_BOARD_PLATFORM), omap4) + LOCAL_CFLAGS += -DUSE_FENCE_SYNC +endif +ifeq ($(TARGET_BOARD_PLATFORM), s5pc110) + LOCAL_CFLAGS += -DUSE_FENCE_SYNC +endif + ifeq ($(TARGET_BOARD_PLATFORM), tegra) LOCAL_CFLAGS += -DALLOW_DEQUEUE_CURRENT_BUFFER endif diff --git a/libs/gui/SensorChannel.cpp b/libs/gui/BitTube.cpp index 147e1c2..785da39 100644 --- a/libs/gui/SensorChannel.cpp +++ b/libs/gui/BitTube.cpp @@ -24,12 +24,12 @@ #include <binder/Parcel.h> -#include <gui/SensorChannel.h> +#include <gui/BitTube.h> namespace android { // ---------------------------------------------------------------------------- -SensorChannel::SensorChannel() +BitTube::BitTube() : mSendFd(-1), mReceiveFd(-1) { int fds[2]; @@ -38,17 +38,26 @@ SensorChannel::SensorChannel() mSendFd = fds[1]; fcntl(mReceiveFd, F_SETFL, O_NONBLOCK); fcntl(mSendFd, F_SETFL, O_NONBLOCK); + } else { + mReceiveFd = -errno; + ALOGE("BitTube: pipe creation failed (%s)", strerror(-mReceiveFd)); } } -SensorChannel::SensorChannel(const Parcel& data) +BitTube::BitTube(const Parcel& data) : mSendFd(-1), mReceiveFd(-1) { mReceiveFd = dup(data.readFileDescriptor()); - fcntl(mReceiveFd, F_SETFL, O_NONBLOCK); + if (mReceiveFd >= 0) { + fcntl(mReceiveFd, F_SETFL, O_NONBLOCK); + } else { + mReceiveFd = -errno; + ALOGE("BitTube(Parcel): can't dup filedescriptor (%s)", + strerror(-mReceiveFd)); + } } -SensorChannel::~SensorChannel() +BitTube::~BitTube() { if (mSendFd >= 0) close(mSendFd); @@ -57,28 +66,46 @@ SensorChannel::~SensorChannel() close(mReceiveFd); } -int SensorChannel::getFd() const +status_t BitTube::initCheck() const +{ + if (mReceiveFd < 0) { + return status_t(mReceiveFd); + } + return NO_ERROR; +} + +int BitTube::getFd() const { return mReceiveFd; } -ssize_t SensorChannel::write(void const* vaddr, size_t size) +ssize_t BitTube::write(void const* vaddr, size_t size) { - ssize_t len = ::write(mSendFd, vaddr, size); - if (len < 0) - return -errno; - return len; + ssize_t err, len; + do { + len = ::write(mSendFd, vaddr, size); + err = len < 0 ? errno : 0; + } while (err == EINTR); + return err == 0 ? len : -err; + } -ssize_t SensorChannel::read(void* vaddr, size_t size) +ssize_t BitTube::read(void* vaddr, size_t size) { - ssize_t len = ::read(mReceiveFd, vaddr, size); - if (len < 0) - return -errno; - return len; + ssize_t err, len; + do { + len = ::read(mReceiveFd, vaddr, size); + err = len < 0 ? errno : 0; + } while (err == EINTR); + if (err == EAGAIN || err == EWOULDBLOCK) { + // EAGAIN means that we have non-blocking I/O but there was + // no data to be read. Nothing the client should care about. + return 0; + } + return err == 0 ? len : -err; } -status_t SensorChannel::writeToParcel(Parcel* reply) const +status_t BitTube::writeToParcel(Parcel* reply) const { if (mReceiveFd < 0) return -EINVAL; diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp new file mode 100644 index 0000000..6a4763d --- /dev/null +++ b/libs/gui/DisplayEventReceiver.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <string.h> + +#include <utils/Errors.h> + +#include <gui/BitTube.h> +#include <gui/DisplayEventReceiver.h> +#include <gui/IDisplayEventConnection.h> + +#include <private/gui/ComposerService.h> + +#include <surfaceflinger/ISurfaceComposer.h> + +// --------------------------------------------------------------------------- + +namespace android { + +// --------------------------------------------------------------------------- + +DisplayEventReceiver::DisplayEventReceiver() { + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + if (sf != NULL) { + mEventConnection = sf->createDisplayEventConnection(); + if (mEventConnection != NULL) { + mDataChannel = mEventConnection->getDataChannel(); + } + } +} + +DisplayEventReceiver::~DisplayEventReceiver() { +} + +status_t DisplayEventReceiver::initCheck() const { + if (mDataChannel != NULL) + return NO_ERROR; + return NO_INIT; +} + +int DisplayEventReceiver::getFd() const { + if (mDataChannel == NULL) + return NO_INIT; + + return mDataChannel->getFd(); +} + +status_t DisplayEventReceiver::setVsyncRate(uint32_t count) { + if (int32_t(count) < 0) + return BAD_VALUE; + + if (mEventConnection != NULL) { + mEventConnection->setVsyncRate(count); + return NO_ERROR; + } + return NO_INIT; +} + +status_t DisplayEventReceiver::requestNextVsync() { + if (mEventConnection != NULL) { + mEventConnection->requestNextVsync(); + return NO_ERROR; + } + return NO_INIT; +} + + +ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events, + size_t count) { + return DisplayEventReceiver::getEvents(mDataChannel, events, count); +} + +ssize_t DisplayEventReceiver::getEvents(const sp<BitTube>& dataChannel, + Event* events, size_t count) +{ + ssize_t size = dataChannel->read(events, sizeof(events[0])*count); + ALOGE_IF(size<0, + "DisplayEventReceiver::getEvents error (%s)", + strerror(-size)); + if (size >= 0) { + // Note: if (size % sizeof(events[0])) != 0, we've got a + // partial read. This can happen if the queue filed up (ie: if we + // didn't pull from it fast enough). + // We discard the partial event and rely on the sender to + // re-send the event if appropriate (some events, like VSYNC + // can be lost forever). + + // returns number of events read + size /= sizeof(events[0]); + } + return size; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp new file mode 100644 index 0000000..887d176 --- /dev/null +++ b/libs/gui/IDisplayEventConnection.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include <utils/Timers.h> + +#include <binder/Parcel.h> +#include <binder/IInterface.h> + +#include <gui/IDisplayEventConnection.h> +#include <gui/BitTube.h> + +namespace android { +// ---------------------------------------------------------------------------- + +enum { + GET_DATA_CHANNEL = IBinder::FIRST_CALL_TRANSACTION, + SET_VSYNC_RATE, + REQUEST_NEXT_VSYNC +}; + +class BpDisplayEventConnection : public BpInterface<IDisplayEventConnection> +{ +public: + BpDisplayEventConnection(const sp<IBinder>& impl) + : BpInterface<IDisplayEventConnection>(impl) + { + } + + virtual sp<BitTube> getDataChannel() const + { + Parcel data, reply; + data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor()); + remote()->transact(GET_DATA_CHANNEL, data, &reply); + return new BitTube(reply); + } + + virtual void setVsyncRate(uint32_t count) { + Parcel data, reply; + data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor()); + data.writeInt32(count); + remote()->transact(SET_VSYNC_RATE, data, &reply); + } + + virtual void requestNextVsync() { + Parcel data, reply; + data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor()); + remote()->transact(REQUEST_NEXT_VSYNC, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(DisplayEventConnection, "android.gui.DisplayEventConnection"); + +// ---------------------------------------------------------------------------- + +status_t BnDisplayEventConnection::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case GET_DATA_CHANNEL: { + CHECK_INTERFACE(IDisplayEventConnection, data, reply); + sp<BitTube> channel(getDataChannel()); + channel->writeToParcel(reply); + return NO_ERROR; + } break; + case SET_VSYNC_RATE: { + CHECK_INTERFACE(IDisplayEventConnection, data, reply); + setVsyncRate(data.readInt32()); + return NO_ERROR; + } break; + case REQUEST_NEXT_VSYNC: { + CHECK_INTERFACE(IDisplayEventConnection, data, reply); + requestNextVsync(); + return NO_ERROR; + } break; + } + return BBinder::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/gui/ISensorEventConnection.cpp b/libs/gui/ISensorEventConnection.cpp index a5083fe..0e51e8e 100644 --- a/libs/gui/ISensorEventConnection.cpp +++ b/libs/gui/ISensorEventConnection.cpp @@ -25,7 +25,7 @@ #include <binder/IInterface.h> #include <gui/ISensorEventConnection.h> -#include <gui/SensorChannel.h> +#include <gui/BitTube.h> namespace android { // ---------------------------------------------------------------------------- @@ -44,12 +44,12 @@ public: { } - virtual sp<SensorChannel> getSensorChannel() const + virtual sp<BitTube> getSensorChannel() const { Parcel data, reply; data.writeInterfaceToken(ISensorEventConnection::getInterfaceDescriptor()); remote()->transact(GET_SENSOR_CHANNEL, data, &reply); - return new SensorChannel(reply); + return new BitTube(reply); } virtual status_t enableDisable(int handle, bool enabled) @@ -83,7 +83,7 @@ status_t BnSensorEventConnection::onTransact( switch(code) { case GET_SENSOR_CHANNEL: { CHECK_INTERFACE(ISensorEventConnection, data, reply); - sp<SensorChannel> channel(getSensorChannel()); + sp<BitTube> channel(getSensorChannel()); channel->writeToParcel(reply); return NO_ERROR; } break; diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index b1b9adb..95b2379 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -29,6 +29,9 @@ #include <surfaceflinger/ISurfaceComposer.h> +#include <gui/BitTube.h> +#include <gui/IDisplayEventConnection.h> + #include <ui/DisplayInfo.h> #include <gui/ISurfaceTexture.h> @@ -37,13 +40,10 @@ // --------------------------------------------------------------------------- -#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) -#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) - -// --------------------------------------------------------------------------- - namespace android { +class IDisplayEventConnection; + class BpSurfaceComposer : public BpInterface<ISurfaceComposer> { public: @@ -174,6 +174,27 @@ public: } return result != 0; } + + virtual sp<IDisplayEventConnection> createDisplayEventConnection() + { + Parcel data, reply; + sp<IDisplayEventConnection> result; + int err = data.writeInterfaceToken( + ISurfaceComposer::getInterfaceDescriptor()); + if (err != NO_ERROR) { + return result; + } + err = remote()->transact( + BnSurfaceComposer::CREATE_DISPLAY_EVENT_CONNECTION, + data, &reply); + if (err != NO_ERROR) { + ALOGE("ISurfaceComposer::createDisplayEventConnection: error performing " + "transaction: %s (%d)", strerror(-err), -err); + return result; + } + result = interface_cast<IDisplayEventConnection>(reply.readStrongBinder()); + return result; + } }; IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer"); @@ -254,6 +275,12 @@ status_t BnSurfaceComposer::onTransact( int32_t result = authenticateSurfaceTexture(surfaceTexture) ? 1 : 0; reply->writeInt32(result); } break; + case CREATE_DISPLAY_EVENT_CONNECTION: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IDisplayEventConnection> connection(createDisplayEventConnection()); + reply->writeStrongBinder(connection->asBinder()); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp index ace16aa..8fe96b1 100644 --- a/libs/gui/ISurfaceComposerClient.cpp +++ b/libs/gui/ISurfaceComposerClient.cpp @@ -35,18 +35,6 @@ // --------------------------------------------------------------------------- -/* ideally AID_GRAPHICS would be in a semi-public header - * or there would be a way to map a user/group name to its id - */ -#ifndef AID_GRAPHICS -#define AID_GRAPHICS 1003 -#endif - -#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) -#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) - -// --------------------------------------------------------------------------- - namespace android { enum { diff --git a/libs/gui/SensorEventQueue.cpp b/libs/gui/SensorEventQueue.cpp index ac362fc..b95dd90 100644 --- a/libs/gui/SensorEventQueue.cpp +++ b/libs/gui/SensorEventQueue.cpp @@ -24,7 +24,7 @@ #include <utils/Looper.h> #include <gui/Sensor.h> -#include <gui/SensorChannel.h> +#include <gui/BitTube.h> #include <gui/SensorEventQueue.h> #include <gui/ISensorEventConnection.h> @@ -104,7 +104,7 @@ status_t SensorEventQueue::waitForEvent() const do { result = looper->pollOnce(-1); if (result == ALOOPER_EVENT_ERROR) { - ALOGE("SensorChannel::waitForEvent error (errno=%d)", errno); + ALOGE("SensorEventQueue::waitForEvent error (errno=%d)", errno); result = -EPIPE; // unknown error, so we make up one break; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 4ad6c22..699438c 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -39,6 +39,7 @@ #include <private/surfaceflinger/LayerState.h> #include <private/surfaceflinger/SharedBufferStack.h> +#include <private/gui/ComposerService.h> namespace android { // --------------------------------------------------------------------------- diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index c80d93d..3abe84a 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -29,6 +29,8 @@ #include <hardware/hardware.h> +#include <private/gui/ComposerService.h> + #include <surfaceflinger/ISurfaceComposer.h> #include <surfaceflinger/SurfaceComposerClient.h> #include <surfaceflinger/IGraphicBufferAlloc.h> diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index 5f01ae9..d0934ba 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -23,6 +23,8 @@ #include <utils/Log.h> +#include <private/gui/ComposerService.h> + namespace android { SurfaceTextureClient::SurfaceTextureClient( diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 693b7b8..ea52750 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -22,6 +22,8 @@ #include <surfaceflinger/SurfaceComposerClient.h> #include <utils/String8.h> +#include <private/gui/ComposerService.h> + namespace android { class SurfaceTest : public ::testing::Test { diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 9bfc94c..5ec3983 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -26,6 +26,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) ShapeCache.cpp \ SkiaColorFilter.cpp \ SkiaShader.cpp \ + Snapshot.cpp \ TextureCache.cpp \ TextDropShadowCache.cpp @@ -38,7 +39,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) external/skia/src/ports \ external/skia/include/utils - LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER + LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DGL_GLEXT_PROTOTYPES LOCAL_CFLAGS += -fvisibility=hidden LOCAL_MODULE_CLASS := SHARED_LIBRARIES LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index b6b35ea..0ef8469 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -55,6 +55,16 @@ Caches::Caches(): Singleton<Caches>(), mInitialized(false) { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + if (extensions.hasDebugMarker()) { + eventMark = glInsertEventMarkerEXT; + startMark = glPushGroupMarkerEXT; + endMark = glPopGroupMarkerEXT; + } else { + eventMark = eventMarkNull; + startMark = startMarkNull; + endMark = endMarkNull; + } + init(); mDebugLevel = readDebugLevel(); @@ -73,6 +83,17 @@ void Caches::init() { glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW); mCurrentBuffer = meshBuffer; + mCurrentIndicesBuffer = 0; + mCurrentPositionPointer = this; + mCurrentTexCoordsPointer = this; + + mTexCoordsArrayEnabled = false; + + mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0; + + glActiveTexture(gTextureUnits[0]); + mTextureUnit = 0; + mRegionMesh = NULL; blend = false; @@ -218,24 +239,106 @@ void Caches::flush(FlushMode mode) { // VBO /////////////////////////////////////////////////////////////////////////////// -void Caches::bindMeshBuffer() { - bindMeshBuffer(meshBuffer); +bool Caches::bindMeshBuffer() { + return bindMeshBuffer(meshBuffer); } -void Caches::bindMeshBuffer(const GLuint buffer) { +bool Caches::bindMeshBuffer(const GLuint buffer) { if (mCurrentBuffer != buffer) { glBindBuffer(GL_ARRAY_BUFFER, buffer); mCurrentBuffer = buffer; + return true; } + return false; } -void Caches::unbindMeshBuffer() { +bool Caches::unbindMeshBuffer() { if (mCurrentBuffer) { glBindBuffer(GL_ARRAY_BUFFER, 0); mCurrentBuffer = 0; + return true; + } + return false; +} + +bool Caches::bindIndicesBuffer(const GLuint buffer) { + if (mCurrentIndicesBuffer != buffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); + mCurrentIndicesBuffer = buffer; + return true; + } + return false; +} + +bool Caches::unbindIndicesBuffer() { + if (mCurrentIndicesBuffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + mCurrentIndicesBuffer = 0; + return true; + } + return false; +} + +void Caches::bindPositionVertexPointer(bool force, GLuint slot, GLvoid* vertices, GLsizei stride) { + if (force || vertices != mCurrentPositionPointer) { + glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices); + mCurrentPositionPointer = vertices; + } +} + +void Caches::bindTexCoordsVertexPointer(bool force, GLuint slot, GLvoid* vertices) { + if (force || vertices != mCurrentTexCoordsPointer) { + glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, gMeshStride, vertices); + mCurrentTexCoordsPointer = vertices; } } +void Caches::resetVertexPointers() { + mCurrentPositionPointer = this; + mCurrentTexCoordsPointer = this; +} + +void Caches::resetTexCoordsVertexPointer() { + mCurrentTexCoordsPointer = this; +} + +void Caches::enableTexCoordsVertexArray() { + if (!mTexCoordsArrayEnabled) { + glEnableVertexAttribArray(Program::kBindingTexCoords); + mCurrentTexCoordsPointer = this; + mTexCoordsArrayEnabled = true; + } +} + +void Caches::disbaleTexCoordsVertexArray() { + if (mTexCoordsArrayEnabled) { + glDisableVertexAttribArray(Program::kBindingTexCoords); + mTexCoordsArrayEnabled = false; + } +} + +void Caches::activeTexture(GLuint textureUnit) { + if (mTextureUnit != textureUnit) { + glActiveTexture(gTextureUnits[textureUnit]); + mTextureUnit = textureUnit; + } +} + +void Caches::setScissor(GLint x, GLint y, GLint width, GLint height) { + if (x != mScissorX || y != mScissorY || width != mScissorWidth || height != mScissorHeight) { + glScissor(x, y, width, height); + + mScissorX = x; + mScissorY = y; + mScissorWidth = width; + mScissorHeight = height; + } +} + +void Caches::resetScissor() { + mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0; +} + TextureVertex* Caches::getRegionMesh() { // Create the mesh, 2 triangles and 4 vertices per rectangle in the region if (!mRegionMesh) { @@ -254,13 +357,13 @@ TextureVertex* Caches::getRegionMesh() { } glGenBuffers(1, &mRegionMeshIndices); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mRegionMeshIndices); + bindIndicesBuffer(mRegionMeshIndices); glBufferData(GL_ELEMENT_ARRAY_BUFFER, REGION_MESH_QUAD_COUNT * 6 * sizeof(uint16_t), regionIndices, GL_STATIC_DRAW); delete[] regionIndices; } else { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mRegionMeshIndices); + bindIndicesBuffer(mRegionMeshIndices); } return mRegionMesh; diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 5e58a9e..f8c7bcc 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -70,6 +70,12 @@ static const GLsizei gVertexAAWidthOffset = 2 * sizeof(float); static const GLsizei gVertexAALengthOffset = 3 * sizeof(float); static const GLsizei gMeshCount = 4; +static const GLenum gTextureUnits[] = { + GL_TEXTURE0, + GL_TEXTURE1, + GL_TEXTURE2 +}; + /////////////////////////////////////////////////////////////////////////////// // Debug /////////////////////////////////////////////////////////////////////////////// @@ -91,15 +97,6 @@ class ANDROID_API Caches: public Singleton<Caches> { CacheLogger mLogger; - GLuint mCurrentBuffer; - - // Used to render layers - TextureVertex* mRegionMesh; - GLuint mRegionMeshIndices; - - mutable Mutex mGarbageLock; - Vector<Layer*> mLayerGarbage; - public: enum FlushMode { kFlushMode_Layers = 0, @@ -147,17 +144,58 @@ public: /** * Binds the VBO used to render simple textured quads. */ - void bindMeshBuffer(); + bool bindMeshBuffer(); /** * Binds the specified VBO if needed. */ - void bindMeshBuffer(const GLuint buffer); + bool bindMeshBuffer(const GLuint buffer); /** * Unbinds the VBO used to render simple textured quads. */ - void unbindMeshBuffer(); + bool unbindMeshBuffer(); + + bool bindIndicesBuffer(const GLuint buffer); + bool unbindIndicesBuffer(); + + /** + * Binds an attrib to the specified float vertex pointer. + * Assumes a stride of gMeshStride and a size of 2. + */ + void bindPositionVertexPointer(bool force, GLuint slot, GLvoid* vertices, + GLsizei stride = gMeshStride); + + /** + * Binds an attrib to the specified float vertex pointer. + * Assumes a stride of gMeshStride and a size of 2. + */ + void bindTexCoordsVertexPointer(bool force, GLuint slot, GLvoid* vertices); + + /** + * Resets the vertex pointers. + */ + void resetVertexPointers(); + void resetTexCoordsVertexPointer(); + + void enableTexCoordsVertexArray(); + void disbaleTexCoordsVertexArray(); + + /** + * Activate the specified texture unit. The texture unit must + * be specified using an integer number (0 for GL_TEXTURE0 etc.) + */ + void activeTexture(GLuint textureUnit); + + /** + * Sets the scissor for the current surface. + */ + void setScissor(GLint x, GLint y, GLint width, GLint height); + + /** + * Resets the scissor state. + */ + void resetScissor(); /** * Returns the mesh used to draw regions. Calling this method will @@ -202,7 +240,36 @@ public: GammaFontRenderer fontRenderer; ResourceCache resourceCache; + PFNGLINSERTEVENTMARKEREXTPROC eventMark; + PFNGLPUSHGROUPMARKEREXTPROC startMark; + PFNGLPOPGROUPMARKEREXTPROC endMark; + private: + static void eventMarkNull(GLsizei length, const GLchar *marker) { } + static void startMarkNull(GLsizei length, const GLchar *marker) { } + static void endMarkNull() { } + + GLuint mCurrentBuffer; + GLuint mCurrentIndicesBuffer; + void* mCurrentPositionPointer; + void* mCurrentTexCoordsPointer; + + bool mTexCoordsArrayEnabled; + + GLuint mTextureUnit; + + GLint mScissorX; + GLint mScissorY; + GLint mScissorWidth; + GLint mScissorHeight; + + // Used to render layers + TextureVertex* mRegionMesh; + GLuint mRegionMeshIndices; + + mutable Mutex mGarbageLock; + Vector<Layer*> mLayerGarbage; + DebugLevel mDebugLevel; bool mInitialized; }; // class Caches diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h index 0ad0c2a..16a3d73 100644 --- a/libs/hwui/Debug.h +++ b/libs/hwui/Debug.h @@ -62,6 +62,9 @@ // Turn on to display debug info about the layer renderer #define DEBUG_LAYER_RENDERER 0 +// Turn on to enable additional debugging in the font renderers +#define DEBUG_FONT_RENDERER 0 + // Turn on to dump display list state #define DEBUG_DISPLAY_LIST 0 diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 751da44..1a11fbc 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -19,9 +19,10 @@ #include "DisplayListLogBuffer.h" #include "DisplayListRenderer.h" -#include <utils/String8.h> #include "Caches.h" +#include <utils/String8.h> + namespace android { namespace uirenderer { @@ -60,12 +61,15 @@ const char* DisplayList::OP_NAMES[] = { "DrawLines", "DrawPoints", "DrawText", + "DrawPosText", "ResetShader", "SetupShader", "ResetColorFilter", "SetupColorFilter", "ResetShadow", "SetupShadow", + "ResetPaintFilter", + "SetupPaintFilter", "DrawGLFunction" }; @@ -214,7 +218,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { indent[i] = ' '; } indent[count] = '\0'; - ALOGD("%sStart display list (%p)", (char*) indent + 2, this); + ALOGD("%sStart display list (%p, %s)", (char*) indent + 2, this, mName.string()); int saveCount = renderer.getSaveCount() - 1; @@ -248,7 +252,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { float f2 = getFloat(); float f3 = getFloat(); float f4 = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); int flags = getInt(); ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p, 0x%x", (char*) indent, OP_NAMES[op], f1, f2, f3, f4, paint, flags); @@ -321,7 +325,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { Layer* layer = (Layer*) getInt(); float x = getFloat(); float y = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); ALOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], layer, x, y, paint); } @@ -330,7 +334,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { SkBitmap* bitmap = getBitmap(); float x = getFloat(); float y = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); ALOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], bitmap, x, y, paint); } @@ -338,7 +342,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { case DrawBitmapMatrix: { SkBitmap* bitmap = getBitmap(); SkMatrix* matrix = getMatrix(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); ALOGD("%s%s %p, %p, %p", (char*) indent, OP_NAMES[op], bitmap, matrix, paint); } @@ -353,7 +357,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { float f6 = getFloat(); float f7 = getFloat(); float f8 = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); ALOGD("%s%s %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], bitmap, f1, f2, f3, f4, f5, f6, f7, f8, paint); } @@ -367,7 +371,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { float* vertices = getFloats(verticesCount); bool hasColors = getInt(); int* colors = hasColors ? getInts(colorsCount) : NULL; - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); ALOGD("%s%s", (char*) indent, OP_NAMES[op]); } break; @@ -386,7 +390,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { float top = getFloat(); float right = getFloat(); float bottom = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); ALOGD("%s%s %.2f, %.2f, %.2f, %.2f", (char*) indent, OP_NAMES[op], left, top, right, bottom); } @@ -402,7 +406,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { float f2 = getFloat(); float f3 = getFloat(); float f4 = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], f1, f2, f3, f4, paint); } @@ -414,7 +418,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { float f4 = getFloat(); float f5 = getFloat(); float f6 = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, paint); } @@ -423,7 +427,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { float f1 = getFloat(); float f2 = getFloat(); float f3 = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); ALOGD("%s%s %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], f1, f2, f3, paint); } @@ -433,7 +437,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { float f2 = getFloat(); float f3 = getFloat(); float f4 = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], f1, f2, f3, f4, paint); } @@ -446,28 +450,28 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { float f5 = getFloat(); float f6 = getFloat(); int i1 = getInt(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); ALOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p", (char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, i1, paint); } break; case DrawPath: { SkPath* path = getPath(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); ALOGD("%s%s %p, %p", (char*) indent, OP_NAMES[op], path, paint); } break; case DrawLines: { int count = 0; float* points = getFloats(count); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); ALOGD("%s%s", (char*) indent, OP_NAMES[op]); } break; case DrawPoints: { int count = 0; float* points = getFloats(count); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); ALOGD("%s%s", (char*) indent, OP_NAMES[op]); } break; @@ -476,11 +480,21 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { int count = getInt(); float x = getFloat(); float y = getFloat(); - SkPaint* paint = getPaint(); - ALOGD("%s%s %s, %d, %d, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], - text.text(), text.length(), count, x, y, paint); + SkPaint* paint = getPaint(renderer); + float length = getFloat(); + ALOGD("%s%s %s, %d, %d, %.2f, %.2f, %p, %.2f", (char*) indent, OP_NAMES[op], + text.text(), text.length(), count, x, y, paint, length); } break; + case DrawPosText: { + getText(&text); + int count = getInt(); + int positionsCount = 0; + float* positions = getFloats(positionsCount); + SkPaint* paint = getPaint(renderer); + ALOGD("%s%s %s, %d, %d, %p", (char*) indent, OP_NAMES[op], + text.text(), text.length(), count, paint); + } case ResetShader: { ALOGD("%s%s", (char*) indent, OP_NAMES[op]); } @@ -512,6 +526,16 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { radius, dx, dy, color); } break; + case ResetPaintFilter: { + ALOGD("%s%s", (char*) indent, OP_NAMES[op]); + } + break; + case SetupPaintFilter: { + int clearBits = getInt(); + int setBits = getInt(); + ALOGD("%s%s 0x%x, 0x%x", (char*) indent, OP_NAMES[op], clearBits, setBits); + } + break; default: ALOGD("Display List error: op not handled: %s%s", (char*) indent, OP_NAMES[op]); @@ -539,9 +563,11 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) indent[i] = ' '; } indent[count] = '\0'; - DISPLAY_LIST_LOGD("%sStart display list (%p)", (char*) indent + 2, this); + DISPLAY_LIST_LOGD("%sStart display list (%p, %s)", (char*) indent + 2, this, mName.string()); #endif + renderer.startMark(mName.string()); + DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); int saveCount = renderer.getSaveCount() - 1; while (!mReader.eof()) { @@ -552,7 +578,9 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) case DrawGLFunction: { Functor *functor = (Functor *) getInt(); DISPLAY_LIST_LOGD("%s%s %p", (char*) indent, OP_NAMES[op], functor); + renderer.startMark("GL functor"); needsInvalidate |= renderer.callDrawGLFunction(functor, dirty); + renderer.endMark(); } break; case Save: { @@ -577,7 +605,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) float f2 = getFloat(); float f3 = getFloat(); float f4 = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); int flags = getInt(); DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p, 0x%x", (char*) indent, OP_NAMES[op], f1, f2, f3, f4, paint, flags); @@ -660,7 +688,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) Layer* layer = (Layer*) getInt(); float x = getFloat(); float y = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], layer, x, y, paint); renderer.drawLayer(layer, x, y, paint); @@ -670,7 +698,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) SkBitmap* bitmap = getBitmap(); float x = getFloat(); float y = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], bitmap, x, y, paint); renderer.drawBitmap(bitmap, x, y, paint); @@ -679,7 +707,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) case DrawBitmapMatrix: { SkBitmap* bitmap = getBitmap(); SkMatrix* matrix = getMatrix(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s %p, %p, %p", (char*) indent, OP_NAMES[op], bitmap, matrix, paint); renderer.drawBitmap(bitmap, matrix, paint); @@ -695,7 +723,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) float f6 = getFloat(); float f7 = getFloat(); float f8 = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], bitmap, f1, f2, f3, f4, f5, f6, f7, f8, paint); renderer.drawBitmap(bitmap, f1, f2, f3, f4, f5, f6, f7, f8, paint); @@ -711,7 +739,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) float* vertices = getFloats(verticesCount); bool hasColors = getInt(); int* colors = hasColors ? getInts(colorsCount) : NULL; - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); renderer.drawBitmapMesh(bitmap, meshWidth, meshHeight, vertices, colors, paint); @@ -735,7 +763,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) float top = getFloat(); float right = getFloat(); float bottom = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); renderer.drawPatch(bitmap, xDivs, yDivs, colors, xDivsCount, yDivsCount, @@ -754,7 +782,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) float f2 = getFloat(); float f3 = getFloat(); float f4 = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], f1, f2, f3, f4, paint); renderer.drawRect(f1, f2, f3, f4, paint); @@ -767,7 +795,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) float f4 = getFloat(); float f5 = getFloat(); float f6 = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, paint); renderer.drawRoundRect(f1, f2, f3, f4, f5, f6, paint); @@ -777,7 +805,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) float f1 = getFloat(); float f2 = getFloat(); float f3 = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], f1, f2, f3, paint); renderer.drawCircle(f1, f2, f3, paint); @@ -788,7 +816,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) float f2 = getFloat(); float f3 = getFloat(); float f4 = getFloat(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], f1, f2, f3, f4, paint); renderer.drawOval(f1, f2, f3, f4, paint); @@ -802,7 +830,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) float f5 = getFloat(); float f6 = getFloat(); int i1 = getInt(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p", (char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, i1, paint); renderer.drawArc(f1, f2, f3, f4, f5, f6, i1 == 1, paint); @@ -810,7 +838,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) break; case DrawPath: { SkPath* path = getPath(); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s %p, %p", (char*) indent, OP_NAMES[op], path, paint); renderer.drawPath(path, paint); } @@ -818,7 +846,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) case DrawLines: { int count = 0; float* points = getFloats(count); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); renderer.drawLines(points, count, paint); } @@ -826,7 +854,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) case DrawPoints: { int count = 0; float* points = getFloats(count); - SkPaint* paint = getPaint(); + SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); renderer.drawPoints(points, count, paint); } @@ -836,10 +864,22 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) int count = getInt(); float x = getFloat(); float y = getFloat(); - SkPaint* paint = getPaint(); - DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], - text.text(), text.length(), count, x, y, paint); - renderer.drawText(text.text(), text.length(), count, x, y, paint); + SkPaint* paint = getPaint(renderer); + float length = getFloat(); + DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %.2f, %.2f, %p, %.2f", (char*) indent, + OP_NAMES[op], text.text(), text.length(), count, x, y, paint, length); + renderer.drawText(text.text(), text.length(), count, x, y, paint, length); + } + break; + case DrawPosText: { + getText(&text); + int count = getInt(); + int positionsCount = 0; + float* positions = getFloats(positionsCount); + SkPaint* paint = getPaint(renderer); + DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %p", (char*) indent, + OP_NAMES[op], text.text(), text.length(), count, paint); + renderer.drawPosText(text.text(), text.length(), count, positions, paint); } break; case ResetShader: { @@ -879,6 +919,19 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) renderer.setupShadow(radius, dx, dy, color); } break; + case ResetPaintFilter: { + DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); + renderer.resetPaintFilter(); + } + break; + case SetupPaintFilter: { + int clearBits = getInt(); + int setBits = getInt(); + DISPLAY_LIST_LOGD("%s%s 0x%x, 0x%x", (char*) indent, OP_NAMES[op], + clearBits, setBits); + renderer.setupPaintFilter(clearBits, setBits); + } + break; default: DISPLAY_LIST_LOGD("Display List error: op not handled: %s%s", (char*) indent, OP_NAMES[op]); @@ -886,6 +939,8 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) } } + renderer.endMark(); + DISPLAY_LIST_LOGD("%sDone, returning %d", (char*) indent + 2, needsInvalidate); return needsInvalidate; } @@ -1196,12 +1251,32 @@ void DisplayListRenderer::drawPoints(float* points, int count, SkPaint* paint) { } void DisplayListRenderer::drawText(const char* text, int bytesCount, int count, - float x, float y, SkPaint* paint) { + float x, float y, SkPaint* paint, float length) { if (count <= 0) return; addOp(DisplayList::DrawText); addText(text, bytesCount); addInt(count); addPoint(x, y); + // TODO: We should probably make a copy of the paint instead of modifying + // it; modifying the paint will change its generationID the first + // time, which might impact caches. More investigation needed to + // see if it matters. + // If we make a copy, then drawTextDecorations() should *not* make + // its own copy as it does right now. + // Beware: this needs Glyph encoding (already done on the Paint constructor) + paint->setAntiAlias(true); + addPaint(paint); + addFloat(length < 0.0f ? paint->measureText(text, bytesCount) : length); +} + +void DisplayListRenderer::drawPosText(const char* text, int bytesCount, int count, + const float* positions, SkPaint* paint) { + if (count <= 0) return; + addOp(DisplayList::DrawPosText); + addText(text, bytesCount); + addInt(count); + addFloats(positions, count * 2); + paint->setAntiAlias(true); addPaint(paint); } @@ -1234,5 +1309,15 @@ void DisplayListRenderer::setupShadow(float radius, float dx, float dy, int colo addInt(color); } +void DisplayListRenderer::resetPaintFilter() { + addOp(DisplayList::ResetPaintFilter); +} + +void DisplayListRenderer::setupPaintFilter(int clearBits, int setBits) { + addOp(DisplayList::SetupPaintFilter); + addInt(clearBits); + addInt(setBits); +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index f4ae573..46506e4 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -28,6 +28,8 @@ #include <cutils/compiler.h> +#include <utils/String8.h> + #include "DisplayListLogBuffer.h" #include "OpenGLRenderer.h" #include "utils/Functor.h" @@ -96,12 +98,15 @@ public: DrawLines, DrawPoints, DrawText, + DrawPosText, ResetShader, SetupShader, ResetColorFilter, SetupColorFilter, ResetShadow, SetupShadow, + ResetPaintFilter, + SetupPaintFilter, DrawGLFunction, }; @@ -125,6 +130,12 @@ public: return mIsRenderable; } + void setName(const char* name) { + if (name) { + mName.setTo(name); + } + } + private: void init(); @@ -176,8 +187,8 @@ private: return (SkPath*) getInt(); } - SkPaint* getPaint() { - return (SkPaint*) getInt(); + SkPaint* getPaint(OpenGLRenderer& renderer) { + return renderer.filterPaint((SkPaint*) getInt()); } DisplayList* getDisplayList() { @@ -221,6 +232,8 @@ private: size_t mSize; bool mIsRenderable; + + String8 mName; }; /////////////////////////////////////////////////////////////////////////////// @@ -290,6 +303,8 @@ public: virtual void drawLines(float* points, int count, SkPaint* paint); virtual void drawPoints(float* points, int count, SkPaint* paint); virtual void drawText(const char* text, int bytesCount, int count, float x, float y, + SkPaint* paint, float length = 1.0f); + virtual void drawPosText(const char* text, int bytesCount, int count, const float* positions, SkPaint* paint); virtual void resetShader(); @@ -301,6 +316,9 @@ public: virtual void resetShadow(); virtual void setupShadow(float radius, float dx, float dy, int color); + virtual void resetPaintFilter(); + virtual void setupPaintFilter(int clearBits, int setBits); + ANDROID_API void reset(); const SkWriter32& writeStream() const { @@ -419,7 +437,7 @@ private: return; } - SkPaint* paintCopy = mPaintMap.valueFor(paint); + SkPaint* paintCopy = mPaintMap.valueFor(paint); if (paintCopy == NULL || paintCopy->getGenerationID() != paint->getGenerationID()) { paintCopy = new SkPaint(*paint); mPaintMap.add(paint, paintCopy); diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h index 68e33cd..f11fecc 100644 --- a/libs/hwui/Extensions.h +++ b/libs/hwui/Extensions.h @@ -66,6 +66,8 @@ public: mHasNPot = hasExtension("GL_OES_texture_npot"); mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch"); + mHasDiscardFramebuffer = hasExtension("GL_EXT_discard_framebuffer"); + mHasDebugMarker = hasExtension("GL_EXT_debug_marker"); const char* vendor = (const char*) glGetString(GL_VENDOR); EXT_LOGD("Vendor: %s", vendor); @@ -80,6 +82,8 @@ public: inline bool hasNPot() const { return mHasNPot; } inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; } inline bool needsHighpTexCoords() const { return mNeedsHighpTexCoords; } + inline bool hasDiscardFramebuffer() const { return mHasDiscardFramebuffer; } + inline bool hasDebugMarker() const { return mHasDebugMarker; } bool hasExtension(const char* extension) const { const String8 s(extension); @@ -98,6 +102,8 @@ private: bool mHasNPot; bool mNeedsHighpTexCoords; bool mHasFramebufferFetch; + bool mHasDiscardFramebuffer; + bool mHasDebugMarker; }; // class Extensions }; // namespace uirenderer diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 42e672b..3df105b 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -22,8 +22,10 @@ #include <utils/Log.h> +#include "Caches.h" #include "Debug.h" #include "FontRenderer.h" +#include "Caches.h" namespace android { namespace uirenderer { @@ -34,10 +36,28 @@ namespace uirenderer { #define DEFAULT_TEXT_CACHE_WIDTH 1024 #define DEFAULT_TEXT_CACHE_HEIGHT 256 - -// We should query these values from the GL context #define MAX_TEXT_CACHE_WIDTH 2048 -#define MAX_TEXT_CACHE_HEIGHT 2048 +#define TEXTURE_BORDER_SIZE 2 + +/////////////////////////////////////////////////////////////////////////////// +// CacheTextureLine +/////////////////////////////////////////////////////////////////////////////// + +bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) { + if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) { + return false; + } + + if (mCurrentCol + glyph.fWidth + TEXTURE_BORDER_SIZE < mMaxWidth) { + *retOriginX = mCurrentCol + 1; + *retOriginY = mCurrentRow + 1; + mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE; + mDirty = true; + return true; + } + + return false; +} /////////////////////////////////////////////////////////////////////////////// // Font @@ -65,13 +85,17 @@ Font::~Font() { } } -void Font::invalidateTextureCache() { +void Font::invalidateTextureCache(CacheTextureLine *cacheLine) { for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { - mCachedGlyphs.valueAt(i)->mIsValid = false; + CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i); + if (cacheLine == NULL || cachedGlyph->mCachedTextureLine == cacheLine) { + cachedGlyph->mIsValid = false; + } } } -void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) { +void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, + uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { int nPenX = x + glyph->mBitmapLeft; int nPenY = y + glyph->mBitmapTop; @@ -92,7 +116,8 @@ void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds } } -void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) { +void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, + uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { int nPenX = x + glyph->mBitmapLeft; int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight; @@ -104,39 +129,41 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) { int width = (int) glyph->mBitmapWidth; int height = (int) glyph->mBitmapHeight; - mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2, - nPenX + width, nPenY, 0, u2, v2, - nPenX + width, nPenY - height, 0, u2, v1, - nPenX, nPenY - height, 0, u1, v1); + mState->appendMeshQuad(nPenX, nPenY, u1, v2, + nPenX + width, nPenY, u2, v2, + nPenX + width, nPenY - height, u2, v1, + nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture); } -void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, - uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) { +void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, + uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { int nPenX = x + glyph->mBitmapLeft; int nPenY = y + glyph->mBitmapTop; uint32_t endX = glyph->mStartX + glyph->mBitmapWidth; uint32_t endY = glyph->mStartY + glyph->mBitmapHeight; - uint32_t cacheWidth = mState->getCacheWidth(); - const uint8_t* cacheBuffer = mState->getTextTextureData(); + CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture; + uint32_t cacheWidth = cacheTexture->mWidth; + const uint8_t* cacheBuffer = cacheTexture->mTexture; uint32_t cacheX = 0, cacheY = 0; int32_t bX = 0, bY = 0; for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) { for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) { +#if DEBUG_FONT_RENDERER if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) { ALOGE("Skipping invalid index"); continue; } +#endif uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX]; bitmap[bY * bitmapW + bX] = tempCol; } } - } -Font::CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) { +CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) { CachedGlyphInfo* cachedGlyph = NULL; ssize_t index = mCachedGlyphs.indexOfKey(textUnit); if (index >= 0) { @@ -158,13 +185,19 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) { if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) { render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap, - bitmapW, bitmapH, NULL); + bitmapW, bitmapH, NULL, NULL); } else { render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, - 0, 0, NULL); + 0, 0, NULL, NULL); } } +void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, + int numGlyphs, int x, int y, const float* positions) { + render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, + 0, 0, NULL, positions); +} + void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, Rect *bounds) { if (bounds == NULL) { @@ -172,62 +205,95 @@ void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t le return; } bounds->set(1e6, -1e6, -1e6, 1e6); - render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds); + render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, NULL); } #define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16) void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, - uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) { + uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions) { if (numGlyphs == 0 || text == NULL || len == 0) { return; } - float penX = x; - int penY = y; - int glyphsLeft = 1; - if (numGlyphs > 0) { - glyphsLeft = numGlyphs; - } - - SkFixed prevRsbDelta = 0; - penX += 0.5f; + int glyphsCount = 0; text += start; - while (glyphsLeft > 0) { - glyph_t glyph = GET_GLYPH(text); + static RenderGlyph gRenderGlyph[] = { + &android::uirenderer::Font::drawCachedGlyph, + &android::uirenderer::Font::drawCachedGlyphBitmap, + &android::uirenderer::Font::measureCachedGlyph + }; + RenderGlyph render = gRenderGlyph[mode]; - // Reached the end of the string - if (IS_END_OF_STRING(glyph)) { - break; - } + if (CC_LIKELY(positions == NULL)) { + SkFixed prevRsbDelta = 0; - CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); - penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta)); - prevRsbDelta = cachedGlyph->mRsbDelta; + float penX = x; + int penY = y; - // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage - if (cachedGlyph->mIsValid) { - switch(mode) { - case FRAMEBUFFER: - drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY); - break; - case BITMAP: - drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bitmap, bitmapW, bitmapH); - break; - case MEASURE: - measureCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bounds); + penX += 0.5f; + + while (glyphsCount < numGlyphs) { + glyph_t glyph = GET_GLYPH(text); + + // Reached the end of the string + if (IS_END_OF_STRING(glyph)) { break; } + + CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); + penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta)); + prevRsbDelta = cachedGlyph->mRsbDelta; + + // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage + if (cachedGlyph->mIsValid) { + (*this.*render)(cachedGlyph, (int) floorf(penX), penY, + bitmap, bitmapW, bitmapH, bounds, positions); + } + + penX += SkFixedToFloat(cachedGlyph->mAdvanceX); + + glyphsCount++; } + } else { + const SkPaint::Align align = paint->getTextAlign(); + + // This is for renderPosText() + while (glyphsCount < numGlyphs) { + glyph_t glyph = GET_GLYPH(text); - penX += SkFixedToFloat(cachedGlyph->mAdvanceX); + // Reached the end of the string + if (IS_END_OF_STRING(glyph)) { + break; + } - // If we were given a specific number of glyphs, decrement - if (numGlyphs > 0) { - glyphsLeft--; + CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); + + // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage + if (cachedGlyph->mIsValid) { + int penX = x + positions[(glyphsCount << 1)]; + int penY = y + positions[(glyphsCount << 1) + 1]; + + switch (align) { + case SkPaint::kRight_Align: + penX -= SkFixedToFloat(cachedGlyph->mAdvanceX); + penY -= SkFixedToFloat(cachedGlyph->mAdvanceY); + break; + case SkPaint::kCenter_Align: + penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1); + penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1); + default: + break; + } + + (*this.*render)(cachedGlyph, penX, penY, + bitmap, bitmapW, bitmapH, bounds, positions); + } + + glyphsCount++; } } } @@ -245,7 +311,7 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp // Get the bitmap for the glyph paint->findImage(skiaGlyph); - glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY); + mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY); if (!glyph->mIsValid) { return; @@ -259,8 +325,8 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp glyph->mBitmapWidth = skiaGlyph.fWidth; glyph->mBitmapHeight = skiaGlyph.fHeight; - uint32_t cacheWidth = mState->getCacheWidth(); - uint32_t cacheHeight = mState->getCacheHeight(); + uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth; + uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight; glyph->mBitmapMinU = (float) startX / (float) cacheWidth; glyph->mBitmapMinV = (float) startY / (float) cacheHeight; @@ -270,7 +336,7 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp mState->mUploadTexture = true; } -Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) { +CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) { CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); mCachedGlyphs.add(glyph, newGlyph); @@ -319,27 +385,31 @@ FontRenderer::FontRenderer() { mInitialized = false; mMaxNumberOfQuads = 1024; mCurrentQuadIndex = 0; - mTextureId = 0; mTextMeshPtr = NULL; - mTextTexture = NULL; + mCurrentCacheTexture = NULL; + mLastCacheTexture = NULL; + mCacheTextureSmall = NULL; + mCacheTexture128 = NULL; + mCacheTexture256 = NULL; + mCacheTexture512 = NULL; + + mLinearFiltering = false; mIndexBufferID = 0; - mPositionAttrSlot = -1; - mTexcoordAttrSlot = -1; - mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH; - mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT; + mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH; + mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT; char property[PROPERTY_VALUE_MAX]; if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) { if (sLogFontRendererCreate) { INIT_LOGD(" Setting text cache width to %s pixels", property); } - mCacheWidth = atoi(property); + mSmallCacheWidth = atoi(property); } else { if (sLogFontRendererCreate) { - INIT_LOGD(" Using default text cache width of %i pixels", mCacheWidth); + INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth); } } @@ -347,10 +417,10 @@ FontRenderer::FontRenderer() { if (sLogFontRendererCreate) { INIT_LOGD(" Setting text cache width to %s pixels", property); } - mCacheHeight = atoi(property); + mSmallCacheHeight = atoi(property); } else { if (sLogFontRendererCreate) { - INIT_LOGD(" Using default text cache height of %i pixels", mCacheHeight); + INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight); } } @@ -365,11 +435,10 @@ FontRenderer::~FontRenderer() { if (mInitialized) { delete[] mTextMeshPtr; - delete[] mTextTexture; - } - - if (mTextureId) { - glDeleteTextures(1, &mTextureId); + delete mCacheTextureSmall; + delete mCacheTexture128; + delete mCacheTexture256; + delete mCacheTexture512; } Vector<Font*> fontsToDereference = mActiveFonts; @@ -391,30 +460,79 @@ void FontRenderer::flushAllAndInvalidate() { } } -bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) { - // If the glyph is too tall, don't cache it - if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { - if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) { - // Default cache not large enough for large glyphs - resize cache to - // max size and try again - flushAllAndInvalidate(); - initTextTexture(true); - } - if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { - ALOGE("Font size to large to fit in cache. width, height = %i, %i", - (int) glyph.fWidth, (int) glyph.fHeight); - return false; +void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) { + if (cacheTexture && cacheTexture->mTexture) { + glDeleteTextures(1, &cacheTexture->mTextureId); + delete cacheTexture->mTexture; + cacheTexture->mTexture = NULL; + } +} + +void FontRenderer::flushLargeCaches() { + if ((!mCacheTexture128 || !mCacheTexture128->mTexture) && + (!mCacheTexture256 || !mCacheTexture256->mTexture) && + (!mCacheTexture512 || !mCacheTexture512->mTexture)) { + // Typical case; no large glyph caches allocated + return; + } + + for (uint32_t i = 0; i < mCacheLines.size(); i++) { + CacheTextureLine* cacheLine = mCacheLines[i]; + if ((cacheLine->mCacheTexture == mCacheTexture128 || + cacheLine->mCacheTexture == mCacheTexture256 || + cacheLine->mCacheTexture == mCacheTexture512) && + cacheLine->mCacheTexture->mTexture != NULL) { + cacheLine->mCurrentCol = 0; + for (uint32_t i = 0; i < mActiveFonts.size(); i++) { + mActiveFonts[i]->invalidateTextureCache(cacheLine); + } } } + deallocateTextureMemory(mCacheTexture128); + deallocateTextureMemory(mCacheTexture256); + deallocateTextureMemory(mCacheTexture512); +} + +void FontRenderer::allocateTextureMemory(CacheTexture *cacheTexture) { + int width = cacheTexture->mWidth; + int height = cacheTexture->mHeight; + cacheTexture->mTexture = new uint8_t[width * height]; + memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t)); + glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + // Initialize texture dimensions + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, 0); + + const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, + uint32_t* retOriginX, uint32_t* retOriginY) { + cachedGlyph->mIsValid = false; + // If the glyph is too tall, don't cache it + if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { + ALOGE("Font size to large to fit in cache. width, height = %i, %i", + (int) glyph.fWidth, (int) glyph.fHeight); + return; + } + // Now copy the bitmap into the cache texture uint32_t startX = 0; uint32_t startY = 0; bool bitmapFit = false; + CacheTextureLine *cacheLine; for (uint32_t i = 0; i < mCacheLines.size(); i++) { bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); if (bitmapFit) { + cacheLine = mCacheLines[i]; break; } } @@ -427,27 +545,33 @@ bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint3 for (uint32_t i = 0; i < mCacheLines.size(); i++) { bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); if (bitmapFit) { + cacheLine = mCacheLines[i]; break; } } // if we still don't fit, something is wrong and we shouldn't draw if (!bitmapFit) { - ALOGE("Bitmap doesn't fit in cache. width, height = %i, %i", - (int) glyph.fWidth, (int) glyph.fHeight); - return false; + return; } } + cachedGlyph->mCachedTextureLine = cacheLine; + *retOriginX = startX; *retOriginY = startY; uint32_t endX = startX + glyph.fWidth; uint32_t endY = startY + glyph.fHeight; - uint32_t cacheWidth = mCacheWidth; + uint32_t cacheWidth = cacheLine->mMaxWidth; - uint8_t* cacheBuffer = mTextTexture; + CacheTexture *cacheTexture = cacheLine->mCacheTexture; + if (cacheTexture->mTexture == NULL) { + // Large-glyph texture memory is allocated only as needed + allocateTextureMemory(cacheTexture); + } + uint8_t* cacheBuffer = cacheTexture->mTexture; uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; unsigned int stride = glyph.rowBytes(); @@ -458,69 +582,73 @@ bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint3 cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol]; } } - - return true; + cachedGlyph->mIsValid = true; } -void FontRenderer::initTextTexture(bool largeFonts) { - mCacheLines.clear(); - if (largeFonts) { - mCacheWidth = MAX_TEXT_CACHE_WIDTH; - mCacheHeight = MAX_TEXT_CACHE_HEIGHT; - } +CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) { + GLuint textureId; + glGenTextures(1, &textureId); + uint8_t* textureMemory = NULL; - mTextTexture = new uint8_t[mCacheWidth * mCacheHeight]; - memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t)); - - mUploadTexture = false; - - if (mTextureId != 0) { - glDeleteTextures(1, &mTextureId); + CacheTexture* cacheTexture = new CacheTexture(textureMemory, textureId, width, height); + if (allocate) { + allocateTextureMemory(cacheTexture); } - glGenTextures(1, &mTextureId); - glBindTexture(GL_TEXTURE_2D, mTextureId); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - // Initialize texture dimensions - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, - GL_ALPHA, GL_UNSIGNED_BYTE, 0); + return cacheTexture; +} - mLinearFiltering = false; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +void FontRenderer::initTextTexture() { + mCacheLines.clear(); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // Next, use other, separate caches for large glyphs. + uint16_t maxWidth = 0; + if (Caches::hasInstance()) { + maxWidth = Caches::getInstance().maxTextureSize; + } + if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) { + maxWidth = MAX_TEXT_CACHE_WIDTH; + } + if (mCacheTextureSmall != NULL) { + delete mCacheTextureSmall; + delete mCacheTexture128; + delete mCacheTexture256; + delete mCacheTexture512; + } + mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true); + mCacheTexture128 = createCacheTexture(maxWidth, 256, false); + mCacheTexture256 = createCacheTexture(maxWidth, 256, false); + mCacheTexture512 = createCacheTexture(maxWidth, 512, false); + mCurrentCacheTexture = mCacheTextureSmall; - // Split up our cache texture into lines of certain widths + mUploadTexture = false; + // Split up our default cache texture into lines of certain widths int nextLine = 0; - mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0)); + mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall)); nextLine += mCacheLines.top()->mMaxHeight; - mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0)); + mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall)); nextLine += mCacheLines.top()->mMaxHeight; - mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0)); + mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall)); nextLine += mCacheLines.top()->mMaxHeight; - mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0)); + mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall)); nextLine += mCacheLines.top()->mMaxHeight; - mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0)); + mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall)); nextLine += mCacheLines.top()->mMaxHeight; - mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0)); + mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall)); nextLine += mCacheLines.top()->mMaxHeight; - if (largeFonts) { - int nextSize = 76; - // Make several new lines with increasing font sizes - while (nextSize < (int)(mCacheHeight - nextLine - (2 * nextSize))) { - mCacheLines.push(new CacheTextureLine(mCacheWidth, nextSize, nextLine, 0)); - nextLine += mCacheLines.top()->mMaxHeight; - nextSize += 50; - } - } - mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0)); + mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine, + nextLine, 0, mCacheTextureSmall)); + + // The first cache is split into 2 lines of height 128, the rest have just one cache line. + mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128)); + mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128)); + mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256)); + mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512)); } // Avoid having to reallocate memory and render quad by quad void FontRenderer::initVertexArrayBuffers() { - uint32_t numIndicies = mMaxNumberOfQuads * 6; - uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t); + uint32_t numIndices = mMaxNumberOfQuads * 6; + uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t); uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); // Four verts, two triangles , six indices per quad @@ -538,13 +666,12 @@ void FontRenderer::initVertexArrayBuffers() { } glGenBuffers(1, &mIndexBufferID); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID); + Caches::getInstance().bindIndicesBuffer(mIndexBufferID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); free(indexBufferData); - uint32_t coordSize = 3; + uint32_t coordSize = 2; uint32_t uvSize = 2; uint32_t vertsPerQuad = 4; uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; @@ -570,22 +697,28 @@ void FontRenderer::checkInit() { } void FontRenderer::checkTextureUpdate() { - if (!mUploadTexture) { + if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) { return; } - glBindTexture(GL_TEXTURE_2D, mTextureId); - + Caches& caches = Caches::getInstance(); + GLuint lastTextureId = 0; // Iterate over all the cache lines and see which ones need to be updated for (uint32_t i = 0; i < mCacheLines.size(); i++) { CacheTextureLine* cl = mCacheLines[i]; - if(cl->mDirty) { + if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) { + CacheTexture* cacheTexture = cl->mCacheTexture; uint32_t xOffset = 0; uint32_t yOffset = cl->mCurrentRow; - uint32_t width = mCacheWidth; + uint32_t width = cl->mMaxWidth; uint32_t height = cl->mMaxHeight; - void* textureData = mTextTexture + yOffset*width; + void* textureData = cacheTexture->mTexture + (yOffset * width); + if (cacheTexture->mTextureId != lastTextureId) { + caches.activeTexture(0); + glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId); + lastTextureId = cacheTexture->mTextureId; + } glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, GL_ALPHA, GL_UNSIGNED_BYTE, textureData); @@ -593,57 +726,78 @@ void FontRenderer::checkTextureUpdate() { } } + glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId); + if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) { + const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); + mCurrentCacheTexture->mLinearFiltering = mLinearFiltering; + } + mLastCacheTexture = mCurrentCacheTexture; + mUploadTexture = false; } void FontRenderer::issueDrawCommand() { checkTextureUpdate(); - float* vtx = mTextMeshPtr; - float* tex = vtx + 3; + Caches& caches = Caches::getInstance(); + caches.bindIndicesBuffer(mIndexBufferID); + if (!mDrawn) { + float* buffer = mTextMeshPtr; + int offset = 2; - glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx); - glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, tex); + bool force = caches.unbindMeshBuffer(); + caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer); + caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords, + buffer + offset); + } - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID); glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL); mDrawn = true; } -void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, - float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, - float x4, float y4, float z4, float u4, float v4) { +void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, + float x2, float y2, float u2, float v2, + float x3, float y3, float u3, float v3, + float x4, float y4, float u4, float v4, CacheTexture* texture) { + if (mClip && (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { return; } + if (texture != mCurrentCacheTexture) { + if (mCurrentQuadIndex != 0) { + // First, draw everything stored already which uses the previous texture + issueDrawCommand(); + mCurrentQuadIndex = 0; + } + // Now use the new texture id + mCurrentCacheTexture = texture; + } const uint32_t vertsPerQuad = 4; - const uint32_t floatsPerVert = 5; + const uint32_t floatsPerVert = 4; float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; (*currentPos++) = x1; (*currentPos++) = y1; - (*currentPos++) = z1; (*currentPos++) = u1; (*currentPos++) = v1; (*currentPos++) = x2; (*currentPos++) = y2; - (*currentPos++) = z2; (*currentPos++) = u2; (*currentPos++) = v2; (*currentPos++) = x3; (*currentPos++) = y3; - (*currentPos++) = z3; (*currentPos++) = u3; (*currentPos++) = v3; (*currentPos++) = x4; (*currentPos++) = y4; - (*currentPos++) = z4; (*currentPos++) = u4; (*currentPos++) = v4; @@ -723,6 +877,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch return image; } + mDrawn = false; mClip = NULL; mBounds = NULL; @@ -750,29 +905,19 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch image.image = dataBuffer; image.penX = penX; image.penY = penY; + return image; } -bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, - uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) { +void FontRenderer::initRender(const Rect* clip, Rect* bounds) { checkInit(); - if (!mCurrentFont) { - ALOGE("No font set"); - return false; - } - - if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) { - ALOGE("Font renderer unable to draw, attribute slots undefined"); - return false; - } - mDrawn = false; mBounds = bounds; mClip = clip; +} - mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y); - +void FontRenderer::finishRender() { mBounds = NULL; mClip = NULL; @@ -780,6 +925,33 @@ bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text issueDrawCommand(); mCurrentQuadIndex = 0; } +} + +bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text, + uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) { + if (!mCurrentFont) { + ALOGE("No font set"); + return false; + } + + initRender(clip, bounds); + mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y); + finishRender(); + + return mDrawn; +} + +bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, + uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, + const float* positions, Rect* bounds) { + if (!mCurrentFont) { + ALOGE("No font set"); + return false; + } + + initRender(clip, bounds); + mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); + finishRender(); return mDrawn; } @@ -914,9 +1086,12 @@ void FontRenderer::verticalBlur(float* weights, int32_t radius, void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) { float *gaussian = new float[2 * radius + 1]; computeGaussianWeights(gaussian, radius); + uint8_t* scratch = new uint8_t[width * height]; + horizontalBlur(gaussian, radius, image, scratch, width, height); verticalBlur(gaussian, radius, scratch, image, width, height); + delete[] gaussian; delete[] scratch; } diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index 1922812..b767be5 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -55,6 +55,79 @@ namespace uirenderer { class FontRenderer; +class CacheTexture { +public: + CacheTexture(){} + CacheTexture(uint8_t* texture, GLuint textureId, uint16_t width, uint16_t height) : + mTexture(texture), mTextureId(textureId), mWidth(width), mHeight(height), + mLinearFiltering(false) {} + ~CacheTexture() { + if (mTexture != NULL) { + delete[] mTexture; + } + if (mTextureId != 0) { + glDeleteTextures(1, &mTextureId); + } + } + + uint8_t* mTexture; + GLuint mTextureId; + uint16_t mWidth; + uint16_t mHeight; + bool mLinearFiltering; +}; + +class CacheTextureLine { +public: + CacheTextureLine(uint16_t maxWidth, uint16_t maxHeight, uint32_t currentRow, + uint32_t currentCol, CacheTexture* cacheTexture): + mMaxHeight(maxHeight), + mMaxWidth(maxWidth), + mCurrentRow(currentRow), + mCurrentCol(currentCol), + mDirty(false), + mCacheTexture(cacheTexture){ + } + + bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY); + + uint16_t mMaxHeight; + uint16_t mMaxWidth; + uint32_t mCurrentRow; + uint32_t mCurrentCol; + bool mDirty; + CacheTexture *mCacheTexture; +}; + +struct CachedGlyphInfo { + // Has the cache been invalidated? + bool mIsValid; + // Location of the cached glyph in the bitmap + // in case we need to resize the texture or + // render to bitmap + uint32_t mStartX; + uint32_t mStartY; + uint32_t mBitmapWidth; + uint32_t mBitmapHeight; + // Also cache texture coords for the quad + float mBitmapMinU; + float mBitmapMinV; + float mBitmapMaxU; + float mBitmapMaxV; + // Minimize how much we call freetype + uint32_t mGlyphIndex; + uint32_t mAdvanceX; + uint32_t mAdvanceY; + // Values below contain a glyph's origin in the bitmap + int32_t mBitmapLeft; + int32_t mBitmapTop; + // Auto-kerning + SkFixed mLsbDelta; + SkFixed mRsbDelta; + CacheTextureLine* mCachedTextureLine; +}; + + /////////////////////////////////////////////////////////////////////////////// // Font /////////////////////////////////////////////////////////////////////////////// @@ -78,6 +151,10 @@ public: void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, uint8_t *bitmap = NULL, uint32_t bitmapW = 0, uint32_t bitmapH = 0); + + void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, + int numGlyphs, int x, int y, const float* positions); + /** * Creates a new font associated with the specified font state. */ @@ -87,6 +164,8 @@ public: protected: friend class FontRenderer; + typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*, + uint32_t, uint32_t, Rect*, const float*); enum RenderMode { FRAMEBUFFER, @@ -96,52 +175,31 @@ protected: void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, - uint32_t bitmapW, uint32_t bitmapH, Rect *bounds); + uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions); void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, Rect *bounds); - struct CachedGlyphInfo { - // Has the cache been invalidated? - bool mIsValid; - // Location of the cached glyph in the bitmap - // in case we need to resize the texture or - // render to bitmap - uint32_t mStartX; - uint32_t mStartY; - uint32_t mBitmapWidth; - uint32_t mBitmapHeight; - // Also cache texture coords for the quad - float mBitmapMinU; - float mBitmapMinV; - float mBitmapMaxU; - float mBitmapMaxV; - // Minimize how much we call freetype - uint32_t mGlyphIndex; - uint32_t mAdvanceX; - uint32_t mAdvanceY; - // Values below contain a glyph's origin in the bitmap - int32_t mBitmapLeft; - int32_t mBitmapTop; - // Auto-kerning - SkFixed mLsbDelta; - SkFixed mRsbDelta; - }; - Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle, uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth); // Cache of glyphs DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs; - void invalidateTextureCache(); + void invalidateTextureCache(CacheTextureLine *cacheLine = NULL); CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph); - void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph); - void measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds); - void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y); - void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y, - uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH); + void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph); + + void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y, + uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, + Rect* bounds, const float* pos); + void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, + uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, + Rect* bounds, const float* pos); + void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, + uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, + Rect* bounds, const float* pos); CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit); @@ -173,19 +231,19 @@ public: void init(); void deinit(); + void flushLargeCaches(); void setGammaTable(const uint8_t* gammaTable) { mGammaTable = gammaTable; } - void setAttributeBindingSlots(int positionSlot, int texCoordSlot) { - mPositionAttrSlot = positionSlot; - mTexcoordAttrSlot = texCoordSlot; - } - void setFont(SkPaint* paint, uint32_t fontId, float fontSize); + // bounds is an out parameter bool renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds); + // bounds is an out parameter + bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, + uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds); struct DropShadow { DropShadow() { }; @@ -210,23 +268,33 @@ public: GLuint getTexture(bool linearFiltering = false) { checkInit(); - if (linearFiltering != mLinearFiltering) { + if (linearFiltering != mCurrentCacheTexture->mLinearFiltering) { + mCurrentCacheTexture->mLinearFiltering = linearFiltering; mLinearFiltering = linearFiltering; const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST; - glBindTexture(GL_TEXTURE_2D, mTextureId); + glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); } - return mTextureId; - } - - uint32_t getCacheWidth() const { - return mCacheWidth; + return mCurrentCacheTexture->mTextureId; } - uint32_t getCacheHeight() const { - return mCacheHeight; + uint32_t getCacheSize() const { + uint32_t size = 0; + if (mCacheTextureSmall != NULL && mCacheTextureSmall->mTexture != NULL) { + size += mCacheTextureSmall->mWidth * mCacheTextureSmall->mHeight; + } + if (mCacheTexture128 != NULL && mCacheTexture128->mTexture != NULL) { + size += mCacheTexture128->mWidth * mCacheTexture128->mHeight; + } + if (mCacheTexture256 != NULL && mCacheTexture256->mTexture != NULL) { + size += mCacheTexture256->mWidth * mCacheTexture256->mHeight; + } + if (mCacheTexture512 != NULL && mCacheTexture512->mTexture != NULL) { + size += mCacheTexture512->mWidth * mCacheTexture512->mHeight; + } + return size; } protected: @@ -234,57 +302,31 @@ protected: const uint8_t* mGammaTable; - struct CacheTextureLine { - uint16_t mMaxHeight; - uint16_t mMaxWidth; - uint32_t mCurrentRow; - uint32_t mCurrentCol; - bool mDirty; - - CacheTextureLine(uint16_t maxWidth, uint16_t maxHeight, uint32_t currentRow, - uint32_t currentCol): - mMaxHeight(maxHeight), - mMaxWidth(maxWidth), - mCurrentRow(currentRow), - mCurrentCol(currentCol), - mDirty(false) { - } - - bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) { - if (glyph.fHeight + 2 > mMaxHeight) { - return false; - } - - if (mCurrentCol + glyph.fWidth + 2 < mMaxWidth) { - *retOriginX = mCurrentCol + 1; - *retOriginY = mCurrentRow + 1; - mCurrentCol += glyph.fWidth + 2; - mDirty = true; - return true; - } - - return false; - } - }; - - void initTextTexture(bool largeFonts = false); - bool cacheBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY); + void allocateTextureMemory(CacheTexture* cacheTexture); + void deallocateTextureMemory(CacheTexture* cacheTexture); + void initTextTexture(); + CacheTexture *createCacheTexture(int width, int height, bool allocate); + void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, + uint32_t *retOriginX, uint32_t *retOriginY); void flushAllAndInvalidate(); void initVertexArrayBuffers(); void checkInit(); + void initRender(const Rect* clip, Rect* bounds); + void finishRender(); String16 mLatinPrecache; void precacheLatin(SkPaint* paint); void issueDrawCommand(); - void appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, float y2, - float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, - float x4, float y4, float z4, float u4, float v4); + void appendMeshQuad(float x1, float y1, float u1, float v1, + float x2, float y2, float u2, float v2, + float x3, float y3, float u3, float v3, + float x4, float y4, float u4, float v4, CacheTexture* texture); - uint32_t mCacheWidth; - uint32_t mCacheHeight; + uint32_t mSmallCacheWidth; + uint32_t mSmallCacheHeight; Vector<CacheTextureLine*> mCacheLines; uint32_t getRemainingCacheCapacity(); @@ -292,12 +334,13 @@ protected: Font* mCurrentFont; Vector<Font*> mActiveFonts; - // Texture to cache glyph bitmaps - uint8_t* mTextTexture; - const uint8_t* getTextTextureData() const { - return mTextTexture; - } - GLuint mTextureId; + CacheTexture* mCurrentCacheTexture; + CacheTexture* mLastCacheTexture; + CacheTexture* mCacheTextureSmall; + CacheTexture* mCacheTexture128; + CacheTexture* mCacheTexture256; + CacheTexture* mCacheTexture512; + void checkTextureUpdate(); bool mUploadTexture; @@ -308,9 +351,6 @@ protected: uint32_t mIndexBufferID; - int32_t mPositionAttrSlot; - int32_t mTexcoordAttrSlot; - const Rect* mClip; Rect* mBounds; bool mDrawn; diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp index eb863e9..1be957f 100644 --- a/libs/hwui/GammaFontRenderer.cpp +++ b/libs/hwui/GammaFontRenderer.cpp @@ -113,6 +113,13 @@ void GammaFontRenderer::flush() { delete mRenderers[min]; mRenderers[min] = NULL; + + // Also eliminate the caches for large glyphs, as they consume significant memory + for (int i = 0; i < kGammaCount; ++i) { + if (mRenderers[i]) { + mRenderers[i]->flushLargeCaches(); + } + } } FontRenderer* GammaFontRenderer::getRenderer(Gamma gamma) { diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h index 54c208e..99f08f0 100644 --- a/libs/hwui/GammaFontRenderer.h +++ b/libs/hwui/GammaFontRenderer.h @@ -50,7 +50,7 @@ struct GammaFontRenderer { FontRenderer* renderer = mRenderers[fontRenderer]; if (!renderer) return 0; - return renderer->getCacheHeight() * renderer->getCacheWidth(); + return renderer->getCacheSize(); } private: diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index 27c2677..3678788 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -171,8 +171,8 @@ void GradientCache::generateTexture(SkBitmap* bitmap, Texture* texture) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels()); - texture->setFilter(GL_LINEAR, GL_LINEAR); - texture->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); + texture->setFilter(GL_LINEAR); + texture->setWrap(GL_CLAMP_TO_EDGE); } }; // namespace uirenderer diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index a8ae5c6..ee6ef1a 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -147,12 +147,12 @@ struct Layer { this->renderTarget = renderTarget; } - void setWrap(GLenum wrapS, GLenum wrapT, bool bindTexture = false, bool force = false) { - texture.setWrap(wrapS, wrapT, bindTexture, force, renderTarget); + void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) { + texture.setWrap(wrap, bindTexture, force, renderTarget); } - void setFilter(GLenum min, GLenum mag, bool bindTexture = false, bool force = false) { - texture.setFilter(min, mag,bindTexture, force, renderTarget); + void setFilter(GLenum filter, bool bindTexture = false, bool force = false) { + texture.setFilter(filter, bindTexture, force, renderTarget); } inline bool isCacheable() { diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index 5298125..d304b37 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -20,6 +20,7 @@ #include <utils/Log.h> +#include "Caches.h" #include "Debug.h" #include "LayerCache.h" #include "Properties.h" @@ -108,8 +109,8 @@ Layer* LayerCache::get(const uint32_t width, const uint32_t height) { layer->generateTexture(); layer->bindTexture(); - layer->setFilter(GL_NEAREST, GL_NEAREST); - layer->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, false); + layer->setFilter(GL_NEAREST); + layer->setWrap(GL_CLAMP_TO_EDGE, false); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); #if DEBUG_LAYERS @@ -140,7 +141,7 @@ bool LayerCache::resize(Layer* layer, const uint32_t width, const uint32_t heigh uint32_t oldWidth = layer->getWidth(); uint32_t oldHeight = layer->getHeight(); - glActiveTexture(GL_TEXTURE0); + Caches::getInstance().activeTexture(0); layer->bindTexture(); layer->setSize(entry.mWidth, entry.mHeight); layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE); diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index 60c9d60..e320eb2 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -182,14 +182,15 @@ void LayerRenderer::generateMesh() { Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) { LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height); - GLuint fbo = Caches::getInstance().fboCache.get(); + Caches& caches = Caches::getInstance(); + GLuint fbo = caches.fboCache.get(); if (!fbo) { ALOGW("Could not obtain an FBO"); return NULL; } - glActiveTexture(GL_TEXTURE0); - Layer* layer = Caches::getInstance().layerCache.get(width, height); + caches.activeTexture(0); + Layer* layer = caches.layerCache.get(width, height); if (!layer) { ALOGW("Could not obtain a layer"); return NULL; @@ -220,7 +221,7 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque fbo, width, height); glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - Caches::getInstance().fboCache.put(fbo); + caches.fboCache.put(fbo); layer->deleteTexture(); delete layer; @@ -233,7 +234,6 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque layer->getTexture(), 0); glDisable(GL_SCISSOR_TEST); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_SCISSOR_TEST); @@ -275,7 +275,7 @@ Layer* LayerRenderer::createTextureLayer(bool isOpaque) { layer->region.clear(); layer->setRenderTarget(GL_NONE); // see ::updateTextureLayer() - glActiveTexture(GL_TEXTURE0); + Caches::getInstance().activeTexture(0); layer->generateTexture(); return layer; @@ -294,8 +294,8 @@ void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t he if (renderTarget != layer->getRenderTarget()) { layer->setRenderTarget(renderTarget); layer->bindTexture(); - layer->setFilter(GL_NEAREST, GL_NEAREST, false, true); - layer->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, false, true); + layer->setFilter(GL_NEAREST, false, true); + layer->setWrap(GL_CLAMP_TO_EDGE, false, true); } } } @@ -305,8 +305,10 @@ void LayerRenderer::destroyLayer(Layer* layer) { LAYER_RENDERER_LOGD("Recycling layer, %dx%d fbo = %d", layer->getWidth(), layer->getHeight(), layer->getFbo()); - if (layer->getFbo()) { - Caches::getInstance().fboCache.put(layer->getFbo()); + GLuint fbo = layer->getFbo(); + if (fbo) { + flushLayer(layer); + Caches::getInstance().fboCache.put(fbo); } if (!Caches::getInstance().layerCache.put(layer)) { @@ -331,6 +333,26 @@ void LayerRenderer::destroyLayerDeferred(Layer* layer) { } } +void LayerRenderer::flushLayer(Layer* layer) { +#ifdef GL_EXT_discard_framebuffer + GLuint fbo = layer->getFbo(); + if (layer && fbo) { + // If possible, discard any enqueud operations on deferred + // rendering architectures + if (Caches::getInstance().extensions.hasDiscardFramebuffer()) { + GLuint previousFbo; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); + + GLenum attachments = GL_COLOR_ATTACHMENT0; + if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, &attachments); + + if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + } + } +#endif +} + bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { Caches& caches = Caches::getInstance(); if (layer && layer->isTextureLayer() && bitmap->width() <= caches.maxTextureSize && @@ -385,7 +407,7 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { glGenTextures(1, &texture); if ((error = glGetError()) != GL_NO_ERROR) goto error; - glActiveTexture(GL_TEXTURE0); + caches.activeTexture(0); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -437,6 +459,8 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { } error: + glEnable(GL_SCISSOR_TEST); + #if DEBUG_OPENGL if (error != GL_NO_ERROR) { ALOGD("GL error while copying layer into bitmap = 0x%x", error); diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h index fe28e31..c461ea7 100644 --- a/libs/hwui/LayerRenderer.h +++ b/libs/hwui/LayerRenderer.h @@ -61,6 +61,7 @@ public: bool isOpaque, GLenum renderTarget, float* transform); ANDROID_API static void destroyLayer(Layer* layer); ANDROID_API static void destroyLayerDeferred(Layer* layer); + ANDROID_API static void flushLayer(Layer* layer); ANDROID_API static bool copyLayer(Layer* layer, SkBitmap* bitmap); private: diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 5089c5c..afae70f 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -47,6 +47,8 @@ namespace uirenderer { // TODO: This should be set in properties #define ALPHA_THRESHOLD (0x7f / PANEL_BIT_DEPTH) +#define FILTER(paint) (paint && paint->isFilterBitmap() ? GL_LINEAR : GL_NEAREST) + /////////////////////////////////////////////////////////////////////////////// // Globals /////////////////////////////////////////////////////////////////////////////// @@ -101,12 +103,6 @@ static const Blender gBlendsSwap[] = { { SkXfermode::kScreen_Mode, GL_ONE_MINUS_DST_COLOR, GL_ONE } }; -static const GLenum gTextureUnits[] = { - GL_TEXTURE0, - GL_TEXTURE1, - GL_TEXTURE2 -}; - /////////////////////////////////////////////////////////////////////////////// // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// @@ -115,6 +111,7 @@ OpenGLRenderer::OpenGLRenderer(): mCaches(Caches::getInstance()) { mShader = NULL; mColorFilter = NULL; mHasShadow = false; + mHasDrawFilter = false; memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices)); @@ -127,12 +124,26 @@ OpenGLRenderer::~OpenGLRenderer() { } /////////////////////////////////////////////////////////////////////////////// +// Debug +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::startMark(const char* name) const { + mCaches.startMark(0, name); +} + +void OpenGLRenderer::endMark() const { + mCaches.endMark(); +} + +/////////////////////////////////////////////////////////////////////////////// // Setup /////////////////////////////////////////////////////////////////////////////// +uint32_t OpenGLRenderer::getStencilSize() { + return STENCIL_BUFFER_SIZE; +} + void OpenGLRenderer::setViewport(int width, int height) { - glDisable(GL_DITHER); - glViewport(0, 0, width, height); mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); mWidth = width; @@ -141,7 +152,11 @@ void OpenGLRenderer::setViewport(int width, int height) { mFirstSnapshot->height = height; mFirstSnapshot->viewport.set(0, 0, width, height); - mDirtyClip = false; + glDisable(GL_DITHER); + glEnable(GL_SCISSOR_TEST); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + glEnableVertexAttribArray(Program::kBindingPosition); } void OpenGLRenderer::prepare(bool opaque) { @@ -154,17 +169,15 @@ void OpenGLRenderer::prepareDirty(float left, float top, float right, float bott mSnapshot = new Snapshot(mFirstSnapshot, SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); mSnapshot->fbo = getTargetFbo(); - mSaveCount = 1; glViewport(0, 0, mWidth, mHeight); + mCaches.setScissor(left, mSnapshot->height - bottom, right - left, bottom - top); - glEnable(GL_SCISSOR_TEST); - glScissor(left, mSnapshot->height - bottom, right - left, bottom - top); mSnapshot->setClip(left, top, right, bottom); + mDirtyClip = false; if (!opaque) { - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); } } @@ -198,20 +211,23 @@ void OpenGLRenderer::interrupt() { } } mCaches.unbindMeshBuffer(); + mCaches.unbindIndicesBuffer(); + mCaches.resetVertexPointers(); + mCaches.disbaleTexCoordsVertexArray(); } void OpenGLRenderer::resume() { sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot; glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight()); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glEnable(GL_SCISSOR_TEST); + mCaches.resetScissor(); dirtyClip(); - glDisable(GL_DITHER); - + mCaches.activeTexture(0); glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); mCaches.blend = true; glEnable(GL_BLEND); @@ -451,7 +467,7 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, return false; } - glActiveTexture(gTextureUnits[0]); + mCaches.activeTexture(0); Layer* layer = mCaches.layerCache.get(bounds.getWidth(), bounds.getHeight()); if (!layer) { return false; @@ -552,9 +568,8 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> sna #endif // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering - glScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f, + mCaches.setScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f, clip.getWidth() + 2.0f, clip.getHeight() + 2.0f); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); dirtyClip(); @@ -594,7 +609,7 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { mCaches.unbindMeshBuffer(); - glActiveTexture(gTextureUnits[0]); + mCaches.activeTexture(0); // When the layer is stored in an FBO, we can save a bit of fillrate by // drawing only the dirty region @@ -613,6 +628,11 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { } if (fboLayer) { + // Note: No need to use glDiscardFramebufferEXT() since we never + // create/compose layers that are not on screen with this + // code path + // See LayerRenderer::destroyLayer(Layer*) + // Detach the texture from the FBO glBindFramebuffer(GL_FRAMEBUFFER, current->fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); @@ -665,10 +685,10 @@ void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { const float x = (int) floorf(rect.left + mSnapshot->transform->getTranslateX() + 0.5f); const float y = (int) floorf(rect.top + mSnapshot->transform->getTranslateY() + 0.5f); - layer->setFilter(GL_NEAREST, GL_NEAREST); + layer->setFilter(GL_NEAREST); setupDrawModelView(x, y, x + rect.getWidth(), y + rect.getHeight(), true); } else { - layer->setFilter(GL_LINEAR, GL_LINEAR); + layer->setFilter(GL_LINEAR); setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom); } setupDrawTextureTransformUniforms(layer->getTexTransform()); @@ -702,9 +722,9 @@ void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) y = (int) floorf(rect.top + mSnapshot->transform->getTranslateY() + 0.5f); } - layer->setFilter(GL_NEAREST, GL_NEAREST, true); + layer->setFilter(GL_NEAREST, true); } else { - layer->setFilter(GL_LINEAR, GL_LINEAR, true); + layer->setFilter(GL_LINEAR, true); } drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(), @@ -734,7 +754,7 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { // TODO: See LayerRenderer.cpp::generateMesh() for important // information about this implementation - if (!layer->region.isEmpty()) { + if (CC_LIKELY(!layer->region.isEmpty())) { size_t count; const android::Rect* rects = layer->region.getArray(&count); @@ -760,13 +780,13 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { const float x = (int) floorf(rect.left + mSnapshot->transform->getTranslateX() + 0.5f); const float y = (int) floorf(rect.top + mSnapshot->transform->getTranslateY() + 0.5f); - layer->setFilter(GL_NEAREST, GL_NEAREST); + layer->setFilter(GL_NEAREST); setupDrawModelViewTranslate(x, y, x + rect.getWidth(), y + rect.getHeight(), true); } else { - layer->setFilter(GL_LINEAR, GL_LINEAR); + layer->setFilter(GL_LINEAR); setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom); } - setupDrawMesh(&mesh[0].position[0], &mesh[0].texture[0]); + setupDrawMeshIndices(&mesh[0].position[0], &mesh[0].texture[0]); for (size_t i = 0; i < count; i++) { const android::Rect* r = &rects[i]; @@ -795,7 +815,6 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { glDrawElements(GL_TRIANGLES, numQuads * 6, GL_UNSIGNED_SHORT, NULL); } - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); finishDrawTexture(); #if DEBUG_LAYERS_AS_REGIONS @@ -904,10 +923,8 @@ void OpenGLRenderer::clearLayerRegions() { setupDrawProgram(); setupDrawPureColorUniforms(); setupDrawModelViewTranslate(0.0f, 0.0f, 0.0f, 0.0f, true); + setupDrawVertices(&mesh[0].position[0]); - mCaches.unbindMeshBuffer(); - glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, - gVertexStride, &mesh[0].position[0]); glDrawArrays(GL_TRIANGLES, 0, count * 6); glEnable(GL_SCISSOR_TEST); @@ -941,7 +958,11 @@ void OpenGLRenderer::skew(float sx, float sy) { } void OpenGLRenderer::setMatrix(SkMatrix* matrix) { - mSnapshot->transform->load(*matrix); + if (matrix) { + mSnapshot->transform->load(*matrix); + } else { + mSnapshot->transform->loadIdentity(); + } } void OpenGLRenderer::getMatrix(SkMatrix* matrix) { @@ -962,7 +983,10 @@ void OpenGLRenderer::concatMatrix(SkMatrix* matrix) { void OpenGLRenderer::setScissorFromClip() { Rect clip(*mSnapshot->clipRect); clip.snapToPixelBoundaries(); - glScissor(clip.left, mSnapshot->height - clip.bottom, clip.getWidth(), clip.getHeight()); + + mCaches.setScissor(clip.left, mSnapshot->height - clip.bottom, + clip.getWidth(), clip.getHeight()); + mDirtyClip = false; } @@ -1008,7 +1032,6 @@ void OpenGLRenderer::setupDraw(bool clear) { mColorA = mColorR = mColorG = mColorB = 0.0f; mTextureUnit = 0; mTrackDirtyRegions = true; - mTexCoordsSlot = -1; } void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) { @@ -1020,6 +1043,10 @@ void OpenGLRenderer::setupDrawWithExternalTexture() { mDescription.hasExternalTexture = true; } +void OpenGLRenderer::setupDrawNoTexture() { + mCaches.disbaleTexCoordsVertexArray(); +} + void OpenGLRenderer::setupDrawAALine() { mDescription.isAA = true; } @@ -1194,25 +1221,21 @@ void OpenGLRenderer::setupDrawColorFilterUniforms() { } void OpenGLRenderer::setupDrawSimpleMesh() { - mCaches.bindMeshBuffer(); - glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, - gMeshStride, 0); + bool force = mCaches.bindMeshBuffer(); + mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, 0); + mCaches.unbindIndicesBuffer(); } void OpenGLRenderer::setupDrawTexture(GLuint texture) { bindTexture(texture); - glUniform1i(mCaches.currentProgram->getUniform("sampler"), mTextureUnit++); - - mTexCoordsSlot = mCaches.currentProgram->getAttrib("texCoords"); - glEnableVertexAttribArray(mTexCoordsSlot); + mTextureUnit++; + mCaches.enableTexCoordsVertexArray(); } void OpenGLRenderer::setupDrawExternalTexture(GLuint texture) { bindExternalTexture(texture); - glUniform1i(mCaches.currentProgram->getUniform("sampler"), mTextureUnit++); - - mTexCoordsSlot = mCaches.currentProgram->getAttrib("texCoords"); - glEnableVertexAttribArray(mTexCoordsSlot); + mTextureUnit++; + mCaches.enableTexCoordsVertexArray(); } void OpenGLRenderer::setupDrawTextureTransform() { @@ -1225,22 +1248,34 @@ void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) { } void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) { + bool force = false; if (!vertices) { - mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo); + force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo); } else { - mCaches.unbindMeshBuffer(); + force = mCaches.unbindMeshBuffer(); } - glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, - gMeshStride, vertices); - if (mTexCoordsSlot >= 0) { - glVertexAttribPointer(mTexCoordsSlot, 2, GL_FLOAT, GL_FALSE, gMeshStride, texCoords); + + mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, vertices); + if (mCaches.currentProgram->texCoords >= 0) { + mCaches.bindTexCoordsVertexPointer(force, mCaches.currentProgram->texCoords, texCoords); + } + + mCaches.unbindIndicesBuffer(); +} + +void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords) { + bool force = mCaches.unbindMeshBuffer(); + mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, vertices); + if (mCaches.currentProgram->texCoords >= 0) { + mCaches.bindTexCoordsVertexPointer(force, mCaches.currentProgram->texCoords, texCoords); } } void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) { - mCaches.unbindMeshBuffer(); - glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, - gVertexStride, vertices); + bool force = mCaches.unbindMeshBuffer(); + mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, + vertices, gVertexStride); + mCaches.unbindIndicesBuffer(); } /** @@ -1256,24 +1291,29 @@ void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) { */ void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords, GLvoid* lengthCoords, float boundaryWidthProportion) { - mCaches.unbindMeshBuffer(); - glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, - gAAVertexStride, vertices); + bool force = mCaches.unbindMeshBuffer(); + mCaches.bindPositionVertexPointer(force, mCaches.currentProgram->position, + vertices, gAAVertexStride); + mCaches.resetTexCoordsVertexPointer(); + mCaches.unbindIndicesBuffer(); + int widthSlot = mCaches.currentProgram->getAttrib("vtxWidth"); glEnableVertexAttribArray(widthSlot); glVertexAttribPointer(widthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, widthCoords); + int lengthSlot = mCaches.currentProgram->getAttrib("vtxLength"); glEnableVertexAttribArray(lengthSlot); glVertexAttribPointer(lengthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, lengthCoords); + int boundaryWidthSlot = mCaches.currentProgram->getUniform("boundaryWidth"); glUniform1f(boundaryWidthSlot, boundaryWidthProportion); + // Setting the inverse value saves computations per-fragment in the shader int inverseBoundaryWidthSlot = mCaches.currentProgram->getUniform("inverseBoundaryWidth"); glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion)); } void OpenGLRenderer::finishDrawTexture() { - glDisableVertexAttribArray(mTexCoordsSlot); } /////////////////////////////////////////////////////////////////////////////// @@ -1316,6 +1356,8 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, Sk y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f); ignoreTransform = true; filter = GL_NEAREST; + } else { + filter = FILTER(paint); } setupDraw(); @@ -1330,8 +1372,8 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, Sk setupDrawModelView(x, y, x + texture->width, y + texture->height, ignoreTransform); setupDrawTexture(texture->id); - texture->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); - texture->setFilter(filter, filter); + texture->setWrap(GL_CLAMP_TO_EDGE); + texture->setFilter(filter); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); @@ -1351,12 +1393,12 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint return; } - glActiveTexture(gTextureUnits[0]); + mCaches.activeTexture(0); Texture* texture = mCaches.textureCache.get(bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); - if (bitmap->getConfig() == SkBitmap::kA8_Config) { + if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) { drawAlphaBitmap(texture, left, top, paint); } else { drawTextureRect(left, top, right, bottom, texture, paint); @@ -1372,7 +1414,7 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* pai return; } - glActiveTexture(gTextureUnits[0]); + mCaches.activeTexture(0); Texture* texture = mCaches.textureCache.get(bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); @@ -1392,13 +1434,13 @@ void OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHei return; } - glActiveTexture(gTextureUnits[0]); + mCaches.activeTexture(0); Texture* texture = mCaches.textureCache.get(bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); - texture->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, true); - texture->setFilter(GL_LINEAR, GL_LINEAR, true); + texture->setWrap(GL_CLAMP_TO_EDGE, true); + texture->setFilter(FILTER(paint), true); int alpha; SkXfermode::Mode mode; @@ -1412,9 +1454,9 @@ void OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHei float bottom = FLT_MIN; #if RENDER_LAYERS_AS_REGIONS - bool hasActiveLayer = hasLayer(); + const bool hasActiveLayer = hasLayer(); #else - bool hasActiveLayer = false; + const bool hasActiveLayer = false; #endif // TODO: Support the colors array @@ -1477,11 +1519,10 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, return; } - glActiveTexture(gTextureUnits[0]); + mCaches.activeTexture(0); Texture* texture = mCaches.textureCache.get(bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); - texture->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, true); const float width = texture->width; const float height = texture->height; @@ -1498,24 +1539,25 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkXfermode::Mode mode; getAlphaAndMode(paint, &alpha, &mode); - if (mSnapshot->transform->isPureTranslate()) { + texture->setWrap(GL_CLAMP_TO_EDGE, true); + + if (CC_LIKELY(mSnapshot->transform->isPureTranslate())) { const float x = (int) floorf(dstLeft + mSnapshot->transform->getTranslateX() + 0.5f); const float y = (int) floorf(dstTop + mSnapshot->transform->getTranslateY() + 0.5f); GLenum filter = GL_NEAREST; // Enable linear filtering if the source rectangle is scaled if (srcRight - srcLeft != dstRight - dstLeft || srcBottom - srcTop != dstBottom - dstTop) { - filter = GL_LINEAR; + filter = FILTER(paint); } - texture->setFilter(filter, filter, true); + texture->setFilter(filter, true); drawTextureMesh(x, y, x + (dstRight - dstLeft), y + (dstBottom - dstTop), texture->id, alpha / 255.0f, mode, texture->blend, &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, false, true); } else { - texture->setFilter(GL_LINEAR, GL_LINEAR, true); - + texture->setFilter(FILTER(paint), true); drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom, texture->id, alpha / 255.0f, mode, texture->blend, &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount); @@ -1531,12 +1573,12 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int return; } - glActiveTexture(gTextureUnits[0]); + mCaches.activeTexture(0); Texture* texture = mCaches.textureCache.get(bitmap); if (!texture) return; const AutoTexture autoCleanup(texture); - texture->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, true); - texture->setFilter(GL_LINEAR, GL_LINEAR, true); + texture->setWrap(GL_CLAMP_TO_EDGE, true); + texture->setFilter(GL_LINEAR, true); int alpha; SkXfermode::Mode mode; @@ -1545,7 +1587,7 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int const Patch* mesh = mCaches.patchCache.get(bitmap->width(), bitmap->height(), right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors); - if (mesh && mesh->verticesCount > 0) { + if (CC_LIKELY(mesh && mesh->verticesCount > 0)) { const bool pureTranslate = mSnapshot->transform->isPureTranslate(); #if RENDER_LAYERS_AS_REGIONS // Mark the current layer dirty where we are going to draw the patch @@ -1555,7 +1597,7 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int const size_t count = mesh->quads.size(); for (size_t i = 0; i < count; i++) { const Rect& bounds = mesh->quads.itemAt(i); - if (pureTranslate) { + if (CC_LIKELY(pureTranslate)) { const float x = (int) floorf(bounds.left + offsetX + 0.5f); const float y = (int) floorf(bounds.top + offsetY + 0.5f); dirtyLayer(x, y, x + bounds.getWidth(), y + bounds.getHeight()); @@ -1567,7 +1609,7 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int } #endif - if (pureTranslate) { + if (CC_LIKELY(pureTranslate)) { const float x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f); const float y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f); @@ -1595,7 +1637,7 @@ void OpenGLRenderer::drawAARect(float left, float top, float right, float bottom float inverseScaleX = 1.0f; float inverseScaleY = 1.0f; // The quad that we use needs to account for scaling. - if (!mSnapshot->transform->isPureTranslate()) { + if (CC_UNLIKELY(!mSnapshot->transform->isPureTranslate())) { Matrix4 *mat = mSnapshot->transform; float m00 = mat->data[Matrix4::kScaleX]; float m01 = mat->data[Matrix4::kSkewY]; @@ -1610,6 +1652,7 @@ void OpenGLRenderer::drawAARect(float left, float top, float right, float bottom } setupDraw(); + setupDrawNoTexture(); setupDrawAALine(); setupDrawColor(color); setupDrawColorFilter(); @@ -1700,7 +1743,7 @@ void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { // The quad that we use for AA and hairlines needs to account for scaling. For hairlines // the line on the screen should always be one pixel wide regardless of scale. For // AA lines, we only want one pixel of translucent boundary around the quad. - if (!mSnapshot->transform->isPureTranslate()) { + if (CC_UNLIKELY(!mSnapshot->transform->isPureTranslate())) { Matrix4 *mat = mSnapshot->transform; float m00 = mat->data[Matrix4::kScaleX]; float m01 = mat->data[Matrix4::kSkewY]; @@ -1708,8 +1751,8 @@ void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { float m10 = mat->data[Matrix4::kSkewX]; float m11 = mat->data[Matrix4::kScaleX]; float m12 = mat->data[6]; - float scaleX = sqrt(m00*m00 + m01*m01); - float scaleY = sqrt(m10*m10 + m11*m11); + float scaleX = sqrtf(m00 * m00 + m01 * m01); + float scaleY = sqrtf(m10 * m10 + m11 * m11); inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0; inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0; if (inverseScaleX != 1.0f || inverseScaleY != 1.0f) { @@ -1720,17 +1763,14 @@ void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { getAlphaAndMode(paint, &alpha, &mode); setupDraw(); + setupDrawNoTexture(); if (isAA) { setupDrawAALine(); } setupDrawColor(paint->getColor(), alpha); setupDrawColorFilter(); setupDrawShader(); - if (isAA) { - setupDrawBlending(true, mode); - } else { - setupDrawBlending(mode); - } + setupDrawBlending(isAA, mode); setupDrawProgram(); setupDrawModelViewIdentity(true); setupDrawColorUniforms(); @@ -1748,7 +1788,7 @@ void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { Vertex* vertices = &lines[0]; AAVertex wLines[verticesCount]; AAVertex* aaVertices = &wLines[0]; - if (!isAA) { + if (CC_UNLIKELY(!isAA)) { setupDrawVertices(vertices); } else { void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset; @@ -1929,6 +1969,7 @@ void OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) { TextureVertex* vertex = &pointsData[0]; setupDraw(); + setupDrawNoTexture(); setupDrawPoint(strokeWidth); setupDrawColor(paint->getColor(), alpha); setupDrawColorFilter(); @@ -1979,7 +2020,7 @@ void OpenGLRenderer::drawRoundRect(float left, float top, float right, float bot float rx, float ry, SkPaint* paint) { if (mSnapshot->isIgnored()) return; - glActiveTexture(gTextureUnits[0]); + mCaches.activeTexture(0); const PathTexture* texture = mCaches.roundRectShapeCache.getRoundRect( right - left, bottom - top, rx, ry, paint); drawShape(left, top, texture, paint); @@ -1988,7 +2029,7 @@ void OpenGLRenderer::drawRoundRect(float left, float top, float right, float bot void OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) { if (mSnapshot->isIgnored()) return; - glActiveTexture(gTextureUnits[0]); + mCaches.activeTexture(0); const PathTexture* texture = mCaches.circleShapeCache.getCircle(radius, paint); drawShape(x - radius, y - radius, texture, paint); } @@ -1996,7 +2037,7 @@ void OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) void OpenGLRenderer::drawOval(float left, float top, float right, float bottom, SkPaint* paint) { if (mSnapshot->isIgnored()) return; - glActiveTexture(gTextureUnits[0]); + mCaches.activeTexture(0); const PathTexture* texture = mCaches.ovalShapeCache.getOval(right - left, bottom - top, paint); drawShape(left, top, texture, paint); } @@ -2010,7 +2051,7 @@ void OpenGLRenderer::drawArc(float left, float top, float right, float bottom, return; } - glActiveTexture(gTextureUnits[0]); + mCaches.activeTexture(0); const PathTexture* texture = mCaches.arcShapeCache.getArc(right - left, bottom - top, startAngle, sweepAngle, useCenter, paint); drawShape(left, top, texture, paint); @@ -2020,7 +2061,7 @@ void OpenGLRenderer::drawRectAsShape(float left, float top, float right, float b SkPaint* paint) { if (mSnapshot->isIgnored()) return; - glActiveTexture(gTextureUnits[0]); + mCaches.activeTexture(0); const PathTexture* texture = mCaches.rectShapeCache.getRect(right - left, bottom - top, paint); drawShape(left, top, texture, paint); } @@ -2054,46 +2095,117 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, } } -void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, - float x, float y, SkPaint* paint) { - if (text == NULL || count == 0) { +void OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count, + const float* positions, SkPaint* paint) { + if (text == NULL || count == 0 || mSnapshot->isIgnored() || + (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) { + return; + } + + // NOTE: Skia does not support perspective transform on drawPosText yet + if (!mSnapshot->transform->isSimple()) { return; } - if (mSnapshot->isIgnored()) return; - // TODO: We should probably make a copy of the paint instead of modifying - // it; modifying the paint will change its generationID the first - // time, which might impact caches. More investigation needed to - // see if it matters. - // If we make a copy, then drawTextDecorations() should *not* make - // its own copy as it does right now. - paint->setAntiAlias(true); -#if RENDER_TEXT_AS_GLYPHS - paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); + float x = 0.0f; + float y = 0.0f; + const bool pureTranslate = mSnapshot->transform->isPureTranslate(); + if (pureTranslate) { + x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f); + y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f); + } + + FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint); + fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), + paint->getTextSize()); + + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + // Pick the appropriate texture filtering + bool linearFilter = mSnapshot->transform->changesBounds(); + if (pureTranslate && !linearFilter) { + linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f; + } + + mCaches.activeTexture(0); + setupDraw(); + setupDrawDirtyRegionsDisabled(); + setupDrawWithTexture(true); + setupDrawAlpha8Color(paint->getColor(), alpha); + setupDrawColorFilter(); + setupDrawShader(); + setupDrawBlending(true, mode); + setupDrawProgram(); + setupDrawModelView(x, y, x, y, pureTranslate, true); + setupDrawTexture(fontRenderer.getTexture(linearFilter)); + setupDrawPureColorUniforms(); + setupDrawColorFilterUniforms(); + setupDrawShaderUniforms(pureTranslate); + + const Rect* clip = pureTranslate ? mSnapshot->clipRect : &mSnapshot->getLocalClip(); + Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); + +#if RENDER_LAYERS_AS_REGIONS + const bool hasActiveLayer = hasLayer(); +#else + const bool hasActiveLayer = false; #endif - float length = -1.0f; + if (fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y, + positions, hasActiveLayer ? &bounds : NULL)) { +#if RENDER_LAYERS_AS_REGIONS + if (hasActiveLayer) { + if (!pureTranslate) { + mSnapshot->transform->mapRect(bounds); + } + dirtyLayerUnchecked(bounds, getRegion()); + } +#endif + } +} + +void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, + float x, float y, SkPaint* paint, float length) { + if (text == NULL || count == 0 || mSnapshot->isIgnored() || + (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) { + return; + } + switch (paint->getTextAlign()) { case SkPaint::kCenter_Align: - length = paint->measureText(text, bytesCount); + if (length < 0.0f) length = paint->measureText(text, bytesCount); x -= length / 2.0f; break; case SkPaint::kRight_Align: - length = paint->measureText(text, bytesCount); + if (length < 0.0f) length = paint->measureText(text, bytesCount); x -= length; break; default: break; } + SkPaint::FontMetrics metrics; + paint->getFontMetrics(&metrics, 0.0f); + // If no length was specified, just perform the hit test on the Y axis + if (quickReject(x, y + metrics.fTop, + x + (length >= 0.0f ? length : INT_MAX / 2), y + metrics.fBottom)) { + return; + } + const float oldX = x; const float oldY = y; const bool pureTranslate = mSnapshot->transform->isPureTranslate(); - if (pureTranslate) { + if (CC_LIKELY(pureTranslate)) { x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f); y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f); } +#if DEBUG_GLYPHS + ALOGD("OpenGLRenderer drawText() with FontID=%d", SkTypeface::UniqueID(paint->getTypeface())); +#endif + FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint); fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize()); @@ -2102,7 +2214,9 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, SkXfermode::Mode mode; getAlphaAndMode(paint, &alpha, &mode); - if (mHasShadow) { + if (CC_UNLIKELY(mHasShadow)) { + mCaches.activeTexture(0); + mCaches.dropShadowCache.setFontRenderer(fontRenderer); const ShadowTexture* shadow = mCaches.dropShadowCache.get( paint, text, bytesCount, count, mShadowRadius); @@ -2117,7 +2231,6 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, shadowColor = 0xffffffff; } - glActiveTexture(gTextureUnits[0]); setupDraw(); setupDrawWithTexture(true); setupDrawAlpha8Color(shadowColor, shadowAlpha < 255 ? shadowAlpha : alpha); @@ -2133,12 +2246,6 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset); glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); - - finishDrawTexture(); - } - - if (paint->getAlpha() == 0 && paint->getXfermode() == NULL) { - return; } // Pick the appropriate texture filtering @@ -2147,7 +2254,7 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f; } - glActiveTexture(gTextureUnits[0]); + mCaches.activeTexture(0); setupDraw(); setupDrawDirtyRegionsDisabled(); setupDrawWithTexture(true); @@ -2166,16 +2273,11 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); #if RENDER_LAYERS_AS_REGIONS - bool hasActiveLayer = hasLayer(); + const bool hasActiveLayer = hasLayer(); #else - bool hasActiveLayer = false; + const bool hasActiveLayer = false; #endif - mCaches.unbindMeshBuffer(); - // Tell font renderer the locations of position and texture coord - // attributes so it can bind its data properly - int positionSlot = mCaches.currentProgram->position; - fontRenderer.setAttributeBindingSlots(positionSlot, mTexCoordsSlot); if (fontRenderer.renderText(paint, clip, text, 0, bytesCount, count, x, y, hasActiveLayer ? &bounds : NULL)) { #if RENDER_LAYERS_AS_REGIONS @@ -2188,16 +2290,13 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, #endif } - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords")); - drawTextDecorations(text, bytesCount, length, oldX, oldY, paint); } void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { if (mSnapshot->isIgnored()) return; - glActiveTexture(gTextureUnits[0]); + mCaches.activeTexture(0); const PathTexture* texture = mCaches.pathCache.get(path, paint); if (!texture) return; @@ -2214,7 +2313,7 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) { return; } - glActiveTexture(gTextureUnits[0]); + mCaches.activeTexture(0); int alpha; SkXfermode::Mode mode; @@ -2223,7 +2322,7 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) { layer->setAlpha(alpha, mode); #if RENDER_LAYERS_AS_REGIONS - if (!layer->region.isEmpty()) { + if (CC_LIKELY(!layer->region.isEmpty())) { if (layer->region.isRect()) { composeLayerRect(layer, layer->regionRect); } else if (layer->mesh) { @@ -2239,15 +2338,15 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) { setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); setupDrawTexture(layer->getTexture()); - if (mSnapshot->transform->isPureTranslate()) { + if (CC_LIKELY(mSnapshot->transform->isPureTranslate())) { x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f); y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f); - layer->setFilter(GL_NEAREST, GL_NEAREST); + layer->setFilter(GL_NEAREST); setupDrawModelViewTranslate(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(), true); } else { - layer->setFilter(GL_LINEAR, GL_LINEAR); + layer->setFilter(GL_LINEAR); setupDrawModelViewTranslate(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight()); } @@ -2313,6 +2412,31 @@ void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) { } /////////////////////////////////////////////////////////////////////////////// +// Draw filters +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::resetPaintFilter() { + mHasDrawFilter = false; +} + +void OpenGLRenderer::setupPaintFilter(int clearBits, int setBits) { + mHasDrawFilter = true; + mPaintFilterClearBits = clearBits & SkPaint::kAllFlags; + mPaintFilterSetBits = setBits & SkPaint::kAllFlags; +} + +SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) { + if (!mHasDrawFilter || !paint) return paint; + + uint32_t flags = paint->getFlags(); + + mFilteredPaint = *paint; + mFilteredPaint.setFlags((flags & ~mPaintFilterClearBits) | mPaintFilterSetBits); + + return &mFilteredPaint; +} + +/////////////////////////////////////////////////////////////////////////////// // Drawing implementation /////////////////////////////////////////////////////////////////////////////// @@ -2374,7 +2498,7 @@ void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float break; } - if (underlineWidth > 0.0f) { + if (CC_LIKELY(underlineWidth > 0.0f)) { const float textSize = paintCopy.getTextSize(); const float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); @@ -2420,6 +2544,7 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot } setupDraw(); + setupDrawNoTexture(); setupDrawColor(color); setupDrawShader(); setupDrawColorFilter(); @@ -2440,18 +2565,18 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b SkXfermode::Mode mode; getAlphaAndMode(paint, &alpha, &mode); - texture->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, true); + texture->setWrap(GL_CLAMP_TO_EDGE, true); - if (mSnapshot->transform->isPureTranslate()) { + if (CC_LIKELY(mSnapshot->transform->isPureTranslate())) { const float x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f); const float y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f); - texture->setFilter(GL_NEAREST, GL_NEAREST, true); + texture->setFilter(GL_NEAREST, true); drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id, alpha / 255.0f, mode, texture->blend, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount, false, true); } else { - texture->setFilter(GL_LINEAR, GL_LINEAR, true); + texture->setFilter(FILTER(paint), true); drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode, texture->blend, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount); @@ -2497,32 +2622,38 @@ void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description, bool swapSrcDst) { blend = blend || mode != SkXfermode::kSrcOver_Mode; if (blend) { - if (mode <= SkXfermode::kScreen_Mode) { - if (!mCaches.blend) { - glEnable(GL_BLEND); - } - - GLenum sourceMode = swapSrcDst ? gBlendsSwap[mode].src : gBlends[mode].src; - GLenum destMode = swapSrcDst ? gBlendsSwap[mode].dst : gBlends[mode].dst; - - if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) { - glBlendFunc(sourceMode, destMode); - mCaches.lastSrcMode = sourceMode; - mCaches.lastDstMode = destMode; - } - } else { - // These blend modes are not supported by OpenGL directly and have - // to be implemented using shaders. Since the shader will perform - // the blending, turn blending off here - if (mCaches.extensions.hasFramebufferFetch()) { + // These blend modes are not supported by OpenGL directly and have + // to be implemented using shaders. Since the shader will perform + // the blending, turn blending off here + // If the blend mode cannot be implemented using shaders, fall + // back to the default SrcOver blend mode instead + if CC_UNLIKELY((mode > SkXfermode::kScreen_Mode)) { + if (CC_UNLIKELY(mCaches.extensions.hasFramebufferFetch())) { description.framebufferMode = mode; description.swapSrcDst = swapSrcDst; - } - if (mCaches.blend) { - glDisable(GL_BLEND); + if (mCaches.blend) { + glDisable(GL_BLEND); + mCaches.blend = false; + } + + return; + } else { + mode = SkXfermode::kSrcOver_Mode; } - blend = false; + } + + if (!mCaches.blend) { + glEnable(GL_BLEND); + } + + GLenum sourceMode = swapSrcDst ? gBlendsSwap[mode].src : gBlends[mode].src; + GLenum destMode = swapSrcDst ? gBlendsSwap[mode].dst : gBlends[mode].dst; + + if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) { + glBlendFunc(sourceMode, destMode); + mCaches.lastSrcMode = sourceMode; + mCaches.lastDstMode = destMode; } } else if (mCaches.blend) { glDisable(GL_BLEND); diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 2fc88e1..3c2d09e 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -123,6 +123,8 @@ public: virtual void drawLines(float* points, int count, SkPaint* paint); virtual void drawPoints(float* points, int count, SkPaint* paint); virtual void drawText(const char* text, int bytesCount, int count, float x, float y, + SkPaint* paint, float length = -1.0f); + virtual void drawPosText(const char* text, int bytesCount, int count, const float* positions, SkPaint* paint); virtual void resetShader(); @@ -134,6 +136,16 @@ public: virtual void resetShadow(); virtual void setupShadow(float radius, float dx, float dy, int color); + virtual void resetPaintFilter(); + virtual void setupPaintFilter(int clearBits, int setBits); + + SkPaint* filterPaint(SkPaint* paint); + + ANDROID_API static uint32_t getStencilSize(); + + void startMark(const char* name) const; + void endMark() const; + protected: /** * Compose the layer defined in the current snapshot with the layer @@ -498,6 +510,7 @@ private: */ void setupDrawWithTexture(bool isAlpha8 = false); void setupDrawWithExternalTexture(); + void setupDrawNoTexture(); void setupDrawAALine(); void setupDrawPoint(float pointSize); void setupDrawColor(int color); @@ -530,6 +543,7 @@ private: void setupDrawTextureTransform(); void setupDrawTextureTransformUniforms(mat4& transform); void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0); + void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords); void setupDrawVertices(GLvoid* vertices); void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, GLvoid* lengthCoords, float strokeWidth); @@ -577,6 +591,12 @@ private: float mShadowDy; int mShadowColor; + // Draw filters + bool mHasDrawFilter; + int mPaintFilterClearBits; + int mPaintFilterSetBits; + SkPaint mFilteredPaint; + // Various caches Caches& mCaches; @@ -601,8 +621,6 @@ private: GLuint mTextureUnit; // Track dirty regions, true by default bool mTrackDirtyRegions; - // Texture coordinates slot - int mTexCoordsSlot; friend class DisplayListRenderer; diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp index 47a2c99..27f530c 100644 --- a/libs/hwui/Patch.cpp +++ b/libs/hwui/Patch.cpp @@ -197,7 +197,8 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, } if (verticesCount > 0) { - Caches::getInstance().bindMeshBuffer(meshBuffer); + Caches& caches = Caches::getInstance(); + caches.bindMeshBuffer(meshBuffer); if (!mUploaded) { glBufferData(GL_ARRAY_BUFFER, sizeof(TextureVertex) * verticesCount, mVertices, GL_DYNAMIC_DRAW); @@ -206,6 +207,7 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(TextureVertex) * verticesCount, mVertices); } + caches.resetVertexPointers(); } PATCH_LOGD(" patch: new vertices count = %d", verticesCount); diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index 367c627..e893f7a 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -34,8 +34,8 @@ PathCache::PathCache(): ShapeCache<PathCacheEntry>("path", void PathCache::remove(SkPath* path) { // TODO: Linear search... - Vector<uint32_t> pathsToRemove; - for (uint32_t i = 0; i < mCache.size(); i++) { + Vector<size_t> pathsToRemove; + for (size_t i = 0; i < mCache.size(); i++) { if (mCache.getKeyAt(i).path == path) { pathsToRemove.push(i); removeTexture(mCache.getValueAt(i)); diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp index 043a092..984461c 100644 --- a/libs/hwui/Program.cpp +++ b/libs/hwui/Program.cpp @@ -25,80 +25,107 @@ namespace uirenderer { // Base program /////////////////////////////////////////////////////////////////////////////// -Program::Program(const char* vertex, const char* fragment) { +// TODO: Program instance should be created from a factory method +Program::Program(const ProgramDescription& description, const char* vertex, const char* fragment) { mInitialized = false; + mHasColorUniform = false; + mHasSampler = false; + mUse = false; - vertexShader = buildShader(vertex, GL_VERTEX_SHADER); - if (vertexShader) { + // No need to cache compiled shaders, rely instead on Android's + // persistent shaders cache + mVertexShader = buildShader(vertex, GL_VERTEX_SHADER); + if (mVertexShader) { + mFragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER); + if (mFragmentShader) { + mProgramId = glCreateProgram(); - fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER); - if (fragmentShader) { + glAttachShader(mProgramId, mVertexShader); + glAttachShader(mProgramId, mFragmentShader); - id = glCreateProgram(); - glAttachShader(id, vertexShader); - glAttachShader(id, fragmentShader); - glLinkProgram(id); + position = bindAttrib("position", kBindingPosition); + if (description.hasTexture || description.hasExternalTexture) { + texCoords = bindAttrib("texCoords", kBindingTexCoords); + } else { + texCoords = -1; + } + + glLinkProgram(mProgramId); GLint status; - glGetProgramiv(id, GL_LINK_STATUS, &status); + glGetProgramiv(mProgramId, GL_LINK_STATUS, &status); if (status != GL_TRUE) { ALOGE("Error while linking shaders:"); GLint infoLen = 0; - glGetProgramiv(id, GL_INFO_LOG_LENGTH, &infoLen); + glGetProgramiv(mProgramId, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen > 1) { GLchar log[infoLen]; - glGetProgramInfoLog(id, infoLen, 0, &log[0]); + glGetProgramInfoLog(mProgramId, infoLen, 0, &log[0]); ALOGE("%s", log); } - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); - glDeleteProgram(id); + + glDetachShader(mProgramId, mVertexShader); + glDetachShader(mProgramId, mFragmentShader); + + glDeleteShader(mVertexShader); + glDeleteShader(mFragmentShader); + + glDeleteProgram(mProgramId); } else { mInitialized = true; } + } else { + glDeleteShader(mVertexShader); } } - mUse = false; - if (mInitialized) { - position = addAttrib("position"); transform = addUniform("transform"); } } Program::~Program() { if (mInitialized) { - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); - glDeleteProgram(id); + glDetachShader(mProgramId, mVertexShader); + glDetachShader(mProgramId, mFragmentShader); + + glDeleteShader(mVertexShader); + glDeleteShader(mFragmentShader); + + glDeleteProgram(mProgramId); } } int Program::addAttrib(const char* name) { - int slot = glGetAttribLocation(id, name); - attributes.add(name, slot); + int slot = glGetAttribLocation(mProgramId, name); + mAttributes.add(name, slot); return slot; } +int Program::bindAttrib(const char* name, ShaderBindings bindingSlot) { + glBindAttribLocation(mProgramId, bindingSlot, name); + mAttributes.add(name, bindingSlot); + return bindingSlot; +} + int Program::getAttrib(const char* name) { - ssize_t index = attributes.indexOfKey(name); + ssize_t index = mAttributes.indexOfKey(name); if (index >= 0) { - return attributes.valueAt(index); + return mAttributes.valueAt(index); } return addAttrib(name); } int Program::addUniform(const char* name) { - int slot = glGetUniformLocation(id, name); - uniforms.add(name, slot); + int slot = glGetUniformLocation(mProgramId, name); + mUniforms.add(name, slot); return slot; } int Program::getUniform(const char* name) { - ssize_t index = uniforms.indexOfKey(name); + ssize_t index = mUniforms.indexOfKey(name); if (index >= 0) { - return uniforms.valueAt(index); + return mUniforms.valueAt(index); } return addUniform(name); } @@ -127,10 +154,11 @@ void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix, const mat4& transformMatrix, bool offset) { mat4 t(projectionMatrix); if (offset) { - // offset screenspace xy by an amount that compensates for typical precision issues - // in GPU hardware that tends to paint hor/vert lines in pixels shifted up and to the left. - // This offset value is based on an assumption that some hardware may use as little - // as 12.4 precision, so we offset by slightly more than 1/16. + // offset screenspace xy by an amount that compensates for typical precision + // issues in GPU hardware that tends to paint hor/vert lines in pixels shifted + // up and to the left. + // This offset value is based on an assumption that some hardware may use as + // little as 12.4 precision, so we offset by slightly more than 1/16. t.translate(.375, .375, 0); } t.multiply(transformMatrix); @@ -140,20 +168,24 @@ void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix, } void Program::setColor(const float r, const float g, const float b, const float a) { - glUniform4f(getUniform("color"), r, g, b, a); + if (!mHasColorUniform) { + mColorUniform = getUniform("color"); + mHasColorUniform = true; + } + glUniform4f(mColorUniform, r, g, b, a); } void Program::use() { - glUseProgram(id); + glUseProgram(mProgramId); + if (texCoords >= 0 && !mHasSampler) { + glUniform1i(getUniform("sampler"), 0); + mHasSampler = true; + } mUse = true; - - glEnableVertexAttribArray(position); } void Program::remove() { mUse = false; - - glDisableVertexAttribArray(position); } }; // namespace uirenderer diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index 764cb05..eb9ee7b 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -17,27 +17,280 @@ #ifndef ANDROID_HWUI_PROGRAM_H #define ANDROID_HWUI_PROGRAM_H +#include <utils/KeyedVector.h> + #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> -#include <utils/KeyedVector.h> +#include <SkXfermode.h> #include "Matrix.h" +#include "Properties.h" namespace android { namespace uirenderer { +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +// Debug +#if DEBUG_PROGRAMS + #define PROGRAM_LOGD(...) ALOGD(__VA_ARGS__) +#else + #define PROGRAM_LOGD(...) +#endif + +#define COLOR_COMPONENT_THRESHOLD (1.0f - (0.5f / PANEL_BIT_DEPTH)) +#define COLOR_COMPONENT_INV_THRESHOLD (0.5f / PANEL_BIT_DEPTH) + +#define PROGRAM_KEY_TEXTURE 0x1 +#define PROGRAM_KEY_A8_TEXTURE 0x2 +#define PROGRAM_KEY_BITMAP 0x4 +#define PROGRAM_KEY_GRADIENT 0x8 +#define PROGRAM_KEY_BITMAP_FIRST 0x10 +#define PROGRAM_KEY_COLOR_MATRIX 0x20 +#define PROGRAM_KEY_COLOR_LIGHTING 0x40 +#define PROGRAM_KEY_COLOR_BLEND 0x80 +#define PROGRAM_KEY_BITMAP_NPOT 0x100 +#define PROGRAM_KEY_SWAP_SRC_DST 0x2000 + +#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 +#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800 + +// Encode the xfermodes on 6 bits +#define PROGRAM_MAX_XFERMODE 0x1f +#define PROGRAM_XFERMODE_SHADER_SHIFT 26 +#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20 +#define PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT 14 + +#define PROGRAM_BITMAP_WRAPS_SHIFT 9 +#define PROGRAM_BITMAP_WRAPT_SHIFT 11 + +#define PROGRAM_GRADIENT_TYPE_SHIFT 33 +#define PROGRAM_MODULATE_SHIFT 35 + +#define PROGRAM_IS_POINT_SHIFT 36 + +#define PROGRAM_HAS_AA_SHIFT 37 + +#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38 +#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39 + +/////////////////////////////////////////////////////////////////////////////// +// Types +/////////////////////////////////////////////////////////////////////////////// + +typedef uint64_t programid; + +/////////////////////////////////////////////////////////////////////////////// +// Program description +/////////////////////////////////////////////////////////////////////////////// + +/** + * Describe the features required for a given program. The features + * determine the generation of both the vertex and fragment shaders. + * A ProgramDescription must be used in conjunction with a ProgramCache. + */ +struct ProgramDescription { + enum ColorModifier { + kColorNone, + kColorMatrix, + kColorLighting, + kColorBlend + }; + + enum Gradient { + kGradientLinear, + kGradientCircular, + kGradientSweep + }; + + ProgramDescription() { + reset(); + } + + // Texturing + bool hasTexture; + bool hasAlpha8Texture; + bool hasExternalTexture; + bool hasTextureTransform; + + // Modulate, this should only be set when setColor() return true + bool modulate; + + // Shaders + bool hasBitmap; + bool isBitmapNpot; + + bool isAA; + + bool hasGradient; + Gradient gradientType; + + SkXfermode::Mode shadersMode; + + bool isBitmapFirst; + GLenum bitmapWrapS; + GLenum bitmapWrapT; + + // Color operations + ColorModifier colorOp; + SkXfermode::Mode colorMode; + + // Framebuffer blending (requires Extensions.hasFramebufferFetch()) + // Ignored for all values < SkXfermode::kPlus_Mode + SkXfermode::Mode framebufferMode; + bool swapSrcDst; + + bool isPoint; + float pointSize; + + /** + * Resets this description. All fields are reset back to the default + * values they hold after building a new instance. + */ + void reset() { + hasTexture = false; + hasAlpha8Texture = false; + hasExternalTexture = false; + hasTextureTransform = false; + + isAA = false; + + modulate = false; + + hasBitmap = false; + isBitmapNpot = false; + + hasGradient = false; + gradientType = kGradientLinear; + + shadersMode = SkXfermode::kClear_Mode; + + isBitmapFirst = false; + bitmapWrapS = GL_CLAMP_TO_EDGE; + bitmapWrapT = GL_CLAMP_TO_EDGE; + + colorOp = kColorNone; + colorMode = SkXfermode::kClear_Mode; + + framebufferMode = SkXfermode::kClear_Mode; + swapSrcDst = false; + + isPoint = false; + pointSize = 0.0f; + } + + /** + * Indicates, for a given color, whether color modulation is required in + * the fragment shader. When this method returns true, the program should + * be provided with a modulation color. + */ + bool setColor(const float r, const float g, const float b, const float a) { + modulate = a < COLOR_COMPONENT_THRESHOLD || r < COLOR_COMPONENT_THRESHOLD || + g < COLOR_COMPONENT_THRESHOLD || b < COLOR_COMPONENT_THRESHOLD; + return modulate; + } + + /** + * Indicates, for a given color, whether color modulation is required in + * the fragment shader. When this method returns true, the program should + * be provided with a modulation color. + */ + bool setAlpha8Color(const float r, const float g, const float b, const float a) { + modulate = a < COLOR_COMPONENT_THRESHOLD || r > COLOR_COMPONENT_INV_THRESHOLD || + g > COLOR_COMPONENT_INV_THRESHOLD || b > COLOR_COMPONENT_INV_THRESHOLD; + return modulate; + } + + /** + * Computes the unique key identifying this program. + */ + programid key() const { + programid key = 0; + if (hasTexture) key |= PROGRAM_KEY_TEXTURE; + if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE; + if (hasBitmap) { + key |= PROGRAM_KEY_BITMAP; + if (isBitmapNpot) { + key |= PROGRAM_KEY_BITMAP_NPOT; + key |= getEnumForWrap(bitmapWrapS) << PROGRAM_BITMAP_WRAPS_SHIFT; + key |= getEnumForWrap(bitmapWrapT) << PROGRAM_BITMAP_WRAPT_SHIFT; + } + } + if (hasGradient) key |= PROGRAM_KEY_GRADIENT; + key |= programid(gradientType) << PROGRAM_GRADIENT_TYPE_SHIFT; + if (isBitmapFirst) key |= PROGRAM_KEY_BITMAP_FIRST; + if (hasBitmap && hasGradient) { + key |= (shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT; + } + switch (colorOp) { + case kColorMatrix: + key |= PROGRAM_KEY_COLOR_MATRIX; + break; + case kColorLighting: + key |= PROGRAM_KEY_COLOR_LIGHTING; + break; + case kColorBlend: + key |= PROGRAM_KEY_COLOR_BLEND; + key |= (colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT; + break; + case kColorNone: + break; + } + key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT; + if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST; + if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT; + if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT; + if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT; + if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT; + if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT; + return key; + } + + /** + * Logs the specified message followed by the key identifying this program. + */ + void log(const char* message) const { +#if DEBUG_PROGRAMS + programid k = key(); + PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32), + uint32_t(k & 0xffffffff)); +#endif + } + +private: + inline uint32_t getEnumForWrap(GLenum wrap) const { + switch (wrap) { + case GL_CLAMP_TO_EDGE: + return 0; + case GL_REPEAT: + return 1; + case GL_MIRRORED_REPEAT: + return 2; + } + return 0; + } + +}; // struct ProgramDescription + /** * A program holds a vertex and a fragment shader. It offers several utility * methods to query attributes and uniforms. */ class Program { public: + enum ShaderBindings { + kBindingPosition, + kBindingTexCoords + }; + /** * Creates a new program with the specified vertex and fragment * shaders sources. */ - Program(const char* vertex, const char* fragment); + Program(const ProgramDescription& description, const char* vertex, const char* fragment); virtual ~Program(); /** @@ -94,6 +347,11 @@ public: int position; /** + * Name of the texCoords attribute if it exists, -1 otherwise. + */ + int texCoords; + + /** * Name of the transform uniform. */ int transform; @@ -107,6 +365,11 @@ protected: int addAttrib(const char* name); /** + * Binds the specified attribute name to the specified slot. + */ + int bindAttrib(const char* name, ShaderBindings bindingSlot); + + /** * Adds a uniform with the specified name. * * @return The OpenGL name of the uniform. @@ -121,19 +384,22 @@ private: */ GLuint buildShader(const char* source, GLenum type); - // Name of the OpenGL program - GLuint id; - - // Name of the shaders - GLuint vertexShader; - GLuint fragmentShader; + // Name of the OpenGL program and shaders + GLuint mProgramId; + GLuint mVertexShader; + GLuint mFragmentShader; // Keeps track of attributes and uniforms slots - KeyedVector<const char*, int> attributes; - KeyedVector<const char*, int> uniforms; + KeyedVector<const char*, int> mAttributes; + KeyedVector<const char*, int> mUniforms; bool mUse; bool mInitialized; + + bool mHasColorUniform; + int mColorUniform; + + bool mHasSampler; }; // class Program }; // namespace uirenderer diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index c2383f4..a7f1277 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -388,7 +388,7 @@ Program* ProgramCache::generateProgram(const ProgramDescription& description, pr String8 vertexShader = generateVertexShader(description); String8 fragmentShader = generateFragmentShader(description); - Program* program = new Program(vertexShader.string(), fragmentShader.string()); + Program* program = new Program(description, vertexShader.string(), fragmentShader.string()); return program; } diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index 0ff2148..6cfe0c7 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -23,10 +23,9 @@ #include <GLES2/gl2.h> -#include <SkXfermode.h> - #include "Debug.h" #include "Program.h" +#include "Properties.h" namespace android { namespace uirenderer { @@ -42,243 +41,11 @@ namespace uirenderer { #define PROGRAM_LOGD(...) #endif -// TODO: This should be set in properties -#define PANEL_BIT_DEPTH 20 -#define COLOR_COMPONENT_THRESHOLD (1.0f - (0.5f / PANEL_BIT_DEPTH)) -#define COLOR_COMPONENT_INV_THRESHOLD (0.5f / PANEL_BIT_DEPTH) - -#define PROGRAM_KEY_TEXTURE 0x1 -#define PROGRAM_KEY_A8_TEXTURE 0x2 -#define PROGRAM_KEY_BITMAP 0x4 -#define PROGRAM_KEY_GRADIENT 0x8 -#define PROGRAM_KEY_BITMAP_FIRST 0x10 -#define PROGRAM_KEY_COLOR_MATRIX 0x20 -#define PROGRAM_KEY_COLOR_LIGHTING 0x40 -#define PROGRAM_KEY_COLOR_BLEND 0x80 -#define PROGRAM_KEY_BITMAP_NPOT 0x100 -#define PROGRAM_KEY_SWAP_SRC_DST 0x2000 - -#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 -#define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800 - -// Encode the xfermodes on 6 bits -#define PROGRAM_MAX_XFERMODE 0x1f -#define PROGRAM_XFERMODE_SHADER_SHIFT 26 -#define PROGRAM_XFERMODE_COLOR_OP_SHIFT 20 -#define PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT 14 - -#define PROGRAM_BITMAP_WRAPS_SHIFT 9 -#define PROGRAM_BITMAP_WRAPT_SHIFT 11 - -#define PROGRAM_GRADIENT_TYPE_SHIFT 33 -#define PROGRAM_MODULATE_SHIFT 35 - -#define PROGRAM_IS_POINT_SHIFT 36 - -#define PROGRAM_HAS_AA_SHIFT 37 - -#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38 -#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39 - -/////////////////////////////////////////////////////////////////////////////// -// Types -/////////////////////////////////////////////////////////////////////////////// - -typedef uint64_t programid; - /////////////////////////////////////////////////////////////////////////////// // Cache /////////////////////////////////////////////////////////////////////////////// /** - * Describe the features required for a given program. The features - * determine the generation of both the vertex and fragment shaders. - * A ProgramDescription must be used in conjunction with a ProgramCache. - */ -struct ProgramDescription { - enum ColorModifier { - kColorNone, - kColorMatrix, - kColorLighting, - kColorBlend - }; - - enum Gradient { - kGradientLinear, - kGradientCircular, - kGradientSweep - }; - - ProgramDescription() { - reset(); - } - - // Texturing - bool hasTexture; - bool hasAlpha8Texture; - bool hasExternalTexture; - bool hasTextureTransform; - - // Modulate, this should only be set when setColor() return true - bool modulate; - - // Shaders - bool hasBitmap; - bool isBitmapNpot; - - bool isAA; - - bool hasGradient; - Gradient gradientType; - - SkXfermode::Mode shadersMode; - - bool isBitmapFirst; - GLenum bitmapWrapS; - GLenum bitmapWrapT; - - // Color operations - ColorModifier colorOp; - SkXfermode::Mode colorMode; - - // Framebuffer blending (requires Extensions.hasFramebufferFetch()) - // Ignored for all values < SkXfermode::kPlus_Mode - SkXfermode::Mode framebufferMode; - bool swapSrcDst; - - bool isPoint; - float pointSize; - - /** - * Resets this description. All fields are reset back to the default - * values they hold after building a new instance. - */ - void reset() { - hasTexture = false; - hasAlpha8Texture = false; - hasExternalTexture = false; - hasTextureTransform = false; - - isAA = false; - - modulate = false; - - hasBitmap = false; - isBitmapNpot = false; - - hasGradient = false; - gradientType = kGradientLinear; - - shadersMode = SkXfermode::kClear_Mode; - - isBitmapFirst = false; - bitmapWrapS = GL_CLAMP_TO_EDGE; - bitmapWrapT = GL_CLAMP_TO_EDGE; - - colorOp = kColorNone; - colorMode = SkXfermode::kClear_Mode; - - framebufferMode = SkXfermode::kClear_Mode; - swapSrcDst = false; - - isPoint = false; - pointSize = 0.0f; - } - - /** - * Indicates, for a given color, whether color modulation is required in - * the fragment shader. When this method returns true, the program should - * be provided with a modulation color. - */ - bool setColor(const float r, const float g, const float b, const float a) { - modulate = a < COLOR_COMPONENT_THRESHOLD || r < COLOR_COMPONENT_THRESHOLD || - g < COLOR_COMPONENT_THRESHOLD || b < COLOR_COMPONENT_THRESHOLD; - return modulate; - } - - /** - * Indicates, for a given color, whether color modulation is required in - * the fragment shader. When this method returns true, the program should - * be provided with a modulation color. - */ - bool setAlpha8Color(const float r, const float g, const float b, const float a) { - modulate = a < COLOR_COMPONENT_THRESHOLD || r > COLOR_COMPONENT_INV_THRESHOLD || - g > COLOR_COMPONENT_INV_THRESHOLD || b > COLOR_COMPONENT_INV_THRESHOLD; - return modulate; - } - - /** - * Computes the unique key identifying this program. - */ - programid key() const { - programid key = 0; - if (hasTexture) key |= PROGRAM_KEY_TEXTURE; - if (hasAlpha8Texture) key |= PROGRAM_KEY_A8_TEXTURE; - if (hasBitmap) { - key |= PROGRAM_KEY_BITMAP; - if (isBitmapNpot) { - key |= PROGRAM_KEY_BITMAP_NPOT; - key |= getEnumForWrap(bitmapWrapS) << PROGRAM_BITMAP_WRAPS_SHIFT; - key |= getEnumForWrap(bitmapWrapT) << PROGRAM_BITMAP_WRAPT_SHIFT; - } - } - if (hasGradient) key |= PROGRAM_KEY_GRADIENT; - key |= programid(gradientType) << PROGRAM_GRADIENT_TYPE_SHIFT; - if (isBitmapFirst) key |= PROGRAM_KEY_BITMAP_FIRST; - if (hasBitmap && hasGradient) { - key |= (shadersMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_SHADER_SHIFT; - } - switch (colorOp) { - case kColorMatrix: - key |= PROGRAM_KEY_COLOR_MATRIX; - break; - case kColorLighting: - key |= PROGRAM_KEY_COLOR_LIGHTING; - break; - case kColorBlend: - key |= PROGRAM_KEY_COLOR_BLEND; - key |= (colorMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_COLOR_OP_SHIFT; - break; - case kColorNone: - break; - } - key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT; - if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST; - if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT; - if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT; - if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT; - if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT; - if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT; - return key; - } - - /** - * Logs the specified message followed by the key identifying this program. - */ - void log(const char* message) const { -#if DEBUG_PROGRAMS - programid k = key(); - PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32), - uint32_t(k & 0xffffffff)); -#endif - } - -private: - inline uint32_t getEnumForWrap(GLenum wrap) const { - switch (wrap) { - case GL_CLAMP_TO_EDGE: - return 0; - case GL_REPEAT: - return 1; - case GL_MIRRORED_REPEAT: - return 2; - } - return 0; - } - -}; // struct ProgramDescription - -/** * Generates and caches program. Programs are generated based on * ProgramDescriptions. */ diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 8c01e3a..7854729 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -37,6 +37,11 @@ // Textures used by layers must have dimensions multiples of this number #define LAYER_SIZE 64 +// Defines the size in bits of the stencil buffer +// Note: Only 1 bit is required for clipping but more bits are required +// to properly implement the winding fill rule when rasterizing paths +#define STENCIL_BUFFER_SIZE 0 + /** * Debug level for app developers. */ @@ -73,6 +78,9 @@ enum DebugLevel { #define PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD "ro.text_gamma.black_threshold" #define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "ro.text_gamma.white_threshold" +// TODO: This should be set by a system property +#define PANEL_BIT_DEPTH 20 + // Converts a number of mega-bytes into bytes #define MB(s) s * 1024 * 1024 diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h index 5baada3..2ca4f50 100644 --- a/libs/hwui/Rect.h +++ b/libs/hwui/Rect.h @@ -29,17 +29,6 @@ namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// class Rect { - static inline float min(float a, float b) { return (a<b) ? a : b; } - static inline float max(float a, float b) { return (a>b) ? a : b; } - Rect intersectWith(float l, float t, float r, float b) const { - Rect tmp; - tmp.left = max(left, l); - tmp.top = max(top, t); - tmp.right = min(right, r); - tmp.bottom = min(bottom, b); - return tmp; - } - public: float left; float top; @@ -115,7 +104,7 @@ public: } bool intersects(float l, float t, float r, float b) const { - return !intersectWith(l,t,r,b).isEmpty(); + return !intersectWith(l, t, r, b).isEmpty(); } bool intersects(const Rect& r) const { @@ -123,7 +112,8 @@ public: } bool intersect(float l, float t, float r, float b) { - Rect tmp(intersectWith(l,t,r,b)); + Rect tmp(l, t, r, b); + intersectWith(tmp); if (!tmp.isEmpty()) { set(tmp); return true; @@ -135,6 +125,14 @@ public: return intersect(r.left, r.top, r.right, r.bottom); } + bool contains(float l, float t, float r, float b) { + return l >= left && t >= top && r <= right && b <= bottom; + } + + bool contains(const Rect& r) { + return contains(r.left, r.top, r.right, r.bottom); + } + bool unionWith(const Rect& r) { if (r.left < r.right && r.top < r.bottom) { if (left < right && top < bottom) { @@ -172,6 +170,26 @@ public: ALOGD("Rect[l=%f t=%f r=%f b=%f]", left, top, right, bottom); } +private: + static inline float min(float a, float b) { return (a < b) ? a : b; } + static inline float max(float a, float b) { return (a > b) ? a : b; } + + void intersectWith(Rect& tmp) const { + tmp.left = max(left, tmp.left); + tmp.top = max(top, tmp.top); + tmp.right = min(right, tmp.right); + tmp.bottom = min(bottom, tmp.bottom); + } + + Rect intersectWith(float l, float t, float r, float b) const { + Rect tmp; + tmp.left = max(left, l); + tmp.top = max(top, t); + tmp.right = min(right, r); + tmp.bottom = min(bottom, b); + return tmp; + } + }; // class Rect }; // namespace uirenderer diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h index c07a6c9..30ce690 100644 --- a/libs/hwui/ShapeCache.h +++ b/libs/hwui/ShapeCache.h @@ -503,7 +503,8 @@ PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *pat const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5); if (width > mMaxTextureSize || height > mMaxTextureSize) { - ALOGW("Shape %s too large to be rendered into a texture", mName); + ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)", + mName, width, height, mMaxTextureSize, mMaxTextureSize); return NULL; } @@ -584,8 +585,8 @@ void ShapeCache<Entry>::generateTexture(SkBitmap& bitmap, Texture* texture) { glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels()); - texture->setFilter(GL_LINEAR, GL_LINEAR); - texture->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); + texture->setFilter(GL_LINEAR); + texture->setWrap(GL_CLAMP_TO_EDGE); } }; // namespace uirenderer diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 2428295..66993a4 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -20,6 +20,7 @@ #include <SkMatrix.h> +#include "Caches.h" #include "SkiaShader.h" #include "Texture.h" #include "Matrix.h" @@ -31,12 +32,6 @@ namespace uirenderer { // Support /////////////////////////////////////////////////////////////////////////////// -static const GLenum gTextureUnitsMap[] = { - GL_TEXTURE0, - GL_TEXTURE1, - GL_TEXTURE2 -}; - static const GLint gTileModes[] = { GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode GL_REPEAT, // == SkShader::kRepeat_Mode @@ -77,7 +72,7 @@ void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Sna void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT) { glBindTexture(GL_TEXTURE_2D, texture->id); - texture->setWrap(wrapS, wrapT); + texture->setWrapST(wrapS, wrapT); } void SkiaShader::computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView) { @@ -129,7 +124,7 @@ void SkiaBitmapShader::describe(ProgramDescription& description, const Extension void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, GLuint* textureUnit) { GLuint textureSlot = (*textureUnit)++; - glActiveTexture(gTextureUnitsMap[textureSlot]); + Caches::getInstance().activeTexture(textureSlot); Texture* texture = mTexture; mTexture = NULL; @@ -148,7 +143,7 @@ void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView, // ::updateTransforms() but we don't have the texture object // available at that point. The optimization is not worth the // effort for now. - texture->setFilter(GL_LINEAR, GL_LINEAR); + texture->setFilter(GL_LINEAR); glUniform1i(program->getUniform("bitmapSampler"), textureSlot); glUniformMatrix4fv(program->getUniform("textureTransform"), 1, @@ -223,7 +218,7 @@ void SkiaLinearGradientShader::describe(ProgramDescription& description, void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, GLuint* textureUnit) { GLuint textureSlot = (*textureUnit)++; - glActiveTexture(gTextureUnitsMap[textureSlot]); + Caches::getInstance().activeTexture(textureSlot); Texture* texture = mGradientCache->get(mColors, mPositions, mCount, mTileX); @@ -335,7 +330,7 @@ void SkiaSweepGradientShader::describe(ProgramDescription& description, void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, GLuint* textureUnit) { GLuint textureSlot = (*textureUnit)++; - glActiveTexture(gTextureUnitsMap[textureSlot]); + Caches::getInstance().activeTexture(textureSlot); Texture* texture = mGradientCache->get(mColors, mPositions, mCount); diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp new file mode 100644 index 0000000..a85362d --- /dev/null +++ b/libs/hwui/Snapshot.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2012 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 "Snapshot.h" + +#include <SkCanvas.h> + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors +/////////////////////////////////////////////////////////////////////////////// + +Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), + invisible(false), empty(false) { + + transform = &mTransformRoot; + clipRect = &mClipRectRoot; + region = NULL; +} + +/** + * Copies the specified snapshot/ The specified snapshot is stored as + * the previous snapshot. + */ +Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags): + flags(0), previous(s), layer(NULL), fbo(s->fbo), + invisible(s->invisible), empty(false), + viewport(s->viewport), height(s->height) { + + if (saveFlags & SkCanvas::kMatrix_SaveFlag) { + mTransformRoot.load(*s->transform); + transform = &mTransformRoot; + } else { + transform = s->transform; + } + + if (saveFlags & SkCanvas::kClip_SaveFlag) { + mClipRectRoot.set(*s->clipRect); + clipRect = &mClipRectRoot; + } else { + clipRect = s->clipRect; + } + + if (s->flags & Snapshot::kFlagFboTarget) { + flags |= Snapshot::kFlagFboTarget; + region = s->region; + } else { + region = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Clipping +/////////////////////////////////////////////////////////////////////////////// + +bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) { + Rect r(left, top, right, bottom); + transform->mapRect(r); + return clipTransformed(r, op); +} + +bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) { + bool clipped = false; + + // NOTE: The unimplemented operations require support for regions + // Supporting regions would require using a stencil buffer instead + // of the scissor. The stencil buffer itself is not too expensive + // (memory cost excluded) but on fillrate limited devices, managing + // the stencil might have a negative impact on the framerate. + switch (op) { + case SkRegion::kDifference_Op: + break; + case SkRegion::kIntersect_Op: + clipped = clipRect->intersect(r); + if (!clipped) { + clipRect->setEmpty(); + clipped = true; + } + break; + case SkRegion::kUnion_Op: + clipped = clipRect->unionWith(r); + break; + case SkRegion::kXOR_Op: + break; + case SkRegion::kReverseDifference_Op: + break; + case SkRegion::kReplace_Op: + clipRect->set(r); + clipped = true; + break; + } + + if (clipped) { + flags |= Snapshot::kFlagClipSet; + } + + return clipped; +} + +void Snapshot::setClip(float left, float top, float right, float bottom) { + clipRect->set(left, top, right, bottom); + flags |= Snapshot::kFlagClipSet; +} + +const Rect& Snapshot::getLocalClip() { + mat4 inverse; + inverse.loadInverse(*transform); + + mLocalClip.set(*clipRect); + inverse.mapRect(mLocalClip); + + return mLocalClip; +} + +void Snapshot::resetClip(float left, float top, float right, float bottom) { + clipRect = &mClipRectRoot; + clipRect->set(left, top, right, bottom); + flags |= Snapshot::kFlagClipSet; +} + +/////////////////////////////////////////////////////////////////////////////// +// Transforms +/////////////////////////////////////////////////////////////////////////////// + +void Snapshot::resetTransform(float x, float y, float z) { + transform = &mTransformRoot; + transform->loadTranslate(x, y, z); +} + +/////////////////////////////////////////////////////////////////////////////// +// Queries +/////////////////////////////////////////////////////////////////////////////// + +bool Snapshot::isIgnored() const { + return invisible || empty; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index aff7b93..c94af7e 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -23,7 +23,7 @@ #include <utils/RefBase.h> #include <ui/Region.h> -#include <SkCanvas.h> +#include <SkRegion.h> #include "Layer.h" #include "Matrix.h" @@ -43,43 +43,12 @@ namespace uirenderer { */ class Snapshot: public LightRefBase<Snapshot> { public: - Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), invisible(false), empty(false) { - transform = &mTransformRoot; - clipRect = &mClipRectRoot; - region = NULL; - } - /** - * Copies the specified snapshot/ The specified snapshot is stored as - * the previous snapshot. - */ - Snapshot(const sp<Snapshot>& s, int saveFlags): - flags(0), previous(s), layer(NULL), fbo(s->fbo), - invisible(s->invisible), empty(false), viewport(s->viewport), height(s->height) { - if (saveFlags & SkCanvas::kMatrix_SaveFlag) { - mTransformRoot.load(*s->transform); - transform = &mTransformRoot; - } else { - transform = s->transform; - } - - if (saveFlags & SkCanvas::kClip_SaveFlag) { - mClipRectRoot.set(*s->clipRect); - clipRect = &mClipRectRoot; - } else { - clipRect = s->clipRect; - } - - if (s->flags & Snapshot::kFlagFboTarget) { - flags |= Snapshot::kFlagFboTarget; - region = s->region; - } else { - region = NULL; - } - } + Snapshot(); + Snapshot(const sp<Snapshot>& s, int saveFlags); /** - * Various flags set on #flags. + * Various flags set on ::flags. */ enum Flags { /** @@ -115,87 +84,41 @@ public: * by this snapshot's trasnformation. */ bool clip(float left, float top, float right, float bottom, - SkRegion::Op op = SkRegion::kIntersect_Op) { - Rect r(left, top, right, bottom); - transform->mapRect(r); - return clipTransformed(r, op); - } + SkRegion::Op op = SkRegion::kIntersect_Op); /** * Modifies the current clip with the new clip rectangle and * the specified operation. The specified rectangle is considered * already transformed. */ - bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op) { - bool clipped = false; - - // NOTE: The unimplemented operations require support for regions - // Supporting regions would require using a stencil buffer instead - // of the scissor. The stencil buffer itself is not too expensive - // (memory cost excluded) but on fillrate limited devices, managing - // the stencil might have a negative impact on the framerate. - switch (op) { - case SkRegion::kDifference_Op: - break; - case SkRegion::kIntersect_Op: - clipped = clipRect->intersect(r); - if (!clipped) { - clipRect->setEmpty(); - clipped = true; - } - break; - case SkRegion::kUnion_Op: - clipped = clipRect->unionWith(r); - break; - case SkRegion::kXOR_Op: - break; - case SkRegion::kReverseDifference_Op: - break; - case SkRegion::kReplace_Op: - clipRect->set(r); - clipped = true; - break; - } - - if (clipped) { - flags |= Snapshot::kFlagClipSet; - } - - return clipped; - } + bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op); /** * Sets the current clip. */ - void setClip(float left, float top, float right, float bottom) { - clipRect->set(left, top, right, bottom); - flags |= Snapshot::kFlagClipSet; - } - - const Rect& getLocalClip() { - mat4 inverse; - inverse.loadInverse(*transform); + void setClip(float left, float top, float right, float bottom); - mLocalClip.set(*clipRect); - inverse.mapRect(mLocalClip); - - return mLocalClip; - } + /** + * Returns the current clip in local coordinates. The clip rect is + * transformed by the inverse transform matrix. + */ + const Rect& getLocalClip(); - void resetTransform(float x, float y, float z) { - transform = &mTransformRoot; - transform->loadTranslate(x, y, z); - } + /** + * Resets the clip to the specified rect. + */ + void resetClip(float left, float top, float right, float bottom); - void resetClip(float left, float top, float right, float bottom) { - clipRect = &mClipRectRoot; - clipRect->set(left, top, right, bottom); - flags |= Snapshot::kFlagClipSet; - } + /** + * Resets the current transform to a pure 3D translation. + */ + void resetTransform(float x, float y, float z); - bool isIgnored() const { - return invisible || empty; - } + /** + * Indicates whether this snapshot should be ignored. A snapshot + * is typicalled ignored if its layer is invisible or empty. + */ + bool isIgnored() const; /** * Dirty flags. @@ -209,6 +132,8 @@ public: /** * Only set when the flag kFlagIsLayer is set. + * + * This snapshot does not own the layer, this pointer must not be freed. */ Layer* layer; @@ -249,17 +174,26 @@ public: /** * Local transformation. Holds the current translation, scale and * rotation values. + * + * This is a reference to a matrix owned by this snapshot or another + * snapshot. This pointer must not be freed. See ::mTransformRoot. */ mat4* transform; /** * Current clip region. The clip is stored in canvas-space coordinates, * (screen-space coordinates in the regular case.) + * + * This is a reference to a rect owned by this snapshot or another + * snapshot. This pointer must not be freed. See ::mClipRectRoot. */ Rect* clipRect; /** * The ancestor layer's dirty region. + * + * This is a reference to a region owned by a layer. This pointer must + * not be freed. */ Region* region; diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp index ee8d828..bef1373 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -137,8 +137,8 @@ ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image); - texture->setFilter(GL_LINEAR, GL_LINEAR); - texture->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); + texture->setFilter(GL_LINEAR); + texture->setWrap(GL_CLAMP_TO_EDGE); if (size < mMaxSize) { if (mDebugEnabled) { diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index 48229b6..1adf2c7 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -40,11 +40,16 @@ struct Texture { firstWrap = true; } - void setWrap(GLenum wrapS, GLenum wrapT, bool bindTexture = false, bool force = false, + void setWrap(GLenum wrap, bool bindTexture = false, bool force = false, + GLenum renderTarget = GL_TEXTURE_2D) { + setWrapST(wrap, wrap, bindTexture, force, renderTarget); + } + + void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, bool force = false, GLenum renderTarget = GL_TEXTURE_2D) { if (firstWrap || force || wrapS != this->wrapS || wrapT != this->wrapT) { - firstWrap = true; + firstWrap = false; this->wrapS = wrapS; this->wrapT = wrapT; @@ -58,7 +63,12 @@ struct Texture { } } - void setFilter(GLenum min, GLenum mag, bool bindTexture = false, bool force = false, + void setFilter(GLenum filter, bool bindTexture = false, bool force = false, + GLenum renderTarget = GL_TEXTURE_2D) { + setFilterMinMag(filter, filter, bindTexture, force, renderTarget); + } + + void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false, bool force = false, GLenum renderTarget = GL_TEXTURE_2D) { if (firstFilter || force || min != minFilter || mag != magFilter) { diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 7a88f2a..cc09aae 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -125,7 +125,8 @@ Texture* TextureCache::get(SkBitmap* bitmap) { if (!texture) { if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) { - ALOGW("Bitmap too large to be uploaded into a texture"); + ALOGW("Bitmap too large to be uploaded into a texture (%dx%d, max=%dx%d)", + bitmap->width(), bitmap->height(), mMaxTextureSize, mMaxTextureSize); return NULL; } @@ -217,11 +218,15 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege texture->height = bitmap->height(); glBindTexture(GL_TEXTURE_2D, texture->id); - glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); + if (!regenerate) { + glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); + } switch (bitmap->getConfig()) { case SkBitmap::kA8_Config: - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (!regenerate) { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + } uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels()); texture->blend = true; @@ -248,8 +253,10 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege break; } - texture->setFilter(GL_LINEAR, GL_LINEAR); - texture->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); + if (!regenerate) { + texture->setFilter(GL_NEAREST); + texture->setWrap(GL_CLAMP_TO_EDGE); + } } void TextureCache::uploadLoFiTexture(bool resize, SkBitmap* bitmap, diff --git a/libs/rs/driver/rsdBcc.cpp b/libs/rs/driver/rsdBcc.cpp index a36bae9..bec6fff 100644 --- a/libs/rs/driver/rsdBcc.cpp +++ b/libs/rs/driver/rsdBcc.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2011-2012 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. @@ -36,6 +36,7 @@ using namespace android::renderscript; struct DrvScript { int (*mRoot)(); + int (*mRootExpand)(); void (*mInit)(); void (*mFreeChildren)(); @@ -52,6 +53,10 @@ struct DrvScript { uint32_t mScriptTextLength; }; +typedef void (*outer_foreach_t)( + const android::renderscript::RsForEachStubParamStruct *, + uint32_t x1, uint32_t x2, + uint32_t instep, uint32_t outstep); static Script * setTLS(Script *sc) { ScriptTLSStruct * tls = (ScriptTLSStruct *)pthread_getspecific(rsdgThreadTLSKey); @@ -72,7 +77,7 @@ bool rsdScriptInit(const Context *rsc, //ALOGE("rsdScriptCreate %p %p %p %p %i %i %p", rsc, resName, cacheDir, bitcode, bitcodeSize, flags, lookupFunc); pthread_mutex_lock(&rsdgInitMutex); - char *cachePath = NULL; + size_t exportFuncCount = 0; size_t exportVarCount = 0; size_t objectSlotCount = 0; @@ -122,9 +127,8 @@ bool rsdScriptInit(const Context *rsc, goto error; } - free(cachePath); - drv->mRoot = reinterpret_cast<int (*)()>(bccGetFuncAddr(drv->mBccScript, "root")); + drv->mRootExpand = reinterpret_cast<int (*)()>(bccGetFuncAddr(drv->mBccScript, "root.expand")); drv->mInit = reinterpret_cast<void (*)()>(bccGetFuncAddr(drv->mBccScript, "init")); drv->mFreeChildren = reinterpret_cast<void (*)()>(bccGetFuncAddr(drv->mBccScript, ".rs.dtor")); @@ -167,7 +171,12 @@ bool rsdScriptInit(const Context *rsc, script->mHal.info.exportedPragmaCount = drv->ME->getPragmaCount(); script->mHal.info.exportedPragmaKeyList = drv->ME->getPragmaKeyList(); script->mHal.info.exportedPragmaValueList = drv->ME->getPragmaValueList(); - script->mHal.info.root = drv->mRoot; + + if (drv->mRootExpand) { + script->mHal.info.root = drv->mRootExpand; + } else { + script->mHal.info.root = drv->mRoot; + } pthread_mutex_unlock(&rsdgInitMutex); return true; @@ -226,7 +235,7 @@ static void wc_xy(void *usr, uint32_t idx) { RsdHal * dc = (RsdHal *)mtls->rsc->mHal.drv; uint32_t sig = mtls->sig; - outer_foreach_t fn = dc->mForEachLaunch[sig]; + outer_foreach_t fn = (outer_foreach_t) mtls->script->mHal.info.root; while (1) { uint32_t slice = (uint32_t)android_atomic_inc(&mtls->mSliceNum); uint32_t yStart = mtls->yStart + slice * mtls->mSliceSize; @@ -242,8 +251,7 @@ static void wc_xy(void *usr, uint32_t idx) { uint32_t offset = mtls->dimX * p.y; p.out = mtls->ptrOut + (mtls->eStrideOut * offset); p.in = mtls->ptrIn + (mtls->eStrideIn * offset); - fn(&mtls->script->mHal.info.root, &p, mtls->xStart, mtls->xEnd, - mtls->eStrideIn, mtls->eStrideOut); + fn(&p, mtls->xStart, mtls->xEnd, mtls->eStrideIn, mtls->eStrideOut); } } } @@ -257,7 +265,7 @@ static void wc_x(void *usr, uint32_t idx) { RsdHal * dc = (RsdHal *)mtls->rsc->mHal.drv; uint32_t sig = mtls->sig; - outer_foreach_t fn = dc->mForEachLaunch[sig]; + outer_foreach_t fn = (outer_foreach_t) mtls->script->mHal.info.root; while (1) { uint32_t slice = (uint32_t)android_atomic_inc(&mtls->mSliceNum); uint32_t xStart = mtls->xStart + slice * mtls->mSliceSize; @@ -272,7 +280,7 @@ static void wc_x(void *usr, uint32_t idx) { p.out = mtls->ptrOut + (mtls->eStrideOut * xStart); p.in = mtls->ptrIn + (mtls->eStrideIn * xStart); - fn(&mtls->script->mHal.info.root, &p, xStart, xEnd, mtls->eStrideIn, mtls->eStrideOut); + fn(&p, xStart, xEnd, mtls->eStrideIn, mtls->eStrideOut); } } @@ -383,7 +391,7 @@ void rsdScriptInvokeForEach(const Context *rsc, uint32_t sig = mtls.sig; //ALOGE("launch 3"); - outer_foreach_t fn = dc->mForEachLaunch[sig]; + outer_foreach_t fn = (outer_foreach_t) mtls.script->mHal.info.root; for (p.ar[0] = mtls.arrayStart; p.ar[0] < mtls.arrayEnd; p.ar[0]++) { for (p.z = mtls.zStart; p.z < mtls.zEnd; p.z++) { for (p.y = mtls.yStart; p.y < mtls.yEnd; p.y++) { @@ -392,8 +400,8 @@ void rsdScriptInvokeForEach(const Context *rsc, mtls.dimX * p.y; p.out = mtls.ptrOut + (mtls.eStrideOut * offset); p.in = mtls.ptrIn + (mtls.eStrideIn * offset); - fn(&mtls.script->mHal.info.root, &p, mtls.xStart, mtls.xEnd, - mtls.eStrideIn, mtls.eStrideOut); + fn(&p, mtls.xStart, mtls.xEnd, mtls.eStrideIn, + mtls.eStrideOut); } } } diff --git a/libs/rs/driver/rsdCore.cpp b/libs/rs/driver/rsdCore.cpp index b514e21..1a535d0 100644 --- a/libs/rs/driver/rsdCore.cpp +++ b/libs/rs/driver/rsdCore.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2011-2012 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. @@ -43,7 +43,6 @@ using namespace android::renderscript; static void Shutdown(Context *rsc); static void SetPriority(const Context *rsc, int32_t priority); -static void initForEach(outer_foreach_t* forEachLaunch); static RsdHalFunctions FunctionTable = { rsdGLInit, @@ -208,8 +207,6 @@ bool rsdHalInit(Context *rsc, uint32_t version_major, uint32_t version_minor) { rsdgThreadTLSKeyCount++; pthread_mutex_unlock(&rsdgInitMutex); - initForEach(dc->mForEachLaunch); - dc->mTlsStruct.mContext = rsc; dc->mTlsStruct.mScript = NULL; int status = pthread_setspecific(rsdgThreadTLSKey, &dc->mTlsStruct); @@ -291,173 +288,3 @@ void Shutdown(Context *rsc) { } -static void rsdForEach17(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p, - uint32_t x1, uint32_t x2, - uint32_t instep, uint32_t outstep) { - typedef void (*fe)(const void *, uint32_t); - (*(fe*)vRoot)(p->in, p->y); -} - -static void rsdForEach18(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p, - uint32_t x1, uint32_t x2, - uint32_t instep, uint32_t outstep) { - typedef void (*fe)(void *, uint32_t); - (*(fe*)vRoot)(p->out, p->y); -} - -static void rsdForEach19(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p, - uint32_t x1, uint32_t x2, - uint32_t instep, uint32_t outstep) { - typedef void (*fe)(const void *, void *, uint32_t); - (*(fe*)vRoot)(p->in, p->out, p->y); -} - -static void rsdForEach21(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p, - uint32_t x1, uint32_t x2, - uint32_t instep, uint32_t outstep) { - typedef void (*fe)(const void *, const void *, uint32_t); - (*(fe*)vRoot)(p->in, p->usr, p->y); -} - -static void rsdForEach22(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p, - uint32_t x1, uint32_t x2, - uint32_t instep, uint32_t outstep) { - typedef void (*fe)(void *, const void *, uint32_t); - (*(fe*)vRoot)(p->out, p->usr, p->y); -} - -static void rsdForEach23(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p, - uint32_t x1, uint32_t x2, - uint32_t instep, uint32_t outstep) { - typedef void (*fe)(const void *, void *, const void *, uint32_t); - (*(fe*)vRoot)(p->in, p->out, p->usr, p->y); -} - -static void rsdForEach25(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p, - uint32_t x1, uint32_t x2, - uint32_t instep, uint32_t outstep) { - typedef void (*fe)(const void *, uint32_t, uint32_t); - const uint8_t *pin = (const uint8_t *)p->in; - uint32_t y = p->y; - for (uint32_t x = x1; x < x2; x++) { - (*(fe*)vRoot)(pin, x, y); - pin += instep; - } -} - -static void rsdForEach26(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p, - uint32_t x1, uint32_t x2, - uint32_t instep, uint32_t outstep) { - typedef void (*fe)(void *, uint32_t, uint32_t); - uint8_t *pout = (uint8_t *)p->out; - uint32_t y = p->y; - for (uint32_t x = x1; x < x2; x++) { - (*(fe*)vRoot)(pout, x, y); - pout += outstep; - } -} - -static void rsdForEach27(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p, - uint32_t x1, uint32_t x2, - uint32_t instep, uint32_t outstep) { - typedef void (*fe)(const void *, void *, uint32_t, uint32_t); - uint8_t *pout = (uint8_t *)p->out; - const uint8_t *pin = (const uint8_t *)p->in; - uint32_t y = p->y; - for (uint32_t x = x1; x < x2; x++) { - (*(fe*)vRoot)(pin, pout, x, y); - pin += instep; - pout += outstep; - } -} - -static void rsdForEach29(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p, - uint32_t x1, uint32_t x2, - uint32_t instep, uint32_t outstep) { - typedef void (*fe)(const void *, const void *, uint32_t, uint32_t); - const uint8_t *pin = (const uint8_t *)p->in; - const void *usr = p->usr; - const uint32_t y = p->y; - for (uint32_t x = x1; x < x2; x++) { - (*(fe*)vRoot)(pin, usr, x, y); - pin += instep; - } -} - -static void rsdForEach30(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p, - uint32_t x1, uint32_t x2, - uint32_t instep, uint32_t outstep) { - typedef void (*fe)(void *, const void *, uint32_t, uint32_t); - uint8_t *pout = (uint8_t *)p->out; - const void *usr = p->usr; - const uint32_t y = p->y; - for (uint32_t x = x1; x < x2; x++) { - (*(fe*)vRoot)(pout, usr, x, y); - pout += outstep; - } -} - -static void rsdForEach31(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p, - uint32_t x1, uint32_t x2, - uint32_t instep, uint32_t outstep) { - typedef void (*fe)(const void *, void *, const void *, uint32_t, uint32_t); - uint8_t *pout = (uint8_t *)p->out; - const uint8_t *pin = (const uint8_t *)p->in; - const void *usr = p->usr; - const uint32_t y = p->y; - for (uint32_t x = x1; x < x2; x++) { - (*(fe*)vRoot)(pin, pout, usr, x, y); - pin += instep; - pout += outstep; - } -} - - -static void initForEach(outer_foreach_t* forEachLaunch) { - rsAssert(forEachLaunch); - forEachLaunch[0x00] = NULL; - forEachLaunch[0x01] = rsdForEach31; // in - forEachLaunch[0x02] = rsdForEach30; // out - forEachLaunch[0x03] = rsdForEach31; // in, out - forEachLaunch[0x04] = NULL; - forEachLaunch[0x05] = rsdForEach29; // in, usr - forEachLaunch[0x06] = rsdForEach30; // out, usr - forEachLaunch[0x07] = rsdForEach31; // in, out, usr - forEachLaunch[0x08] = NULL; - forEachLaunch[0x09] = rsdForEach25; // in, x - forEachLaunch[0x0a] = rsdForEach26; // out, x - forEachLaunch[0x0b] = rsdForEach27; // in, out, x - forEachLaunch[0x0c] = NULL; - forEachLaunch[0x0d] = rsdForEach29; // in, usr, x - forEachLaunch[0x0e] = rsdForEach30; // out, usr, x - forEachLaunch[0x0f] = rsdForEach31; // in, out, usr, x - forEachLaunch[0x10] = NULL; - forEachLaunch[0x11] = rsdForEach17; // in y - forEachLaunch[0x12] = rsdForEach18; // out, y - forEachLaunch[0x13] = rsdForEach19; // in, out, y - forEachLaunch[0x14] = NULL; - forEachLaunch[0x15] = rsdForEach21; // in, usr, y - forEachLaunch[0x16] = rsdForEach22; // out, usr, y - forEachLaunch[0x17] = rsdForEach23; // in, out, usr, y - forEachLaunch[0x18] = NULL; - forEachLaunch[0x19] = rsdForEach25; // in, x, y - forEachLaunch[0x1a] = rsdForEach26; // out, x, y - forEachLaunch[0x1b] = rsdForEach27; // in, out, x, y - forEachLaunch[0x1c] = NULL; - forEachLaunch[0x1d] = rsdForEach29; // in, usr, x, y - forEachLaunch[0x1e] = rsdForEach30; // out, usr, x, y - forEachLaunch[0x1f] = rsdForEach31; // in, out, usr, x, y -} - diff --git a/libs/rs/driver/rsdCore.h b/libs/rs/driver/rsdCore.h index ce86d11..126c87a 100644 --- a/libs/rs/driver/rsdCore.h +++ b/libs/rs/driver/rsdCore.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2011-2012 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. @@ -27,11 +27,6 @@ typedef void (* InvokeFunc_t)(void); typedef void (*WorkerCallback_t)(void *usr, uint32_t idx); -typedef void (*outer_foreach_t)(const void *, - const android::renderscript::RsForEachStubParamStruct *, - uint32_t x1, uint32_t x2, - uint32_t instep, uint32_t outstep); - typedef struct RsdSymbolTableRec { const char * mName; void * mPtr; @@ -62,8 +57,6 @@ typedef struct RsdHalRec { Workers mWorkers; bool mExit; - outer_foreach_t mForEachLaunch[32]; - ScriptTLSStruct mTlsStruct; RsdGL gl; diff --git a/libs/rs/driver/rsdGL.cpp b/libs/rs/driver/rsdGL.cpp index b53a68c..7acc054 100644 --- a/libs/rs/driver/rsdGL.cpp +++ b/libs/rs/driver/rsdGL.cpp @@ -340,9 +340,9 @@ bool rsdGLInit(const Context *rsc) { dc->gl.gl.OES_texture_npot = NULL != strstr((const char *)dc->gl.gl.extensions, "GL_OES_texture_npot"); - dc->gl.gl.GL_IMG_texture_npot = NULL != strstr((const char *)dc->gl.gl.extensions, + dc->gl.gl.IMG_texture_npot = NULL != strstr((const char *)dc->gl.gl.extensions, "GL_IMG_texture_npot"); - dc->gl.gl.GL_NV_texture_npot_2D_mipmap = NULL != strstr((const char *)dc->gl.gl.extensions, + dc->gl.gl.NV_texture_npot_2D_mipmap = NULL != strstr((const char *)dc->gl.gl.extensions, "GL_NV_texture_npot_2D_mipmap"); dc->gl.gl.EXT_texture_max_aniso = 1.0f; bool hasAniso = NULL != strstr((const char *)dc->gl.gl.extensions, diff --git a/libs/rs/driver/rsdGL.h b/libs/rs/driver/rsdGL.h index fc33885..51893c3 100644 --- a/libs/rs/driver/rsdGL.h +++ b/libs/rs/driver/rsdGL.h @@ -61,8 +61,8 @@ typedef struct RsdGLRec { int32_t maxVertexTextureUnits; bool OES_texture_npot; - bool GL_IMG_texture_npot; - bool GL_NV_texture_npot_2D_mipmap; + bool IMG_texture_npot; + bool NV_texture_npot_2D_mipmap; float EXT_texture_max_aniso; } gl; diff --git a/libs/rs/driver/rsdRuntimeMath.cpp b/libs/rs/driver/rsdRuntimeMath.cpp index 4c300b7..753ef73 100644 --- a/libs/rs/driver/rsdRuntimeMath.cpp +++ b/libs/rs/driver/rsdRuntimeMath.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <cutils/compiler.h> + #include "rsContext.h" #include "rsScriptC.h" #include "rsMatrix4x4.h" @@ -306,7 +308,7 @@ static int32_t SC_AtomicSub(volatile int32_t *ptr, int32_t value) { do { prev = *ptr; status = android_atomic_release_cas(prev, prev - value, ptr); - } while (__builtin_expect(status != 0, 0)); + } while (CC_UNLIKELY(status != 0)); return prev; } @@ -323,7 +325,17 @@ static int32_t SC_AtomicXor(volatile int32_t *ptr, int32_t value) { do { prev = *ptr; status = android_atomic_release_cas(prev, prev ^ value, ptr); - } while (__builtin_expect(status != 0, 0)); + } while (CC_UNLIKELY(status != 0)); + return prev; +} + +static uint32_t SC_AtomicUMin(volatile uint32_t *ptr, uint32_t value) { + uint32_t prev, status; + do { + prev = *ptr; + uint32_t n = rsMin(value, prev); + status = android_atomic_release_cas((int32_t) prev, (int32_t)n, (volatile int32_t*) ptr); + } while (CC_UNLIKELY(status != 0)); return prev; } @@ -333,7 +345,17 @@ static int32_t SC_AtomicMin(volatile int32_t *ptr, int32_t value) { prev = *ptr; int32_t n = rsMin(value, prev); status = android_atomic_release_cas(prev, n, ptr); - } while (__builtin_expect(status != 0, 0)); + } while (CC_UNLIKELY(status != 0)); + return prev; +} + +static uint32_t SC_AtomicUMax(volatile uint32_t *ptr, uint32_t value) { + uint32_t prev, status; + do { + prev = *ptr; + uint32_t n = rsMax(value, prev); + status = android_atomic_release_cas((int32_t) prev, (int32_t) n, (volatile int32_t*) ptr); + } while (CC_UNLIKELY(status != 0)); return prev; } @@ -343,7 +365,7 @@ static int32_t SC_AtomicMax(volatile int32_t *ptr, int32_t value) { prev = *ptr; int32_t n = rsMax(value, prev); status = android_atomic_release_cas(prev, n, ptr); - } while (__builtin_expect(status != 0, 0)); + } while (CC_UNLIKELY(status != 0)); return prev; } @@ -522,9 +544,9 @@ static RsdSymbolTable gSyms[] = { { "_Z11rsAtomicXorPVii", (void *)&SC_AtomicXor, true }, { "_Z11rsAtomicXorPVjj", (void *)&SC_AtomicXor, true }, { "_Z11rsAtomicMinPVii", (void *)&SC_AtomicMin, true }, - { "_Z11rsAtomicMinPVjj", (void *)&SC_AtomicMin, true }, + { "_Z11rsAtomicMinPVjj", (void *)&SC_AtomicUMin, true }, { "_Z11rsAtomicMaxPVii", (void *)&SC_AtomicMax, true }, - { "_Z11rsAtomicMaxPVjj", (void *)&SC_AtomicMax, true }, + { "_Z11rsAtomicMaxPVjj", (void *)&SC_AtomicUMax, true }, { "_Z11rsAtomicCasPViii", (void *)&SC_AtomicCas, true }, { "_Z11rsAtomicCasPVjjj", (void *)&SC_AtomicCas, true }, diff --git a/libs/rs/driver/rsdShader.cpp b/libs/rs/driver/rsdShader.cpp index a10deb4..c70193a 100644 --- a/libs/rs/driver/rsdShader.cpp +++ b/libs/rs/driver/rsdShader.cpp @@ -349,8 +349,8 @@ void RsdShader::setupSampler(const Context *rsc, const Sampler *s, const Allocat if (!dc->gl.gl.OES_texture_npot && tex->getType()->getIsNp2()) { if (tex->getHasGraphicsMipmaps() && - (dc->gl.gl.GL_NV_texture_npot_2D_mipmap || dc->gl.gl.GL_IMG_texture_npot)) { - if (dc->gl.gl.GL_NV_texture_npot_2D_mipmap) { + (dc->gl.gl.NV_texture_npot_2D_mipmap || dc->gl.gl.IMG_texture_npot)) { + if (dc->gl.gl.NV_texture_npot_2D_mipmap) { RSD_CALL_GL(glTexParameteri, target, GL_TEXTURE_MIN_FILTER, trans[s->mHal.state.minFilter]); } else { diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec index 20b1f52..6887b22 100644 --- a/libs/rs/rs.spec +++ b/libs/rs/rs.spec @@ -162,7 +162,7 @@ Allocation1DElementData { param uint32_t x param uint32_t lod param const void *data - param uint32_t comp_offset + param size_t comp_offset } Allocation2DData { @@ -183,7 +183,7 @@ Allocation2DElementData { param uint32_t lod param RsAllocationCubemapFace face param const void *data - param uint32_t element_offset + param size_t element_offset } AllocationGenerateMipmaps { diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp index 7b92b39..fd85b07 100644 --- a/libs/rs/rsAllocation.cpp +++ b/libs/rs/rsAllocation.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2009-2012 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. @@ -71,11 +71,11 @@ void Allocation::read(void *data) { } void Allocation::data(Context *rsc, uint32_t xoff, uint32_t lod, - uint32_t count, const void *data, uint32_t sizeBytes) { - const uint32_t eSize = mHal.state.type->getElementSizeBytes(); + uint32_t count, const void *data, size_t sizeBytes) { + const size_t eSize = mHal.state.type->getElementSizeBytes(); if ((count * eSize) != sizeBytes) { - ALOGE("Allocation::subData called with mismatched size expected %i, got %i", + ALOGE("Allocation::subData called with mismatched size expected %zu, got %zu", (count * eSize), sizeBytes); mHal.state.type->dumpLOGV("type info"); return; @@ -86,14 +86,14 @@ void Allocation::data(Context *rsc, uint32_t xoff, uint32_t lod, } void Allocation::data(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t lod, RsAllocationCubemapFace face, - uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes) { - const uint32_t eSize = mHal.state.elementSizeBytes; - const uint32_t lineSize = eSize * w; + uint32_t w, uint32_t h, const void *data, size_t sizeBytes) { + const size_t eSize = mHal.state.elementSizeBytes; + const size_t lineSize = eSize * w; //ALOGE("data2d %p, %i %i %i %i %i %i %p %i", this, xoff, yoff, lod, face, w, h, data, sizeBytes); if ((lineSize * h) != sizeBytes) { - ALOGE("Allocation size mismatch, expected %i, got %i", (lineSize * h), sizeBytes); + ALOGE("Allocation size mismatch, expected %zu, got %zu", (lineSize * h), sizeBytes); rsAssert(!"Allocation::subData called with mismatched size"); return; } @@ -104,12 +104,12 @@ void Allocation::data(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t lod, void Allocation::data(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t zoff, uint32_t lod, RsAllocationCubemapFace face, - uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes) { + uint32_t w, uint32_t h, uint32_t d, const void *data, size_t sizeBytes) { } void Allocation::elementData(Context *rsc, uint32_t x, const void *data, - uint32_t cIdx, uint32_t sizeBytes) { - uint32_t eSize = mHal.state.elementSizeBytes; + uint32_t cIdx, size_t sizeBytes) { + size_t eSize = mHal.state.elementSizeBytes; if (cIdx >= mHal.state.type->getElement()->getFieldCount()) { ALOGE("Error Allocation::subElementData component %i out of range.", cIdx); @@ -124,8 +124,9 @@ void Allocation::elementData(Context *rsc, uint32_t x, const void *data, } const Element * e = mHal.state.type->getElement()->getField(cIdx); - if (sizeBytes != e->getSizeBytes()) { - ALOGE("Error Allocation::subElementData data size %i does not match field size %zu.", sizeBytes, e->getSizeBytes()); + uint32_t elemArraySize = mHal.state.type->getElement()->getFieldArraySize(cIdx); + if (sizeBytes != e->getSizeBytes() * elemArraySize) { + ALOGE("Error Allocation::subElementData data size %zu does not match field size %zu.", sizeBytes, e->getSizeBytes()); rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size."); return; } @@ -135,8 +136,8 @@ void Allocation::elementData(Context *rsc, uint32_t x, const void *data, } void Allocation::elementData(Context *rsc, uint32_t x, uint32_t y, - const void *data, uint32_t cIdx, uint32_t sizeBytes) { - uint32_t eSize = mHal.state.elementSizeBytes; + const void *data, uint32_t cIdx, size_t sizeBytes) { + size_t eSize = mHal.state.elementSizeBytes; if (x >= mHal.state.dimensionX) { ALOGE("Error Allocation::subElementData X offset %i out of range.", x); @@ -157,9 +158,9 @@ void Allocation::elementData(Context *rsc, uint32_t x, uint32_t y, } const Element * e = mHal.state.type->getElement()->getField(cIdx); - - if (sizeBytes != e->getSizeBytes()) { - ALOGE("Error Allocation::subElementData data size %i does not match field size %zu.", sizeBytes, e->getSizeBytes()); + uint32_t elemArraySize = mHal.state.type->getElement()->getFieldArraySize(cIdx); + if (sizeBytes != e->getSizeBytes() * elemArraySize) { + ALOGE("Error Allocation::subElementData data size %zu does not match field size %zu.", sizeBytes, e->getSizeBytes()); rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size."); return; } @@ -195,6 +196,81 @@ void Allocation::dumpLOGV(const char *prefix) const { prefix, getPtr(), mHal.state.usageFlags, mHal.state.mipmapControl); } +uint32_t Allocation::getPackedSize() const { + uint32_t numItems = mHal.state.type->getSizeBytes() / mHal.state.type->getElementSizeBytes(); + return numItems * mHal.state.type->getElement()->getSizeBytesUnpadded(); +} + +void Allocation::writePackedData(const Type *type, + uint8_t *dst, const uint8_t *src, bool dstPadded) { + const Element *elem = type->getElement(); + uint32_t unpaddedBytes = elem->getSizeBytesUnpadded(); + uint32_t paddedBytes = elem->getSizeBytes(); + uint32_t numItems = type->getSizeBytes() / paddedBytes; + + uint32_t srcInc = !dstPadded ? paddedBytes : unpaddedBytes; + uint32_t dstInc = dstPadded ? paddedBytes : unpaddedBytes; + + // no sub-elements + uint32_t fieldCount = elem->getFieldCount(); + if (fieldCount == 0) { + for (uint32_t i = 0; i < numItems; i ++) { + memcpy(dst, src, unpaddedBytes); + src += srcInc; + dst += dstInc; + } + return; + } + + // Cache offsets + uint32_t *offsetsPadded = new uint32_t[fieldCount]; + uint32_t *offsetsUnpadded = new uint32_t[fieldCount]; + uint32_t *sizeUnpadded = new uint32_t[fieldCount]; + + for (uint32_t i = 0; i < fieldCount; i++) { + offsetsPadded[i] = elem->getFieldOffsetBytes(i); + offsetsUnpadded[i] = elem->getFieldOffsetBytesUnpadded(i); + sizeUnpadded[i] = elem->getField(i)->getSizeBytesUnpadded(); + } + + uint32_t *srcOffsets = !dstPadded ? offsetsPadded : offsetsUnpadded; + uint32_t *dstOffsets = dstPadded ? offsetsPadded : offsetsUnpadded; + + // complex elements, need to copy subelem after subelem + for (uint32_t i = 0; i < numItems; i ++) { + for (uint32_t fI = 0; fI < fieldCount; fI++) { + memcpy(dst + dstOffsets[fI], src + srcOffsets[fI], sizeUnpadded[fI]); + } + src += srcInc; + dst += dstInc; + } + + delete[] offsetsPadded; + delete[] offsetsUnpadded; + delete[] sizeUnpadded; +} + +void Allocation::unpackVec3Allocation(const void *data, size_t dataSize) { + const uint8_t *src = (const uint8_t*)data; + uint8_t *dst = (uint8_t*)getPtr(); + + writePackedData(getType(), dst, src, true); +} + +void Allocation::packVec3Allocation(OStream *stream) const { + uint32_t paddedBytes = getType()->getElement()->getSizeBytes(); + uint32_t unpaddedBytes = getType()->getElement()->getSizeBytesUnpadded(); + uint32_t numItems = mHal.state.type->getSizeBytes() / paddedBytes; + + const uint8_t *src = (const uint8_t*)getPtr(); + uint8_t *dst = new uint8_t[numItems * unpaddedBytes]; + + writePackedData(getType(), dst, src, false); + stream->addByteArray(dst, getPackedSize()); + + delete[] dst; +} + void Allocation::serialize(OStream *stream) const { // Need to identify ourselves stream->addU32((uint32_t)getClassId()); @@ -207,10 +283,17 @@ void Allocation::serialize(OStream *stream) const { mHal.state.type->serialize(stream); uint32_t dataSize = mHal.state.type->getSizeBytes(); + // 3 element vectors are padded to 4 in memory, but padding isn't serialized + uint32_t packedSize = getPackedSize(); // Write how much data we are storing - stream->addU32(dataSize); - // Now write the data - stream->addByteArray(getPtr(), dataSize); + stream->addU32(packedSize); + if (dataSize == packedSize) { + // Now write the data + stream->addByteArray(getPtr(), dataSize); + } else { + // Now write the data + packVec3Allocation(stream); + } } Allocation *Allocation::createFromStream(Context *rsc, IStream *stream) { @@ -230,22 +313,30 @@ Allocation *Allocation::createFromStream(Context *rsc, IStream *stream) { } type->compute(); + Allocation *alloc = Allocation::createAllocation(rsc, type, RS_ALLOCATION_USAGE_SCRIPT); + type->decUserRef(); + // Number of bytes we wrote out for this allocation uint32_t dataSize = stream->loadU32(); - if (dataSize != type->getSizeBytes()) { + // 3 element vectors are padded to 4 in memory, but padding isn't serialized + uint32_t packedSize = alloc->getPackedSize(); + if (dataSize != type->getSizeBytes() && + dataSize != packedSize) { ALOGE("failed to read allocation because numbytes written is not the same loaded type wants\n"); + ObjectBase::checkDelete(alloc); ObjectBase::checkDelete(type); return NULL; } - Allocation *alloc = Allocation::createAllocation(rsc, type, RS_ALLOCATION_USAGE_SCRIPT); alloc->setName(name.string(), name.size()); - type->decUserRef(); - uint32_t count = dataSize / type->getElementSizeBytes(); - - // Read in all of our allocation data - alloc->data(rsc, 0, 0, count, stream->getPtr() + stream->getPos(), dataSize); + if (dataSize == type->getSizeBytes()) { + uint32_t count = dataSize / type->getElementSizeBytes(); + // Read in all of our allocation data + alloc->data(rsc, 0, 0, count, stream->getPtr() + stream->getPos(), dataSize); + } else { + alloc->unpackVec3Allocation(stream->getPtr() + stream->getPos(), dataSize); + } stream->reset(stream->getPos() + dataSize); return alloc; @@ -429,13 +520,13 @@ void rsi_Allocation1DData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t } void rsi_Allocation2DElementData(Context *rsc, RsAllocation va, uint32_t x, uint32_t y, uint32_t lod, RsAllocationCubemapFace face, - const void *data, size_t eoff, uint32_t sizeBytes) { // TODO: this seems wrong, eoff and sizeBytes may be swapped + const void *data, size_t sizeBytes, size_t eoff) { Allocation *a = static_cast<Allocation *>(va); a->elementData(rsc, x, y, data, eoff, sizeBytes); } void rsi_Allocation1DElementData(Context *rsc, RsAllocation va, uint32_t x, uint32_t lod, - const void *data, size_t eoff, uint32_t sizeBytes) { // TODO: this seems wrong, eoff and sizeBytes may be swapped + const void *data, size_t sizeBytes, size_t eoff) { Allocation *a = static_cast<Allocation *>(va); a->elementData(rsc, x, data, eoff, sizeBytes); } diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h index 714798a..20201ca 100644 --- a/libs/rs/rsAllocation.h +++ b/libs/rs/rsAllocation.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2009-2012 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. @@ -80,16 +80,16 @@ public: void resize1D(Context *rsc, uint32_t dimX); void resize2D(Context *rsc, uint32_t dimX, uint32_t dimY); - void data(Context *rsc, uint32_t xoff, uint32_t lod, uint32_t count, const void *data, uint32_t sizeBytes); + void data(Context *rsc, uint32_t xoff, uint32_t lod, uint32_t count, const void *data, size_t sizeBytes); void data(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t lod, RsAllocationCubemapFace face, - uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes); + uint32_t w, uint32_t h, const void *data, size_t sizeBytes); void data(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t zoff, uint32_t lod, RsAllocationCubemapFace face, - uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes); + uint32_t w, uint32_t h, uint32_t d, const void *data, size_t sizeBytes); void elementData(Context *rsc, uint32_t x, - const void *data, uint32_t elementOff, uint32_t sizeBytes); + const void *data, uint32_t elementOff, size_t sizeBytes); void elementData(Context *rsc, uint32_t x, uint32_t y, - const void *data, uint32_t elementOff, uint32_t sizeBytes); + const void *data, uint32_t elementOff, size_t sizeBytes); void read(void *data); @@ -135,6 +135,11 @@ protected: private: void freeChildrenUnlocked(); Allocation(Context *rsc, const Type *, uint32_t usages, RsAllocationMipmapControl mc); + + uint32_t getPackedSize() const; + static void writePackedData(const Type *type, uint8_t *dst, const uint8_t *src, bool dstPadded); + void unpackVec3Allocation(const void *data, size_t dataSize); + void packVec3Allocation(OStream *stream) const; }; } diff --git a/libs/rs/rsComponent.cpp b/libs/rs/rsComponent.cpp index 7d9cf0b..21b98f6 100644 --- a/libs/rs/rsComponent.cpp +++ b/libs/rs/rsComponent.cpp @@ -169,7 +169,8 @@ void Component::set(RsDataType dt, RsDataKind dk, bool norm, uint32_t vecSize) { break; } - mBits = mTypeBits * mVectorSize; + mBitsUnpadded = mTypeBits * mVectorSize; + mBits = mTypeBits * rsHigherPow2(mVectorSize); } bool Component::isReference() const { diff --git a/libs/rs/rsComponent.h b/libs/rs/rsComponent.h index 6ddc990..8629d0d 100644 --- a/libs/rs/rsComponent.h +++ b/libs/rs/rsComponent.h @@ -41,6 +41,7 @@ public: bool getIsFloat() const {return mIsFloat;} bool getIsSigned() const {return mIsSigned;} uint32_t getBits() const {return mBits;} + uint32_t getBitsUnpadded() const {return mBitsUnpadded;} // Helpers for reading / writing this class out void serialize(OStream *stream) const; @@ -56,6 +57,7 @@ protected: // derived uint32_t mBits; + uint32_t mBitsUnpadded; uint32_t mTypeBits; bool mIsFloat; bool mIsSigned; diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index 54fe529..ad2ff0f 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -472,6 +472,30 @@ void Context::setSurface(uint32_t w, uint32_t h, RsNativeWindow sur) { } } +uint32_t Context::getCurrentSurfaceWidth() const { + for (uint32_t i = 0; i < mFBOCache.mHal.state.colorTargetsCount; i ++) { + if (mFBOCache.mHal.state.colorTargets[i] != NULL) { + return mFBOCache.mHal.state.colorTargets[i]->getType()->getDimX(); + } + } + if (mFBOCache.mHal.state.depthTarget != NULL) { + return mFBOCache.mHal.state.depthTarget->getType()->getDimX(); + } + return mWidth; +} + +uint32_t Context::getCurrentSurfaceHeight() const { + for (uint32_t i = 0; i < mFBOCache.mHal.state.colorTargetsCount; i ++) { + if (mFBOCache.mHal.state.colorTargets[i] != NULL) { + return mFBOCache.mHal.state.colorTargets[i]->getType()->getDimY(); + } + } + if (mFBOCache.mHal.state.depthTarget != NULL) { + return mFBOCache.mHal.state.depthTarget->getType()->getDimY(); + } + return mHeight; +} + void Context::pause() { rsAssert(mIsGraphicsContext); mPaused = true; diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h index 35221e0..61c29f9 100644 --- a/libs/rs/rsContext.h +++ b/libs/rs/rsContext.h @@ -164,6 +164,9 @@ public: uint32_t getWidth() const {return mWidth;} uint32_t getHeight() const {return mHeight;} + uint32_t getCurrentSurfaceWidth() const; + uint32_t getCurrentSurfaceHeight() const; + mutable ThreadIO mIO; // Timers diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp index b65f380..dff9585 100644 --- a/libs/rs/rsElement.cpp +++ b/libs/rs/rsElement.cpp @@ -23,6 +23,7 @@ using namespace android::renderscript; Element::Element(Context *rsc) : ObjectBase(rsc) { mBits = 0; + mBitsUnpadded = 0; mFields = NULL; mFieldCount = 0; mHasReference = false; @@ -60,6 +61,18 @@ size_t Element::getSizeBits() const { return total; } +size_t Element::getSizeBitsUnpadded() const { + if (!mFieldCount) { + return mBitsUnpadded; + } + + size_t total = 0; + for (size_t ct=0; ct < mFieldCount; ct++) { + total += mFields[ct].e->mBitsUnpadded * mFields[ct].arraySize; + } + return total; +} + void Element::dumpLOGV(const char *prefix) const { ObjectBase::dumpLOGV(prefix); ALOGV("%s Element: fieldCount: %zu, size bytes: %zu", prefix, mFieldCount, getSizeBytes()); @@ -146,14 +159,18 @@ Element *Element::createFromStream(Context *rsc, IStream *stream) { void Element::compute() { if (mFieldCount == 0) { mBits = mComponent.getBits(); + mBitsUnpadded = mComponent.getBitsUnpadded(); mHasReference = mComponent.isReference(); return; } size_t bits = 0; + size_t bitsUnpadded = 0; for (size_t ct=0; ct < mFieldCount; ct++) { mFields[ct].offsetBits = bits; + mFields[ct].offsetBitsUnpadded = bitsUnpadded; bits += mFields[ct].e->getSizeBits() * mFields[ct].arraySize; + bitsUnpadded += mFields[ct].e->getSizeBitsUnpadded() * mFields[ct].arraySize; if (mFields[ct].e->mHasReference) { mHasReference = true; diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h index bfdec53..04010fa 100644 --- a/libs/rs/rsElement.h +++ b/libs/rs/rsElement.h @@ -43,6 +43,11 @@ public: uint32_t getGLType() const; uint32_t getGLFormat() const; + size_t getSizeBitsUnpadded() const; + size_t getSizeBytesUnpadded() const { + return (getSizeBitsUnpadded() + 7) >> 3; + } + size_t getSizeBits() const; size_t getSizeBytes() const { return (getSizeBits() + 7) >> 3; @@ -55,6 +60,10 @@ public: return mFields[componentNumber].offsetBits >> 3; } + size_t getFieldOffsetBytesUnpadded(uint32_t componentNumber) const { + return mFields[componentNumber].offsetBitsUnpadded >> 3; + } + uint32_t getFieldCount() const {return mFieldCount;} const Element * getField(uint32_t idx) const {return mFields[idx].e.get();} const char * getFieldName(uint32_t idx) const {return mFields[idx].name.string();} @@ -64,6 +73,7 @@ public: RsDataType getType() const {return mComponent.getType();} RsDataKind getKind() const {return mComponent.getKind();} uint32_t getBits() const {return mBits;} + uint32_t getBitsUnpadded() const {return mBitsUnpadded;} void dumpLOGV(const char *prefix) const; virtual void serialize(OStream *stream) const; @@ -112,6 +122,7 @@ protected: String8 name; ObjectBaseRef<const Element> e; uint32_t offsetBits; + uint32_t offsetBitsUnpadded; uint32_t arraySize; } ElementField_t; ElementField_t *mFields; @@ -123,6 +134,7 @@ protected: Element(Context *); Component mComponent; + uint32_t mBitsUnpadded; uint32_t mBits; void compute(); diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp index e09f81c..4f21b3b 100644 --- a/libs/rs/rsFont.cpp +++ b/libs/rs/rsFont.cpp @@ -651,14 +651,10 @@ void FontState::appendMeshQuad(float x1, float y1, float z1, float x4, float y4, float z4, float u4, float v4) { const uint32_t vertsPerQuad = 4; - const uint32_t floatsPerVert = 5; + const uint32_t floatsPerVert = 6; float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; - // Cull things that are off the screen - float width = (float)mRSC->getWidth(); - float height = (float)mRSC->getHeight(); - - if (x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) { + if (x1 > mSurfaceWidth || y1 < 0.0f || x2 < 0 || y4 > mSurfaceHeight) { return; } @@ -670,24 +666,28 @@ void FontState::appendMeshQuad(float x1, float y1, float z1, (*currentPos++) = x1; (*currentPos++) = y1; (*currentPos++) = z1; + (*currentPos++) = 0; (*currentPos++) = u1; (*currentPos++) = v1; (*currentPos++) = x2; (*currentPos++) = y2; (*currentPos++) = z2; + (*currentPos++) = 0; (*currentPos++) = u2; (*currentPos++) = v2; (*currentPos++) = x3; (*currentPos++) = y3; (*currentPos++) = z3; + (*currentPos++) = 0; (*currentPos++) = u3; (*currentPos++) = v3; (*currentPos++) = x4; (*currentPos++) = y4; (*currentPos++) = z4; + (*currentPos++) = 0; (*currentPos++) = u4; (*currentPos++) = v4; @@ -746,6 +746,10 @@ void FontState::renderText(const char *text, uint32_t len, int32_t x, int32_t y, return; } + // Cull things that are off the screen + mSurfaceWidth = (float)mRSC->getCurrentSurfaceWidth(); + mSurfaceHeight = (float)mRSC->getCurrentSurfaceHeight(); + currentFont->renderUTF(text, len, x, y, startIndex, numGlyphs, mode, bounds, bitmap, bitmapW, bitmapH); diff --git a/libs/rs/rsFont.h b/libs/rs/rsFont.h index 679591c..4ca794d 100644 --- a/libs/rs/rsFont.h +++ b/libs/rs/rsFont.h @@ -160,6 +160,9 @@ public: protected: + float mSurfaceWidth; + float mSurfaceHeight; + friend class Font; struct CacheTextureLine { diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp index 4a13622..871caac 100644 --- a/libs/rs/rsProgramVertex.cpp +++ b/libs/rs/rsProgramVertex.cpp @@ -206,8 +206,11 @@ void ProgramVertexState::init(Context *rsc) { void ProgramVertexState::updateSize(Context *rsc) { float *f = static_cast<float *>(mDefaultAlloc->getPtr()); + float surfaceWidth = (float)rsc->getCurrentSurfaceWidth(); + float surfaceHeight = (float)rsc->getCurrentSurfaceHeight(); + Matrix4x4 m; - m.loadOrtho(0,rsc->getWidth(), rsc->getHeight(),0, -1,1); + m.loadOrtho(0, surfaceWidth, surfaceHeight, 0, -1, 1); memcpy(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], m.m, sizeof(m)); memcpy(&f[RS_PROGRAM_VERTEX_MVP_OFFSET], m.m, sizeof(m)); diff --git a/libs/rs/rsScript.cpp b/libs/rs/rsScript.cpp index fad9c08..357dbe3 100644 --- a/libs/rs/rsScript.cpp +++ b/libs/rs/rsScript.cpp @@ -15,6 +15,7 @@ */ #include "rsContext.h" +#include <time.h> using namespace android; using namespace android::renderscript; @@ -25,6 +26,7 @@ Script::Script(Context *rsc) : ObjectBase(rsc) { mSlots = NULL; mTypes = NULL; + mInitialized = false; } Script::~Script() { @@ -89,8 +91,22 @@ void rsi_ScriptBindAllocation(Context * rsc, RsScript vs, RsAllocation va, uint3 } void rsi_ScriptSetTimeZone(Context * rsc, RsScript vs, const char * timeZone, size_t length) { - Script *s = static_cast<Script *>(vs); - s->mEnviroment.mTimeZone = timeZone; + // We unfortunately need to make a new copy of the string, since it is + // not NULL-terminated. We then use setenv(), which properly handles + // freeing/duplicating the actual string for the environment. + char *tz = (char *) malloc(length + 1); + if (!tz) { + ALOGE("Couldn't allocate memory for timezone buffer"); + return; + } + strncpy(tz, timeZone, length); + tz[length] = '\0'; + if (setenv("TZ", tz, 1) == 0) { + tzset(); + } else { + ALOGE("Error setting timezone"); + } + free(tz); } void rsi_ScriptForEach(Context *rsc, RsScript vs, uint32_t slot, diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h index d645421..99dceaf 100644 --- a/libs/rs/rsScript.h +++ b/libs/rs/rsScript.h @@ -59,7 +59,6 @@ public: struct Enviroment_t { int64_t mStartTimeMillis; int64_t mLastDtTime; - const char* mTimeZone; ObjectBaseRef<ProgramVertex> mVertex; ObjectBaseRef<ProgramFragment> mFragment; @@ -68,7 +67,6 @@ public: }; Enviroment_t mEnviroment; - void initSlots(); void setSlot(uint32_t slot, Allocation *a); void setVar(uint32_t slot, const void *val, size_t len); void setVarObj(uint32_t slot, ObjectBase *val); @@ -86,6 +84,7 @@ public: virtual void setupScript(Context *rsc) = 0; virtual uint32_t run(Context *) = 0; protected: + bool mInitialized; ObjectBaseRef<Allocation> *mSlots; ObjectBaseRef<const Type> *mTypes; diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp index 5079b63..b4eb995 100644 --- a/libs/rs/rsScriptC.cpp +++ b/libs/rs/rsScriptC.cpp @@ -45,8 +45,10 @@ ScriptC::~ScriptC() { BT = NULL; } #endif - mRSC->mHal.funcs.script.invokeFreeChildren(mRSC, this); - mRSC->mHal.funcs.script.destroy(mRSC, this); + if (mInitialized) { + mRSC->mHal.funcs.script.invokeFreeChildren(mRSC, this); + mRSC->mHal.funcs.script.destroy(mRSC, this); + } } void ScriptC::setupScript(Context *rsc) { @@ -204,7 +206,6 @@ bool ScriptC::runCompiler(Context *rsc, return false; } - rsAssert(bcWrapper.getHeaderVersion() == 0); if (bcWrapper.getBCFileType() == bcinfo::BC_WRAPPER) { sdkVersion = bcWrapper.getTargetAPI(); } @@ -230,8 +231,11 @@ bool ScriptC::runCompiler(Context *rsc, bitcodeLen = BT->getTranslatedBitcodeSize(); #endif - rsc->mHal.funcs.script.init(rsc, this, resName, cacheDir, bitcode, bitcodeLen, 0); + if (!rsc->mHal.funcs.script.init(rsc, this, resName, cacheDir, bitcode, bitcodeLen, 0)) { + return false; + } + mInitialized = true; mEnviroment.mFragment.set(rsc->getDefaultProgramFragment()); mEnviroment.mVertex.set(rsc->getDefaultProgramVertex()); mEnviroment.mFragmentStore.set(rsc->getDefaultProgramStore()); @@ -318,7 +322,7 @@ RsScript rsi_ScriptCCreate(Context *rsc, if (!s->runCompiler(rsc, resName, cacheDir, (uint8_t *)text, text_length)) { // Error during compile, destroy s and return null. - delete s; + ObjectBase::checkDelete(s); return NULL; } diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp index a7ba7d2..7964792 100644 --- a/libs/rs/rsScriptC_LibGL.cpp +++ b/libs/rs/rsScriptC_LibGL.cpp @@ -79,23 +79,28 @@ void rsrBindProgramRaster(Context *rsc, Script *sc, ProgramRaster *pr) { void rsrBindFrameBufferObjectColorTarget(Context *rsc, Script *sc, Allocation *a, uint32_t slot) { CHECK_OBJ(va); rsc->mFBOCache.bindColorTarget(rsc, a, slot); + rsc->mStateVertex.updateSize(rsc); } void rsrBindFrameBufferObjectDepthTarget(Context *rsc, Script *sc, Allocation *a) { CHECK_OBJ(va); rsc->mFBOCache.bindDepthTarget(rsc, a); + rsc->mStateVertex.updateSize(rsc); } void rsrClearFrameBufferObjectColorTarget(Context *rsc, Script *sc, uint32_t slot) { rsc->mFBOCache.bindColorTarget(rsc, NULL, slot); + rsc->mStateVertex.updateSize(rsc); } void rsrClearFrameBufferObjectDepthTarget(Context *rsc, Script *sc) { rsc->mFBOCache.bindDepthTarget(rsc, NULL); + rsc->mStateVertex.updateSize(rsc); } void rsrClearFrameBufferObjectTargets(Context *rsc, Script *sc) { rsc->mFBOCache.resetAll(rsc); + rsc->mStateVertex.updateSize(rsc); } ////////////////////////////////////////////////////////////////////////////// diff --git a/libs/rs/rsThreadIO.cpp b/libs/rs/rsThreadIO.cpp index 8ba1a0e..1917774 100644 --- a/libs/rs/rsThreadIO.cpp +++ b/libs/rs/rsThreadIO.cpp @@ -124,7 +124,6 @@ bool ThreadIO::playCoreCommands(Context *con, bool waitForCommand, uint64_t time while (!mToCore.isEmpty() || waitForCommand) { uint32_t cmdID = 0; uint32_t cmdSize = 0; - ret = true; if (con->props.mLogTimes) { con->timerSet(Context::RS_TIMER_IDLE); } @@ -136,11 +135,17 @@ bool ThreadIO::playCoreCommands(Context *con, bool waitForCommand, uint64_t time delay = 0; } } + + if (delay == 0 && timeToWait != 0 && mToCore.isEmpty()) { + break; + } + const void * data = mToCore.get(&cmdID, &cmdSize, delay); if (!cmdSize) { // exception or timeout occurred. - return false; + break; } + ret = true; if (con->props.mLogTimes) { con->timerSet(Context::RS_TIMER_INTERNAL); } diff --git a/libs/rs/scriptc/rs_atomic.rsh b/libs/rs/scriptc/rs_atomic.rsh index 87c6c02..a455edd 100644 --- a/libs/rs/scriptc/rs_atomic.rsh +++ b/libs/rs/scriptc/rs_atomic.rsh @@ -242,7 +242,7 @@ extern int32_t __attribute__((overloadable)) * @return old value */ extern uint32_t __attribute__((overloadable)) - rsAtomicCas(volatile uint32_t* addr, int32_t compareValue, int32_t newValue); + rsAtomicCas(volatile uint32_t* addr, uint32_t compareValue, uint32_t newValue); #endif //defined(RS_VERSION) && (RS_VERSION >= 14) diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh index e9c3c5e..84bca9c 100644 --- a/libs/rs/scriptc/rs_types.rsh +++ b/libs/rs/scriptc/rs_types.rsh @@ -364,7 +364,7 @@ typedef struct { typedef float4 rs_quaternion; #define RS_PACKED __attribute__((packed, aligned(4))) -#define NULL ((const void *)0) +#define NULL ((void *)0) #if (defined(RS_VERSION) && (RS_VERSION >= 14)) diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index fbabfc4..f8b4452 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -47,7 +47,6 @@ LOCAL_SRC_FILES:= \ GraphicBuffer.cpp \ GraphicBufferAllocator.cpp \ GraphicBufferMapper.cpp \ - GraphicLog.cpp \ InputTransport.cpp \ PixelFormat.cpp \ Rect.cpp \ diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp index f5ed981..d1dca0c 100644 --- a/libs/ui/FramebufferNativeWindow.cpp +++ b/libs/ui/FramebufferNativeWindow.cpp @@ -29,7 +29,6 @@ #include <ui/Rect.h> #include <ui/FramebufferNativeWindow.h> -#include <ui/GraphicLog.h> #include <EGL/egl.h> @@ -211,9 +210,6 @@ int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, if (self->mBufferHead >= self->mNumBuffers) self->mBufferHead = 0; - GraphicLog& logger(GraphicLog::getInstance()); - logger.log(GraphicLog::SF_FB_DEQUEUE_BEFORE, index); - // wait for a free buffer while (!self->mNumFreeBuffers) { self->mCondition.wait(self->mutex); @@ -224,7 +220,6 @@ int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, *buffer = self->buffers[index].get(); - logger.log(GraphicLog::SF_FB_DEQUEUE_AFTER, index); return 0; } @@ -235,16 +230,12 @@ int FramebufferNativeWindow::lockBuffer(ANativeWindow* window, Mutex::Autolock _l(self->mutex); const int index = self->mCurrentBufferIndex; - GraphicLog& logger(GraphicLog::getInstance()); - logger.log(GraphicLog::SF_FB_LOCK_BEFORE, index); // wait that the buffer we're locking is not front anymore while (self->front == buffer) { self->mCondition.wait(self->mutex); } - logger.log(GraphicLog::SF_FB_LOCK_AFTER, index); - return NO_ERROR; } @@ -257,13 +248,7 @@ int FramebufferNativeWindow::queueBuffer(ANativeWindow* window, buffer_handle_t handle = static_cast<NativeBuffer*>(buffer)->handle; const int index = self->mCurrentBufferIndex; - GraphicLog& logger(GraphicLog::getInstance()); - logger.log(GraphicLog::SF_FB_POST_BEFORE, index); - int res = fb->post(fb, handle); - - logger.log(GraphicLog::SF_FB_POST_AFTER, index); - self->front = static_cast<NativeBuffer*>(buffer); self->mNumFreeBuffers++; self->mCondition.broadcast(); diff --git a/libs/ui/GraphicLog.cpp b/libs/ui/GraphicLog.cpp deleted file mode 100644 index 7ba2779..0000000 --- a/libs/ui/GraphicLog.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2010 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 <unistd.h> -#include <cutils/log.h> -#include <cutils/properties.h> -#include <utils/Endian.h> -#include <utils/Timers.h> - -#include <ui/GraphicLog.h> - -namespace android { - -ANDROID_SINGLETON_STATIC_INSTANCE(GraphicLog) - -static inline -void writeInt32(uint8_t* base, size_t& pos, int32_t value) { -#ifdef HAVE_LITTLE_ENDIAN - int32_t v = value; -#else - int32_t v = htole32(value); -#endif - base[pos] = EVENT_TYPE_INT; - memcpy(&base[pos+1], &v, sizeof(int32_t)); - pos += 1+sizeof(int32_t); -} - -static inline -void writeInt64(uint8_t* base, size_t& pos, int64_t value) { -#ifdef HAVE_LITTLE_ENDIAN - int64_t v = value; -#else - int64_t v = htole64(value); -#endif - base[pos] = EVENT_TYPE_LONG; - memcpy(&base[pos+1], &v, sizeof(int64_t)); - pos += 1+sizeof(int64_t); -} - -void GraphicLog::logImpl(int32_t tag, int32_t buffer) -{ - uint8_t scratch[2 + 2 + sizeof(int32_t) + sizeof(int64_t)]; - size_t pos = 0; - scratch[pos++] = EVENT_TYPE_LIST; - scratch[pos++] = 2; - writeInt32(scratch, pos, buffer); - writeInt64(scratch, pos, ns2ms( systemTime( SYSTEM_TIME_MONOTONIC ) )); - android_bWriteLog(tag, scratch, sizeof(scratch)); -} - -void GraphicLog::logImpl(int32_t tag, int32_t identity, int32_t buffer) -{ - uint8_t scratch[2 + 3 + sizeof(int32_t) + sizeof(int32_t) + sizeof(int64_t)]; - size_t pos = 0; - scratch[pos++] = EVENT_TYPE_LIST; - scratch[pos++] = 3; - writeInt32(scratch, pos, buffer); - writeInt32(scratch, pos, identity); - writeInt64(scratch, pos, ns2ms( systemTime( SYSTEM_TIME_MONOTONIC ) )); - android_bWriteLog(tag, scratch, sizeof(scratch)); -} - -GraphicLog::GraphicLog() - : mEnabled(0) -{ - char property[PROPERTY_VALUE_MAX]; - if (property_get("debug.graphic_log", property, NULL) > 0) { - mEnabled = atoi(property); - } -} - -void GraphicLog::setEnabled(bool enable) -{ - mEnabled = enable; -} - -} diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 831d9e3..24cf504 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -21,6 +21,7 @@ commonSources:= \ Asset.cpp \ AssetDir.cpp \ AssetManager.cpp \ + BasicHashtable.cpp \ BlobCache.cpp \ BufferedTextOutput.cpp \ CallStack.cpp \ @@ -97,7 +98,8 @@ endif LOCAL_C_INCLUDES += \ external/zlib \ - external/icu4c/common + external/icu4c/common \ + bionic/libc/private LOCAL_LDLIBS += -lpthread @@ -105,14 +107,18 @@ LOCAL_SHARED_LIBRARIES := \ libz \ liblog \ libcutils \ - libdl + libdl \ + libcorkscrew LOCAL_MODULE:= libutils include $(BUILD_SHARED_LIBRARY) ifeq ($(TARGET_OS),linux) include $(CLEAR_VARS) -LOCAL_C_INCLUDES += external/zlib external/icu4c/common +LOCAL_C_INCLUDES += \ + external/zlib \ + external/icu4c/common \ + bionic/libc/private LOCAL_LDLIBS := -lrt -ldl -lpthread LOCAL_MODULE := libutils LOCAL_SRC_FILES := $(commonSources) BackupData.cpp BackupHelpers.cpp diff --git a/libs/utils/BasicHashtable.cpp b/libs/utils/BasicHashtable.cpp new file mode 100644 index 0000000..fb8ec9f --- /dev/null +++ b/libs/utils/BasicHashtable.cpp @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BasicHashtable" + +#include <math.h> + +#include <utils/Log.h> +#include <utils/BasicHashtable.h> +#include <utils/misc.h> + +namespace android { + +BasicHashtableImpl::BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor, + size_t minimumInitialCapacity, float loadFactor) : + mBucketSize(entrySize + sizeof(Bucket)), mHasTrivialDestructor(hasTrivialDestructor), + mLoadFactor(loadFactor), mSize(0), + mFilledBuckets(0), mBuckets(NULL) { + determineCapacity(minimumInitialCapacity, mLoadFactor, &mBucketCount, &mCapacity); +} + +BasicHashtableImpl::BasicHashtableImpl(const BasicHashtableImpl& other) : + mBucketSize(other.mBucketSize), mHasTrivialDestructor(other.mHasTrivialDestructor), + mCapacity(other.mCapacity), mLoadFactor(other.mLoadFactor), + mSize(other.mSize), mFilledBuckets(other.mFilledBuckets), + mBucketCount(other.mBucketCount), mBuckets(other.mBuckets) { + if (mBuckets) { + SharedBuffer::bufferFromData(mBuckets)->acquire(); + } +} + +void BasicHashtableImpl::dispose() { + if (mBuckets) { + releaseBuckets(mBuckets, mBucketCount); + } +} + +void BasicHashtableImpl::clone() { + if (mBuckets) { + void* newBuckets = allocateBuckets(mBucketCount); + copyBuckets(mBuckets, newBuckets, mBucketCount); + releaseBuckets(mBuckets, mBucketCount); + mBuckets = newBuckets; + } +} + +void BasicHashtableImpl::setTo(const BasicHashtableImpl& other) { + if (mBuckets) { + releaseBuckets(mBuckets, mBucketCount); + } + + mCapacity = other.mCapacity; + mLoadFactor = other.mLoadFactor; + mSize = other.mSize; + mFilledBuckets = other.mFilledBuckets; + mBucketCount = other.mBucketCount; + mBuckets = other.mBuckets; + + if (mBuckets) { + SharedBuffer::bufferFromData(mBuckets)->acquire(); + } +} + +void BasicHashtableImpl::clear() { + if (mBuckets) { + if (mFilledBuckets) { + SharedBuffer* sb = SharedBuffer::bufferFromData(mBuckets); + if (sb->onlyOwner()) { + destroyBuckets(mBuckets, mBucketCount); + for (size_t i = 0; i < mSize; i++) { + Bucket& bucket = bucketAt(mBuckets, i); + bucket.cookie = 0; + } + } else { + releaseBuckets(mBuckets, mBucketCount); + mBuckets = NULL; + } + mFilledBuckets = 0; + } + mSize = 0; + } +} + +ssize_t BasicHashtableImpl::next(ssize_t index) const { + if (mSize) { + while (size_t(++index) < mBucketCount) { + const Bucket& bucket = bucketAt(mBuckets, index); + if (bucket.cookie & Bucket::PRESENT) { + return index; + } + } + } + return -1; +} + +ssize_t BasicHashtableImpl::find(ssize_t index, hash_t hash, + const void* __restrict__ key) const { + if (!mSize) { + return -1; + } + + hash = trimHash(hash); + if (index < 0) { + index = chainStart(hash, mBucketCount); + + const Bucket& bucket = bucketAt(mBuckets, size_t(index)); + if (bucket.cookie & Bucket::PRESENT) { + if (compareBucketKey(bucket, key)) { + return index; + } + } else { + if (!(bucket.cookie & Bucket::COLLISION)) { + return -1; + } + } + } + + size_t inc = chainIncrement(hash, mBucketCount); + for (;;) { + index = chainSeek(index, inc, mBucketCount); + + const Bucket& bucket = bucketAt(mBuckets, size_t(index)); + if (bucket.cookie & Bucket::PRESENT) { + if ((bucket.cookie & Bucket::HASH_MASK) == hash + && compareBucketKey(bucket, key)) { + return index; + } + } + if (!(bucket.cookie & Bucket::COLLISION)) { + return -1; + } + } +} + +size_t BasicHashtableImpl::add(hash_t hash, const void* entry) { + if (!mBuckets) { + mBuckets = allocateBuckets(mBucketCount); + } else { + edit(); + } + + hash = trimHash(hash); + for (;;) { + size_t index = chainStart(hash, mBucketCount); + Bucket* bucket = &bucketAt(mBuckets, size_t(index)); + if (bucket->cookie & Bucket::PRESENT) { + size_t inc = chainIncrement(hash, mBucketCount); + do { + bucket->cookie |= Bucket::COLLISION; + index = chainSeek(index, inc, mBucketCount); + bucket = &bucketAt(mBuckets, size_t(index)); + } while (bucket->cookie & Bucket::PRESENT); + } + + uint32_t collision = bucket->cookie & Bucket::COLLISION; + if (!collision) { + if (mFilledBuckets >= mCapacity) { + rehash(mCapacity * 2, mLoadFactor); + continue; + } + mFilledBuckets += 1; + } + + bucket->cookie = collision | Bucket::PRESENT | hash; + mSize += 1; + initializeBucketEntry(*bucket, entry); + return index; + } +} + +void BasicHashtableImpl::removeAt(size_t index) { + edit(); + + Bucket& bucket = bucketAt(mBuckets, index); + bucket.cookie &= ~Bucket::PRESENT; + if (!(bucket.cookie & Bucket::COLLISION)) { + mFilledBuckets -= 1; + } + mSize -= 1; + if (!mHasTrivialDestructor) { + destroyBucketEntry(bucket); + } +} + +void BasicHashtableImpl::rehash(size_t minimumCapacity, float loadFactor) { + if (minimumCapacity < mSize) { + minimumCapacity = mSize; + } + size_t newBucketCount, newCapacity; + determineCapacity(minimumCapacity, loadFactor, &newBucketCount, &newCapacity); + + if (newBucketCount != mBucketCount || newCapacity != mCapacity) { + if (mBuckets) { + void* newBuckets; + if (mSize) { + newBuckets = allocateBuckets(newBucketCount); + for (size_t i = 0; i < mBucketCount; i++) { + const Bucket& fromBucket = bucketAt(mBuckets, i); + if (fromBucket.cookie & Bucket::PRESENT) { + hash_t hash = fromBucket.cookie & Bucket::HASH_MASK; + size_t index = chainStart(hash, newBucketCount); + Bucket* toBucket = &bucketAt(newBuckets, size_t(index)); + if (toBucket->cookie & Bucket::PRESENT) { + size_t inc = chainIncrement(hash, newBucketCount); + do { + toBucket->cookie |= Bucket::COLLISION; + index = chainSeek(index, inc, newBucketCount); + toBucket = &bucketAt(newBuckets, size_t(index)); + } while (toBucket->cookie & Bucket::PRESENT); + } + toBucket->cookie = Bucket::PRESENT | hash; + initializeBucketEntry(*toBucket, fromBucket.entry); + } + } + } else { + newBuckets = NULL; + } + releaseBuckets(mBuckets, mBucketCount); + mBuckets = newBuckets; + mFilledBuckets = mSize; + } + mBucketCount = newBucketCount; + mCapacity = newCapacity; + } + mLoadFactor = loadFactor; +} + +void* BasicHashtableImpl::allocateBuckets(size_t count) const { + size_t bytes = count * mBucketSize; + SharedBuffer* sb = SharedBuffer::alloc(bytes); + LOG_ALWAYS_FATAL_IF(!sb, "Could not allocate %u bytes for hashtable with %u buckets.", + uint32_t(bytes), uint32_t(count)); + void* buckets = sb->data(); + for (size_t i = 0; i < count; i++) { + Bucket& bucket = bucketAt(buckets, i); + bucket.cookie = 0; + } + return buckets; +} + +void BasicHashtableImpl::releaseBuckets(void* __restrict__ buckets, size_t count) const { + SharedBuffer* sb = SharedBuffer::bufferFromData(buckets); + if (sb->release(SharedBuffer::eKeepStorage) == 1) { + destroyBuckets(buckets, count); + SharedBuffer::dealloc(sb); + } +} + +void BasicHashtableImpl::destroyBuckets(void* __restrict__ buckets, size_t count) const { + if (!mHasTrivialDestructor) { + for (size_t i = 0; i < count; i++) { + Bucket& bucket = bucketAt(buckets, i); + if (bucket.cookie & Bucket::PRESENT) { + destroyBucketEntry(bucket); + } + } + } +} + +void BasicHashtableImpl::copyBuckets(const void* __restrict__ fromBuckets, + void* __restrict__ toBuckets, size_t count) const { + for (size_t i = 0; i < count; i++) { + const Bucket& fromBucket = bucketAt(fromBuckets, i); + Bucket& toBucket = bucketAt(toBuckets, i); + toBucket.cookie = fromBucket.cookie; + if (fromBucket.cookie & Bucket::PRESENT) { + initializeBucketEntry(toBucket, fromBucket.entry); + } + } +} + +// Table of 31-bit primes where each prime is no less than twice as large +// as the previous one. Generated by "primes.py". +static size_t PRIMES[] = { + 5, + 11, + 23, + 47, + 97, + 197, + 397, + 797, + 1597, + 3203, + 6421, + 12853, + 25717, + 51437, + 102877, + 205759, + 411527, + 823117, + 1646237, + 3292489, + 6584983, + 13169977, + 26339969, + 52679969, + 105359939, + 210719881, + 421439783, + 842879579, + 1685759167, + 0, +}; + +void BasicHashtableImpl::determineCapacity(size_t minimumCapacity, float loadFactor, + size_t* __restrict__ outBucketCount, size_t* __restrict__ outCapacity) { + LOG_ALWAYS_FATAL_IF(loadFactor <= 0.0f || loadFactor > 1.0f, + "Invalid load factor %0.3f. Must be in the range (0, 1].", loadFactor); + + size_t count = ceilf(minimumCapacity / loadFactor) + 1; + size_t i = 0; + while (count > PRIMES[i] && i < NELEM(PRIMES)) { + i++; + } + count = PRIMES[i]; + LOG_ALWAYS_FATAL_IF(!count, "Could not determine required number of buckets for " + "hashtable with minimum capacity %u and load factor %0.3f.", + uint32_t(minimumCapacity), loadFactor); + *outBucketCount = count; + *outCapacity = ceilf((count - 1) * loadFactor); +} + +}; // namespace android diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index bd7e239..18fd84f 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -17,218 +17,33 @@ #define LOG_TAG "CallStack" #include <string.h> -#include <stdlib.h> -#include <stdio.h> - -#if HAVE_DLADDR -#include <dlfcn.h> -#endif - -#if HAVE_CXXABI -#include <cxxabi.h> -#endif - -#include <unwind.h> #include <utils/Log.h> #include <utils/Errors.h> #include <utils/CallStack.h> -#include <utils/threads.h> - +#include <corkscrew/backtrace.h> /*****************************************************************************/ namespace android { - -typedef struct { - size_t count; - size_t ignore; - const void** addrs; -} stack_crawl_state_t; - -static -_Unwind_Reason_Code trace_function(_Unwind_Context *context, void *arg) -{ - stack_crawl_state_t* state = (stack_crawl_state_t*)arg; - if (state->count) { - void* ip = (void*)_Unwind_GetIP(context); - if (ip) { - if (state->ignore) { - state->ignore--; - } else { - state->addrs[0] = ip; - state->addrs++; - state->count--; - } - } - } - return _URC_NO_REASON; -} - -static -int backtrace(const void** addrs, size_t ignore, size_t size) -{ - stack_crawl_state_t state; - state.count = size; - state.ignore = ignore; - state.addrs = addrs; - _Unwind_Backtrace(trace_function, (void*)&state); - return size - state.count; -} - -/*****************************************************************************/ - -static -const char *lookup_symbol(const void* addr, void **offset, char* name, size_t bufSize) -{ -#if HAVE_DLADDR - Dl_info info; - if (dladdr(addr, &info)) { - *offset = info.dli_saddr; - return info.dli_sname; - } -#endif - return NULL; -} - -static -int32_t linux_gcc_demangler(const char *mangled_name, char *unmangled_name, size_t buffersize) -{ - size_t out_len = 0; -#if HAVE_CXXABI - int status = 0; - char *demangled = abi::__cxa_demangle(mangled_name, 0, &out_len, &status); - if (status == 0) { - // OK - if (out_len < buffersize) memcpy(unmangled_name, demangled, out_len); - else out_len = 0; - free(demangled); - } else { - out_len = 0; - } -#endif - return out_len; -} - -/*****************************************************************************/ - -class MapInfo { - struct mapinfo { - struct mapinfo *next; - uint64_t start; - uint64_t end; - char name[]; - }; - - const char *map_to_name(uint64_t pc, const char* def, uint64_t* start) { - mapinfo* mi = getMapInfoList(); - while(mi) { - if ((pc >= mi->start) && (pc < mi->end)) { - if (start) - *start = mi->start; - return mi->name; - } - mi = mi->next; - } - if (start) - *start = 0; - return def; - } - - mapinfo *parse_maps_line(char *line) { - mapinfo *mi; - int len = strlen(line); - if (len < 1) return 0; - line[--len] = 0; - if (len < 50) return 0; - if (line[20] != 'x') return 0; - mi = (mapinfo*)malloc(sizeof(mapinfo) + (len - 47)); - if (mi == 0) return 0; - mi->start = strtoull(line, 0, 16); - mi->end = strtoull(line + 9, 0, 16); - mi->next = 0; - strcpy(mi->name, line + 49); - return mi; - } - - mapinfo* getMapInfoList() { - Mutex::Autolock _l(mLock); - if (milist == 0) { - char data[1024]; - FILE *fp; - sprintf(data, "/proc/%d/maps", getpid()); - fp = fopen(data, "r"); - if (fp) { - while(fgets(data, 1024, fp)) { - mapinfo *mi = parse_maps_line(data); - if(mi) { - mi->next = milist; - milist = mi; - } - } - fclose(fp); - } - } - return milist; - } - mapinfo* milist; - Mutex mLock; - static MapInfo sMapInfo; - -public: - MapInfo() - : milist(0) { - } - - ~MapInfo() { - while (milist) { - mapinfo *next = milist->next; - free(milist); - milist = next; - } - } - - static const char *mapAddressToName(const void* pc, const char* def, - void const** start) - { - uint64_t s; - char const* name = sMapInfo.map_to_name(uint64_t(uintptr_t(pc)), def, &s); - if (start) { - *start = (void*)s; - } - return name; - } - -}; - -/*****************************************************************************/ - -MapInfo MapInfo::sMapInfo; - -/*****************************************************************************/ - -CallStack::CallStack() - : mCount(0) -{ +CallStack::CallStack() : + mCount(0) { } -CallStack::CallStack(const CallStack& rhs) - : mCount(rhs.mCount) -{ +CallStack::CallStack(const CallStack& rhs) : + mCount(rhs.mCount) { if (mCount) { - memcpy(mStack, rhs.mStack, mCount*sizeof(void*)); + memcpy(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)); } } -CallStack::~CallStack() -{ +CallStack::~CallStack() { } -CallStack& CallStack::operator = (const CallStack& rhs) -{ +CallStack& CallStack::operator = (const CallStack& rhs) { mCount = rhs.mCount; if (mCount) { - memcpy(mStack, rhs.mStack, mCount*sizeof(void*)); + memcpy(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)); } return *this; } @@ -236,7 +51,7 @@ CallStack& CallStack::operator = (const CallStack& rhs) bool CallStack::operator == (const CallStack& rhs) const { if (mCount != rhs.mCount) return false; - return !mCount || (memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) == 0); + return !mCount || memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) == 0; } bool CallStack::operator != (const CallStack& rhs) const { @@ -246,7 +61,7 @@ bool CallStack::operator != (const CallStack& rhs) const { bool CallStack::operator < (const CallStack& rhs) const { if (mCount != rhs.mCount) return mCount < rhs.mCount; - return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) < 0; + return memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) < 0; } bool CallStack::operator >= (const CallStack& rhs) const { @@ -256,7 +71,7 @@ bool CallStack::operator >= (const CallStack& rhs) const { bool CallStack::operator > (const CallStack& rhs) const { if (mCount != rhs.mCount) return mCount > rhs.mCount; - return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) > 0; + return memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) > 0; } bool CallStack::operator <= (const CallStack& rhs) const { @@ -266,84 +81,49 @@ bool CallStack::operator <= (const CallStack& rhs) const { const void* CallStack::operator [] (int index) const { if (index >= int(mCount)) return 0; - return mStack[index]; + return reinterpret_cast<const void*>(mStack[index].absolute_pc); } - -void CallStack::clear() -{ +void CallStack::clear() { mCount = 0; } -void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) -{ - if (maxDepth > MAX_DEPTH) +void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) { + if (maxDepth > MAX_DEPTH) { maxDepth = MAX_DEPTH; - mCount = backtrace(mStack, ignoreDepth, maxDepth); -} - -// Return the stack frame name on the designated level -String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const -{ - String8 res; - char namebuf[1024]; - char tmp[256]; - char tmp1[32]; - char tmp2[32]; - void *offs; - - const void* ip = mStack[level]; - if (!ip) return res; - - if (prefix) res.append(prefix); - snprintf(tmp1, 32, "#%02d ", level); - res.append(tmp1); - - const char* name = lookup_symbol(ip, &offs, namebuf, sizeof(namebuf)); - if (name) { - if (linux_gcc_demangler(name, tmp, 256) != 0) - name = tmp; - snprintf(tmp1, 32, "0x%p: <", ip); - snprintf(tmp2, 32, ">+0x%p", offs); - res.append(tmp1); - res.append(name); - res.append(tmp2); - } else { - void const* start = 0; - name = MapInfo::mapAddressToName(ip, "<unknown>", &start); - snprintf(tmp, 256, "pc %08lx %s", - long(uintptr_t(ip)-uintptr_t(start)), name); - res.append(tmp); } - res.append("\n"); - - return res; + ssize_t count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth); + mCount = count > 0 ? count : 0; } -// Dump a stack trace to the log -void CallStack::dump(const char* prefix) const -{ - /* - * Sending a single long log may be truncated since the stack levels can - * get very deep. So we request function names of each frame individually. - */ - for (int i=0; i<int(mCount); i++) { - ALOGD("%s", toStringSingleLevel(prefix, i).string()); +void CallStack::dump(const char* prefix) const { + backtrace_symbol_t symbols[mCount]; + + get_backtrace_symbols(mStack, mCount, symbols); + for (size_t i = 0; i < mCount; i++) { + char line[MAX_BACKTRACE_LINE_LENGTH]; + format_backtrace_line(i, &mStack[i], &symbols[i], + line, MAX_BACKTRACE_LINE_LENGTH); + ALOGD("%s%s", prefix, line); } + free_backtrace_symbols(symbols, mCount); } -// Return a string (possibly very long) containing the complete stack trace -String8 CallStack::toString(const char* prefix) const -{ - String8 res; +String8 CallStack::toString(const char* prefix) const { + String8 str; + backtrace_symbol_t symbols[mCount]; - for (int i=0; i<int(mCount); i++) { - res.append(toStringSingleLevel(prefix, i).string()); + get_backtrace_symbols(mStack, mCount, symbols); + for (size_t i = 0; i < mCount; i++) { + char line[MAX_BACKTRACE_LINE_LENGTH]; + format_backtrace_line(i, &mStack[i], &symbols[i], + line, MAX_BACKTRACE_LINE_LENGTH); + str.append(prefix); + str.append(line); + str.append("\n"); } - - return res; + free_backtrace_symbols(symbols, mCount); + return str; } -/*****************************************************************************/ - }; // namespace android diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 15b83bb..3fa562e 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -221,7 +221,7 @@ static void deserializeInternal(const void* inData, Res_png_9patch* outData) { static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes) { if (sizeBytes < ResTable::IDMAP_HEADER_SIZE_BYTES) { - ALOGW("idmap assertion failed: size=%d bytes\n", sizeBytes); + ALOGW("idmap assertion failed: size=%d bytes\n", (int)sizeBytes); return false; } if (*map != htodl(IDMAP_MAGIC)) { // htodl: map data expected to be in correct endianess @@ -250,7 +250,7 @@ static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, return UNKNOWN_ERROR; } if (typeCount > size) { - ALOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, size); + ALOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, (int)size); return UNKNOWN_ERROR; } const uint32_t typeOffset = map[type]; @@ -260,7 +260,7 @@ static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, } if (typeOffset + 1 > size) { ALOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n", - typeOffset, size); + typeOffset, (int)size); return UNKNOWN_ERROR; } const uint32_t entryCount = map[typeOffset]; @@ -271,7 +271,7 @@ static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, } const uint32_t index = typeOffset + 2 + entry - entryOffset; if (index > size) { - ALOGW("Resource ID map: entry index=%d exceeds size of map=%d\n", index, size); + ALOGW("Resource ID map: entry index=%d exceeds size of map=%d\n", index, (int)size); *outValue = 0; return NO_ERROR; } @@ -659,6 +659,16 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const return NULL; } +const String8 ResStringPool::string8ObjectAt(size_t idx) const +{ + size_t len; + const char *str = (const char*)string8At(idx, &len); + if (str != NULL) { + return String8(str); + } + return String8(stringAt(idx, &len)); +} + const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const { return styleAt(ref.index); @@ -738,12 +748,25 @@ size_t ResStringPool::size() const return (mError == NO_ERROR) ? mHeader->stringCount : 0; } -#ifndef HAVE_ANDROID_OS +size_t ResStringPool::styleCount() const +{ + return (mError == NO_ERROR) ? mHeader->styleCount : 0; +} + +size_t ResStringPool::bytes() const +{ + return (mError == NO_ERROR) ? mHeader->header.size : 0; +} + +bool ResStringPool::isSorted() const +{ + return (mHeader->flags&ResStringPool_header::SORTED_FLAG)!=0; +} + bool ResStringPool::isUTF8() const { return (mHeader->flags&ResStringPool_header::UTF8_FLAG)!=0; } -#endif // -------------------------------------------------------------------- // -------------------------------------------------------------------- @@ -1367,6 +1390,873 @@ status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const // -------------------------------------------------------------------- // -------------------------------------------------------------------- +void ResTable_config::copyFromDeviceNoSwap(const ResTable_config& o) { + const size_t size = dtohl(o.size); + if (size >= sizeof(ResTable_config)) { + *this = o; + } else { + memcpy(this, &o, size); + memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size); + } +} + +void ResTable_config::copyFromDtoH(const ResTable_config& o) { + copyFromDeviceNoSwap(o); + size = sizeof(ResTable_config); + mcc = dtohs(mcc); + mnc = dtohs(mnc); + density = dtohs(density); + screenWidth = dtohs(screenWidth); + screenHeight = dtohs(screenHeight); + sdkVersion = dtohs(sdkVersion); + minorVersion = dtohs(minorVersion); + smallestScreenWidthDp = dtohs(smallestScreenWidthDp); + screenWidthDp = dtohs(screenWidthDp); + screenHeightDp = dtohs(screenHeightDp); +} + +void ResTable_config::swapHtoD() { + size = htodl(size); + mcc = htods(mcc); + mnc = htods(mnc); + density = htods(density); + screenWidth = htods(screenWidth); + screenHeight = htods(screenHeight); + sdkVersion = htods(sdkVersion); + minorVersion = htods(minorVersion); + smallestScreenWidthDp = htods(smallestScreenWidthDp); + screenWidthDp = htods(screenWidthDp); + screenHeightDp = htods(screenHeightDp); +} + +int ResTable_config::compare(const ResTable_config& o) const { + int32_t diff = (int32_t)(imsi - o.imsi); + if (diff != 0) return diff; + diff = (int32_t)(locale - o.locale); + if (diff != 0) return diff; + diff = (int32_t)(screenType - o.screenType); + if (diff != 0) return diff; + diff = (int32_t)(input - o.input); + if (diff != 0) return diff; + diff = (int32_t)(screenSize - o.screenSize); + if (diff != 0) return diff; + diff = (int32_t)(version - o.version); + if (diff != 0) return diff; + diff = (int32_t)(screenLayout - o.screenLayout); + if (diff != 0) return diff; + diff = (int32_t)(uiMode - o.uiMode); + if (diff != 0) return diff; + diff = (int32_t)(smallestScreenWidthDp - o.smallestScreenWidthDp); + if (diff != 0) return diff; + diff = (int32_t)(screenSizeDp - o.screenSizeDp); + return (int)diff; +} + +int ResTable_config::compareLogical(const ResTable_config& o) const { + if (mcc != o.mcc) { + return mcc < o.mcc ? -1 : 1; + } + if (mnc != o.mnc) { + return mnc < o.mnc ? -1 : 1; + } + if (language[0] != o.language[0]) { + return language[0] < o.language[0] ? -1 : 1; + } + if (language[1] != o.language[1]) { + return language[1] < o.language[1] ? -1 : 1; + } + if (country[0] != o.country[0]) { + return country[0] < o.country[0] ? -1 : 1; + } + if (country[1] != o.country[1]) { + return country[1] < o.country[1] ? -1 : 1; + } + if (smallestScreenWidthDp != o.smallestScreenWidthDp) { + return smallestScreenWidthDp < o.smallestScreenWidthDp ? -1 : 1; + } + if (screenWidthDp != o.screenWidthDp) { + return screenWidthDp < o.screenWidthDp ? -1 : 1; + } + if (screenHeightDp != o.screenHeightDp) { + return screenHeightDp < o.screenHeightDp ? -1 : 1; + } + if (screenWidth != o.screenWidth) { + return screenWidth < o.screenWidth ? -1 : 1; + } + if (screenHeight != o.screenHeight) { + return screenHeight < o.screenHeight ? -1 : 1; + } + if (density != o.density) { + return density < o.density ? -1 : 1; + } + if (orientation != o.orientation) { + return orientation < o.orientation ? -1 : 1; + } + if (touchscreen != o.touchscreen) { + return touchscreen < o.touchscreen ? -1 : 1; + } + if (input != o.input) { + return input < o.input ? -1 : 1; + } + if (screenLayout != o.screenLayout) { + return screenLayout < o.screenLayout ? -1 : 1; + } + if (uiMode != o.uiMode) { + return uiMode < o.uiMode ? -1 : 1; + } + if (version != o.version) { + return version < o.version ? -1 : 1; + } + return 0; +} + +int ResTable_config::diff(const ResTable_config& o) const { + int diffs = 0; + if (mcc != o.mcc) diffs |= CONFIG_MCC; + if (mnc != o.mnc) diffs |= CONFIG_MNC; + if (locale != o.locale) diffs |= CONFIG_LOCALE; + if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION; + if (density != o.density) diffs |= CONFIG_DENSITY; + if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN; + if (((inputFlags^o.inputFlags)&(MASK_KEYSHIDDEN|MASK_NAVHIDDEN)) != 0) + diffs |= CONFIG_KEYBOARD_HIDDEN; + if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD; + if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION; + if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE; + if (version != o.version) diffs |= CONFIG_VERSION; + if (screenLayout != o.screenLayout) diffs |= CONFIG_SCREEN_LAYOUT; + if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE; + if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE; + if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE; + return diffs; +} + +bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const { + // The order of the following tests defines the importance of one + // configuration parameter over another. Those tests first are more + // important, trumping any values in those following them. + if (imsi || o.imsi) { + if (mcc != o.mcc) { + if (!mcc) return false; + if (!o.mcc) return true; + } + + if (mnc != o.mnc) { + if (!mnc) return false; + if (!o.mnc) return true; + } + } + + if (locale || o.locale) { + if (language[0] != o.language[0]) { + if (!language[0]) return false; + if (!o.language[0]) return true; + } + + if (country[0] != o.country[0]) { + if (!country[0]) return false; + if (!o.country[0]) return true; + } + } + + if (smallestScreenWidthDp || o.smallestScreenWidthDp) { + if (smallestScreenWidthDp != o.smallestScreenWidthDp) { + if (!smallestScreenWidthDp) return false; + if (!o.smallestScreenWidthDp) return true; + } + } + + if (screenSizeDp || o.screenSizeDp) { + if (screenWidthDp != o.screenWidthDp) { + if (!screenWidthDp) return false; + if (!o.screenWidthDp) return true; + } + + if (screenHeightDp != o.screenHeightDp) { + if (!screenHeightDp) return false; + if (!o.screenHeightDp) return true; + } + } + + if (screenLayout || o.screenLayout) { + if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0) { + if (!(screenLayout & MASK_SCREENSIZE)) return false; + if (!(o.screenLayout & MASK_SCREENSIZE)) return true; + } + if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0) { + if (!(screenLayout & MASK_SCREENLONG)) return false; + if (!(o.screenLayout & MASK_SCREENLONG)) return true; + } + } + + if (orientation != o.orientation) { + if (!orientation) return false; + if (!o.orientation) return true; + } + + if (uiMode || o.uiMode) { + if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0) { + if (!(uiMode & MASK_UI_MODE_TYPE)) return false; + if (!(o.uiMode & MASK_UI_MODE_TYPE)) return true; + } + if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0) { + if (!(uiMode & MASK_UI_MODE_NIGHT)) return false; + if (!(o.uiMode & MASK_UI_MODE_NIGHT)) return true; + } + } + + // density is never 'more specific' + // as the default just equals 160 + + if (touchscreen != o.touchscreen) { + if (!touchscreen) return false; + if (!o.touchscreen) return true; + } + + if (input || o.input) { + if (((inputFlags^o.inputFlags) & MASK_KEYSHIDDEN) != 0) { + if (!(inputFlags & MASK_KEYSHIDDEN)) return false; + if (!(o.inputFlags & MASK_KEYSHIDDEN)) return true; + } + + if (((inputFlags^o.inputFlags) & MASK_NAVHIDDEN) != 0) { + if (!(inputFlags & MASK_NAVHIDDEN)) return false; + if (!(o.inputFlags & MASK_NAVHIDDEN)) return true; + } + + if (keyboard != o.keyboard) { + if (!keyboard) return false; + if (!o.keyboard) return true; + } + + if (navigation != o.navigation) { + if (!navigation) return false; + if (!o.navigation) return true; + } + } + + if (screenSize || o.screenSize) { + if (screenWidth != o.screenWidth) { + if (!screenWidth) return false; + if (!o.screenWidth) return true; + } + + if (screenHeight != o.screenHeight) { + if (!screenHeight) return false; + if (!o.screenHeight) return true; + } + } + + if (version || o.version) { + if (sdkVersion != o.sdkVersion) { + if (!sdkVersion) return false; + if (!o.sdkVersion) return true; + } + + if (minorVersion != o.minorVersion) { + if (!minorVersion) return false; + if (!o.minorVersion) return true; + } + } + return false; +} + +bool ResTable_config::isBetterThan(const ResTable_config& o, + const ResTable_config* requested) const { + if (requested) { + if (imsi || o.imsi) { + if ((mcc != o.mcc) && requested->mcc) { + return (mcc); + } + + if ((mnc != o.mnc) && requested->mnc) { + return (mnc); + } + } + + if (locale || o.locale) { + if ((language[0] != o.language[0]) && requested->language[0]) { + return (language[0]); + } + + if ((country[0] != o.country[0]) && requested->country[0]) { + return (country[0]); + } + } + + if (smallestScreenWidthDp || o.smallestScreenWidthDp) { + // The configuration closest to the actual size is best. + // We assume that larger configs have already been filtered + // out at this point. That means we just want the largest one. + return smallestScreenWidthDp >= o.smallestScreenWidthDp; + } + + if (screenSizeDp || o.screenSizeDp) { + // "Better" is based on the sum of the difference between both + // width and height from the requested dimensions. We are + // assuming the invalid configs (with smaller dimens) have + // already been filtered. Note that if a particular dimension + // is unspecified, we will end up with a large value (the + // difference between 0 and the requested dimension), which is + // good since we will prefer a config that has specified a + // dimension value. + int myDelta = 0, otherDelta = 0; + if (requested->screenWidthDp) { + myDelta += requested->screenWidthDp - screenWidthDp; + otherDelta += requested->screenWidthDp - o.screenWidthDp; + } + if (requested->screenHeightDp) { + myDelta += requested->screenHeightDp - screenHeightDp; + otherDelta += requested->screenHeightDp - o.screenHeightDp; + } + //ALOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d", + // screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp, + // requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta); + return (myDelta <= otherDelta); + } + + if (screenLayout || o.screenLayout) { + if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0 + && (requested->screenLayout & MASK_SCREENSIZE)) { + // A little backwards compatibility here: undefined is + // considered equivalent to normal. But only if the + // requested size is at least normal; otherwise, small + // is better than the default. + int mySL = (screenLayout & MASK_SCREENSIZE); + int oSL = (o.screenLayout & MASK_SCREENSIZE); + int fixedMySL = mySL; + int fixedOSL = oSL; + if ((requested->screenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL) { + if (fixedMySL == 0) fixedMySL = SCREENSIZE_NORMAL; + if (fixedOSL == 0) fixedOSL = SCREENSIZE_NORMAL; + } + // For screen size, the best match is the one that is + // closest to the requested screen size, but not over + // (the not over part is dealt with in match() below). + if (fixedMySL == fixedOSL) { + // If the two are the same, but 'this' is actually + // undefined, then the other is really a better match. + if (mySL == 0) return false; + return true; + } + return fixedMySL >= fixedOSL; + } + if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0 + && (requested->screenLayout & MASK_SCREENLONG)) { + return (screenLayout & MASK_SCREENLONG); + } + } + + if ((orientation != o.orientation) && requested->orientation) { + return (orientation); + } + + if (uiMode || o.uiMode) { + if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0 + && (requested->uiMode & MASK_UI_MODE_TYPE)) { + return (uiMode & MASK_UI_MODE_TYPE); + } + if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0 + && (requested->uiMode & MASK_UI_MODE_NIGHT)) { + return (uiMode & MASK_UI_MODE_NIGHT); + } + } + + if (screenType || o.screenType) { + if (density != o.density) { + // density is tough. Any density is potentially useful + // because the system will scale it. Scaling down + // is generally better than scaling up. + // Default density counts as 160dpi (the system default) + // TODO - remove 160 constants + int h = (density?density:160); + int l = (o.density?o.density:160); + bool bImBigger = true; + if (l > h) { + int t = h; + h = l; + l = t; + bImBigger = false; + } + + int reqValue = (requested->density?requested->density:160); + if (reqValue >= h) { + // requested value higher than both l and h, give h + return bImBigger; + } + if (l >= reqValue) { + // requested value lower than both l and h, give l + return !bImBigger; + } + // saying that scaling down is 2x better than up + if (((2 * l) - reqValue) * h > reqValue * reqValue) { + return !bImBigger; + } else { + return bImBigger; + } + } + + if ((touchscreen != o.touchscreen) && requested->touchscreen) { + return (touchscreen); + } + } + + if (input || o.input) { + const int keysHidden = inputFlags & MASK_KEYSHIDDEN; + const int oKeysHidden = o.inputFlags & MASK_KEYSHIDDEN; + if (keysHidden != oKeysHidden) { + const int reqKeysHidden = + requested->inputFlags & MASK_KEYSHIDDEN; + if (reqKeysHidden) { + + if (!keysHidden) return false; + if (!oKeysHidden) return true; + // For compatibility, we count KEYSHIDDEN_NO as being + // the same as KEYSHIDDEN_SOFT. Here we disambiguate + // these by making an exact match more specific. + if (reqKeysHidden == keysHidden) return true; + if (reqKeysHidden == oKeysHidden) return false; + } + } + + const int navHidden = inputFlags & MASK_NAVHIDDEN; + const int oNavHidden = o.inputFlags & MASK_NAVHIDDEN; + if (navHidden != oNavHidden) { + const int reqNavHidden = + requested->inputFlags & MASK_NAVHIDDEN; + if (reqNavHidden) { + + if (!navHidden) return false; + if (!oNavHidden) return true; + } + } + + if ((keyboard != o.keyboard) && requested->keyboard) { + return (keyboard); + } + + if ((navigation != o.navigation) && requested->navigation) { + return (navigation); + } + } + + if (screenSize || o.screenSize) { + // "Better" is based on the sum of the difference between both + // width and height from the requested dimensions. We are + // assuming the invalid configs (with smaller sizes) have + // already been filtered. Note that if a particular dimension + // is unspecified, we will end up with a large value (the + // difference between 0 and the requested dimension), which is + // good since we will prefer a config that has specified a + // size value. + int myDelta = 0, otherDelta = 0; + if (requested->screenWidth) { + myDelta += requested->screenWidth - screenWidth; + otherDelta += requested->screenWidth - o.screenWidth; + } + if (requested->screenHeight) { + myDelta += requested->screenHeight - screenHeight; + otherDelta += requested->screenHeight - o.screenHeight; + } + return (myDelta <= otherDelta); + } + + if (version || o.version) { + if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) { + return (sdkVersion > o.sdkVersion); + } + + if ((minorVersion != o.minorVersion) && + requested->minorVersion) { + return (minorVersion); + } + } + + return false; + } + return isMoreSpecificThan(o); +} + +bool ResTable_config::match(const ResTable_config& settings) const { + if (imsi != 0) { + if (mcc != 0 && mcc != settings.mcc) { + return false; + } + if (mnc != 0 && mnc != settings.mnc) { + return false; + } + } + if (locale != 0) { + if (language[0] != 0 + && (language[0] != settings.language[0] + || language[1] != settings.language[1])) { + return false; + } + if (country[0] != 0 + && (country[0] != settings.country[0] + || country[1] != settings.country[1])) { + return false; + } + } + if (screenConfig != 0) { + const int screenSize = screenLayout&MASK_SCREENSIZE; + const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE; + // Any screen sizes for larger screens than the setting do not + // match. + if (screenSize != 0 && screenSize > setScreenSize) { + return false; + } + + const int screenLong = screenLayout&MASK_SCREENLONG; + const int setScreenLong = settings.screenLayout&MASK_SCREENLONG; + if (screenLong != 0 && screenLong != setScreenLong) { + return false; + } + + const int uiModeType = uiMode&MASK_UI_MODE_TYPE; + const int setUiModeType = settings.uiMode&MASK_UI_MODE_TYPE; + if (uiModeType != 0 && uiModeType != setUiModeType) { + return false; + } + + const int uiModeNight = uiMode&MASK_UI_MODE_NIGHT; + const int setUiModeNight = settings.uiMode&MASK_UI_MODE_NIGHT; + if (uiModeNight != 0 && uiModeNight != setUiModeNight) { + return false; + } + + if (smallestScreenWidthDp != 0 + && smallestScreenWidthDp > settings.smallestScreenWidthDp) { + return false; + } + } + if (screenSizeDp != 0) { + if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) { + //ALOGI("Filtering out width %d in requested %d", screenWidthDp, settings.screenWidthDp); + return false; + } + if (screenHeightDp != 0 && screenHeightDp > settings.screenHeightDp) { + //ALOGI("Filtering out height %d in requested %d", screenHeightDp, settings.screenHeightDp); + return false; + } + } + if (screenType != 0) { + if (orientation != 0 && orientation != settings.orientation) { + return false; + } + // density always matches - we can scale it. See isBetterThan + if (touchscreen != 0 && touchscreen != settings.touchscreen) { + return false; + } + } + if (input != 0) { + const int keysHidden = inputFlags&MASK_KEYSHIDDEN; + const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN; + if (keysHidden != 0 && keysHidden != setKeysHidden) { + // For compatibility, we count a request for KEYSHIDDEN_NO as also + // matching the more recent KEYSHIDDEN_SOFT. Basically + // KEYSHIDDEN_NO means there is some kind of keyboard available. + //ALOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden); + if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) { + //ALOGI("No match!"); + return false; + } + } + const int navHidden = inputFlags&MASK_NAVHIDDEN; + const int setNavHidden = settings.inputFlags&MASK_NAVHIDDEN; + if (navHidden != 0 && navHidden != setNavHidden) { + return false; + } + if (keyboard != 0 && keyboard != settings.keyboard) { + return false; + } + if (navigation != 0 && navigation != settings.navigation) { + return false; + } + } + if (screenSize != 0) { + if (screenWidth != 0 && screenWidth > settings.screenWidth) { + return false; + } + if (screenHeight != 0 && screenHeight > settings.screenHeight) { + return false; + } + } + if (version != 0) { + if (sdkVersion != 0 && sdkVersion > settings.sdkVersion) { + return false; + } + if (minorVersion != 0 && minorVersion != settings.minorVersion) { + return false; + } + } + return true; +} + +void ResTable_config::getLocale(char str[6]) const { + memset(str, 0, 6); + if (language[0]) { + str[0] = language[0]; + str[1] = language[1]; + if (country[0]) { + str[2] = '_'; + str[3] = country[0]; + str[4] = country[1]; + } + } +} + +String8 ResTable_config::toString() const { + String8 res; + + if (mcc != 0) { + if (res.size() > 0) res.append("-"); + res.appendFormat("%dmcc", dtohs(mcc)); + } + if (mnc != 0) { + if (res.size() > 0) res.append("-"); + res.appendFormat("%dmnc", dtohs(mnc)); + } + if (language[0] != 0) { + if (res.size() > 0) res.append("-"); + res.append(language, 2); + } + if (country[0] != 0) { + if (res.size() > 0) res.append("-"); + res.append(country, 2); + } + if (smallestScreenWidthDp != 0) { + if (res.size() > 0) res.append("-"); + res.appendFormat("sw%ddp", dtohs(smallestScreenWidthDp)); + } + if (screenWidthDp != 0) { + if (res.size() > 0) res.append("-"); + res.appendFormat("w%ddp", dtohs(screenWidthDp)); + } + if (screenHeightDp != 0) { + if (res.size() > 0) res.append("-"); + res.appendFormat("h%ddp", dtohs(screenHeightDp)); + } + if ((screenLayout&MASK_SCREENSIZE) != SCREENSIZE_ANY) { + if (res.size() > 0) res.append("-"); + switch (screenLayout&ResTable_config::MASK_SCREENSIZE) { + case ResTable_config::SCREENSIZE_SMALL: + res.append("small"); + break; + case ResTable_config::SCREENSIZE_NORMAL: + res.append("normal"); + break; + case ResTable_config::SCREENSIZE_LARGE: + res.append("large"); + break; + case ResTable_config::SCREENSIZE_XLARGE: + res.append("xlarge"); + break; + default: + res.appendFormat("screenLayoutSize=%d", + dtohs(screenLayout&ResTable_config::MASK_SCREENSIZE)); + break; + } + } + if ((screenLayout&MASK_SCREENLONG) != 0) { + if (res.size() > 0) res.append("-"); + switch (screenLayout&ResTable_config::MASK_SCREENLONG) { + case ResTable_config::SCREENLONG_NO: + res.append("notlong"); + break; + case ResTable_config::SCREENLONG_YES: + res.append("long"); + break; + default: + res.appendFormat("screenLayoutLong=%d", + dtohs(screenLayout&ResTable_config::MASK_SCREENLONG)); + break; + } + } + if (orientation != ORIENTATION_ANY) { + if (res.size() > 0) res.append("-"); + switch (orientation) { + case ResTable_config::ORIENTATION_PORT: + res.append("port"); + break; + case ResTable_config::ORIENTATION_LAND: + res.append("land"); + break; + case ResTable_config::ORIENTATION_SQUARE: + res.append("square"); + break; + default: + res.appendFormat("orientation=%d", dtohs(orientation)); + break; + } + } + if ((uiMode&MASK_UI_MODE_TYPE) != UI_MODE_TYPE_ANY) { + if (res.size() > 0) res.append("-"); + switch (uiMode&ResTable_config::MASK_UI_MODE_TYPE) { + case ResTable_config::UI_MODE_TYPE_DESK: + res.append("desk"); + break; + case ResTable_config::UI_MODE_TYPE_CAR: + res.append("car"); + break; + case ResTable_config::UI_MODE_TYPE_TELEVISION: + res.append("television"); + break; + case ResTable_config::UI_MODE_TYPE_APPLIANCE: + res.append("appliance"); + break; + default: + res.appendFormat("uiModeType=%d", + dtohs(screenLayout&ResTable_config::MASK_UI_MODE_TYPE)); + break; + } + } + if ((uiMode&MASK_UI_MODE_NIGHT) != 0) { + if (res.size() > 0) res.append("-"); + switch (uiMode&ResTable_config::MASK_UI_MODE_NIGHT) { + case ResTable_config::UI_MODE_NIGHT_NO: + res.append("notnight"); + break; + case ResTable_config::UI_MODE_NIGHT_YES: + res.append("night"); + break; + default: + res.appendFormat("uiModeNight=%d", + dtohs(uiMode&MASK_UI_MODE_NIGHT)); + break; + } + } + if (density != DENSITY_DEFAULT) { + if (res.size() > 0) res.append("-"); + switch (density) { + case ResTable_config::DENSITY_LOW: + res.append("ldpi"); + break; + case ResTable_config::DENSITY_MEDIUM: + res.append("mdpi"); + break; + case ResTable_config::DENSITY_TV: + res.append("tvdpi"); + break; + case ResTable_config::DENSITY_HIGH: + res.append("hdpi"); + break; + case ResTable_config::DENSITY_XHIGH: + res.append("xhdpi"); + break; + case ResTable_config::DENSITY_XXHIGH: + res.append("xxhdpi"); + break; + case ResTable_config::DENSITY_NONE: + res.append("nodpi"); + break; + default: + res.appendFormat("density=%d", dtohs(density)); + break; + } + } + if (touchscreen != TOUCHSCREEN_ANY) { + if (res.size() > 0) res.append("-"); + switch (touchscreen) { + case ResTable_config::TOUCHSCREEN_NOTOUCH: + res.append("notouch"); + break; + case ResTable_config::TOUCHSCREEN_FINGER: + res.append("finger"); + break; + case ResTable_config::TOUCHSCREEN_STYLUS: + res.append("stylus"); + break; + default: + res.appendFormat("touchscreen=%d", dtohs(touchscreen)); + break; + } + } + if (keyboard != KEYBOARD_ANY) { + if (res.size() > 0) res.append("-"); + switch (keyboard) { + case ResTable_config::KEYBOARD_NOKEYS: + res.append("nokeys"); + break; + case ResTable_config::KEYBOARD_QWERTY: + res.append("qwerty"); + break; + case ResTable_config::KEYBOARD_12KEY: + res.append("12key"); + break; + default: + res.appendFormat("keyboard=%d", dtohs(keyboard)); + break; + } + } + if ((inputFlags&MASK_KEYSHIDDEN) != 0) { + if (res.size() > 0) res.append("-"); + switch (inputFlags&MASK_KEYSHIDDEN) { + case ResTable_config::KEYSHIDDEN_NO: + res.append("keysexposed"); + break; + case ResTable_config::KEYSHIDDEN_YES: + res.append("keyshidden"); + break; + case ResTable_config::KEYSHIDDEN_SOFT: + res.append("keyssoft"); + break; + } + } + if (navigation != NAVIGATION_ANY) { + if (res.size() > 0) res.append("-"); + switch (navigation) { + case ResTable_config::NAVIGATION_NONAV: + res.append("nonav"); + break; + case ResTable_config::NAVIGATION_DPAD: + res.append("dpad"); + break; + case ResTable_config::NAVIGATION_TRACKBALL: + res.append("trackball"); + break; + case ResTable_config::NAVIGATION_WHEEL: + res.append("wheel"); + break; + default: + res.appendFormat("navigation=%d", dtohs(navigation)); + break; + } + } + if ((inputFlags&MASK_NAVHIDDEN) != 0) { + if (res.size() > 0) res.append("-"); + switch (inputFlags&MASK_NAVHIDDEN) { + case ResTable_config::NAVHIDDEN_NO: + res.append("navsexposed"); + break; + case ResTable_config::NAVHIDDEN_YES: + res.append("navhidden"); + break; + default: + res.appendFormat("inputFlagsNavHidden=%d", + dtohs(inputFlags&MASK_NAVHIDDEN)); + break; + } + } + if (screenSize != 0) { + if (res.size() > 0) res.append("-"); + res.appendFormat("%dx%d", dtohs(screenWidth), dtohs(screenHeight)); + } + if (version != 0) { + if (res.size() > 0) res.append("-"); + res.appendFormat("v%d", dtohs(sdkVersion)); + if (minorVersion != 0) { + res.appendFormat(".%d", dtohs(minorVersion)); + } + } + + return res; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- + struct ResTable::Header { Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL), @@ -3953,43 +4843,9 @@ ssize_t ResTable::getEntry( ResTable_config thisConfig; thisConfig.copyFromDtoH(thisType->config); - TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): imsi:%d/%d=%d/%d " - "lang:%c%c=%c%c cnt:%c%c=%c%c orien:%d=%d touch:%d=%d " - "density:%d=%d key:%d=%d inp:%d=%d nav:%d=%d w:%d=%d h:%d=%d " - "swdp:%d=%d wdp:%d=%d hdp:%d=%d\n", + TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): %s\n", entryIndex, typeIndex+1, dtohl(thisType->config.size), - thisConfig.mcc, thisConfig.mnc, - config ? config->mcc : 0, config ? config->mnc : 0, - thisConfig.language[0] ? thisConfig.language[0] : '-', - thisConfig.language[1] ? thisConfig.language[1] : '-', - config && config->language[0] ? config->language[0] : '-', - config && config->language[1] ? config->language[1] : '-', - thisConfig.country[0] ? thisConfig.country[0] : '-', - thisConfig.country[1] ? thisConfig.country[1] : '-', - config && config->country[0] ? config->country[0] : '-', - config && config->country[1] ? config->country[1] : '-', - thisConfig.orientation, - config ? config->orientation : 0, - thisConfig.touchscreen, - config ? config->touchscreen : 0, - thisConfig.density, - config ? config->density : 0, - thisConfig.keyboard, - config ? config->keyboard : 0, - thisConfig.inputFlags, - config ? config->inputFlags : 0, - thisConfig.navigation, - config ? config->navigation : 0, - thisConfig.screenWidth, - config ? config->screenWidth : 0, - thisConfig.screenHeight, - config ? config->screenHeight : 0, - thisConfig.smallestScreenWidthDp, - config ? config->smallestScreenWidthDp : 0, - thisConfig.screenWidthDp, - config ? config->screenWidthDp : 0, - thisConfig.screenHeightDp, - config ? config->screenHeightDp : 0)); + thisConfig.toString().string())); // Check to make sure this one is valid for the current parameters. if (config && !thisConfig.match(*config)) { @@ -4273,26 +5129,8 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, TABLE_GETENTRY( ResTable_config thisConfig; thisConfig.copyFromDtoH(type->config); - ALOGI("Adding config to type %d: imsi:%d/%d lang:%c%c cnt:%c%c " - "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d " - "swdp:%d wdp:%d hdp:%d\n", - type->id, - thisConfig.mcc, thisConfig.mnc, - thisConfig.language[0] ? thisConfig.language[0] : '-', - thisConfig.language[1] ? thisConfig.language[1] : '-', - thisConfig.country[0] ? thisConfig.country[0] : '-', - thisConfig.country[1] ? thisConfig.country[1] : '-', - thisConfig.orientation, - thisConfig.touchscreen, - thisConfig.density, - thisConfig.keyboard, - thisConfig.inputFlags, - thisConfig.navigation, - thisConfig.screenWidth, - thisConfig.screenHeight, - thisConfig.smallestScreenWidthDp, - thisConfig.screenWidthDp, - thisConfig.screenHeightDp)); + ALOGI("Adding config to type %d: %s\n", + type->id, thisConfig.toString().string())); t->configs.add(type); } else { status_t err = validate_chunk(chunk, sizeof(ResChunk_header), @@ -4622,186 +5460,9 @@ void ResTable::print(bool inclValues) const printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); continue; } - char density[16]; - uint16_t dval = dtohs(type->config.density); - if (dval == ResTable_config::DENSITY_DEFAULT) { - strcpy(density, "def"); - } else if (dval == ResTable_config::DENSITY_NONE) { - strcpy(density, "no"); - } else { - sprintf(density, "%d", (int)dval); - } - printf(" config %d", (int)configIndex); - if (type->config.mcc != 0) { - printf(" mcc=%d", dtohs(type->config.mcc)); - } - if (type->config.mnc != 0) { - printf(" mnc=%d", dtohs(type->config.mnc)); - } - if (type->config.locale != 0) { - printf(" lang=%c%c cnt=%c%c", - type->config.language[0] ? type->config.language[0] : '-', - type->config.language[1] ? type->config.language[1] : '-', - type->config.country[0] ? type->config.country[0] : '-', - type->config.country[1] ? type->config.country[1] : '-'); - } - if (type->config.screenLayout != 0) { - printf(" sz=%d", - type->config.screenLayout&ResTable_config::MASK_SCREENSIZE); - switch (type->config.screenLayout&ResTable_config::MASK_SCREENSIZE) { - case ResTable_config::SCREENSIZE_SMALL: - printf(" (small)"); - break; - case ResTable_config::SCREENSIZE_NORMAL: - printf(" (normal)"); - break; - case ResTable_config::SCREENSIZE_LARGE: - printf(" (large)"); - break; - case ResTable_config::SCREENSIZE_XLARGE: - printf(" (xlarge)"); - break; - } - printf(" lng=%d", - type->config.screenLayout&ResTable_config::MASK_SCREENLONG); - switch (type->config.screenLayout&ResTable_config::MASK_SCREENLONG) { - case ResTable_config::SCREENLONG_NO: - printf(" (notlong)"); - break; - case ResTable_config::SCREENLONG_YES: - printf(" (long)"); - break; - } - } - if (type->config.orientation != 0) { - printf(" orient=%d", type->config.orientation); - switch (type->config.orientation) { - case ResTable_config::ORIENTATION_PORT: - printf(" (port)"); - break; - case ResTable_config::ORIENTATION_LAND: - printf(" (land)"); - break; - case ResTable_config::ORIENTATION_SQUARE: - printf(" (square)"); - break; - } - } - if (type->config.uiMode != 0) { - printf(" type=%d", - type->config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); - switch (type->config.uiMode&ResTable_config::MASK_UI_MODE_TYPE) { - case ResTable_config::UI_MODE_TYPE_NORMAL: - printf(" (normal)"); - break; - case ResTable_config::UI_MODE_TYPE_CAR: - printf(" (car)"); - break; - } - printf(" night=%d", - type->config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); - switch (type->config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT) { - case ResTable_config::UI_MODE_NIGHT_NO: - printf(" (no)"); - break; - case ResTable_config::UI_MODE_NIGHT_YES: - printf(" (yes)"); - break; - } - } - if (dval != 0) { - printf(" density=%s", density); - } - if (type->config.touchscreen != 0) { - printf(" touch=%d", type->config.touchscreen); - switch (type->config.touchscreen) { - case ResTable_config::TOUCHSCREEN_NOTOUCH: - printf(" (notouch)"); - break; - case ResTable_config::TOUCHSCREEN_STYLUS: - printf(" (stylus)"); - break; - case ResTable_config::TOUCHSCREEN_FINGER: - printf(" (finger)"); - break; - } - } - if (type->config.inputFlags != 0) { - printf(" keyhid=%d", type->config.inputFlags&ResTable_config::MASK_KEYSHIDDEN); - switch (type->config.inputFlags&ResTable_config::MASK_KEYSHIDDEN) { - case ResTable_config::KEYSHIDDEN_NO: - printf(" (no)"); - break; - case ResTable_config::KEYSHIDDEN_YES: - printf(" (yes)"); - break; - case ResTable_config::KEYSHIDDEN_SOFT: - printf(" (soft)"); - break; - } - printf(" navhid=%d", type->config.inputFlags&ResTable_config::MASK_NAVHIDDEN); - switch (type->config.inputFlags&ResTable_config::MASK_NAVHIDDEN) { - case ResTable_config::NAVHIDDEN_NO: - printf(" (no)"); - break; - case ResTable_config::NAVHIDDEN_YES: - printf(" (yes)"); - break; - } - } - if (type->config.keyboard != 0) { - printf(" kbd=%d", type->config.keyboard); - switch (type->config.keyboard) { - case ResTable_config::KEYBOARD_NOKEYS: - printf(" (nokeys)"); - break; - case ResTable_config::KEYBOARD_QWERTY: - printf(" (qwerty)"); - break; - case ResTable_config::KEYBOARD_12KEY: - printf(" (12key)"); - break; - } - } - if (type->config.navigation != 0) { - printf(" nav=%d", type->config.navigation); - switch (type->config.navigation) { - case ResTable_config::NAVIGATION_NONAV: - printf(" (nonav)"); - break; - case ResTable_config::NAVIGATION_DPAD: - printf(" (dpad)"); - break; - case ResTable_config::NAVIGATION_TRACKBALL: - printf(" (trackball)"); - break; - case ResTable_config::NAVIGATION_WHEEL: - printf(" (wheel)"); - break; - } - } - if (type->config.screenWidth != 0) { - printf(" w=%d", dtohs(type->config.screenWidth)); - } - if (type->config.screenHeight != 0) { - printf(" h=%d", dtohs(type->config.screenHeight)); - } - if (type->config.smallestScreenWidthDp != 0) { - printf(" swdp=%d", dtohs(type->config.smallestScreenWidthDp)); - } - if (type->config.screenWidthDp != 0) { - printf(" wdp=%d", dtohs(type->config.screenWidthDp)); - } - if (type->config.screenHeightDp != 0) { - printf(" hdp=%d", dtohs(type->config.screenHeightDp)); - } - if (type->config.sdkVersion != 0) { - printf(" sdk=%d", dtohs(type->config.sdkVersion)); - } - if (type->config.minorVersion != 0) { - printf(" mver=%d", dtohs(type->config.minorVersion)); - } - printf("\n"); + String8 configStr = type->config.toString(); + printf(" config %s:\n", configStr.size() > 0 + ? configStr.string() : "(default)"); size_t entryCount = dtohl(type->entryCount); uint32_t entriesStart = dtohl(type->entriesStart); if ((entriesStart&0x3) != 0) { diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index e343c62..ab207f5 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -34,6 +34,9 @@ # include <pthread.h> # include <sched.h> # include <sys/resource.h> +#ifdef HAVE_ANDROID_OS +# include <bionic_pthread.h> +#endif #elif defined(HAVE_WIN32_THREADS) # include <windows.h> # include <stdint.h> @@ -86,7 +89,7 @@ struct thread_data_t { char * threadName; // we use this trampoline when we need to set the priority with - // nice/setpriority. + // nice/setpriority, and name with prctl. static int trampoline(const thread_data_t* t) { thread_func_t f = t->entryFunction; void* u = t->userData; @@ -141,8 +144,13 @@ int androidCreateRawThreadEtc(android_thread_func_t entryFunction, #ifdef HAVE_ANDROID_OS /* valgrind is rejecting RT-priority create reqs */ if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) { - // We could avoid the trampoline if there was a way to get to the - // android_thread_id_t (pid) from pthread_t + // Now that the pthread_t has a method to find the associated + // android_thread_id_t (pid) from pthread_t, it would be possible to avoid + // this trampoline in some cases as the parent could set the properties + // for the child. However, there would be a race condition because the + // child becomes ready immediately, and it doesn't work for the name. + // prctl(PR_SET_NAME) only works for self; prctl(PR_SET_THREAD_NAME) was + // proposed but not yet accepted. thread_data_t* t = new thread_data_t; t->priority = threadPriority; t->threadName = threadName ? strdup(threadName) : NULL; @@ -178,6 +186,13 @@ int androidCreateRawThreadEtc(android_thread_func_t entryFunction, return 1; } +#ifdef HAVE_ANDROID_OS +static pthread_t android_thread_id_t_to_pthread(android_thread_id_t thread) +{ + return (pthread_t) thread; +} +#endif + android_thread_id_t androidGetThreadId() { return (android_thread_id_t)pthread_self(); @@ -909,6 +924,23 @@ status_t Thread::join() return mStatus; } +#ifdef HAVE_ANDROID_OS +pid_t Thread::getTid() const +{ + // mTid is not defined until the child initializes it, and the caller may need it earlier + Mutex::Autolock _l(mLock); + pid_t tid; + if (mRunning) { + pthread_t pthread = android_thread_id_t_to_pthread(mThread); + tid = __pthread_gettid(pthread); + } else { + ALOGW("Thread (this=%p): getTid() is undefined before run()", this); + tid = -1; + } + return tid; +} +#endif + bool Thread::exitPending() const { Mutex::Autolock _l(mLock); diff --git a/libs/utils/primes.py b/libs/utils/primes.py new file mode 100755 index 0000000..e161dd8 --- /dev/null +++ b/libs/utils/primes.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python2.6 +# +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# Generates a table of prime numbers for use in BasicHashtable.cpp. +# +# Each prime is chosen such that it is a little more than twice as large as +# the previous prime in the table. This makes it easier to choose a new +# hashtable size when the underlying array is grown by as nominal factor +# of two each time. +# + +def is_odd_prime(n): + limit = (n - 1) / 2 + d = 3 + while d <= limit: + if n % d == 0: + return False + d += 2 + return True + +print "static size_t PRIMES[] = {" + +n = 5 +max = 2**31 - 1 +while n < max: + print " %d," % (n) + n = n * 2 + 1 + while not is_odd_prime(n): + n += 2 + +print " 0," +print "};" diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index b97f52f..58230f4 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -4,9 +4,10 @@ include $(CLEAR_VARS) # Build the unit tests. test_src_files := \ + BasicHashtable_test.cpp \ BlobCache_test.cpp \ - ObbFile_test.cpp \ Looper_test.cpp \ + ObbFile_test.cpp \ String8_test.cpp \ Unicode_test.cpp \ ZipFileRO_test.cpp \ diff --git a/libs/utils/tests/BasicHashtable_test.cpp b/libs/utils/tests/BasicHashtable_test.cpp new file mode 100644 index 0000000..7dcf750 --- /dev/null +++ b/libs/utils/tests/BasicHashtable_test.cpp @@ -0,0 +1,577 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BasicHashtable_test" + +#include <utils/BasicHashtable.h> +#include <cutils/log.h> +#include <gtest/gtest.h> +#include <unistd.h> + +namespace android { + +typedef int SimpleKey; +typedef int SimpleValue; +typedef key_value_pair_t<SimpleKey, SimpleValue> SimpleEntry; +typedef BasicHashtable<SimpleKey, SimpleEntry> SimpleHashtable; + +struct ComplexKey { + int k; + + explicit ComplexKey(int k) : k(k) { + instanceCount += 1; + } + + ComplexKey(const ComplexKey& other) : k(other.k) { + instanceCount += 1; + } + + ~ComplexKey() { + instanceCount -= 1; + } + + bool operator ==(const ComplexKey& other) const { + return k == other.k; + } + + bool operator !=(const ComplexKey& other) const { + return k != other.k; + } + + static ssize_t instanceCount; +}; + +ssize_t ComplexKey::instanceCount = 0; + +template<> inline hash_t hash_type(const ComplexKey& value) { + return hash_type(value.k); +} + +struct ComplexValue { + int v; + + explicit ComplexValue(int v) : v(v) { + instanceCount += 1; + } + + ComplexValue(const ComplexValue& other) : v(other.v) { + instanceCount += 1; + } + + ~ComplexValue() { + instanceCount -= 1; + } + + static ssize_t instanceCount; +}; + +ssize_t ComplexValue::instanceCount = 0; + +typedef key_value_pair_t<ComplexKey, ComplexValue> ComplexEntry; +typedef BasicHashtable<ComplexKey, ComplexEntry> ComplexHashtable; + +class BasicHashtableTest : public testing::Test { +protected: + virtual void SetUp() { + ComplexKey::instanceCount = 0; + ComplexValue::instanceCount = 0; + } + + virtual void TearDown() { + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + } + + void assertInstanceCount(ssize_t keys, ssize_t values) { + if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) { + FAIL() << "Expected " << keys << " keys and " << values << " values " + "but there were actually " << ComplexKey::instanceCount << " keys and " + << ComplexValue::instanceCount << " values"; + } + } + +public: + template <typename TKey, typename TEntry> + static void cookieAt(const BasicHashtable<TKey, TEntry>& h, size_t index, + bool* collision, bool* present, hash_t* hash) { + uint32_t cookie = h.cookieAt(index); + *collision = cookie & BasicHashtable<TKey, TEntry>::Bucket::COLLISION; + *present = cookie & BasicHashtable<TKey, TEntry>::Bucket::PRESENT; + *hash = cookie & BasicHashtable<TKey, TEntry>::Bucket::HASH_MASK; + } + + template <typename TKey, typename TEntry> + static const void* getBuckets(const BasicHashtable<TKey, TEntry>& h) { + return h.mBuckets; + } +}; + +template <typename TKey, typename TValue> +static size_t add(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h, + const TKey& key, const TValue& value) { + return h.add(hash_type(key), key_value_pair_t<TKey, TValue>(key, value)); +} + +template <typename TKey, typename TValue> +static ssize_t find(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h, + ssize_t index, const TKey& key) { + return h.find(index, hash_type(key), key); +} + +template <typename TKey, typename TValue> +static bool remove(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h, + const TKey& key) { + ssize_t index = find(h, -1, key); + if (index >= 0) { + h.removeAt(index); + return true; + } + return false; +} + +template <typename TEntry> +static void getKeyValue(const TEntry& entry, int* key, int* value); + +template <> void getKeyValue(const SimpleEntry& entry, int* key, int* value) { + *key = entry.key; + *value = entry.value; +} + +template <> void getKeyValue(const ComplexEntry& entry, int* key, int* value) { + *key = entry.key.k; + *value = entry.value.v; +} + +template <typename TKey, typename TValue> +static void dump(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h) { + ALOGD("hashtable %p, size=%u, capacity=%u, bucketCount=%u", + &h, h.size(), h.capacity(), h.bucketCount()); + for (size_t i = 0; i < h.bucketCount(); i++) { + bool collision, present; + hash_t hash; + BasicHashtableTest::cookieAt(h, i, &collision, &present, &hash); + if (present) { + int key, value; + getKeyValue(h.entryAt(i), &key, &value); + ALOGD(" [%3u] = collision=%d, present=%d, hash=0x%08x, key=%3d, value=%3d, " + "hash_type(key)=0x%08x", + i, collision, present, hash, key, value, hash_type(key)); + } else { + ALOGD(" [%3u] = collision=%d, present=%d", + i, collision, present); + } + } +} + +TEST_F(BasicHashtableTest, DefaultConstructor_WithDefaultProperties) { + SimpleHashtable h; + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Constructor_WithNonUnityLoadFactor) { + SimpleHashtable h(52, 0.8f); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(77U, h.capacity()); + EXPECT_EQ(97U, h.bucketCount()); + EXPECT_EQ(0.8f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndExactCapacity) { + SimpleHashtable h(46, 1.0f); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f + EXPECT_EQ(47U, h.bucketCount()); + EXPECT_EQ(1.0f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndInexactCapacity) { + SimpleHashtable h(42, 1.0f); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f + EXPECT_EQ(47U, h.bucketCount()); + EXPECT_EQ(1.0f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, FindAddFindRemoveFind_OneEntry) { + SimpleHashtable h; + ssize_t index = find(h, -1, 8); + ASSERT_EQ(-1, index); + + index = add(h, 8, 1); + ASSERT_EQ(1U, h.size()); + + ASSERT_EQ(index, find(h, -1, 8)); + ASSERT_EQ(8, h.entryAt(index).key); + ASSERT_EQ(1, h.entryAt(index).value); + + index = find(h, index, 8); + ASSERT_EQ(-1, index); + + ASSERT_TRUE(remove(h, 8)); + ASSERT_EQ(0U, h.size()); + + index = find(h, -1, 8); + ASSERT_EQ(-1, index); +} + +TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithUniqueKey) { + const size_t N = 11; + + SimpleHashtable h; + for (size_t i = 0; i < N; i++) { + ssize_t index = find(h, -1, int(i)); + ASSERT_EQ(-1, index); + + index = add(h, int(i), int(i * 10)); + ASSERT_EQ(i + 1, h.size()); + + ASSERT_EQ(index, find(h, -1, int(i))); + ASSERT_EQ(int(i), h.entryAt(index).key); + ASSERT_EQ(int(i * 10), h.entryAt(index).value); + + index = find(h, index, int(i)); + ASSERT_EQ(-1, index); + } + + for (size_t i = N; --i > 0; ) { + ASSERT_TRUE(remove(h, int(i))) << "i = " << i; + ASSERT_EQ(i, h.size()); + + ssize_t index = find(h, -1, int(i)); + ASSERT_EQ(-1, index); + } +} + +TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithDuplicateKey) { + const size_t N = 11; + const int K = 1; + + SimpleHashtable h; + for (size_t i = 0; i < N; i++) { + ssize_t index = find(h, -1, K); + if (i == 0) { + ASSERT_EQ(-1, index); + } else { + ASSERT_NE(-1, index); + } + + add(h, K, int(i)); + ASSERT_EQ(i + 1, h.size()); + + index = -1; + int values = 0; + for (size_t j = 0; j <= i; j++) { + index = find(h, index, K); + ASSERT_GE(index, 0); + ASSERT_EQ(K, h.entryAt(index).key); + values |= 1 << h.entryAt(index).value; + } + ASSERT_EQ(values, (1 << (i + 1)) - 1); + + index = find(h, index, K); + ASSERT_EQ(-1, index); + } + + for (size_t i = N; --i > 0; ) { + ASSERT_TRUE(remove(h, K)) << "i = " << i; + ASSERT_EQ(i, h.size()); + + ssize_t index = -1; + for (size_t j = 0; j < i; j++) { + index = find(h, index, K); + ASSERT_GE(index, 0); + ASSERT_EQ(K, h.entryAt(index).key); + } + + index = find(h, index, K); + ASSERT_EQ(-1, index); + } +} + +TEST_F(BasicHashtableTest, Clear_WhenAlreadyEmpty_DoesNothing) { + SimpleHashtable h; + h.clear(); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_RemovesThem) { + SimpleHashtable h; + add(h, 0, 0); + add(h, 1, 0); + h.clear(); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_DestroysThem) { + ComplexHashtable h; + add(h, ComplexKey(0), ComplexValue(0)); + add(h, ComplexKey(1), ComplexValue(0)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + + h.clear(); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Remove_AfterElementsAdded_DestroysThem) { + ComplexHashtable h; + add(h, ComplexKey(0), ComplexValue(0)); + add(h, ComplexKey(1), ComplexValue(0)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + + ASSERT_TRUE(remove(h, ComplexKey(0))); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1)); + + ASSERT_TRUE(remove(h, ComplexKey(1))); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Destructor_AfterElementsAdded_DestroysThem) { + { + ComplexHashtable h; + add(h, ComplexKey(0), ComplexValue(0)); + add(h, ComplexKey(1), ComplexValue(0)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + } // h is destroyed here + + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); +} + +TEST_F(BasicHashtableTest, Next_WhenEmpty_ReturnsMinusOne) { + SimpleHashtable h; + + ASSERT_EQ(-1, h.next(-1)); +} + +TEST_F(BasicHashtableTest, Next_WhenNonEmpty_IteratesOverAllEntries) { + const int N = 88; + + SimpleHashtable h; + for (int i = 0; i < N; i++) { + add(h, i, i * 10); + } + + bool set[N]; + memset(set, 0, sizeof(bool) * N); + int count = 0; + for (ssize_t index = -1; (index = h.next(index)) != -1; ) { + ASSERT_GE(index, 0); + ASSERT_LT(size_t(index), h.bucketCount()); + + const SimpleEntry& entry = h.entryAt(index); + ASSERT_GE(entry.key, 0); + ASSERT_LT(entry.key, N); + ASSERT_EQ(false, set[entry.key]); + ASSERT_EQ(entry.key * 10, entry.value); + + set[entry.key] = true; + count += 1; + } + ASSERT_EQ(N, count); +} + +TEST_F(BasicHashtableTest, Add_RehashesOnDemand) { + SimpleHashtable h; + size_t initialCapacity = h.capacity(); + size_t initialBucketCount = h.bucketCount(); + + for (size_t i = 0; i < initialCapacity; i++) { + add(h, int(i), 0); + } + + EXPECT_EQ(initialCapacity, h.size()); + EXPECT_EQ(initialCapacity, h.capacity()); + EXPECT_EQ(initialBucketCount, h.bucketCount()); + + add(h, -1, -1); + + EXPECT_EQ(initialCapacity + 1, h.size()); + EXPECT_GT(h.capacity(), initialCapacity); + EXPECT_GT(h.bucketCount(), initialBucketCount); + EXPECT_GT(h.bucketCount(), h.capacity()); +} + +TEST_F(BasicHashtableTest, Rehash_WhenCapacityAndBucketCountUnchanged_DoesNothing) { + ComplexHashtable h; + add(h, ComplexKey(0), ComplexValue(0)); + const void* oldBuckets = getBuckets(h); + ASSERT_NE((void*)NULL, oldBuckets); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1)); + + h.rehash(h.capacity(), h.loadFactor()); + + ASSERT_EQ(oldBuckets, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1)); +} + +TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasNoBuckets_ButDoesNotAllocateBuckets) { + ComplexHashtable h; + ASSERT_EQ((void*)NULL, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + + h.rehash(9, 1.0f); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(10U, h.capacity()); + EXPECT_EQ(11U, h.bucketCount()); + EXPECT_EQ(1.0f, h.loadFactor()); + EXPECT_EQ((void*)NULL, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); +} + +TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasBuckets_ReleasesBucketsAndSetsCapacity) { + ComplexHashtable h(10); + add(h, ComplexKey(0), ComplexValue(0)); + ASSERT_TRUE(remove(h, ComplexKey(0))); + ASSERT_NE((void*)NULL, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + + h.rehash(0, 0.75f); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); + EXPECT_EQ((void*)NULL, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); +} + +TEST_F(BasicHashtableTest, Rehash_WhenLessThanCurrentCapacity_ShrinksBuckets) { + ComplexHashtable h(10); + add(h, ComplexKey(0), ComplexValue(0)); + add(h, ComplexKey(1), ComplexValue(1)); + const void* oldBuckets = getBuckets(h); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + + h.rehash(0, 0.75f); + + EXPECT_EQ(2U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); + EXPECT_NE(oldBuckets, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); +} + +TEST_F(BasicHashtableTest, CopyOnWrite) { + ComplexHashtable h1; + add(h1, ComplexKey(0), ComplexValue(0)); + add(h1, ComplexKey(1), ComplexValue(1)); + const void* originalBuckets = getBuckets(h1); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ssize_t index0 = find(h1, -1, ComplexKey(0)); + EXPECT_GE(index0, 0); + + // copy constructor acquires shared reference + ComplexHashtable h2(h1); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ASSERT_EQ(originalBuckets, getBuckets(h2)); + EXPECT_EQ(h1.size(), h2.size()); + EXPECT_EQ(h1.capacity(), h2.capacity()); + EXPECT_EQ(h1.bucketCount(), h2.bucketCount()); + EXPECT_EQ(h1.loadFactor(), h2.loadFactor()); + EXPECT_EQ(index0, find(h2, -1, ComplexKey(0))); + + // operator= acquires shared reference + ComplexHashtable h3; + h3 = h2; + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ASSERT_EQ(originalBuckets, getBuckets(h3)); + EXPECT_EQ(h1.size(), h3.size()); + EXPECT_EQ(h1.capacity(), h3.capacity()); + EXPECT_EQ(h1.bucketCount(), h3.bucketCount()); + EXPECT_EQ(h1.loadFactor(), h3.loadFactor()); + EXPECT_EQ(index0, find(h3, -1, ComplexKey(0))); + + // editEntryAt copies shared contents + h1.editEntryAt(index0).value.v = 42; + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4)); + ASSERT_NE(originalBuckets, getBuckets(h1)); + EXPECT_EQ(42, h1.entryAt(index0).value.v); + EXPECT_EQ(0, h2.entryAt(index0).value.v); + EXPECT_EQ(0, h3.entryAt(index0).value.v); + + // clear releases reference to shared contents + h2.clear(); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4)); + EXPECT_EQ(0U, h2.size()); + ASSERT_NE(originalBuckets, getBuckets(h2)); + + // operator= acquires shared reference, destroys unshared contents + h1 = h3; + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ASSERT_EQ(originalBuckets, getBuckets(h1)); + EXPECT_EQ(h3.size(), h1.size()); + EXPECT_EQ(h3.capacity(), h1.capacity()); + EXPECT_EQ(h3.bucketCount(), h1.bucketCount()); + EXPECT_EQ(h3.loadFactor(), h1.loadFactor()); + EXPECT_EQ(index0, find(h1, -1, ComplexKey(0))); + + // add copies shared contents + add(h1, ComplexKey(2), ComplexValue(2)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(5, 5)); + ASSERT_NE(originalBuckets, getBuckets(h1)); + EXPECT_EQ(3U, h1.size()); + EXPECT_EQ(0U, h2.size()); + EXPECT_EQ(2U, h3.size()); + + // remove copies shared contents + h1 = h3; + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ASSERT_EQ(originalBuckets, getBuckets(h1)); + h1.removeAt(index0); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(3, 3)); + ASSERT_NE(originalBuckets, getBuckets(h1)); + EXPECT_EQ(1U, h1.size()); + EXPECT_EQ(0U, h2.size()); + EXPECT_EQ(2U, h3.size()); + + // rehash copies shared contents + h1 = h3; + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ASSERT_EQ(originalBuckets, getBuckets(h1)); + h1.rehash(10, 1.0f); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4)); + ASSERT_NE(originalBuckets, getBuckets(h1)); + EXPECT_EQ(2U, h1.size()); + EXPECT_EQ(0U, h2.size()); + EXPECT_EQ(2U, h3.size()); +} + +} // namespace android |