am 89ee578b: am fd035829: Add initial gamepad support.
Merge commit '89ee578b7053d27a50922f82feb94bf6054b330c'
* commit '89ee578b7053d27a50922f82feb94bf6054b330c':
Add initial gamepad support.
diff --git a/include/binder/CursorWindow.h b/include/binder/CursorWindow.h
new file mode 100644
index 0000000..4fbff2a
--- /dev/null
+++ b/include/binder/CursorWindow.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID__DATABASE_WINDOW_H
+#define _ANDROID__DATABASE_WINDOW_H
+
+#include <cutils/log.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <binder/IMemory.h>
+#include <utils/RefBase.h>
+
+#define DEFAULT_WINDOW_SIZE 4096
+#define MAX_WINDOW_SIZE (1024 * 1024)
+#define WINDOW_ALLOCATION_SIZE 4096
+
+#define ROW_SLOT_CHUNK_NUM_ROWS 16
+
+// Row slots are allocated in chunks of ROW_SLOT_CHUNK_NUM_ROWS,
+// with an offset after the rows that points to the next chunk
+#define ROW_SLOT_CHUNK_SIZE ((ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t)) + sizeof(uint32_t))
+
+
+#if LOG_NDEBUG
+
+#define IF_LOG_WINDOW() if (false)
+#define LOG_WINDOW(...)
+
+#else
+
+#define IF_LOG_WINDOW() IF_LOG(LOG_DEBUG, "CursorWindow")
+#define LOG_WINDOW(...) LOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__)
+
+#endif
+
+
+// When defined to true strings are stored as UTF8, otherwise they're UTF16
+#define WINDOW_STORAGE_UTF8 1
+
+// When defined to true numberic values are stored inline in the field_slot_t, otherwise they're allocated in the window
+#define WINDOW_STORAGE_INLINE_NUMERICS 1
+
+namespace android {
+
+typedef struct
+{
+ uint32_t numRows;
+ uint32_t numColumns;
+} window_header_t;
+
+typedef struct
+{
+ uint32_t offset;
+} row_slot_t;
+
+typedef struct
+{
+ uint8_t type;
+ union {
+ double d;
+ int64_t l;
+ struct {
+ uint32_t offset;
+ uint32_t size;
+ } buffer;
+ } data;
+} __attribute__((packed)) field_slot_t;
+
+#define FIELD_TYPE_NULL 0
+#define FIELD_TYPE_INTEGER 1
+#define FIELD_TYPE_FLOAT 2
+#define FIELD_TYPE_STRING 3
+#define FIELD_TYPE_BLOB 4
+
+/**
+ * This class stores a set of rows from a database in a buffer. The begining of the
+ * window has first chunk of row_slot_ts, which are offsets to the row directory, followed by
+ * an offset to the next chunk in a linked-list of additional chunk of row_slot_ts in case
+ * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a
+ * field_slot_t per column, which has the size, offset, and type of the data for that field.
+ * Note that the data types come from sqlite3.h.
+ */
+class CursorWindow
+{
+public:
+ CursorWindow(size_t maxSize);
+ CursorWindow(){}
+ bool setMemory(const sp<IMemory>&);
+ ~CursorWindow();
+
+ bool initBuffer(bool localOnly);
+ sp<IMemory> getMemory() {return mMemory;}
+
+ size_t size() {return mSize;}
+ uint8_t * data() {return mData;}
+ uint32_t getNumRows() {return mHeader->numRows;}
+ uint32_t getNumColumns() {return mHeader->numColumns;}
+ void freeLastRow() {
+ if (mHeader->numRows > 0) {
+ mHeader->numRows--;
+ }
+ }
+ bool setNumColumns(uint32_t numColumns)
+ {
+ uint32_t cur = mHeader->numColumns;
+ if (cur > 0 && cur != numColumns) {
+ LOGE("Trying to go from %d columns to %d", cur, numColumns);
+ return false;
+ }
+ mHeader->numColumns = numColumns;
+ return true;
+ }
+
+ int32_t freeSpace();
+
+ void clear();
+
+ /**
+ * Allocate a row slot and its directory. The returned
+ * pointer points to the begining of the row's directory
+ * or NULL if there wasn't room. The directory is
+ * initialied with NULL entries for each field.
+ */
+ field_slot_t * allocRow();
+
+ /**
+ * Allocate a portion of the window. Returns the offset
+ * of the allocation, or 0 if there isn't enough space.
+ * If aligned is true, the allocation gets 4 byte alignment.
+ */
+ uint32_t alloc(size_t size, bool aligned = false);
+
+ uint32_t read_field_slot(int row, int column, field_slot_t * slot);
+
+ /**
+ * Copy data into the window at the given offset.
+ */
+ void copyIn(uint32_t offset, uint8_t const * data, size_t size);
+ void copyIn(uint32_t offset, int64_t data);
+ void copyIn(uint32_t offset, double data);
+
+ void copyOut(uint32_t offset, uint8_t * data, size_t size);
+ int64_t copyOutLong(uint32_t offset);
+ double copyOutDouble(uint32_t offset);
+
+ bool putLong(unsigned int row, unsigned int col, int64_t value);
+ bool putDouble(unsigned int row, unsigned int col, double value);
+ bool putNull(unsigned int row, unsigned int col);
+
+ bool getLong(unsigned int row, unsigned int col, int64_t * valueOut);
+ bool getDouble(unsigned int row, unsigned int col, double * valueOut);
+ bool getNull(unsigned int row, unsigned int col, bool * valueOut);
+
+ uint8_t * offsetToPtr(uint32_t offset) {return mData + offset;}
+
+ row_slot_t * allocRowSlot();
+
+ row_slot_t * getRowSlot(int row);
+
+ /**
+ * return NULL if Failed to find rowSlot or
+ * Invalid rowSlot
+ */
+ field_slot_t * getFieldSlotWithCheck(int row, int column);
+ field_slot_t * getFieldSlot(int row, int column)
+ {
+ int fieldDirOffset = getRowSlot(row)->offset;
+ return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column;
+ }
+
+private:
+ uint8_t * mData;
+ size_t mSize;
+ size_t mMaxSize;
+ window_header_t * mHeader;
+ sp<IMemory> mMemory;
+
+ /**
+ * Offset of the lowest unused data byte in the array.
+ */
+ uint32_t mFreeOffset;
+};
+
+}; // namespace android
+
+#endif
diff --git a/libs/audioflinger/AudioPolicyManagerBase.cpp b/libs/audioflinger/AudioPolicyManagerBase.cpp
index 381a958..549d661 100644
--- a/libs/audioflinger/AudioPolicyManagerBase.cpp
+++ b/libs/audioflinger/AudioPolicyManagerBase.cpp
@@ -902,7 +902,8 @@
#ifdef AUDIO_POLICY_TEST
Thread(false),
#endif //AUDIO_POLICY_TEST
- mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0), mLimitRingtoneVolume(false)
+ mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0), mMusicStopTime(0), mLimitRingtoneVolume(false),
+ mLastVoiceVolume(-1.0f)
{
mpClientInterface = clientInterface;
@@ -1713,29 +1714,38 @@
}
float volume = computeVolume(stream, index, output, device);
- // do not set volume if the float value did not change
- if (volume != mOutputs.valueFor(output)->mCurVolume[stream] || force) {
+ // We actually change the volume if:
+ // - the float value returned by computeVolume() changed
+ // - the force flag is set
+ if (volume != mOutputs.valueFor(output)->mCurVolume[stream] ||
+ force) {
mOutputs.valueFor(output)->mCurVolume[stream] = volume;
LOGV("setStreamVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs);
if (stream == AudioSystem::VOICE_CALL ||
stream == AudioSystem::DTMF ||
stream == AudioSystem::BLUETOOTH_SCO) {
- float voiceVolume = -1.0;
// offset value to reflect actual hardware volume that never reaches 0
// 1% corresponds roughly to first step in VOICE_CALL stream volume setting (see AudioService.java)
volume = 0.01 + 0.99 * volume;
- if (stream == AudioSystem::VOICE_CALL) {
- voiceVolume = (float)index/(float)mStreams[stream].mIndexMax;
- } else if (stream == AudioSystem::BLUETOOTH_SCO) {
- voiceVolume = 1.0;
- }
- if (voiceVolume >= 0 && output == mHardwareOutput) {
- mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
- }
}
mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);
}
+ if (stream == AudioSystem::VOICE_CALL ||
+ stream == AudioSystem::BLUETOOTH_SCO) {
+ float voiceVolume;
+ // Force voice volume to max for bluetooth SCO as volume is managed by the headset
+ if (stream == AudioSystem::VOICE_CALL) {
+ voiceVolume = (float)index/(float)mStreams[stream].mIndexMax;
+ } else {
+ voiceVolume = 1.0;
+ }
+ if (voiceVolume != mLastVoiceVolume && output == mHardwareOutput) {
+ mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
+ mLastVoiceVolume = voiceVolume;
+ }
+ }
+
return NO_ERROR;
}
diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk
index 13dc500..f9d9f25 100644
--- a/libs/binder/Android.mk
+++ b/libs/binder/Android.mk
@@ -16,6 +16,7 @@
sources := \
Binder.cpp \
BpBinder.cpp \
+ CursorWindow.cpp \
IInterface.cpp \
IMemory.cpp \
IPCThreadState.cpp \
diff --git a/libs/binder/CursorWindow.cpp b/libs/binder/CursorWindow.cpp
new file mode 100644
index 0000000..20b27c9
--- /dev/null
+++ b/libs/binder/CursorWindow.cpp
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2006-2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "CursorWindow"
+
+#include <utils/Log.h>
+#include <binder/CursorWindow.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+namespace android {
+
+CursorWindow::CursorWindow(size_t maxSize) :
+ mMaxSize(maxSize)
+{
+}
+
+bool CursorWindow::setMemory(const sp<IMemory>& memory)
+{
+ mMemory = memory;
+ mData = (uint8_t *) memory->pointer();
+ if (mData == NULL) {
+ return false;
+ }
+ mHeader = (window_header_t *) mData;
+
+ // Make the window read-only
+ ssize_t size = memory->size();
+ mSize = size;
+ mMaxSize = size;
+ mFreeOffset = size;
+LOG_WINDOW("Created CursorWindow from existing IMemory: mFreeOffset = %d, numRows = %d, numColumns = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mHeader->numRows, mHeader->numColumns, mSize, mMaxSize, mData);
+ return true;
+}
+
+bool CursorWindow::initBuffer(bool localOnly)
+{
+ //TODO Use a non-memory dealer mmap region for localOnly
+
+ sp<MemoryHeapBase> heap;
+ heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow");
+ if (heap != NULL) {
+ mMemory = new MemoryBase(heap, 0, mMaxSize);
+ if (mMemory != NULL) {
+ mData = (uint8_t *) mMemory->pointer();
+ if (mData) {
+ mHeader = (window_header_t *) mData;
+ mSize = mMaxSize;
+
+ // Put the window into a clean state
+ clear();
+ LOG_WINDOW("Created CursorWindow with new MemoryDealer: mFreeOffset = %d, mSize = %d, mMaxSize = %d, mData = %p", mFreeOffset, mSize, mMaxSize, mData);
+ return true;
+ }
+ }
+ LOGE("CursorWindow heap allocation failed");
+ return false;
+ } else {
+ LOGE("failed to create the CursorWindow heap");
+ return false;
+ }
+}
+
+CursorWindow::~CursorWindow()
+{
+ // Everything that matters is a smart pointer
+}
+
+void CursorWindow::clear()
+{
+ mHeader->numRows = 0;
+ mHeader->numColumns = 0;
+ mFreeOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE;
+ // Mark the first chunk's next 'pointer' as null
+ *((uint32_t *)(mData + mFreeOffset - sizeof(uint32_t))) = 0;
+}
+
+int32_t CursorWindow::freeSpace()
+{
+ int32_t freeSpace = mSize - mFreeOffset;
+ if (freeSpace < 0) {
+ freeSpace = 0;
+ }
+ return freeSpace;
+}
+
+field_slot_t * CursorWindow::allocRow()
+{
+ // Fill in the row slot
+ row_slot_t * rowSlot = allocRowSlot();
+ if (rowSlot == NULL) {
+ return NULL;
+ }
+
+ // Allocate the slots for the field directory
+ size_t fieldDirSize = mHeader->numColumns * sizeof(field_slot_t);
+ uint32_t fieldDirOffset = alloc(fieldDirSize);
+ if (!fieldDirOffset) {
+ mHeader->numRows--;
+ LOGE("The row failed, so back out the new row accounting from allocRowSlot %d", mHeader->numRows);
+ return NULL;
+ }
+ field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(fieldDirOffset);
+ memset(fieldDir, 0x0, fieldDirSize);
+
+LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", (mHeader->numRows - 1), ((uint8_t *)rowSlot) - mData, fieldDirSize, fieldDirOffset);
+ rowSlot->offset = fieldDirOffset;
+
+ return fieldDir;
+}
+
+uint32_t CursorWindow::alloc(size_t requestedSize, bool aligned)
+{
+ int32_t size;
+ uint32_t padding;
+ if (aligned) {
+ // 4 byte alignment
+ padding = 4 - (mFreeOffset & 0x3);
+ } else {
+ padding = 0;
+ }
+
+ size = requestedSize + padding;
+
+ if (size > freeSpace()) {
+ LOGE("need to grow: mSize = %d, size = %d, freeSpace() = %d, numRows = %d", mSize, size, freeSpace(), mHeader->numRows);
+ // Only grow the window if the first row doesn't fit
+ if (mHeader->numRows > 1) {
+LOGE("not growing since there are already %d row(s), max size %d", mHeader->numRows, mMaxSize);
+ return 0;
+ }
+
+ // Find a new size that will fit the allocation
+ int allocated = mSize - freeSpace();
+ int newSize = mSize + WINDOW_ALLOCATION_SIZE;
+ while (size > (newSize - allocated)) {
+ newSize += WINDOW_ALLOCATION_SIZE;
+ if (newSize > mMaxSize) {
+ LOGE("Attempting to grow window beyond max size (%d)", mMaxSize);
+ return 0;
+ }
+ }
+LOG_WINDOW("found size %d", newSize);
+ mSize = newSize;
+ }
+
+ uint32_t offset = mFreeOffset + padding;
+ mFreeOffset += size;
+ return offset;
+}
+
+row_slot_t * CursorWindow::getRowSlot(int row)
+{
+ LOG_WINDOW("enter getRowSlot current row num %d, this row %d", mHeader->numRows, row);
+ int chunkNum = row / ROW_SLOT_CHUNK_NUM_ROWS;
+ int chunkPos = row % ROW_SLOT_CHUNK_NUM_ROWS;
+ int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
+ uint8_t * rowChunk = mData + sizeof(window_header_t);
+ for (int i = 0; i < chunkNum; i++) {
+ rowChunk = offsetToPtr(*((uint32_t *)(mData + chunkPtrOffset)));
+ chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
+ }
+ return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
+ LOG_WINDOW("exit getRowSlot current row num %d, this row %d", mHeader->numRows, row);
+}
+
+row_slot_t * CursorWindow::allocRowSlot()
+{
+ int chunkNum = mHeader->numRows / ROW_SLOT_CHUNK_NUM_ROWS;
+ int chunkPos = mHeader->numRows % ROW_SLOT_CHUNK_NUM_ROWS;
+ int chunkPtrOffset = sizeof(window_header_t) + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t);
+ uint8_t * rowChunk = mData + sizeof(window_header_t);
+LOG_WINDOW("Allocating row slot, mHeader->numRows is %d, chunkNum is %d, chunkPos is %d", mHeader->numRows, chunkNum, chunkPos);
+ for (int i = 0; i < chunkNum; i++) {
+ uint32_t nextChunkOffset = *((uint32_t *)(mData + chunkPtrOffset));
+LOG_WINDOW("nextChunkOffset is %d", nextChunkOffset);
+ if (nextChunkOffset == 0) {
+ // Allocate a new row chunk
+ nextChunkOffset = alloc(ROW_SLOT_CHUNK_SIZE, true);
+ if (nextChunkOffset == 0) {
+ return NULL;
+ }
+ rowChunk = offsetToPtr(nextChunkOffset);
+LOG_WINDOW("allocated new chunk at %d, rowChunk = %p", nextChunkOffset, rowChunk);
+ *((uint32_t *)(mData + chunkPtrOffset)) = rowChunk - mData;
+ // Mark the new chunk's next 'pointer' as null
+ *((uint32_t *)(rowChunk + ROW_SLOT_CHUNK_SIZE - sizeof(uint32_t))) = 0;
+ } else {
+LOG_WINDOW("follwing 'pointer' to next chunk, offset of next pointer is %d", chunkPtrOffset);
+ rowChunk = offsetToPtr(nextChunkOffset);
+ chunkPtrOffset = rowChunk - mData + (ROW_SLOT_CHUNK_NUM_ROWS * sizeof(row_slot_t));
+ }
+ }
+ mHeader->numRows++;
+
+ return (row_slot_t *)(rowChunk + (chunkPos * sizeof(row_slot_t)));
+}
+
+field_slot_t * CursorWindow::getFieldSlotWithCheck(int row, int column)
+{
+ if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
+ LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
+ return NULL;
+ }
+ row_slot_t * rowSlot = getRowSlot(row);
+ if (!rowSlot) {
+ LOGE("Failed to find rowSlot for row %d", row);
+ return NULL;
+ }
+ if (rowSlot->offset == 0 || rowSlot->offset >= mSize) {
+ LOGE("Invalid rowSlot, offset = %d", rowSlot->offset);
+ return NULL;
+ }
+ int fieldDirOffset = rowSlot->offset;
+ return ((field_slot_t *)offsetToPtr(fieldDirOffset)) + column;
+}
+
+uint32_t CursorWindow::read_field_slot(int row, int column, field_slot_t * slotOut)
+{
+ if (row < 0 || row >= mHeader->numRows || column < 0 || column >= mHeader->numColumns) {
+ LOGE("Bad request for field slot %d,%d. numRows = %d, numColumns = %d", row, column, mHeader->numRows, mHeader->numColumns);
+ return -1;
+ }
+ row_slot_t * rowSlot = getRowSlot(row);
+ if (!rowSlot) {
+ LOGE("Failed to find rowSlot for row %d", row);
+ return -1;
+ }
+ if (rowSlot->offset == 0 || rowSlot->offset >= mSize) {
+ LOGE("Invalid rowSlot, offset = %d", rowSlot->offset);
+ return -1;
+ }
+LOG_WINDOW("Found field directory for %d,%d at rowSlot %d, offset %d", row, column, (uint8_t *)rowSlot - mData, rowSlot->offset);
+ field_slot_t * fieldDir = (field_slot_t *)offsetToPtr(rowSlot->offset);
+LOG_WINDOW("Read field_slot_t %d,%d: offset = %d, size = %d, type = %d", row, column, fieldDir[column].data.buffer.offset, fieldDir[column].data.buffer.size, fieldDir[column].type);
+
+ // Copy the data to the out param
+ slotOut->data.buffer.offset = fieldDir[column].data.buffer.offset;
+ slotOut->data.buffer.size = fieldDir[column].data.buffer.size;
+ slotOut->type = fieldDir[column].type;
+ return 0;
+}
+
+void CursorWindow::copyIn(uint32_t offset, uint8_t const * data, size_t size)
+{
+ assert(offset + size <= mSize);
+ memcpy(mData + offset, data, size);
+}
+
+void CursorWindow::copyIn(uint32_t offset, int64_t data)
+{
+ assert(offset + sizeof(int64_t) <= mSize);
+ memcpy(mData + offset, (uint8_t *)&data, sizeof(int64_t));
+}
+
+void CursorWindow::copyIn(uint32_t offset, double data)
+{
+ assert(offset + sizeof(double) <= mSize);
+ memcpy(mData + offset, (uint8_t *)&data, sizeof(double));
+}
+
+void CursorWindow::copyOut(uint32_t offset, uint8_t * data, size_t size)
+{
+ assert(offset + size <= mSize);
+ memcpy(data, mData + offset, size);
+}
+
+int64_t CursorWindow::copyOutLong(uint32_t offset)
+{
+ int64_t value;
+ assert(offset + sizeof(int64_t) <= mSize);
+ memcpy(&value, mData + offset, sizeof(int64_t));
+ return value;
+}
+
+double CursorWindow::copyOutDouble(uint32_t offset)
+{
+ double value;
+ assert(offset + sizeof(double) <= mSize);
+ memcpy(&value, mData + offset, sizeof(double));
+ return value;
+}
+
+bool CursorWindow::putLong(unsigned int row, unsigned int col, int64_t value)
+{
+ field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+ if (!fieldSlot) {
+ return false;
+ }
+
+#if WINDOW_STORAGE_INLINE_NUMERICS
+ fieldSlot->data.l = value;
+#else
+ int offset = alloc(sizeof(int64_t));
+ if (!offset) {
+ return false;
+ }
+
+ copyIn(offset, value);
+
+ fieldSlot->data.buffer.offset = offset;
+ fieldSlot->data.buffer.size = sizeof(int64_t);
+#endif
+ fieldSlot->type = FIELD_TYPE_INTEGER;
+ return true;
+}
+
+bool CursorWindow::putDouble(unsigned int row, unsigned int col, double value)
+{
+ field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+ if (!fieldSlot) {
+ return false;
+ }
+
+#if WINDOW_STORAGE_INLINE_NUMERICS
+ fieldSlot->data.d = value;
+#else
+ int offset = alloc(sizeof(int64_t));
+ if (!offset) {
+ return false;
+ }
+
+ copyIn(offset, value);
+
+ fieldSlot->data.buffer.offset = offset;
+ fieldSlot->data.buffer.size = sizeof(double);
+#endif
+ fieldSlot->type = FIELD_TYPE_FLOAT;
+ return true;
+}
+
+bool CursorWindow::putNull(unsigned int row, unsigned int col)
+{
+ field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+ if (!fieldSlot) {
+ return false;
+ }
+
+ fieldSlot->type = FIELD_TYPE_NULL;
+ fieldSlot->data.buffer.offset = 0;
+ fieldSlot->data.buffer.size = 0;
+ return true;
+}
+
+bool CursorWindow::getLong(unsigned int row, unsigned int col, int64_t * valueOut)
+{
+ field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+ if (!fieldSlot || fieldSlot->type != FIELD_TYPE_INTEGER) {
+ return false;
+ }
+
+#if WINDOW_STORAGE_INLINE_NUMERICS
+ *valueOut = fieldSlot->data.l;
+#else
+ *valueOut = copyOutLong(fieldSlot->data.buffer.offset);
+#endif
+ return true;
+}
+
+bool CursorWindow::getDouble(unsigned int row, unsigned int col, double * valueOut)
+{
+ field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+ if (!fieldSlot || fieldSlot->type != FIELD_TYPE_FLOAT) {
+ return false;
+ }
+
+#if WINDOW_STORAGE_INLINE_NUMERICS
+ *valueOut = fieldSlot->data.d;
+#else
+ *valueOut = copyOutDouble(fieldSlot->data.buffer.offset);
+#endif
+ return true;
+}
+
+bool CursorWindow::getNull(unsigned int row, unsigned int col, bool * valueOut)
+{
+ field_slot_t * fieldSlot = getFieldSlotWithCheck(row, col);
+ if (!fieldSlot) {
+ return false;
+ }
+
+ if (fieldSlot->type != FIELD_TYPE_NULL) {
+ *valueOut = false;
+ } else {
+ *valueOut = true;
+ }
+ return true;
+}
+
+}; // namespace android
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index a1401ad..4362d14 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -317,6 +317,12 @@
mStringPoolSize =
(mHeader->header.size-mHeader->stringsStart)/charSize;
} else {
+ // check invariant: styles starts before end of data
+ if (mHeader->stylesStart >= (mHeader->header.size-sizeof(uint16_t))) {
+ LOGW("Bad style block: style block starts at %d past data size of %d\n",
+ (int)mHeader->stylesStart, (int)mHeader->header.size);
+ return (mError=BAD_TYPE);
+ }
// check invariant: styles follow the strings
if (mHeader->stylesStart <= mHeader->stringsStart) {
LOGW("Bad style block: style block starts at %d, before strings at %d\n",
@@ -1878,6 +1884,12 @@
outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen);
outName->name = grp->basePackage->keyStrings.stringAt(
dtohl(entry->key.index), &outName->nameLen);
+
+ // If we have a bad index for some reason, we should abort.
+ if (outName->type == NULL || outName->name == NULL) {
+ return false;
+ }
+
return true;
}
@@ -2609,6 +2621,24 @@
*outType = *defType;
}
*outName = String16(p, end-p);
+ if(**outPackage == 0) {
+ if(outErrorMsg) {
+ *outErrorMsg = "Resource package cannot be an empty string";
+ }
+ return false;
+ }
+ if(**outType == 0) {
+ if(outErrorMsg) {
+ *outErrorMsg = "Resource type cannot be an empty string";
+ }
+ return false;
+ }
+ if(**outName == 0) {
+ if(outErrorMsg) {
+ *outErrorMsg = "Resource id cannot be an empty string";
+ }
+ return false;
+ }
return true;
}
@@ -4127,13 +4157,16 @@
| (0x00ff0000 & ((typeIndex+1)<<16))
| (0x0000ffff & (entryIndex));
resource_name resName;
- this->getResourceName(resID, &resName);
- printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
- resID,
- CHAR16_TO_CSTR(resName.package, resName.packageLen),
- CHAR16_TO_CSTR(resName.type, resName.typeLen),
- CHAR16_TO_CSTR(resName.name, resName.nameLen),
- dtohl(typeConfigs->typeSpecFlags[entryIndex]));
+ if (this->getResourceName(resID, &resName)) {
+ printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
+ resID,
+ CHAR16_TO_CSTR(resName.package, resName.packageLen),
+ CHAR16_TO_CSTR(resName.type, resName.typeLen),
+ CHAR16_TO_CSTR(resName.name, resName.nameLen),
+ dtohl(typeConfigs->typeSpecFlags[entryIndex]));
+ } else {
+ printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
+ }
}
}
for (size_t configIndex=0; configIndex<NTC; configIndex++) {
@@ -4340,11 +4373,14 @@
| (0x00ff0000 & ((typeIndex+1)<<16))
| (0x0000ffff & (entryIndex));
resource_name resName;
- this->getResourceName(resID, &resName);
- printf(" resource 0x%08x %s:%s/%s: ", resID,
- CHAR16_TO_CSTR(resName.package, resName.packageLen),
- CHAR16_TO_CSTR(resName.type, resName.typeLen),
- CHAR16_TO_CSTR(resName.name, resName.nameLen));
+ if (this->getResourceName(resID, &resName)) {
+ printf(" resource 0x%08x %s:%s/%s: ", resID,
+ CHAR16_TO_CSTR(resName.package, resName.packageLen),
+ CHAR16_TO_CSTR(resName.type, resName.typeLen),
+ CHAR16_TO_CSTR(resName.name, resName.nameLen));
+ } else {
+ printf(" INVALID RESOURCE 0x%08x: ", resID);
+ }
if ((thisOffset&0x3) != 0) {
printf("NON-INTEGER OFFSET: %p\n", (void*)thisOffset);
continue;
@@ -4402,18 +4438,19 @@
print_value(pkg, value);
} else if (bagPtr != NULL) {
const int N = dtohl(bagPtr->count);
- const ResTable_map* mapPtr = (const ResTable_map*)
- (((const uint8_t*)ent) + esize);
+ const uint8_t* baseMapPtr = (const uint8_t*)ent;
+ size_t mapOffset = esize;
+ const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
printf(" Parent=0x%08x, Count=%d\n",
dtohl(bagPtr->parent.ident), N);
- for (int i=0; i<N; i++) {
+ for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) {
printf(" #%i (Key=0x%08x): ",
i, dtohl(mapPtr->name.ident));
value.copyFrom_dtoh(mapPtr->value);
print_value(pkg, value);
const size_t size = dtohs(mapPtr->value.size);
- mapPtr = (ResTable_map*)(((const uint8_t*)mapPtr)
- + size + sizeof(*mapPtr)-sizeof(mapPtr->value));
+ mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value);
+ mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
}
}
}
diff --git a/opengl/tests/gl_perf/Android.mk b/opengl/tests/gl_perf/Android.mk
new file mode 100644
index 0000000..37647ca
--- /dev/null
+++ b/opengl/tests/gl_perf/Android.mk
@@ -0,0 +1,20 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ gl2_perf.cpp \
+ filltest.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libEGL \
+ libGLESv2 \
+ libui
+
+LOCAL_MODULE:= test-opengl-gl2_perf
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES
+
+include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/gl_perf/filltest.cpp b/opengl/tests/gl_perf/filltest.cpp
new file mode 100644
index 0000000..eb398ec
--- /dev/null
+++ b/opengl/tests/gl_perf/filltest.cpp
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <string.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <utils/Timers.h>
+#include <EGL/egl.h>
+
+
+using namespace android;
+
+static void checkGlError(const char* op) {
+ for (GLint error = glGetError(); error; error
+ = glGetError()) {
+ fprintf(stderr, "after %s() glError (0x%x)\n", op, error);
+ }
+}
+
+GLuint loadShader(GLenum shaderType, const char* pSource) {
+ GLuint shader = glCreateShader(shaderType);
+ if (shader) {
+ glShaderSource(shader, 1, &pSource, NULL);
+ glCompileShader(shader);
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ GLint infoLen = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+ if (infoLen) {
+ char* buf = (char*) malloc(infoLen);
+ if (buf) {
+ glGetShaderInfoLog(shader, infoLen, NULL, buf);
+ fprintf(stderr, "Could not compile shader %d:\n%s\n",
+ shaderType, buf);
+ free(buf);
+ }
+ glDeleteShader(shader);
+ shader = 0;
+ }
+ }
+ }
+ return shader;
+}
+
+enum {
+ A_POS,
+ A_COLOR,
+ A_TEX0,
+ A_TEX1
+};
+
+GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
+ GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
+ if (!vertexShader) {
+ return 0;
+ }
+
+ GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
+ if (!pixelShader) {
+ return 0;
+ }
+
+ GLuint program = glCreateProgram();
+ if (program) {
+ glAttachShader(program, vertexShader);
+ checkGlError("glAttachShader v");
+ glAttachShader(program, pixelShader);
+ checkGlError("glAttachShader p");
+
+ glBindAttribLocation(program, A_POS, "a_pos");
+ glBindAttribLocation(program, A_COLOR, "a_color");
+ glBindAttribLocation(program, A_TEX0, "a_tex0");
+ glBindAttribLocation(program, A_TEX1, "a_tex1");
+ glLinkProgram(program);
+ GLint linkStatus = GL_FALSE;
+ glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+ if (linkStatus != GL_TRUE) {
+ GLint bufLength = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+ if (bufLength) {
+ char* buf = (char*) malloc(bufLength);
+ if (buf) {
+ glGetProgramInfoLog(program, bufLength, NULL, buf);
+ printf("Could not link program:\n%s\n", buf);
+ free(buf);
+ }
+ }
+ glDeleteProgram(program);
+ program = 0;
+ }
+ }
+ checkGlError("createProgram");
+ glUseProgram(program);
+ return program;
+}
+
+uint64_t getTime() {
+ struct timespec t;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return t.tv_nsec + ((uint64_t)t.tv_sec * 1000 * 1000 * 1000);
+}
+
+uint64_t gTime;
+void startTimer() {
+ gTime = getTime();
+}
+
+void endTimer(const char *str, int w, int h, double dc, int count) {
+ uint64_t t2 = getTime();
+ double delta = ((double)(t2 - gTime)) / 1000000000;
+ double pixels = dc * (w * h) * count;
+ double mpps = pixels / delta / 1000000;
+ double dc60 = pixels / delta / (w * h) / 60;
+
+ printf("%s, %f, %f\n", str, mpps, dc60);
+}
+
+static const char gVertexShader[] =
+ "attribute vec4 a_pos;\n"
+ "attribute vec4 a_color;\n"
+ "attribute vec2 a_tex0;\n"
+ "attribute vec2 a_tex1;\n"
+ "varying vec4 v_color;\n"
+ "varying vec2 v_tex0;\n"
+ "varying vec2 v_tex1;\n"
+
+ "void main() {\n"
+ " v_color = a_color;\n"
+ " v_tex0 = a_tex0;\n"
+ " v_tex1 = a_tex1;\n"
+ " gl_Position = a_pos;\n"
+ "}\n";
+
+static const char gShaderPrefix[] =
+ "precision mediump float;\n"
+ "uniform vec4 u_color;\n"
+ "uniform vec4 u_0;\n"
+ "uniform vec4 u_1;\n"
+ "uniform vec4 u_2;\n"
+ "uniform vec4 u_3;\n"
+ "varying vec4 v_color;\n"
+ "varying vec2 v_tex0;\n"
+ "varying vec2 v_tex1;\n"
+ "uniform sampler2D u_tex0;\n"
+ "uniform sampler2D u_tex1;\n"
+ "void main() {\n";
+
+static const char gShaderPostfix[] =
+ " gl_FragColor = c;\n"
+ "}\n";
+
+
+static char * append(char *d, const char *s) {
+ size_t len = strlen(s);
+ memcpy(d, s, len);
+ return d + len;
+}
+
+static char * genShader(
+ bool useVarColor,
+ int texCount,
+ bool modulateFirstTex,
+ int extraMath)
+{
+ char *str = (char *)calloc(16 * 1024, 1);
+ char *tmp = append(str, gShaderPrefix);
+
+ if (modulateFirstTex || !texCount) {
+ if (useVarColor) {
+ tmp = append(tmp, " vec4 c = v_color;\n");
+ } else {
+ tmp = append(tmp, " vec4 c = u_color;\n");
+ }
+ } else {
+ tmp = append(tmp, " vec4 c = texture2D(u_tex0, v_tex0);\n");
+ }
+
+ if (modulateFirstTex && texCount) {
+ tmp = append(tmp, " c *= texture2D(u_tex0, v_tex0);\n");
+ }
+ if (texCount > 1) {
+ tmp = append(tmp, " c *= texture2D(u_tex1, v_tex1);\n");
+ }
+
+ if (extraMath > 0) {
+ tmp = append(tmp, " c *= u_0;\n");
+ }
+ if (extraMath > 1) {
+ tmp = append(tmp, " c *= u_1;\n");
+ }
+ if (extraMath > 2) {
+ tmp = append(tmp, " c *= u_2;\n");
+ }
+ if (extraMath > 3) {
+ tmp = append(tmp, " c *= u_3;\n");
+ }
+
+
+ tmp = append(tmp, gShaderPostfix);
+ tmp[0] = 0;
+
+ //printf("%s", str);
+ return str;
+}
+
+static void setupVA() {
+ static const float vtx[] = {
+ -2.0f,-1.0f,
+ 1.0f,-1.0f,
+ -2.0f, 1.0f,
+ 1.0f, 1.0f };
+ static const float color[] = {
+ 1.0f,0.0f,1.0f,1.0f,
+ 0.0f,0.0f,1.0f,1.0f,
+ 1.0f,1.0f,0.0f,1.0f,
+ 1.0f,1.0f,1.0f,1.0f };
+ static const float tex0[] = {
+ 0.0f,0.0f,
+ 1.0f,0.0f,
+ 1.0f,1.0f,
+ 0.0f,1.0f };
+ static const float tex1[] = {
+ 1.0f,0.0f,
+ 1.0f,1.0f,
+ 0.0f,1.0f,
+ 0.0f,0.0f };
+
+ glEnableVertexAttribArray(A_POS);
+ glEnableVertexAttribArray(A_COLOR);
+ glEnableVertexAttribArray(A_TEX0);
+ glEnableVertexAttribArray(A_TEX1);
+
+ glVertexAttribPointer(A_POS, 2, GL_FLOAT, false, 8, vtx);
+ glVertexAttribPointer(A_COLOR, 4, GL_FLOAT, false, 16, color);
+ glVertexAttribPointer(A_TEX0, 2, GL_FLOAT, false, 8, tex0);
+ glVertexAttribPointer(A_TEX1, 2, GL_FLOAT, false, 8, tex1);
+}
+
+//////////////////////////
+
+void ptSwap();
+
+static void doLoop(uint32_t w, uint32_t h, const char *str) {
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ ptSwap();
+ glFinish();
+
+ startTimer();
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ for (int ct=0; ct < 100; ct++) {
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ }
+ ptSwap();
+ glFinish();
+ endTimer(str, w, h, 1, 100);
+}
+
+static void doSingleTest(uint32_t w, uint32_t h,
+ bool useVarColor,
+ int texCount,
+ bool modulateFirstTex,
+ int extraMath,
+ int tex0, int tex1) {
+ char *pgmTxt = genShader(useVarColor, texCount, modulateFirstTex, extraMath);
+ int pgm = createProgram(gVertexShader, pgmTxt);
+ if (!pgm) {
+ printf("error running test\n");
+ return;
+ }
+ int loc = glGetUniformLocation(pgm, "u_tex0");
+ //printf("loc = %i \n", loc);
+ if (loc >= 0) glUniform1i(loc, 0);
+ loc = glGetUniformLocation(pgm, "u_tex1");
+ if (loc >= 0) glUniform1i(loc, 1);
+
+ loc = glGetUniformLocation(pgm, "u_color");
+ if (loc >= 0) glUniform4f(loc, 1.f, 0.4f, 0.6f, 0.8f);
+
+ loc = glGetUniformLocation(pgm, "u_0");
+ if (loc >= 0) glUniform4f(loc, 1.f, 0.4f, 0.6f, 0.8f);
+
+ loc = glGetUniformLocation(pgm, "u_1");
+ if (loc >= 0) glUniform4f(loc, 0.7f, 0.8f, 0.6f, 0.8f);
+
+ loc = glGetUniformLocation(pgm, "u_2");
+ if (loc >= 0) glUniform4f(loc, 0.9f, 0.6f, 0.7f, 1.0f);
+
+ loc = glGetUniformLocation(pgm, "u_3");
+ if (loc >= 0) glUniform4f(loc, 0.88f, 0.2f, 0.4f, 0.2f);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, tex0);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, tex1);
+ glActiveTexture(GL_TEXTURE0);
+
+ char str2[1024];
+
+ glBlendFunc(GL_ONE, GL_ONE);
+ glDisable(GL_BLEND);
+ sprintf(str2, "%i, %i, %i, %i, %i, 0",
+ useVarColor, texCount, modulateFirstTex, extraMath, tex0);
+ doLoop(w, h, str2);
+
+ glEnable(GL_BLEND);
+ sprintf(str2, "%i, %i, %i, %i, %i, 1",
+ useVarColor, texCount, modulateFirstTex, extraMath, tex0);
+ doLoop(w, h, str2);
+}
+
+void genTextures() {
+ uint32_t *m = (uint32_t *)malloc(1024*1024*4);
+ for (int y=0; y < 1024; y++){
+ for (int x=0; x < 1024; x++){
+ m[y*1024 + x] = 0xff0000ff | ((x & 0xff) << 8) | (y << 16);
+ }
+ }
+ glBindTexture(GL_TEXTURE_2D, 1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, m);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ for (int y=0; y < 16; y++){
+ for (int x=0; x < 16; x++){
+ m[y*16 + x] = 0xff0000ff | (x<<12) | (y<<20);
+ }
+ }
+ glBindTexture(GL_TEXTURE_2D, 2);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, m);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+}
+
+bool doTest(uint32_t w, uint32_t h) {
+ setupVA();
+ genTextures();
+
+ printf("\nvarColor, texCount, modulate, extraMath, texSize, blend, Mpps, DC60\n");
+
+ for (int texCount = 0; texCount < 3; texCount++) {
+ for (int extraMath = 0; extraMath < 5; extraMath++) {
+
+ doSingleTest(w, h, false, texCount, false, extraMath, 1, 1);
+ doSingleTest(w, h, true, texCount, false, extraMath, 1, 1);
+ if (texCount) {
+ doSingleTest(w, h, false, texCount, true, extraMath, 1, 1);
+ doSingleTest(w, h, true, texCount, true, extraMath, 1, 1);
+
+ doSingleTest(w, h, false, texCount, false, extraMath, 2, 2);
+ doSingleTest(w, h, true, texCount, false, extraMath, 2, 2);
+ doSingleTest(w, h, false, texCount, true, extraMath, 2, 2);
+ doSingleTest(w, h, true, texCount, true, extraMath, 2, 2);
+ }
+ }
+ }
+
+ exit(0);
+ return true;
+}
diff --git a/opengl/tests/gl_perf/gl2_perf.cpp b/opengl/tests/gl_perf/gl2_perf.cpp
new file mode 100644
index 0000000..9dfcf1c
--- /dev/null
+++ b/opengl/tests/gl_perf/gl2_perf.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/resource.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <utils/Timers.h>
+
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
+
+using namespace android;
+
+
+static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) {
+ if (returnVal != EGL_TRUE) {
+ fprintf(stderr, "%s() returned %d\n", op, returnVal);
+ }
+
+ for (EGLint error = eglGetError(); error != EGL_SUCCESS; error
+ = eglGetError()) {
+ fprintf(stderr, "after %s() eglError %s (0x%x)\n", op, EGLUtils::strerror(error),
+ error);
+ }
+}
+
+static void checkGlError(const char* op) {
+ for (GLint error = glGetError(); error; error
+ = glGetError()) {
+ fprintf(stderr, "after %s() glError (0x%x)\n", op, error);
+ }
+}
+
+bool doTest(uint32_t w, uint32_t h);
+
+static EGLDisplay dpy;
+static EGLSurface surface;
+
+int main(int argc, char** argv) {
+ EGLBoolean returnValue;
+ EGLConfig myConfig = {0};
+
+ EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
+ EGLint s_configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_NONE };
+ EGLint majorVersion;
+ EGLint minorVersion;
+ EGLContext context;
+ EGLint w, h;
+
+
+ checkEglError("<init>");
+ dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ checkEglError("eglGetDisplay");
+ if (dpy == EGL_NO_DISPLAY) {
+ printf("eglGetDisplay returned EGL_NO_DISPLAY.\n");
+ return 0;
+ }
+
+ returnValue = eglInitialize(dpy, &majorVersion, &minorVersion);
+ checkEglError("eglInitialize", returnValue);
+ if (returnValue != EGL_TRUE) {
+ printf("eglInitialize failed\n");
+ return 0;
+ }
+
+ EGLNativeWindowType window = android_createDisplaySurface();
+ returnValue = EGLUtils::selectConfigForNativeWindow(dpy, s_configAttribs, window, &myConfig);
+ if (returnValue) {
+ printf("EGLUtils::selectConfigForNativeWindow() returned %d", returnValue);
+ return 0;
+ }
+
+ checkEglError("EGLUtils::selectConfigForNativeWindow");
+
+ surface = eglCreateWindowSurface(dpy, myConfig, window, NULL);
+ checkEglError("eglCreateWindowSurface");
+ if (surface == EGL_NO_SURFACE) {
+ printf("gelCreateWindowSurface failed.\n");
+ return 0;
+ }
+
+ context = eglCreateContext(dpy, myConfig, EGL_NO_CONTEXT, context_attribs);
+ checkEglError("eglCreateContext");
+ if (context == EGL_NO_CONTEXT) {
+ printf("eglCreateContext failed\n");
+ return 0;
+ }
+ returnValue = eglMakeCurrent(dpy, surface, surface, context);
+ checkEglError("eglMakeCurrent", returnValue);
+ if (returnValue != EGL_TRUE) {
+ return 0;
+ }
+ eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
+ checkEglError("eglQuerySurface");
+ eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
+ checkEglError("eglQuerySurface");
+ GLint dim = w < h ? w : h;
+
+ glViewport(0, 0, w, h);
+
+ for (;;) {
+ doTest(w, h);
+ eglSwapBuffers(dpy, surface);
+ checkEglError("eglSwapBuffers");
+ }
+
+ return 0;
+}
+
+void ptSwap() {
+ eglSwapBuffers(dpy, surface);
+}
+
diff --git a/opengl/tests/testViewport/Android.mk b/opengl/tests/testViewport/Android.mk
new file mode 100644
index 0000000..ab37809
--- /dev/null
+++ b/opengl/tests/testViewport/Android.mk
@@ -0,0 +1,26 @@
+#########################################################################
+# OpenGL ES JNI sample
+# This makefile builds both an activity and a shared library.
+#########################################################################
+ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := TestViewport
+
+# Set a specific SDK version so we can run on Froyo.
+
+LOCAL_SDK_VERSION := 8
+
+include $(BUILD_PACKAGE)
+
+endif # TARGET_SIMULATOR
diff --git a/opengl/tests/testViewport/AndroidManifest.xml b/opengl/tests/testViewport/AndroidManifest.xml
new file mode 100644
index 0000000..90a9d2d
--- /dev/null
+++ b/opengl/tests/testViewport/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, 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.
+*/
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test">
+ <uses-sdk android:targetSdkVersion="8" android:minSdkVersion="8" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <application
+ android:label="@string/test_activity">
+ <activity android:name="TestActivity"
+ android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+ android:configChanges="orientation|keyboardHidden">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/opengl/tests/testViewport/README b/opengl/tests/testViewport/README
new file mode 100644
index 0000000..c06abc9
--- /dev/null
+++ b/opengl/tests/testViewport/README
@@ -0,0 +1,28 @@
+Repro steps:
+
+build, install and run the attached test program TestViewport.apk
+
+Run on Sapphire with Froyo.
+
+The program clears the screen to blue, then draws a full screen white quad that
+is alligned to the screen.
+(Therefore the whole screen should appear to be white.)
+
+
+Note that screen is all white.
+
+Rotate screen 90 degrees.
+
+Expected: screen is still all white.
+
+Actual: screen is blue with offset white rectangle.
+
+This bug only happens on Sapphire, it works correctly on Passion.
+
+What happens:
+
+I think the bug is that the gl.glViewport() call in onSurfaceChanged() is
+being ignored by the OpenGL driver.
+
+NOTE: If a gl.glViewport call is added at the beginning of the onDrawFrame()
+call (which means it is called before every draw), the program runs correctly.
diff --git a/opengl/tests/testViewport/res/values/strings.xml b/opengl/tests/testViewport/res/values/strings.xml
new file mode 100644
index 0000000..f4b8bbb
--- /dev/null
+++ b/opengl/tests/testViewport/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+ them to be changed based on the locale and options. -->
+
+<resources>
+ <!-- Simple strings. -->
+ <string name="test_activity">Test Viewport</string>
+
+</resources>
+
diff --git a/opengl/tests/testViewport/src/com/android/test/TestActivity.java b/opengl/tests/testViewport/src/com/android/test/TestActivity.java
new file mode 100644
index 0000000..cc7e450
--- /dev/null
+++ b/opengl/tests/testViewport/src/com/android/test/TestActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestActivity extends Activity {
+ private final static String TAG = "TestActivity";
+ TestView mView;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ mView = new TestView(getApplication());
+ mView.setFocusableInTouchMode(true);
+ setContentView(mView);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mView.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mView.onResume();
+ }
+}
diff --git a/opengl/tests/testViewport/src/com/android/test/TestView.java b/opengl/tests/testViewport/src/com/android/test/TestView.java
new file mode 100644
index 0000000..23cc37d
--- /dev/null
+++ b/opengl/tests/testViewport/src/com/android/test/TestView.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package com.android.test;
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.CharBuffer;
+import java.nio.FloatBuffer;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.opengles.GL11;
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying an OpenGL animation. This allows the animation to run in a
+ * separate thread, without requiring that it be driven by the update mechanism
+ * of the view hierarchy.
+ *
+ * The application-specific rendering code is delegated to a GLView.Renderer
+ * instance.
+ */
+class TestView extends GLSurfaceView {
+ TestView(Context context) {
+ super(context);
+ init();
+ }
+
+ public TestView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ private void init() {
+ setRenderer(new Renderer());
+ setRenderMode(RENDERMODE_WHEN_DIRTY);
+ }
+
+ /** A grid is a topologically rectangular array of vertices.
+ *
+ * The vertex and index data are held in VBO objects because on most
+ * GPUs VBO objects are the fastest way of rendering static vertex
+ * and index data.
+ *
+ */
+
+ private static class Grid {
+ // Size of vertex data elements in bytes:
+ final static int FLOAT_SIZE = 4;
+ final static int CHAR_SIZE = 2;
+
+ // Vertex structure:
+ // float x, y, z;
+
+ final static int VERTEX_SIZE = 3 * FLOAT_SIZE;
+
+ private int mVertexBufferObjectId;
+ private int mElementBufferObjectId;
+
+ // These buffers are used to hold the vertex and index data while
+ // constructing the grid. Once createBufferObjects() is called
+ // the buffers are nulled out to save memory.
+
+ private ByteBuffer mVertexByteBuffer;
+ private FloatBuffer mVertexBuffer;
+ private CharBuffer mIndexBuffer;
+
+ private int mW;
+ private int mH;
+ private int mIndexCount;
+
+ public Grid(int w, int h) {
+ if (w < 0 || w >= 65536) {
+ throw new IllegalArgumentException("w");
+ }
+ if (h < 0 || h >= 65536) {
+ throw new IllegalArgumentException("h");
+ }
+ if (w * h >= 65536) {
+ throw new IllegalArgumentException("w * h >= 65536");
+ }
+
+ mW = w;
+ mH = h;
+ int size = w * h;
+
+ mVertexByteBuffer = ByteBuffer.allocateDirect(VERTEX_SIZE * size)
+ .order(ByteOrder.nativeOrder());
+ mVertexBuffer = mVertexByteBuffer.asFloatBuffer();
+
+ int quadW = mW - 1;
+ int quadH = mH - 1;
+ int quadCount = quadW * quadH;
+ int indexCount = quadCount * 6;
+ mIndexCount = indexCount;
+ mIndexBuffer = ByteBuffer.allocateDirect(CHAR_SIZE * indexCount)
+ .order(ByteOrder.nativeOrder()).asCharBuffer();
+
+ /*
+ * Initialize triangle list mesh.
+ *
+ * [0]-----[ 1] ...
+ * | / |
+ * | / |
+ * | / |
+ * [w]-----[w+1] ...
+ * | |
+ *
+ */
+
+ {
+ int i = 0;
+ for (int y = 0; y < quadH; y++) {
+ for (int x = 0; x < quadW; x++) {
+ char a = (char) (y * mW + x);
+ char b = (char) (y * mW + x + 1);
+ char c = (char) ((y + 1) * mW + x);
+ char d = (char) ((y + 1) * mW + x + 1);
+
+ mIndexBuffer.put(i++, a);
+ mIndexBuffer.put(i++, c);
+ mIndexBuffer.put(i++, b);
+
+ mIndexBuffer.put(i++, b);
+ mIndexBuffer.put(i++, c);
+ mIndexBuffer.put(i++, d);
+ }
+ }
+ }
+
+ }
+
+ public void set(int i, int j, float x, float y, float z) {
+ if (i < 0 || i >= mW) {
+ throw new IllegalArgumentException("i");
+ }
+ if (j < 0 || j >= mH) {
+ throw new IllegalArgumentException("j");
+ }
+
+ int index = mW * j + i;
+
+ mVertexBuffer.position(index * VERTEX_SIZE / FLOAT_SIZE);
+ mVertexBuffer.put(x);
+ mVertexBuffer.put(y);
+ mVertexBuffer.put(z);
+ }
+
+ public void createBufferObjects(GL gl) {
+ // Generate a the vertex and element buffer IDs
+ int[] vboIds = new int[2];
+ GL11 gl11 = (GL11) gl;
+ gl11.glGenBuffers(2, vboIds, 0);
+ mVertexBufferObjectId = vboIds[0];
+ mElementBufferObjectId = vboIds[1];
+
+ // Upload the vertex data
+ gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertexBufferObjectId);
+ mVertexByteBuffer.position(0);
+ gl11.glBufferData(GL11.GL_ARRAY_BUFFER, mVertexByteBuffer.capacity(), mVertexByteBuffer, GL11.GL_STATIC_DRAW);
+
+ gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mElementBufferObjectId);
+ mIndexBuffer.position(0);
+ gl11.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer.capacity() * CHAR_SIZE, mIndexBuffer, GL11.GL_STATIC_DRAW);
+
+ // We don't need the in-memory data any more
+ mVertexBuffer = null;
+ mVertexByteBuffer = null;
+ mIndexBuffer = null;
+ }
+
+ public void draw(GL10 gl) {
+ GL11 gl11 = (GL11) gl;
+
+ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+
+ gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertexBufferObjectId);
+ gl11.glVertexPointer(3, GL10.GL_FLOAT, VERTEX_SIZE, 0);
+
+ gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mElementBufferObjectId);
+ gl11.glDrawElements(GL10.GL_TRIANGLES, mIndexCount, GL10.GL_UNSIGNED_SHORT, 0);
+ gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
+ gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
+ gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+ }
+
+
+ private class Renderer implements GLSurfaceView.Renderer {
+ private static final String TAG = "Renderer";
+ private Grid mGrid;
+
+ public void onDrawFrame(GL10 gl) {
+ gl.glClearColor(0,0,1,1);
+ gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
+ mGrid.draw(gl);
+ }
+
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ gl.glViewport(0, 0, width, height);
+ gl.glMatrixMode(GL11.GL_PROJECTION);
+ gl.glLoadIdentity();
+ gl.glOrthof(0, width, height, 0, -1, 1);
+ gl.glMatrixMode(GL11.GL_MODELVIEW);
+ createGrid(gl, width, height);
+ }
+
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ }
+
+ private void createGrid(GL10 gl, float w, float h) {
+ mGrid = new Grid(2, 2);
+ for (int j = 0; j < 2; j++) {
+ for (int i = 0; i < 2; i++) {
+ float x = w * i;
+ float y = h * j;
+ float z = 0.0f;
+ mGrid.set(i,j, x, y, z);
+ }
+ }
+ mGrid.createBufferObjects(gl);
+ }
+ }
+}
+