summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/binder/IPCThreadState.cpp8
-rw-r--r--libs/binder/MemoryDealer.cpp6
-rw-r--r--libs/binder/Parcel.cpp10
-rw-r--r--libs/gui/Android.mk11
-rw-r--r--libs/gui/BitTube.cpp (renamed from libs/gui/SensorChannel.cpp)61
-rw-r--r--libs/gui/DisplayEventReceiver.cpp109
-rw-r--r--libs/gui/IDisplayEventConnection.cpp98
-rw-r--r--libs/gui/ISensorEventConnection.cpp8
-rw-r--r--libs/gui/ISurfaceComposer.cpp37
-rw-r--r--libs/gui/ISurfaceComposerClient.cpp12
-rw-r--r--libs/gui/SensorEventQueue.cpp4
-rw-r--r--libs/gui/SurfaceComposerClient.cpp1
-rw-r--r--libs/gui/SurfaceTexture.cpp2
-rw-r--r--libs/gui/SurfaceTextureClient.cpp2
-rw-r--r--libs/gui/tests/Surface_test.cpp2
-rw-r--r--libs/hwui/Android.mk3
-rw-r--r--libs/hwui/Caches.cpp115
-rw-r--r--libs/hwui/Caches.h91
-rw-r--r--libs/hwui/Debug.h3
-rw-r--r--libs/hwui/DisplayListRenderer.cpp167
-rw-r--r--libs/hwui/DisplayListRenderer.h24
-rw-r--r--libs/hwui/Extensions.h6
-rw-r--r--libs/hwui/FontRenderer.cpp519
-rw-r--r--libs/hwui/FontRenderer.h234
-rw-r--r--libs/hwui/GammaFontRenderer.cpp7
-rw-r--r--libs/hwui/GammaFontRenderer.h2
-rw-r--r--libs/hwui/GradientCache.cpp4
-rw-r--r--libs/hwui/Layer.h8
-rw-r--r--libs/hwui/LayerCache.cpp7
-rw-r--r--libs/hwui/LayerRenderer.cpp46
-rw-r--r--libs/hwui/LayerRenderer.h1
-rw-r--r--libs/hwui/OpenGLRenderer.cpp463
-rw-r--r--libs/hwui/OpenGLRenderer.h22
-rw-r--r--libs/hwui/Patch.cpp4
-rw-r--r--libs/hwui/PathCache.cpp4
-rw-r--r--libs/hwui/Program.cpp110
-rw-r--r--libs/hwui/Program.h286
-rw-r--r--libs/hwui/ProgramCache.cpp2
-rw-r--r--libs/hwui/ProgramCache.h235
-rw-r--r--libs/hwui/Properties.h8
-rw-r--r--libs/hwui/Rect.h44
-rw-r--r--libs/hwui/ShapeCache.h7
-rw-r--r--libs/hwui/SkiaShader.cpp17
-rw-r--r--libs/hwui/Snapshot.cpp154
-rw-r--r--libs/hwui/Snapshot.h138
-rw-r--r--libs/hwui/TextDropShadowCache.cpp4
-rw-r--r--libs/hwui/Texture.h16
-rw-r--r--libs/hwui/TextureCache.cpp17
-rw-r--r--libs/rs/driver/rsdBcc.cpp34
-rw-r--r--libs/rs/driver/rsdCore.cpp175
-rw-r--r--libs/rs/driver/rsdCore.h9
-rw-r--r--libs/rs/driver/rsdGL.cpp4
-rw-r--r--libs/rs/driver/rsdGL.h4
-rw-r--r--libs/rs/driver/rsdRuntimeMath.cpp34
-rw-r--r--libs/rs/driver/rsdShader.cpp4
-rw-r--r--libs/rs/rs.spec4
-rw-r--r--libs/rs/rsAllocation.cpp151
-rw-r--r--libs/rs/rsAllocation.h17
-rw-r--r--libs/rs/rsComponent.cpp3
-rw-r--r--libs/rs/rsComponent.h2
-rw-r--r--libs/rs/rsContext.cpp24
-rw-r--r--libs/rs/rsContext.h3
-rw-r--r--libs/rs/rsElement.cpp17
-rw-r--r--libs/rs/rsElement.h12
-rw-r--r--libs/rs/rsFont.cpp16
-rw-r--r--libs/rs/rsFont.h3
-rw-r--r--libs/rs/rsProgramVertex.cpp5
-rw-r--r--libs/rs/rsScript.cpp20
-rw-r--r--libs/rs/rsScript.h3
-rw-r--r--libs/rs/rsScriptC.cpp14
-rw-r--r--libs/rs/rsScriptC_LibGL.cpp5
-rw-r--r--libs/rs/rsThreadIO.cpp9
-rw-r--r--libs/rs/scriptc/rs_atomic.rsh2
-rw-r--r--libs/rs/scriptc/rs_types.rsh2
-rw-r--r--libs/ui/Android.mk1
-rw-r--r--libs/ui/FramebufferNativeWindow.cpp15
-rw-r--r--libs/ui/GraphicLog.cpp92
-rw-r--r--libs/utils/Android.mk12
-rw-r--r--libs/utils/BasicHashtable.cpp338
-rw-r--r--libs/utils/CallStack.cpp302
-rw-r--r--libs/utils/ResourceTypes.cpp1145
-rw-r--r--libs/utils/Threads.cpp38
-rwxr-xr-xlibs/utils/primes.py47
-rw-r--r--libs/utils/tests/Android.mk3
-rw-r--r--libs/utils/tests/BasicHashtable_test.cpp577
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